diff --git a/luci-app-netdata/Makefile b/luci-app-netdata/Makefile deleted file mode 100644 index cecc201..0000000 --- a/luci-app-netdata/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2016 Openwrt.org -# Copyright (C) 2020-2021 sirpdboy -# https://github.com/sirpdboy/luci-app-netdata for v 1.33.1 cn -# This is free software, licensed under the Apache License, Version 2.0 . -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI support for Netdata -LUCI_DEPENDS:=+netdata -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-netdata -PKG_VERSION:=1.0 -PKG_RELEASE:=20220523 - -define Build/Compile -endef - - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature - diff --git a/luci-app-netdata/luasrc/controller/netdata.lua b/luci-app-netdata/luasrc/controller/netdata.lua deleted file mode 100644 index 4b861f8..0000000 --- a/luci-app-netdata/luasrc/controller/netdata.lua +++ /dev/null @@ -1,16 +0,0 @@ -module("luci.controller.netdata", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/netdata") then - return - end - - - local page - entry({"admin", "status", "netdata"}, alias("admin", "status", "netdata", "netdata"),_("NetData"), 10).dependent = true - - entry({"admin", "status", "netdata", "netdata"}, template("netdata/netdata"), _("NetData"), 10).leaf = true - entry({"admin", "status", "netdata", "setting"}, cbi("netdata/netdata"), _("Setting"), 20).leaf=true - -end - diff --git a/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua b/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua deleted file mode 100644 index 9f63c8e..0000000 --- a/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua +++ /dev/null @@ -1,24 +0,0 @@ --- Copyright 2018 sirpdboy (herboy2008@gmail.com) -require("luci.util") - -local m, s ,o - -local m = Map("netdata", translate("NetData")) - -s = m:section(TypedSection, "netdata", translate("Global Settings")) -s.addremove=false -s.anonymous=true - -o=s:option(Flag,"enabled",translate("Enable")) -o.default=0 - -o=s:option(Value, "port",translate("Set the netdata access port")) -o.datatype="uinteger" -o.default=19999 - -local e=luci.http.formvalue("cbi.apply") -if e then - io.popen("/etc/init.d/netdata start") -end - -return m diff --git a/luci-app-netdata/luasrc/view/netdata.htm b/luci-app-netdata/luasrc/view/netdata.htm deleted file mode 100644 index 6344af2..0000000 --- a/luci-app-netdata/luasrc/view/netdata.htm +++ /dev/null @@ -1,20 +0,0 @@ -<%# - Copyright 2008-2020 sirpdboy Wich - https://github.com/sirpdboy/luci-app-netdata - Licensed to the public under the Apache License 2.0. --%> - -<%+header%> -
- - - -
- -<%+footer%> diff --git a/luci-app-netdata/po/zh-cn/netdata.po b/luci-app-netdata/po/zh-cn/netdata.po deleted file mode 100644 index 0879589..0000000 --- a/luci-app-netdata/po/zh-cn/netdata.po +++ /dev/null @@ -1,9 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" - -msgid "NetData" -msgstr "实时监控" - -msgid "Set the netdata access port" -msgstr "设置访问端口" - diff --git a/luci-app-netdata/readme.txt b/luci-app-netdata/readme.txt deleted file mode 100644 index c030d94..0000000 --- a/luci-app-netdata/readme.txt +++ /dev/null @@ -1,21 +0,0 @@ -luci-app-netdata for OpenWRT/Lede - - -Install to OpenWRT/LEDE - -git clone https://github.com/sirpdboy/luci-app-netdata -cp -r luci-app-netdata LEDE_DIR/package/luci-app-netdata - -cd LEDE_DIR -./scripts/feeds update -a -./scripts/feeds install -a - -make menuconfig -LuCI ---> - 1. Collections ---> - <*> luci - 3. Applications ---> - <*> luci-app-netdata.........................LuCI support for Netdata - - -make package/luci-app-netdata/compile V=s \ No newline at end of file diff --git a/luci-app-netdata/root/etc/config/netdata b/luci-app-netdata/root/etc/config/netdata deleted file mode 100644 index 6d866b0..0000000 --- a/luci-app-netdata/root/etc/config/netdata +++ /dev/null @@ -1,6 +0,0 @@ - -config netdata 'netdata' - option logger '1' - option enabled '1' - option port '19990' - diff --git a/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata b/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata deleted file mode 100644 index 5210210..0000000 --- a/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -[ -f /usr/share/netdata/webcn/netdata.conf ] && mv -f /usr/share/netdata/webcn/netdata.conf /etc/netdata/netdata.conf -[ -f /usr/share/netdata/webcn/dashboard.js ] && mv -f /usr/share/netdata/webcn/dashboard.js /usr/share/netdata/web/dashboard.js -[ -f /usr/share/netdata/webcn/dashboard_info.js ] && mv -f /usr/share/netdata/webcn/dashboard_info.js /usr/share/netdata/web/dashboard_info.js -[ -f /usr/share/netdata/webcn/main.js ] && mv -f /usr/share/netdata/webcn/main.js /usr/share/netdata/web/main.js -[ -f /usr/share/netdata/webcn/index.html ] && mv -f /usr/share/netdata/webcn/index.html /usr/share/netdata/web/index.html -[ -f /usr/share/netdata/webcn/netdata ] && mv -f /usr/share/netdata/webcn/index.html /etc/init.d/netdata -rm -rf /tmp/luci-modulecache /tmp/luci-indexcache* -exit 0 diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js b/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js deleted file mode 100644 index 1065e77..0000000 --- a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js +++ /dev/null @@ -1,10377 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// DO NOT EDIT: This file is automatically generated from the source files in src/ - -// ---------------------------------------------------------------------------- -// You can set the following variables before loading this script: - -// 'use strict'; - -/*global netdataNoDygraphs *//* boolean, disable dygraph charts - * (default: false) */ -/*global netdataNoSparklines *//* boolean, disable sparkline charts - * (default: false) */ -/*global netdataNoPeitys *//* boolean, disable peity charts - * (default: false) */ -/*global netdataNoGoogleCharts *//* boolean, disable google charts - * (default: false) */ -/*global netdataNoMorris *//* boolean, disable morris charts - * (default: false) */ -/*global netdataNoEasyPieChart *//* boolean, disable easypiechart charts - * (default: false) */ -/*global netdataNoGauge *//* boolean, disable gauge.js charts - * (default: false) */ -/*global netdataNoD3 *//* boolean, disable d3 charts - * (default: false) */ -/*global netdataNoC3 *//* boolean, disable c3 charts - * (default: false) */ -/*global netdataNoD3pie *//* boolean, disable d3pie charts - * (default: false) */ -/*global netdataNoBootstrap *//* boolean, disable bootstrap - disables help too - * (default: false) */ -/*global netdataNoFontAwesome *//* boolean, disable fontawesome (do not load it) - * (default: false) */ -/*global netdataIcons *//* object, overwrite netdata fontawesome icons - * (default: null) */ -/*global netdataDontStart *//* boolean, do not start the thread to process the charts - * (default: false) */ -/*global netdataErrorCallback *//* function, callback to be called when the dashboard encounters an error - * (default: null) */ -/*global netdataRegistry:true *//* boolean, use the netdata registry - * (default: false) */ -/*global netdataNoRegistry *//* boolean, included only for compatibility with existing custom dashboard - * (obsolete - do not use this any more) */ -/*global netdataRegistryCallback *//* function, callback that will be invoked with one param: the URLs from the registry - * (default: null) */ -/*global netdataShowHelp:true *//* boolean, disable charts help - * (default: true) */ -/*global netdataShowAlarms:true *//* boolean, enable alarms checks and notifications - * (default: false) */ -/*global netdataRegistryAfterMs:true *//* ms, delay registry use at started - * (default: 1500) */ -/*global netdataCallback *//* function, callback to be called when netdata is ready to start - * (default: null) - * netdata will be running while this is called - * (call NETDATA.pause to stop it) */ -/*global netdataPrepCallback *//* function, callback to be called before netdata does anything else - * (default: null) */ -/*global netdataServer *//* string, the URL of the netdata server to use - * (default: the URL the page is hosted at) */ -/*global netdataServerStatic *//* string, the URL of the netdata server to use for static files - * (default: netdataServer) */ -/*global netdataSnapshotData *//* object, a netdata snapshot loaded - * (default: null) */ -/*global netdataAlarmsRecipients *//* array, an array of alarm recipients to show notifications for - * (default: null) */ -/*global netdataAlarmsRemember *//* boolean, keep our position in the alarm log at browser local storage - * (default: true) */ -/*global netdataAlarmsActiveCallback *//* function, a hook for the alarm logs - * (default: undefined) */ -/*global netdataAlarmsNotifCallback *//* function, a hook for alarm notifications - * (default: undefined) */ -/*global netdataIntersectionObserver *//* boolean, enable or disable the use of intersection observer - * (default: true) */ -/*global netdataCheckXSS *//* boolean, enable or disable checking for XSS issues - * (default: false) */ - -// ---------------------------------------------------------------------------- -// global namespace - -// Should stay var! -var NETDATA = window.NETDATA || {}; - -(function(window, document, $, undefined) { - -// *** src/dashboard.js/utils.js - -NETDATA.name2id = function (s) { - return s - .replace(/ /g, '_') - .replace(/:/g, '_') - .replace(/\(/g, '_') - .replace(/\)/g, '_') - .replace(/\./g, '_') - .replace(/\//g, '_'); -}; - -NETDATA.encodeURIComponent = function (s) { - if (typeof(s) === 'string') { - return encodeURIComponent(s); - } - - return s; -}; - -/// A heuristic for detecting slow devices. -let isSlowDeviceResult = undefined; -const isSlowDevice = function () { - if (!isSlowDeviceResult) { - return isSlowDeviceResult; - } - - try { - let ua = navigator.userAgent.toLowerCase(); - - let iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream; - let android = /android/.test(ua) && !window.MSStream; - isSlowDeviceResult = (iOS || android); - } catch (e) { - isSlowDeviceResult = false; - } - - return isSlowDeviceResult; -}; - -NETDATA.guid = function () { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -}; - -NETDATA.zeropad = function (x) { - if (x > -10 && x < 10) { - return '0' + x.toString(); - } else { - return x.toString(); - } -}; - -NETDATA.seconds4human = function (seconds, options) { - let defaultOptions = { - now: 'now', - space: ' ', - negative_suffix: 'ago', - day: 'day', - days: 'days', - hour: 'hour', - hours: 'hours', - minute: 'min', - minutes: 'mins', - second: 'sec', - seconds: 'secs', - and: 'and' - }; - - if (typeof options !== 'object') { - options = defaultOptions; - } else { - for (var x in defaultOptions) { - if (typeof options[x] !== 'string') { - options[x] = defaultOptions[x]; - } - } - } - - if (typeof seconds === 'string') { - seconds = parseInt(seconds, 10); - } - - if (seconds === 0) { - return options.now; - } - - let suffix = ''; - if (seconds < 0) { - seconds = -seconds; - if (options.negative_suffix !== '') { - suffix = options.space + options.negative_suffix; - } - } - - let days = Math.floor(seconds / 86400); - seconds -= (days * 86400); - - let hours = Math.floor(seconds / 3600); - seconds -= (hours * 3600); - - let minutes = Math.floor(seconds / 60); - seconds -= (minutes * 60); - - let strings = []; - - if (days > 1) { - strings.push(days.toString() + options.space + options.days); - } else if (days === 1) { - strings.push(days.toString() + options.space + options.day); - } - - if (hours > 1) { - strings.push(hours.toString() + options.space + options.hours); - } else if (hours === 1) { - strings.push(hours.toString() + options.space + options.hour); - } - - if (minutes > 1) { - strings.push(minutes.toString() + options.space + options.minutes); - } else if (minutes === 1) { - strings.push(minutes.toString() + options.space + options.minute); - } - - if (seconds > 1) { - strings.push(Math.floor(seconds).toString() + options.space + options.seconds); - } else if (seconds === 1) { - strings.push(Math.floor(seconds).toString() + options.space + options.second); - } - - if (strings.length === 1) { - return strings.pop() + suffix; - } - - let last = strings.pop(); - return strings.join(", ") + " " + options.and + " " + last + suffix; -}; - -// ---------------------------------------------------------------------------------------------------------------- -// element data attributes - -NETDATA.dataAttribute = function (element, attribute, def) { - let key = 'data-' + attribute.toString(); - if (element.hasAttribute(key)) { - let data = element.getAttribute(key); - - if (data === 'true') { - return true; - } - if (data === 'false') { - return false; - } - if (data === 'null') { - return null; - } - - // Only convert to a number if it doesn't change the string - if (data === +data + '') { - return +data; - } - - if (/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/.test(data)) { - return JSON.parse(data); - } - - return data; - } else { - return def; - } -}; - -NETDATA.dataAttributeBoolean = function (element, attribute, def) { - let value = NETDATA.dataAttribute(element, attribute, def); - - if (value === true || value === false) // gmosx: Love this :) - { - return value; - } - - if (typeof(value) === 'string') { - if (value === 'yes' || value === 'on') { - return true; - } - - if (value === '' || value === 'no' || value === 'off' || value === 'null') { - return false; - } - - return def; - } - - if (typeof(value) === 'number') { - return value !== 0; - } - - return def; -}; - -// ---------------------------------------------------------------------------------------------------------------- -// fast numbers formatting - -NETDATA.fastNumberFormat = { - formattersFixed: [], - formattersZeroBased: [], - - // this is the fastest and the preferred - getIntlNumberFormat: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - - return this.formattersZeroBased[key]; - } else { - // this is never used - // it is added just for completeness - return new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }, - - // this respects locale - getLocaleString: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - - return this.formattersZeroBased[key]; - } else { - return { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - }, - - // the fallback - getFixed: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - - return this.formattersZeroBased[key]; - } else { - return { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - }, - - testIntlNumberFormat: function () { - let value = 1.12345; - let e1 = "1.12", e2 = "1,12"; - let s = ""; - - try { - let x = new Intl.NumberFormat(undefined, { - useGrouping: true, - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }); - - s = x.format(value); - } catch (e) { - s = ""; - } - - // console.log('NumberFormat: ', s); - return (s === e1 || s === e2); - }, - - testLocaleString: function () { - let value = 1.12345; - let e1 = "1.12", e2 = "1,12"; - let s = ""; - - try { - s = value.toLocaleString(undefined, { - useGrouping: true, - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }); - } catch (e) { - s = ""; - } - - // console.log('localeString: ', s); - return (s === e1 || s === e2); - }, - - // on first run we decide which formatter to use - get: function (min, max) { - if (this.testIntlNumberFormat()) { - // console.log('numberformat'); - this.get = this.getIntlNumberFormat; - } else if (this.testLocaleString()) { - // console.log('localestring'); - this.get = this.getLocaleString; - } else { - // console.log('fixed'); - this.get = this.getFixed; - } - return this.get(min, max); - } -}; - -// ---------------------------------------------------------------------------------------------------------------- -// Detect the netdata server - -// http://stackoverflow.com/questions/984510/what-is-my-script-src-url -// http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url -NETDATA._scriptSource = function () { - let script = null; - - if (typeof document.currentScript !== 'undefined') { - script = document.currentScript; - } else { - const all_scripts = document.getElementsByTagName('script'); - script = all_scripts[all_scripts.length - 1]; - } - - if (typeof script.getAttribute.length !== 'undefined') { - script = script.src; - } else { - script = script.getAttribute('src', -1); - } - - return script; -}; - -// *** src/dashboard.js/server-detection.js - -if (typeof netdataServer !== 'undefined') { - NETDATA.serverDefault = netdataServer; -} else { - let s = NETDATA._scriptSource(); - if (s) { - NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)?$/g, ""); - } else { - console.log('WARNING: Cannot detect the URL of the netdata server.'); - NETDATA.serverDefault = null; - } -} - -if (NETDATA.serverDefault === null) { - NETDATA.serverDefault = ''; -} else if (NETDATA.serverDefault.slice(-1) !== '/') { - NETDATA.serverDefault += '/'; -} - -if (typeof netdataServerStatic !== 'undefined' && netdataServerStatic !== null && netdataServerStatic !== '') { - NETDATA.serverStatic = netdataServerStatic; - if (NETDATA.serverStatic.slice(-1) !== '/') { - NETDATA.serverStatic += '/'; - } -} else { - NETDATA.serverStatic = NETDATA.serverDefault; -} - -// *** src/dashboard.js/dependencies.js - -// default URLs for all the external files we need -// make them RELATIVE so that the whole thing can also be -// installed under a web server -NETDATA.jQuery = NETDATA.serverStatic + 'lib/jquery-3.6.0.min.js'; -NETDATA.peity_js = NETDATA.serverStatic + 'lib/jquery.peity-3.2.0.min.js'; -NETDATA.sparkline_js = NETDATA.serverStatic + 'lib/jquery.sparkline-2.1.2.min.js'; -NETDATA.easypiechart_js = NETDATA.serverStatic + 'lib/jquery.easypiechart-97b5824.min.js'; -NETDATA.gauge_js = NETDATA.serverStatic + 'lib/gauge-1.3.2.min.js'; -NETDATA.dygraph_js = NETDATA.serverStatic + 'lib/dygraph-c91c859.min.js'; -NETDATA.dygraph_smooth_js = NETDATA.serverStatic + 'lib/dygraph-smooth-plotter-c91c859.js'; -// NETDATA.raphael_js = NETDATA.serverStatic + 'lib/raphael-2.2.4-min.js'; -// NETDATA.c3_js = NETDATA.serverStatic + 'lib/c3-0.4.18.min.js'; -// NETDATA.c3_css = NETDATA.serverStatic + 'css/c3-0.4.18.min.css'; -NETDATA.d3pie_js = NETDATA.serverStatic + 'lib/d3pie-0.2.1-netdata-3.js'; -NETDATA.d3_js = NETDATA.serverStatic + 'lib/d3-4.12.2.min.js'; -// NETDATA.morris_js = NETDATA.serverStatic + 'lib/morris-0.5.1.min.js'; -// NETDATA.morris_css = NETDATA.serverStatic + 'css/morris-0.5.1.css'; -NETDATA.google_js = 'https://www.google.com/jsapi'; -// Error Handling - -NETDATA.errorCodes = { - 100: {message: "Cannot load chart library", alert: true}, - 101: {message: "Cannot load jQuery", alert: true}, - 402: {message: "Chart library not found", alert: false}, - 403: {message: "Chart library not enabled/is failed", alert: false}, - 404: {message: "Chart not found", alert: false}, - 405: {message: "Cannot download charts index from server", alert: true}, - 406: {message: "Invalid charts index downloaded from server", alert: true}, - 407: {message: "Cannot HELLO netdata server", alert: false}, - 408: {message: "Netdata servers sent invalid response to HELLO", alert: false}, - 409: {message: "Cannot ACCESS netdata registry", alert: false}, - 410: {message: "Netdata registry ACCESS failed", alert: false}, - 411: {message: "Netdata registry server send invalid response to DELETE ", alert: false}, - 412: {message: "Netdata registry DELETE failed", alert: false}, - 413: {message: "Netdata registry server send invalid response to SWITCH ", alert: false}, - 414: {message: "Netdata registry SWITCH failed", alert: false}, - 415: {message: "Netdata alarms download failed", alert: false}, - 416: {message: "Netdata alarms log download failed", alert: false}, - 417: {message: "Netdata registry server send invalid response to SEARCH ", alert: false}, - 418: {message: "Netdata registry SEARCH failed", alert: false} -}; - -NETDATA.errorLast = { - code: 0, - message: "", - datetime: 0 -}; - -NETDATA.error = function (code, msg) { - NETDATA.errorLast.code = code; - NETDATA.errorLast.message = msg; - NETDATA.errorLast.datetime = Date.now(); - - console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); - - let ret = true; - if (typeof netdataErrorCallback === 'function') { - ret = netdataErrorCallback('system', code, msg); - } - - if (ret && NETDATA.errorCodes[code].alert) { - alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); - } -}; - -NETDATA.errorReset = function () { - NETDATA.errorLast.code = 0; - NETDATA.errorLast.message = "You are doing fine!"; - NETDATA.errorLast.datetime = 0; -}; -// *** src/dashboard.js/compatibility.js - -// Compatibility fixes. - -// fix IE issue with console -if (!window.console) { - window.console = { - log: function () { - } - }; -} - -// if string.endsWith is not defined, define it -if (typeof String.prototype.endsWith !== 'function') { - String.prototype.endsWith = function (s) { - if (s.length > this.length) { - return false; - } - return this.slice(-s.length) === s; - }; -} - -// if string.startsWith is not defined, define it -if (typeof String.prototype.startsWith !== 'function') { - String.prototype.startsWith = function (s) { - if (s.length > this.length) { - return false; - } - return this.slice(s.length) === s; - }; -} -// ---------------------------------------------------------------------------------------------------------------- -// XSS checks - -NETDATA.xss = { - enabled: (typeof netdataCheckXSS === 'undefined') ? false : netdataCheckXSS, - enabled_for_data: (typeof netdataCheckXSS === 'undefined') ? false : netdataCheckXSS, - - string: function (s) { - return s.toString() - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - }, - - object: function (name, obj, ignore_regex) { - if (typeof ignore_regex !== 'undefined' && ignore_regex.test(name)) { - // console.log('XSS: ignoring "' + name + '"'); - return obj; - } - - switch (typeof(obj)) { - case 'string': - const ret = this.string(obj); - if (ret !== obj) { - console.log('XSS protection changed string ' + name + ' from "' + obj + '" to "' + ret + '"'); - } - return ret; - - case 'object': - if (obj === null) { - return obj; - } - - if (Array.isArray(obj)) { - // console.log('checking array "' + name + '"'); - - let len = obj.length; - while (len--) { - obj[len] = this.object(name + '[' + len + ']', obj[len], ignore_regex); - } - } else { - // console.log('checking object "' + name + '"'); - - for (var i in obj) { - if (obj.hasOwnProperty(i) === false) { - continue; - } - if (this.string(i) !== i) { - console.log('XSS protection removed invalid object member "' + name + '.' + i + '"'); - delete obj[i]; - } else { - obj[i] = this.object(name + '.' + i, obj[i], ignore_regex); - } - } - } - return obj; - - default: - return obj; - } - }, - - checkOptional: function (name, obj, ignore_regex) { - if (this.enabled) { - //console.log('XSS: checking optional "' + name + '"...'); - return this.object(name, obj, ignore_regex); - } - return obj; - }, - - checkAlways: function (name, obj, ignore_regex) { - //console.log('XSS: checking always "' + name + '"...'); - return this.object(name, obj, ignore_regex); - }, - - checkData: function (name, obj, ignore_regex) { - if (this.enabled_for_data) { - //console.log('XSS: checking data "' + name + '"...'); - return this.object(name, obj, ignore_regex); - } - return obj; - } -}; -NETDATA.colorHex2Rgb = function (hex) { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function (m, r, g, b) { - return r + r + g + g + b + b; - }); - - let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; -}; - -NETDATA.colorLuminance = function (hex, lum) { - // validate hex string - hex = String(hex).replace(/[^0-9a-f]/gi, ''); - if (hex.length < 6) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - - lum = lum || 0; - - // convert to decimal and change luminosity - let rgb = "#"; - for (let i = 0; i < 3; i++) { - let c = parseInt(hex.substr(i * 2, 2), 16); - c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); - rgb += ("00" + c).substr(c.length); - } - - return rgb; -}; -NETDATA.unitsConversion = { - keys: {}, // keys for data-common-units - latest: {}, // latest selected units for data-common-units - - globalReset: function () { - this.keys = {}; - this.latest = {}; - }, - - scalableUnits: { - 'packets/s': { - 'pps': 1, - 'Kpps': 1000, - 'Mpps': 1000000 - }, - 'pps': { - 'pps': 1, - 'Kpps': 1000, - 'Mpps': 1000000 - }, - 'kilobits/s': { - 'bits/s': 1 / 1000, - 'kilobits/s': 1, - 'megabits/s': 1000, - 'gigabits/s': 1000000, - 'terabits/s': 1000000000 - }, - 'bytes/s': { - 'bytes/s': 1, - 'kilobytes/s': 1024, - 'megabytes/s': 1024 * 1024, - 'gigabytes/s': 1024 * 1024 * 1024, - 'terabytes/s': 1024 * 1024 * 1024 * 1024 - }, - 'kilobytes/s': { - 'bytes/s': 1 / 1024, - 'kilobytes/s': 1, - 'megabytes/s': 1024, - 'gigabytes/s': 1024 * 1024, - 'terabytes/s': 1024 * 1024 * 1024 - }, - 'B/s': { - 'B/s': 1, - 'KiB/s': 1024, - 'MiB/s': 1024 * 1024, - 'GiB/s': 1024 * 1024 * 1024, - 'TiB/s': 1024 * 1024 * 1024 * 1024 - }, - 'KB/s': { - 'B/s': 1 / 1024, - 'KB/s': 1, - 'MB/s': 1024, - 'GB/s': 1024 * 1024, - 'TB/s': 1024 * 1024 * 1024 - }, - 'KiB/s': { - 'B/s': 1 / 1024, - 'KiB/s': 1, - 'MiB/s': 1024, - 'GiB/s': 1024 * 1024, - 'TiB/s': 1024 * 1024 * 1024 - }, - 'B': { - 'B': 1, - 'KiB': 1024, - 'MiB': 1024 * 1024, - 'GiB': 1024 * 1024 * 1024, - 'TiB': 1024 * 1024 * 1024 * 1024, - 'PiB': 1024 * 1024 * 1024 * 1024 * 1024 - }, - 'KB': { - 'B': 1 / 1024, - 'KB': 1, - 'MB': 1024, - 'GB': 1024 * 1024, - 'TB': 1024 * 1024 * 1024 - }, - 'KiB': { - 'B': 1 / 1024, - 'KiB': 1, - 'MiB': 1024, - 'GiB': 1024 * 1024, - 'TiB': 1024 * 1024 * 1024 - }, - 'MB': { - 'B': 1 / (1024 * 1024), - 'KB': 1 / 1024, - 'MB': 1, - 'GB': 1024, - 'TB': 1024 * 1024, - 'PB': 1024 * 1024 * 1024 - }, - 'MiB': { - 'B': 1 / (1024 * 1024), - 'KiB': 1 / 1024, - 'MiB': 1, - 'GiB': 1024, - 'TiB': 1024 * 1024, - 'PiB': 1024 * 1024 * 1024 - }, - 'GB': { - 'B': 1 / (1024 * 1024 * 1024), - 'KB': 1 / (1024 * 1024), - 'MB': 1 / 1024, - 'GB': 1, - 'TB': 1024, - 'PB': 1024 * 1024, - 'EB': 1024 * 1024 * 1024 - }, - 'GiB': { - 'B': 1 / (1024 * 1024 * 1024), - 'KiB': 1 / (1024 * 1024), - 'MiB': 1 / 1024, - 'GiB': 1, - 'TiB': 1024, - 'PiB': 1024 * 1024, - 'EiB': 1024 * 1024 * 1024 - }, - 'num': { - 'num': 1, - 'num (K)': 1000, - 'num (M)': 1000000, - 'num (G)': 1000000000, - 'num (T)': 1000000000000 - } - /* - 'milliseconds': { - 'seconds': 1000 - }, - 'seconds': { - 'milliseconds': 0.001, - 'seconds': 1, - 'minutes': 60, - 'hours': 3600, - 'days': 86400 - } - */ - }, - - convertibleUnits: { - 'Celsius': { - 'Fahrenheit': { - check: function (max) { - void(max); - return NETDATA.options.current.temperature === 'fahrenheit'; - }, - convert: function (value) { - return value * 9 / 5 + 32; - } - } - }, - 'celsius': { - 'fahrenheit': { - check: function (max) { - void(max); - return NETDATA.options.current.temperature === 'fahrenheit'; - }, - convert: function (value) { - return value * 9 / 5 + 32; - } - } - }, - 'seconds': { - 'time': { - check: function (max) { - void(max); - return NETDATA.options.current.seconds_as_time; - }, - convert: function (seconds) { - return NETDATA.unitsConversion.seconds2time(seconds); - } - } - }, - 'milliseconds': { - 'milliseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max < 1000; - }, - convert: function (milliseconds) { - let tms = Math.round(milliseconds * 10); - milliseconds = Math.floor(tms / 10); - - tms -= milliseconds * 10; - - return (milliseconds).toString() + '.' + tms.toString(); - } - }, - 'seconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max >= 1000 && max < 60000; - }, - convert: function (milliseconds) { - milliseconds = Math.round(milliseconds); - - let seconds = Math.floor(milliseconds / 1000); - milliseconds -= seconds * 1000; - - milliseconds = Math.round(milliseconds / 10); - - return seconds.toString() + '.' - + NETDATA.zeropad(milliseconds); - } - }, - 'M:SS.ms': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max >= 60000; - }, - convert: function (milliseconds) { - milliseconds = Math.round(milliseconds); - - let minutes = Math.floor(milliseconds / 60000); - milliseconds -= minutes * 60000; - - let seconds = Math.floor(milliseconds / 1000); - milliseconds -= seconds * 1000; - - milliseconds = Math.round(milliseconds / 10); - - return minutes.toString() + ':' - + NETDATA.zeropad(seconds) + '.' - + NETDATA.zeropad(milliseconds); - } - } - }, - 'nanoseconds': { - 'nanoseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max < 1000; - }, - convert: function (nanoseconds) { - let tms = Math.round(nanoseconds * 10); - nanoseconds = Math.floor(tms / 10); - - tms -= nanoseconds * 10; - - return (nanoseconds).toString() + '.' + tms.toString(); - } - }, - 'microseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 && max < 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let microseconds = Math.floor(nanoseconds / 1000); - nanoseconds -= microseconds * 1000; - - nanoseconds = Math.round(nanoseconds / 10 ); - - return microseconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - 'milliseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 * 1000 && max < 1000 * 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let milliseconds = Math.floor(nanoseconds / 1000 / 1000); - nanoseconds -= milliseconds * 1000 * 1000; - - nanoseconds = Math.round(nanoseconds / 1000 / 10); - - return milliseconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - 'seconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 * 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let seconds = Math.floor(nanoseconds / 1000 / 1000 / 1000); - nanoseconds -= seconds * 1000 * 1000 * 1000; - - nanoseconds = Math.round(nanoseconds / 1000 / 1000 / 10); - - return seconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - } - }, - - seconds2time: function (seconds) { - seconds = Math.abs(seconds); - - let days = Math.floor(seconds / 86400); - seconds -= days * 86400; - - let hours = Math.floor(seconds / 3600); - seconds -= hours * 3600; - - let minutes = Math.floor(seconds / 60); - seconds -= minutes * 60; - - seconds = Math.round(seconds); - - let ms_txt = ''; - /* - let ms = seconds - Math.floor(seconds); - seconds -= ms; - ms = Math.round(ms * 1000); - - if (ms > 1) { - if (ms < 10) - ms_txt = '.00' + ms.toString(); - else if (ms < 100) - ms_txt = '.0' + ms.toString(); - else - ms_txt = '.' + ms.toString(); - } - */ - - return ((days > 0) ? days.toString() + 'd:' : '').toString() - + NETDATA.zeropad(hours) + ':' - + NETDATA.zeropad(minutes) + ':' - + NETDATA.zeropad(seconds) - + ms_txt; - }, - - // get a function that converts the units - // + every time units are switched call the callback - get: function (uuid, min, max, units, desired_units, common_units_name, switch_units_callback) { - // validate the parameters - if (typeof units === 'undefined') { - units = 'undefined'; - } - - // check if we support units conversion - if (typeof this.scalableUnits[units] === 'undefined' && typeof this.convertibleUnits[units] === 'undefined') { - // we can't convert these units - //console.log('DEBUG: ' + uuid.toString() + ' can\'t convert units: ' + units.toString()); - return function (value) { - return value; - }; - } - - // check if the caller wants the original units - if (typeof desired_units === 'undefined' || desired_units === null || desired_units === 'original' || desired_units === units) { - //console.log('DEBUG: ' + uuid.toString() + ' original units wanted'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - - // now we know we can convert the units - // and the caller wants some kind of conversion - - let tunits = null; - let tdivider = 0; - - if (typeof this.scalableUnits[units] !== 'undefined') { - // units that can be scaled - // we decide a divider - - // console.log('NETDATA.unitsConversion.get(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString()); - - if (desired_units === 'auto') { - // the caller wants to auto-scale the units - - // find the absolute maximum value that is rendered on the chart - // based on this we decide the scale - min = Math.abs(min); - max = Math.abs(max); - if (min > max) { - max = min; - } - - // find the smallest scale that provides integers - // for (x in this.scalableUnits[units]) { - // if (this.scalableUnits[units].hasOwnProperty(x)) { - // let m = this.scalableUnits[units][x]; - // if (m <= max && m > tdivider) { - // tunits = x; - // tdivider = m; - // } - // } - // } - const sunit = this.scalableUnits[units]; - for (var x of Object.keys(sunit)) { - let m = sunit[x]; - if (m <= max && m > tdivider) { - tunits = x; - tdivider = m; - } - } - - if (tunits === null || tdivider <= 0) { - // we couldn't find one - //console.log('DEBUG: ' + uuid.toString() + ' cannot find an auto-scaling candidate for units: ' + units.toString() + ' (max: ' + max.toString() + ')'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - - if (typeof common_units_name === 'string' && typeof uuid === 'string') { - // the caller wants several charts to have the same units - // data-common-units - - let common_units_key = common_units_name + '-' + units; - - // add our divider into the list of keys - let t = this.keys[common_units_key]; - if (typeof t === 'undefined') { - this.keys[common_units_key] = {}; - t = this.keys[common_units_key]; - } - t[uuid] = { - units: tunits, - divider: tdivider - }; - - // find the max divider of all charts - let common_units = t[uuid]; - for (var x in t) { - if (t.hasOwnProperty(x) && t[x].divider > common_units.divider) { - common_units = t[x]; - } - } - - // save our common_max to the latest keys - let latest = this.latest[common_units_key]; - if (typeof latest === 'undefined') { - this.latest[common_units_key] = {}; - latest = this.latest[common_units_key]; - } - latest.units = common_units.units; - latest.divider = common_units.divider; - - tunits = latest.units; - tdivider = latest.divider; - - //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', common-units=' + common_units_name.toString() + ((t[uuid].divider !== tdivider)?' USED COMMON, mine was ' + t[uuid].units:' set common').toString()); - - // apply it to this chart - switch_units_callback(tunits); - return function (value) { - if (tdivider !== latest.divider) { - // another chart switched our common units - // we should switch them too - //console.log('DEBUG: ' + uuid + ' switching units due to a common-units change, from ' + tunits.toString() + ' to ' + latest.units.toString()); - tunits = latest.units; - tdivider = latest.divider; - switch_units_callback(tunits); - } - - return value / tdivider; - }; - } else { - // the caller did not give data-common-units - // this chart auto-scales independently of all others - //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', autonomously'); - - switch_units_callback(tunits); - return function (value) { - return value / tdivider; - }; - } - } else { - // the caller wants specific units - - if (typeof this.scalableUnits[units][desired_units] !== 'undefined') { - // all good, set the new units - tdivider = this.scalableUnits[units][desired_units]; - // console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + desired_units.toString() + ' with divider ' + tdivider.toString() + ', by reference'); - switch_units_callback(desired_units); - return function (value) { - return value / tdivider; - }; - } else { - // oops! switch back to original units - console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } - } else if (typeof this.convertibleUnits[units] !== 'undefined') { - // units that can be converted - if (desired_units === 'auto') { - for (var x in this.convertibleUnits[units]) { - if (this.convertibleUnits[units].hasOwnProperty(x)) { - if (this.convertibleUnits[units][x].check(max)) { - //console.log('DEBUG: ' + uuid.toString() + ' converting ' + units.toString() + ' to: ' + x.toString()); - switch_units_callback(x); - return this.convertibleUnits[units][x].convert; - } - } - } - - // none checked ok - //console.log('DEBUG: ' + uuid.toString() + ' no conversion available for ' + units.toString() + ' to: ' + desired_units.toString()); - switch_units_callback(units); - return function (value) { - return value; - }; - } else if (typeof this.convertibleUnits[units][desired_units] !== 'undefined') { - switch_units_callback(desired_units); - return this.convertibleUnits[units][desired_units].convert; - } else { - console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } else { - // hm... did we forget to implement the new type? - console.log(`Unmatched unit conversion method for units ${units.toString()}`); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } -}; - -NETDATA.icons = { - left: '', - reset: '', - right: '', - zoomIn: '', - zoomOut: '', - resize: '', - lineChart: '', - areaChart: '', - noChart: '', - loading: '', - noData: '' -}; - -if (typeof netdataIcons === 'object') { - // for (let icon in NETDATA.icons) { - // if (NETDATA.icons.hasOwnProperty(icon) && typeof(netdataIcons[icon]) === 'string') - // NETDATA.icons[icon] = netdataIcons[icon]; - // } - for (var icon of Object.keys(NETDATA.icons)) { - if (typeof(netdataIcons[icon]) === 'string') { - NETDATA.icons[icon] = netdataIcons[icon] - } - } -} - -if (typeof netdataSnapshotData === 'undefined') { - netdataSnapshotData = null; -} - -if (typeof netdataShowHelp === 'undefined') { - netdataShowHelp = true; -} - -if (typeof netdataShowAlarms === 'undefined') { - netdataShowAlarms = false; -} - -if (typeof netdataRegistryAfterMs !== 'number' || netdataRegistryAfterMs < 0) { - netdataRegistryAfterMs = 0; // 1500; -} - -if (typeof netdataRegistry === 'undefined') { - // backward compatibility - netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false); -} - -if (netdataRegistry === false && typeof netdataRegistryCallback === 'function') { - netdataRegistry = true; -} - -// ---------------------------------------------------------------------------------------------------------------- -// the defaults for all charts - -// if the user does not specify any of these, the following will be used - -NETDATA.chartDefaults = { - width: '100%', // the chart width - can be null - height: '100%', // the chart height - can be null - min_width: null, // the chart minimum width - can be null - library: 'dygraph', // the graphing library to use - method: 'average', // the grouping method - before: 0, // panning - after: -600, // panning - pixels_per_point: 1, // the detail of the chart - fill_luminance: 0.8 // luminance of colors in solid areas -}; - -// ---------------------------------------------------------------------------------------------------------------- -// global options - -NETDATA.options = { - pauseCallback: null, // a callback when we are really paused - - pause: false, // when enabled we don't auto-refresh the charts - - targets: [], // an array of all the state objects that are - // currently active (independently of their - // viewport visibility) - - updated_dom: true, // when true, the DOM has been updated with - // new elements we have to check. - - auto_refresher_fast_weight: 0, // this is the current time in ms, spent - // rendering charts continuously. - // used with .current.fast_render_timeframe - - page_is_visible: true, // when true, this page is visible - - auto_refresher_stop_until: 0, // timestamp in ms - used internally, to stop the - // auto-refresher for some time (when a chart is - // performing pan or zoom, we need to stop refreshing - // all other charts, to have the maximum speed for - // rendering the chart that is panned or zoomed). - // Used with .current.global_pan_sync_time - - on_scroll_refresher_stop_until: 0, // timestamp in ms - used to stop evaluating - // charts for some time, after a page scroll - - last_page_resize: Date.now(), // the timestamp of the last resize request - - last_page_scroll: 0, // the timestamp the last time the page was scrolled - - browser_timezone: 'unknown', // timezone detected by javascript - server_timezone: 'unknown', // timezone reported by the server - - force_data_points: 0, // force the number of points to be returned for charts - fake_chart_rendering: false, // when set to true, the dashboard will download data but will not render the charts - - passive_events: null, // true if the browser supports passive events - - // the current profile - // we may have many... - current: { - units: 'auto', // can be 'auto' or 'original' - temperature: 'celsius', // can be 'celsius' or 'fahrenheit' - seconds_as_time: true, // show seconds as DDd:HH:MM:SS ? - timezone: 'default', // the timezone to use, or 'default' - user_set_server_timezone: 'default', // as set by the user on the dashboard - - legend_toolbox: true, // show the legend toolbox on charts - resize_charts: true, // show the resize handler on charts - - pixels_per_point: isSlowDevice() ? 5 : 1, // the minimum pixels per point for all charts - // increase this to speed javascript up - // each chart library has its own limit too - // the max of this and the chart library is used - // the final is calculated every time, so a change - // here will have immediate effect on the next chart - // update - - idle_between_charts: 100, // ms - how much time to wait between chart updates - - fast_render_timeframe: 200, // ms - render continuously until this time of continuous - // rendering has been reached - // this setting is used to make it render e.g. 10 - // charts at once, sleep idle_between_charts time - // and continue for another 10 charts. - - idle_between_loops: 500, // ms - if all charts have been updated, wait this - // time before starting again. - - idle_parallel_loops: 100, // ms - the time between parallel refresher updates - - idle_lost_focus: 500, // ms - when the window does not have focus, check - // if focus has been regained, every this time - - global_pan_sync_time: 300, // ms - when you pan or zoom a chart, the background - // auto-refreshing of charts is paused for this amount - // of time - - sync_selection_delay: 400, // ms - when you pan or zoom a chart, wait this amount - // of time before setting up synchronized selections - // on hover. - - sync_selection: true, // enable or disable selection sync - - pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart - - sync_pan_and_zoom: true, // enable or disable pan and zoom sync - - pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming - - update_only_visible: true, // enable or disable visibility management / used for printing - - parallel_refresher: !isSlowDevice(), // enable parallel refresh of charts - - concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts - - destroy_on_hide: isSlowDevice(), // destroy charts when they are not visible - - show_help: netdataShowHelp, // when enabled the charts will show some help - show_help_delay_show_ms: 500, - show_help_delay_hide_ms: 0, - - eliminate_zero_dimensions: true, // do not show dimensions with just zeros - - stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus - stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts - - double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap - - smooth_plot: !isSlowDevice(), // enable smooth plot, where possible - - color_fill_opacity_line: 1.0, - color_fill_opacity_area: 0.2, - color_fill_opacity_stacked: 0.8, - - pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox - pan_and_zoom_factor_multiplier_control: 2.0, - pan_and_zoom_factor_multiplier_shift: 3.0, - pan_and_zoom_factor_multiplier_alt: 4.0, - - abort_ajax_on_scroll: false, // kill pending ajax page scroll - async_on_scroll: false, // sync/async onscroll handler - onscroll_worker_duration_threshold: 30, // time in ms, for async scroll handler - - retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server - - setOptionCallback: function () { - } - }, - - debug: { - show_boxes: false, - main_loop: false, - focus: false, - visibility: false, - chart_data_url: false, - chart_errors: true, // remember to set it to false before merging - chart_timing: false, - chart_calls: false, - libraries: false, - dygraph: false, - globalSelectionSync: false, - globalPanAndZoom: false - } -}; - -NETDATA.statistics = { - refreshes_total: 0, - refreshes_active: 0, - refreshes_active_max: 0 -}; - -// local storage options - -NETDATA.localStorage = { - default: {}, - current: {}, - callback: {} // only used for resetting back to defaults -}; - -NETDATA.localStorageTested = -1; -NETDATA.localStorageTest = function () { - if (NETDATA.localStorageTested !== -1) { - return NETDATA.localStorageTested; - } - - if (typeof Storage !== "undefined" && typeof localStorage === 'object') { - let test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - NETDATA.localStorageTested = true; - } catch (e) { - NETDATA.localStorageTested = false; - } - } else { - NETDATA.localStorageTested = false; - } - - return NETDATA.localStorageTested; -}; - -NETDATA.localStorageGet = function (key, def, callback) { - let ret = def; - - if (typeof NETDATA.localStorage.default[key.toString()] === 'undefined') { - NETDATA.localStorage.default[key.toString()] = def; - NETDATA.localStorage.callback[key.toString()] = callback; - } - - if (NETDATA.localStorageTest()) { - try { - // console.log('localStorage: loading "' + key.toString() + '"'); - ret = localStorage.getItem(key.toString()); - // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString()); - if (ret === null || ret === 'undefined') { - // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"'); - localStorage.setItem(key.toString(), JSON.stringify(def)); - ret = def; - } else { - // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"'); - ret = JSON.parse(ret); - // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret)); - } - } catch (error) { - console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"'); - ret = def; - } - } - - if (typeof ret === 'undefined' || ret === 'undefined') { - console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret)); - ret = def; - } - - NETDATA.localStorage.current[key.toString()] = ret; - return ret; -}; - -NETDATA.localStorageSet = function (key, value, callback) { - if (typeof value === 'undefined' || value === 'undefined') { - console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value)); - } - - if (typeof NETDATA.localStorage.default[key.toString()] === 'undefined') { - NETDATA.localStorage.default[key.toString()] = value; - NETDATA.localStorage.current[key.toString()] = value; - NETDATA.localStorage.callback[key.toString()] = callback; - } - - if (NETDATA.localStorageTest()) { - // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"'); - try { - localStorage.setItem(key.toString(), JSON.stringify(value)); - } catch (e) { - console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"'); - } - } - - NETDATA.localStorage.current[key.toString()] = value; - return value; -}; - -NETDATA.localStorageGetRecursive = function (obj, prefix, callback) { - let keys = Object.keys(obj); - let len = keys.length; - while (len--) { - let i = keys[len]; - - if (typeof obj[i] === 'object') { - //console.log('object ' + prefix + '.' + i.toString()); - NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback); - continue; - } - - obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback); - } -}; - -NETDATA.setOption = function (key, value) { - if (key.toString() === 'setOptionCallback') { - if (typeof NETDATA.options.current.setOptionCallback === 'function') { - NETDATA.options.current[key.toString()] = value; - NETDATA.options.current.setOptionCallback(); - } - } else if (NETDATA.options.current[key.toString()] !== value) { - let name = 'options.' + key.toString(); - - if (typeof NETDATA.localStorage.default[name.toString()] === 'undefined') { - console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value); - } - - //console.log(NETDATA.localStorage); - //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()])); - //console.log(NETDATA.options); - NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null); - - if (typeof NETDATA.options.current.setOptionCallback === 'function') { - NETDATA.options.current.setOptionCallback(); - } - } - - return true; -}; - -NETDATA.getOption = function (key) { - return NETDATA.options.current[key.toString()]; -}; - -// read settings from local storage -NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null); - -// always start with this option enabled. -NETDATA.setOption('stop_updates_when_focus_is_lost', true); - -NETDATA.resetOptions = function () { - let keys = Object.keys(NETDATA.localStorage.default); - let len = keys.length; - - while (len--) { - let i = keys[len]; - let a = i.split('.'); - - if (a[0] === 'options') { - if (a[1] === 'setOptionCallback') { - continue; - } - if (typeof NETDATA.localStorage.default[i] === 'undefined') { - continue; - } - if (NETDATA.options.current[i] === NETDATA.localStorage.default[i]) { - continue; - } - - NETDATA.setOption(a[1], NETDATA.localStorage.default[i]); - } else if (a[0] === 'chart_heights') { - if (typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') { - NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]); - } - } - } - - NETDATA.dateTime.init(NETDATA.options.current.timezone); -}; - -// *** src/dashboard.js/timeout.js - -// TODO: Better name needed - -NETDATA.timeout = { - // by default, these are just wrappers to setTimeout() / clearTimeout() - - step: function (callback) { - return window.setTimeout(callback, 1000 / 60); - }, - - set: function (callback, delay) { - return window.setTimeout(callback, delay); - }, - - clear: function (id) { - return window.clearTimeout(id); - }, - - init: function () { - let custom = true; - - if (window.requestAnimationFrame) { - this.step = function (callback) { - return window.requestAnimationFrame(callback); - }; - - this.clear = function (handle) { - return window.cancelAnimationFrame(handle.value); - }; - // } else if (window.webkitRequestAnimationFrame) { - // this.step = function (callback) { - // return window.webkitRequestAnimationFrame(callback); - // }; - - // if (window.webkitCancelAnimationFrame) { - // this.clear = function (handle) { - // return window.webkitCancelAnimationFrame(handle.value); - // }; - // } else if (window.webkitCancelRequestAnimationFrame) { - // this.clear = function (handle) { - // return window.webkitCancelRequestAnimationFrame(handle.value); - // }; - // } - // } else if (window.mozRequestAnimationFrame) { - // this.step = function (callback) { - // return window.mozRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.mozCancelRequestAnimationFrame(handle.value); - // }; - // } else if (window.oRequestAnimationFrame) { - // this.step = function (callback) { - // return window.oRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.oCancelRequestAnimationFrame(handle.value); - // }; - // } else if (window.msRequestAnimationFrame) { - // this.step = function (callback) { - // return window.msRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.msCancelRequestAnimationFrame(handle.value); - // }; - } else { - custom = false; - } - - if (custom) { - // we have installed custom .step() / .clear() functions - // overwrite the .set() too - - this.set = function (callback, delay) { - let start = Date.now(), - handle = new Object(); - - const loop = () => { - let current = Date.now(), - delta = current - start; - - if (delta >= delay) { - callback.call(); - } else { - handle.value = this.step(loop); - } - } - - handle.value = this.step(loop); - return handle; - }; - } - } -}; - -NETDATA.timeout.init(); -// Codacy declarations -/* global netdataTheme */ - -NETDATA.themes = { - white: { - bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-3.3.7.css', - dashboard_css: NETDATA.serverStatic + 'dashboard.css?v20190902-0', - background: '#FFFFFF', - foreground: '#000000', - grid: '#F0F0F0', - axis: '#F0F0F0', - highlight: '#F5F5F5', - colors: ['#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', - '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', - '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707', - '#329262', '#3B3EAC'], - easypiechart_track: '#f0f0f0', - easypiechart_scale: '#dfe0e0', - gauge_pointer: '#C0C0C0', - gauge_stroke: '#F0F0F0', - gauge_gradient: false, - d3pie: { - title: '#333333', - subtitle: '#666666', - footer: '#888888', - other: '#aaaaaa', - mainlabel: '#333333', - percentage: '#dddddd', - value: '#aaaa22', - tooltip_bg: '#000000', - tooltip_fg: '#efefef', - segment_stroke: "#ffffff", - gradient_color: '#000000' - } - }, - slate: { - bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', - dashboard_css: NETDATA.serverStatic + 'dashboard.slate.css?v20190902-0', - background: '#272b30', - foreground: '#C8C8C8', - grid: '#283236', - axis: '#283236', - highlight: '#383838', - /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00', - '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0', - '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a', - '#a6a479', '#a66da8' ], - */ - colors: ['#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00', - '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700', - '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737', - '#329262', '#3B3EFF'], - easypiechart_track: '#373b40', - easypiechart_scale: '#373b40', - gauge_pointer: '#474b50', - gauge_stroke: '#373b40', - gauge_gradient: false, - d3pie: { - title: '#C8C8C8', - subtitle: '#283236', - footer: '#283236', - other: '#283236', - mainlabel: '#C8C8C8', - percentage: '#dddddd', - value: '#cccc44', - tooltip_bg: '#272b30', - tooltip_fg: '#C8C8C8', - segment_stroke: "#283236", - gradient_color: '#000000' - } - } -}; - -if (typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined') { - NETDATA.themes.current = NETDATA.themes[netdataTheme]; -} else { - NETDATA.themes.current = NETDATA.themes.white; -} - -NETDATA.colors = NETDATA.themes.current.colors; - -// these are the colors Google Charts are using -// we have them here to attempt emulate their look and feel on the other chart libraries -// http://there4.io/2012/05/02/google-chart-color-list/ -//NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6', -// '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11', -// '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ]; - -// an alternative set -// http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/ -// (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray) -//NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ]; -// dygraph - -// Codacy declarations -/* global smoothPlotter */ -/* global Dygraph */ - -NETDATA.dygraph = { - smooth: false -}; - -NETDATA.dygraphToolboxPanAndZoom = function (state, after, before) { - if (after < state.netdata_first) { - after = state.netdata_first; - } - - if (before > state.netdata_last) { - before = state.netdata_last; - } - - state.setMode('zoom'); - NETDATA.globalSelectionSync.stop(); - NETDATA.globalSelectionSync.delay(); - state.tmp.dygraph_user_action = true; - state.tmp.dygraph_force_zoom = true; - // state.log('toolboxPanAndZoom'); - state.updateChartPanOrZoom(after, before); - NETDATA.globalPanAndZoom.setMaster(state, after, before); -}; - -NETDATA.dygraphSetSelection = function (state, t) { - if (typeof state.tmp.dygraph_instance !== 'undefined') { - let r = state.calculateRowForTime(t); - if (r !== -1) { - state.tmp.dygraph_instance.setSelection(r); - return true; - } else { - state.tmp.dygraph_instance.clearSelection(); - state.legendShowUndefined(); - } - } - - return false; -}; - -NETDATA.dygraphClearSelection = function (state) { - if (typeof state.tmp.dygraph_instance !== 'undefined') { - state.tmp.dygraph_instance.clearSelection(); - } - return true; -}; - -NETDATA.dygraphSmoothInitialize = function (callback) { - $.ajax({ - url: NETDATA.dygraph_smooth_js, - cache: true, - dataType: "script", - xhrFields: {withCredentials: true} // required for the cookie - }) - .done(function () { - NETDATA.dygraph.smooth = true; - smoothPlotter.smoothing = 0.3; - }) - .fail(function () { - NETDATA.dygraph.smooth = false; - }) - .always(function () { - if (typeof callback === "function") { - return callback(); - } - }); -}; - -NETDATA.dygraphInitialize = function (callback) { - if (typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) { - $.ajax({ - url: NETDATA.dygraph_js, - cache: true, - dataType: "script", - xhrFields: {withCredentials: true} // required for the cookie - }) - .done(function () { - NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js); - }) - .fail(function () { - NETDATA.chartLibraries.dygraph.enabled = false; - NETDATA.error(100, NETDATA.dygraph_js); - }) - .always(function () { - if (NETDATA.chartLibraries.dygraph.enabled && NETDATA.options.current.smooth_plot) { - NETDATA.dygraphSmoothInitialize(callback); - } else if (typeof callback === "function") { - return callback(); - } - }); - } else { - NETDATA.chartLibraries.dygraph.enabled = false; - if (typeof callback === "function") { - return callback(); - } - } -}; - -NETDATA.dygraphChartUpdate = function (state, data) { - let dygraph = state.tmp.dygraph_instance; - - if (typeof dygraph === 'undefined') { - return NETDATA.dygraphChartCreate(state, data); - } - - // when the chart is not visible, and hidden - // if there is a window resize, dygraph detects - // its element size as 0x0. - // this will make it re-appear properly - - if (state.tm.last_unhidden > state.tmp.dygraph_last_rendered) { - dygraph.resize(); - } - - let options = { - file: data.result.data, - colors: state.chartColors(), - labels: data.result.labels, - //labelsDivWidth: state.chartWidth() - 70, - includeZero: state.tmp.dygraph_include_zero, - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names) - }; - - if (state.tmp.dygraph_chart_type === 'stacked') { - if (options.includeZero && state.dimensions_visibility.countSelected() < options.visibility.length) { - options.includeZero = 0; - } - } - - if (!NETDATA.chartLibraries.dygraph.isSparkline(state)) { - options.ylabel = state.units_current; // (state.units_desired === 'auto')?"":state.units_current; - } - - if (state.tmp.dygraph_force_zoom) { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() forced zoom update'); - } - - options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null; - //options.isZoomedIgnoreProgrammaticZoom = true; - state.tmp.dygraph_force_zoom = false; - } else if (state.current.name !== 'auto') { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() loose update'); - } - } else { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() strict update'); - } - - options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null; - //options.isZoomedIgnoreProgrammaticZoom = true; - } - - options.valueRange = state.tmp.dygraph_options.valueRange; - - let oldMax = null, oldMin = null; - if (state.tmp.__commonMin !== null) { - state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; - oldMin = options.valueRange[0] = NETDATA.commonMin.get(state); - } - if (state.tmp.__commonMax !== null) { - state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; - oldMax = options.valueRange[1] = NETDATA.commonMax.get(state); - } - - if (state.tmp.dygraph_smooth_eligible) { - if ((NETDATA.options.current.smooth_plot && state.tmp.dygraph_options.plotter !== smoothPlotter) - || (NETDATA.options.current.smooth_plot === false && state.tmp.dygraph_options.plotter === smoothPlotter)) { - NETDATA.dygraphChartCreate(state, data); - return; - } - } - - if (netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(state) === false) { - // pan and zoom on snapshots - options.dateWindow = [NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms]; - //options.isZoomedIgnoreProgrammaticZoom = true; - } - - if (NETDATA.chartLibraries.dygraph.isLogScale(state)) { - if (Array.isArray(options.valueRange) && options.valueRange[0] <= 0) { - options.valueRange[0] = null; - } - } - - dygraph.updateOptions(options); - - let redraw = false; - if (oldMin !== null && oldMin > state.tmp.dygraph_instance.axes_[0].extremeRange[0]) { - state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; - options.valueRange[0] = NETDATA.commonMin.get(state); - redraw = true; - } - if (oldMax !== null && oldMax < state.tmp.dygraph_instance.axes_[0].extremeRange[1]) { - state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; - options.valueRange[1] = NETDATA.commonMax.get(state); - redraw = true; - } - - if (redraw) { - // state.log('forcing redraw to adapt to common- min/max'); - dygraph.updateOptions(options); - } - - state.tmp.dygraph_last_rendered = Date.now(); - return true; -}; - -NETDATA.dygraphChartCreate = function (state, data) { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartCreate()'); - } - - state.tmp.dygraph_chart_type = NETDATA.dataAttribute(state.element, 'dygraph-type', state.chart.chart_type); - if (state.tmp.dygraph_chart_type === 'stacked' && data.dimensions === 1) { - state.tmp.dygraph_chart_type = 'area'; - } - if (state.tmp.dygraph_chart_type === 'stacked' && NETDATA.chartLibraries.dygraph.isLogScale(state)) { - state.tmp.dygraph_chart_type = 'area'; - } - - let highlightCircleSize = NETDATA.chartLibraries.dygraph.isSparkline(state) ? 3 : 4; - - let smooth = NETDATA.dygraph.smooth - ? (NETDATA.dataAttributeBoolean(state.element, 'dygraph-smooth', (state.tmp.dygraph_chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false))) - : false; - - state.tmp.dygraph_include_zero = NETDATA.dataAttribute(state.element, 'dygraph-includezero', (state.tmp.dygraph_chart_type === 'stacked')); - let drawAxis = NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawaxis', true); - - state.tmp.dygraph_options = { - colors: NETDATA.dataAttribute(state.element, 'dygraph-colors', state.chartColors()), - - // leave a few pixels empty on the right of the chart - rightGap: NETDATA.dataAttribute(state.element, 'dygraph-rightgap', 5), - showRangeSelector: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showrangeselector', false), - showRoller: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showroller', false), - title: NETDATA.dataAttribute(state.element, 'dygraph-title', state.title), - titleHeight: NETDATA.dataAttribute(state.element, 'dygraph-titleheight', 19), - legend: NETDATA.dataAttribute(state.element, 'dygraph-legend', 'always'), // we need this to get selection events - labels: data.result.labels, - labelsDiv: NETDATA.dataAttribute(state.element, 'dygraph-labelsdiv', state.element_legend_childs.hidden), - //labelsDivStyles: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivstyles', { 'fontSize':'1px' }), - //labelsDivWidth: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivwidth', state.chartWidth() - 70), - labelsSeparateLines: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsseparatelines', true), - labelsShowZeroValues: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsshowzerovalues', true), - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showlabelsonhighlight', true), - hideOverlayOnMouseOut: NETDATA.dataAttributeBoolean(state.element, 'dygraph-hideoverlayonmouseout', true), - includeZero: state.tmp.dygraph_include_zero, - xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0), - yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1), - valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [null, null]), - ylabel: state.units_current, // (state.units_desired === 'auto')?"":state.units_current, - yLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-ylabelwidth', 12), - - // the function to plot the chart - plotter: null, - - // The width of the lines connecting data points. - // This can be used to increase the contrast or some graphs. - strokeWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokewidth', ((state.tmp.dygraph_chart_type === 'stacked') ? 0.1 : ((smooth === true) ? 1.5 : 0.7))), - strokePattern: NETDATA.dataAttribute(state.element, 'dygraph-strokepattern', undefined), - - // The size of the dot to draw on each point in pixels (see drawPoints). - // A dot is always drawn when a point is "isolated", - // i.e. there is a missing point on either side of it. - // This also controls the size of those dots. - drawPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawpoints', false), - - // Draw points at the edges of gaps in the data. - // This improves visibility of small data segments or other data irregularities. - drawGapEdgePoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgapedgepoints', true), - connectSeparatedPoints: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-connectseparatedpoints', false), - pointSize: NETDATA.dataAttribute(state.element, 'dygraph-pointsize', 1), - - // enabling this makes the chart with little square lines - stepPlot: NETDATA.dataAttributeBoolean(state.element, 'dygraph-stepplot', false), - - // Draw a border around graph lines to make crossing lines more easily - // distinguishable. Useful for graphs with many lines. - strokeBorderColor: NETDATA.dataAttribute(state.element, 'dygraph-strokebordercolor', NETDATA.themes.current.background), - strokeBorderWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokeborderwidth', (state.tmp.dygraph_chart_type === 'stacked') ? 0.0 : 0.0), - fillGraph: NETDATA.dataAttribute(state.element, 'dygraph-fillgraph', (state.tmp.dygraph_chart_type === 'area' || state.tmp.dygraph_chart_type === 'stacked')), - fillAlpha: NETDATA.dataAttribute(state.element, 'dygraph-fillalpha', - ((state.tmp.dygraph_chart_type === 'stacked') - ? NETDATA.options.current.color_fill_opacity_stacked - : NETDATA.options.current.color_fill_opacity_area) - ), - stackedGraph: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraph', (state.tmp.dygraph_chart_type === 'stacked')), - stackedGraphNaNFill: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraphnanfill', 'none'), - drawAxis: drawAxis, - axisLabelFontSize: NETDATA.dataAttribute(state.element, 'dygraph-axislabelfontsize', 10), - axisLineColor: NETDATA.dataAttribute(state.element, 'dygraph-axislinecolor', NETDATA.themes.current.axis), - axisLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-axislinewidth', 1.0), - drawGrid: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgrid', true), - gridLinePattern: NETDATA.dataAttribute(state.element, 'dygraph-gridlinepattern', null), - gridLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-gridlinewidth', 1.0), - gridLineColor: NETDATA.dataAttribute(state.element, 'dygraph-gridlinecolor', NETDATA.themes.current.grid), - maxNumberWidth: NETDATA.dataAttribute(state.element, 'dygraph-maxnumberwidth', 8), - sigFigs: NETDATA.dataAttribute(state.element, 'dygraph-sigfigs', null), - digitsAfterDecimal: NETDATA.dataAttribute(state.element, 'dygraph-digitsafterdecimal', 2), - valueFormatter: NETDATA.dataAttribute(state.element, 'dygraph-valueformatter', undefined), - highlightCircleSize: NETDATA.dataAttribute(state.element, 'dygraph-highlightcirclesize', highlightCircleSize), - highlightSeriesOpts: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesopts', null), // TOO SLOW: { strokeWidth: 1.5 }, - highlightSeriesBackgroundAlpha: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesbackgroundalpha', null), // TOO SLOW: (state.tmp.dygraph_chart_type === 'stacked')?0.7:0.5, - pointClickCallback: NETDATA.dataAttribute(state.element, 'dygraph-pointclickcallback', undefined), - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), - logscale: NETDATA.chartLibraries.dygraph.isLogScale(state) ? 'y' : undefined, - - // Expects a string in the format ":
You must enable JavaScript in order to use Netdata!
You can do this in your browser settings.
\ No newline at end of file diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/main.js b/luci-app-netdata/root/usr/share/netdata/webcn/main.js deleted file mode 100644 index 8c00600..0000000 --- a/luci-app-netdata/root/usr/share/netdata/webcn/main.js +++ /dev/null @@ -1,5146 +0,0 @@ -// Main JavaScript file for the Netdata GUI. - -// Codacy declarations -/* global NETDATA */ - -// netdata snapshot data -var netdataSnapshotData = null; - -// enable alarms checking and notifications -var netdataShowAlarms = true; - -// enable registry updates -var netdataRegistry = true; - -// forward definition only - not used here -var netdataServer = undefined; -var netdataServerStatic = undefined; -var netdataCheckXSS = undefined; - -// control the welcome modal and analytics -var this_is_demo = null; - -function escapeUserInputHTML(s) { - return s.toString() - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/#/g, '#') - .replace(/'/g, ''') - .replace(/\(/g, '(') - .replace(/\)/g, ')') - .replace(/\//g, '/'); -} - -function verifyURL(s) { - if (typeof (s) === 'string' && (s.startsWith('http://') || s.startsWith('https://'))) { - return s - .replace(/'/g, '%22') - .replace(/"/g, '%27') - .replace(/\)/g, '%28') - .replace(/\(/g, '%29'); - } - - console.log('invalid URL detected:'); - console.log(s); - return 'javascript:alert("invalid url");'; -} - -// -------------------------------------------------------------------- -// urlOptions - -var urlOptions = { - hash: '#', - theme: null, - help: null, - mode: 'live', // 'live', 'print' - update_always: false, - pan_and_zoom: false, - server: null, - after: 0, - before: 0, - highlight: false, - highlight_after: 0, - highlight_before: 0, - nowelcome: false, - show_alarms: false, - chart: null, - family: null, - alarm: null, - alarm_unique_id: 0, - alarm_id: 0, - alarm_event_id: 0, - alarm_when: 0, - - hasProperty: function (property) { - // console.log('checking property ' + property + ' of type ' + typeof(this[property])); - return typeof this[property] !== 'undefined'; - }, - - genHash: function (forReload) { - var hash = urlOptions.hash; - - if (urlOptions.pan_and_zoom === true) { - hash += ';after=' + urlOptions.after.toString() + - ';before=' + urlOptions.before.toString(); - } - - if (urlOptions.highlight === true) { - hash += ';highlight_after=' + urlOptions.highlight_after.toString() + - ';highlight_before=' + urlOptions.highlight_before.toString(); - } - - if (urlOptions.theme !== null) { - hash += ';theme=' + urlOptions.theme.toString(); - } - - if (urlOptions.help !== null) { - hash += ';help=' + urlOptions.help.toString(); - } - - if (urlOptions.update_always === true) { - hash += ';update_always=true'; - } - - if (forReload === true && urlOptions.server !== null) { - hash += ';server=' + urlOptions.server.toString(); - } - - if (urlOptions.mode !== 'live') { - hash += ';mode=' + urlOptions.mode; - } - - return hash; - }, - - parseHash: function () { - var variables = document.location.hash.split(';'); - var len = variables.length; - while (len--) { - if (len !== 0) { - var p = variables[len].split('='); - if (urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined') { - urlOptions[p[0]] = decodeURIComponent(p[1]); - } - } else { - if (variables[len].length > 0) { - urlOptions.hash = variables[len]; - } - } - } - - var booleans = ['nowelcome', 'show_alarms', 'update_always']; - len = booleans.length; - while (len--) { - if (urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1) { - urlOptions[booleans[len]] = true; - } else { - urlOptions[booleans[len]] = false; - } - } - - var numeric = ['after', 'before', 'highlight_after', 'highlight_before', 'alarm_when']; - len = numeric.length; - while (len--) { - if (typeof urlOptions[numeric[len]] === 'string') { - try { - urlOptions[numeric[len]] = parseInt(urlOptions[numeric[len]]); - } - catch (e) { - console.log('failed to parse URL hash parameter ' + numeric[len]); - urlOptions[numeric[len]] = 0; - } - } - } - - if (urlOptions.alarm_when) { - // if alarm_when exists, create after/before params - // -/+ 2 minutes from the alarm, and reload the page - const alarmTime = new Date(urlOptions.alarm_when * 1000).valueOf(); - const timeMarginMs = 120000; // 2 mins - - const after = alarmTime - timeMarginMs; - const before = alarmTime + timeMarginMs; - const newHash = document.location.hash.replace( - /;alarm_when=[0-9]*/i, - ";after=" + after + ";before=" + before, - ); - history.replaceState(null, '', newHash); - location.reload(); - } - - if (urlOptions.server !== null && urlOptions.server !== '') { - netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString(); - netdataServer = urlOptions.server; - netdataCheckXSS = true; - } else { - urlOptions.server = null; - } - - if (urlOptions.before > 0 && urlOptions.after > 0) { - urlOptions.pan_and_zoom = true; - urlOptions.nowelcome = true; - } else { - urlOptions.pan_and_zoom = false; - } - - if (urlOptions.highlight_before > 0 && urlOptions.highlight_after > 0) { - urlOptions.highlight = true; - } else { - urlOptions.highlight = false; - } - - switch (urlOptions.mode) { - case 'print': - urlOptions.theme = 'white'; - urlOptions.welcome = false; - urlOptions.help = false; - urlOptions.show_alarms = false; - - if (urlOptions.pan_and_zoom === false) { - urlOptions.pan_and_zoom = true; - urlOptions.before = Date.now(); - urlOptions.after = urlOptions.before - 600000; - } - - netdataShowAlarms = false; - netdataRegistry = false; - this_is_demo = false; - break; - - case 'live': - default: - urlOptions.mode = 'live'; - break; - } - - // console.log(urlOptions); - }, - - hashUpdate: function () { - history.replaceState(null, '', urlOptions.genHash(true)); - }, - - netdataPanAndZoomCallback: function (status, after, before) { - //console.log(1); - //console.log(new Error().stack); - - if (netdataSnapshotData === null) { - urlOptions.pan_and_zoom = status; - urlOptions.after = after; - urlOptions.before = before; - urlOptions.hashUpdate(); - } - }, - - netdataHighlightCallback: function (status, after, before) { - //console.log(2); - //console.log(new Error().stack); - - if (status === true && (after === null || before === null || after <= 0 || before <= 0 || after >= before)) { - status = false; - after = 0; - before = 0; - } - - if (netdataSnapshotData === null) { - urlOptions.highlight = status; - } else { - urlOptions.highlight = false; - } - - urlOptions.highlight_after = Math.round(after); - urlOptions.highlight_before = Math.round(before); - urlOptions.hashUpdate(); - - var show_eye = NETDATA.globalChartUnderlay.hasViewport(); - - if (status === true && after > 0 && before > 0 && after < before) { - var d1 = NETDATA.dateTime.localeDateString(after); - var d2 = NETDATA.dateTime.localeDateString(before); - if (d1 === d2) { - d2 = ''; - } - document.getElementById('navbar-highlight-content').innerHTML = - ((show_eye === true) ? '' : '').toString() - + 'highlighted time-frame' - + ' ' + d1 + ' ' + NETDATA.dateTime.localeTimeString(after) + ' to ' - + ' ' + d2 + ' ' + NETDATA.dateTime.localeTimeString(before) + ', ' - + 'duration ' + NETDATA.seconds4human(Math.round((before - after) / 1000)) + '' - + '' - + ''; - - $('.navbar-highlight').show(); - - $('.highlight-tooltip').tooltip({ - html: true, - delay: { show: 500, hide: 0 }, - container: 'body' - }); - } else { - $('.navbar-highlight').hide(); - } - }, - - clearHighlight: function () { - NETDATA.globalChartUnderlay.clear(); - - if (NETDATA.globalPanAndZoom.isActive() === true) { - NETDATA.globalPanAndZoom.clearMaster(); - } - }, - - showHighlight: function () { - NETDATA.globalChartUnderlay.focus(); - } -}; - -urlOptions.parseHash(); - -// -------------------------------------------------------------------- -// check options that should be processed before loading netdata.js - -var localStorageTested = -1; - -function localStorageTest() { - if (localStorageTested !== -1) { - return localStorageTested; - } - - if (typeof Storage !== "undefined" && typeof localStorage === 'object') { - var test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - localStorageTested = true; - } - catch (e) { - console.log(e); - localStorageTested = false; - } - } else { - localStorageTested = false; - } - - return localStorageTested; -} - -function loadLocalStorage(name) { - var ret = null; - - try { - if (localStorageTest() === true) { - ret = localStorage.getItem(name); - } else { - console.log('localStorage is not available'); - } - } - catch (error) { - console.log(error); - return null; - } - - if (typeof ret === 'undefined' || ret === null) { - return null; - } - - // console.log('loaded: ' + name.toString() + ' = ' + ret.toString()); - - return ret; -} - -function saveLocalStorage(name, value) { - // console.log('saving: ' + name.toString() + ' = ' + value.toString()); - try { - if (localStorageTest() === true) { - localStorage.setItem(name, value.toString()); - return true; - } - } - catch (error) { - console.log(error); - } - - return false; -} - -function getTheme(def) { - if (urlOptions.mode === 'print') { - return 'white'; - } - - var ret = loadLocalStorage('netdataTheme'); - if (typeof ret === 'undefined' || ret === null || ret === 'undefined') { - return def; - } else { - return ret; - } -} - -function setTheme(theme) { - if (urlOptions.mode === 'print') { - return false; - } - - if (theme === netdataTheme) { - return false; - } - return saveLocalStorage('netdataTheme', theme); -} - -var netdataTheme = getTheme('slate'); -var netdataShowHelp = true; - -if (urlOptions.theme !== null) { - setTheme(urlOptions.theme); - netdataTheme = urlOptions.theme; -} else { - urlOptions.theme = netdataTheme; -} - -if (urlOptions.help !== null) { - saveLocalStorage('options.show_help', urlOptions.help); - netdataShowHelp = urlOptions.help; -} else { - urlOptions.help = loadLocalStorage('options.show_help'); -} - -// -------------------------------------------------------------------- -// natural sorting -// http://www.davekoelle.com/files/alphanum.js - LGPL - -function naturalSortChunkify(t) { - var tz = []; - var x = 0, y = -1, n = 0, i, j; - - while (i = (j = t.charAt(x++)).charCodeAt(0)) { - var m = (i >= 48 && i <= 57); - if (m !== n) { - tz[++y] = ""; - n = m; - } - tz[y] += j; - } - - return tz; -} - -function naturalSortCompare(a, b) { - var aa = naturalSortChunkify(a.toLowerCase()); - var bb = naturalSortChunkify(b.toLowerCase()); - - for (var x = 0; aa[x] && bb[x]; x++) { - if (aa[x] !== bb[x]) { - var c = Number(aa[x]), d = Number(bb[x]); - if (c.toString() === aa[x] && d.toString() === bb[x]) { - return c - d; - } else { - return (aa[x] > bb[x]) ? 1 : -1; - } - } - } - - return aa.length - bb.length; -} - -// -------------------------------------------------------------------- -// saving files to client - -function saveTextToClient(data, filename) { - var blob = new Blob([data], { - type: 'application/octet-stream' - }); - - var url = URL.createObjectURL(blob); - var link = document.createElement('a'); - link.setAttribute('href', url); - link.setAttribute('download', filename); - - var el = document.getElementById('hiddenDownloadLinks'); - el.innerHTML = ''; - el.appendChild(link); - - setTimeout(function () { - el.removeChild(link); - URL.revokeObjectURL(url); - }, 60); - - link.click(); -} - -function saveObjectToClient(data, filename) { - saveTextToClient(JSON.stringify(data), filename); -} - -// ----------------------------------------------------------------------------- -// registry call back to render my-netdata menu - -function toggleExpandIcon(svgEl) { - if (svgEl.getAttribute('data-icon') === 'caret-down') { - svgEl.setAttribute('data-icon', 'caret-up'); - } else { - svgEl.setAttribute('data-icon', 'caret-down'); - } -} - -function toggleAgentItem(e, guid) { - e.stopPropagation(); - e.preventDefault(); - - toggleExpandIcon(e.currentTarget.children[0]); - - const el = document.querySelector(`.agent-alternate-urls.agent-${guid}`); - if (el) { - el.classList.toggle('collapsed'); - } -} - -// When you stream metrics from netdata to netdata, the receiving netdata now -// has multiple host databases. It's own, and multiple mirrored. Mirrored databases -// can be accessed with -const OLD_DASHBOARD_SUFFIX = "old" -let isOldSuffix = true -try { - const currentScriptMainJs = document.currentScript; - const mainJsSrc = currentScriptMainJs.getAttribute("src") - isOldSuffix = mainJsSrc.startsWith("../main.js") -} catch { - console.warn("current script not detecting, assuming the dashboard is running with /old suffix") -} - -function transformWithOldSuffix(url) { - return isOldSuffix ? `../${url}` : url -} - -function renderStreamedHosts(options) { - let html = `
Databases streamed to this agent
`; - - var base = document.location.origin.toString() + - document.location.pathname.toString() - .replace(isOldSuffix ? `/${OLD_DASHBOARD_SUFFIX}` : "", ""); - if (base.endsWith("/host/" + options.hostname + "/")) { - base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length); - } - - if (base.endsWith("/")) { - base = base.substring(0, base.length - 1); - } - - var master = options.hosts[0].hostname; - // We sort a clone of options.hosts, to keep the master as the first element - // for future calls. - var sorted = options.hosts.slice(0).sort(function (a, b) { - if (a.hostname === master) { - return -1; - } - return naturalSortCompare(a.hostname, b.hostname); - }); - - let displayedDatabases = false; - - for (var s of sorted) { - let url, icon; - const hostname = s.hostname; - - if (myNetdataMenuFilterValue !== "") { - if (!hostname.includes(myNetdataMenuFilterValue)) { - continue; - } - } - - displayedDatabases = true; - - if (hostname === master) { - url = isOldSuffix ? `${base}/${OLD_DASHBOARD_SUFFIX}/` : `${base}/`; - icon = 'home'; - } else { - url = isOldSuffix ? `${base}/host/${hostname}/${OLD_DASHBOARD_SUFFIX}/` : `${base}/host/${hostname}/`; - icon = 'window-restore'; - } - - html += ( - `
- - - - - ${hostname} - -
-
` - ) - } - - if (!displayedDatabases) { - html += ( - `
- - no databases match the filter criteria. -
` - ) - } - - return html; -} - -function renderMachines(machinesArray) { - let html = `
My nodes
`; - - if (machinesArray === null) { - let ret = loadLocalStorage("registryCallback"); - if (ret) { - machinesArray = JSON.parse(ret); - console.log("failed to contact the registry - loaded registry data from browser local storage"); - } - } - - let found = false; - let displayedAgents = false; - - const maskedURL = NETDATA.registry.MASKED_DATA; - - if (machinesArray) { - saveLocalStorage("registryCallback", JSON.stringify(machinesArray)); - - var machines = machinesArray.sort(function (a, b) { - return naturalSortCompare(a.name, b.name); - }); - - for (var machine of machines) { - found = true; - - if (myNetdataMenuFilterValue !== "") { - if (!machine.name.includes(myNetdataMenuFilterValue)) { - continue; - } - } - - displayedAgents = true; - - const alternateUrlItems = ( - `` - ) - - html += ( - ` - ${alternateUrlItems}` - ) - } - - if (found && (!displayedAgents)) { - html += ( - `
- - zero nodes are matching the filter value. -
` - ) - } - } - - if (!found) { - if (machines) { - html += ( - `` - ) - } else { - html += ( - `` - ) - } - - html += `
`; - html += `
Demo netdata nodes
`; - - const demoServers = [ - { url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)" }, - { url: "//newyork.netdata.rocks/default.html", title: "US - New York (DigitalOcean.com)" }, - { url: "//sanfrancisco.netdata.rocks/default.html", title: "US - San Francisco (DigitalOcean.com)" }, - { url: "//atlanta.netdata.rocks/default.html", title: "US - Atlanta (CDN77.com)" }, - { url: "//frankfurt.netdata.rocks/default.html", title: "Germany - Frankfurt (DigitalOcean.com)" }, - { url: "//toronto.netdata.rocks/default.html", title: "Canada - Toronto (DigitalOcean.com)" }, - { url: "//singapore.netdata.rocks/default.html", title: "Japan - Singapore (DigitalOcean.com)" }, - { url: "//bangalore.netdata.rocks/default.html", title: "India - Bangalore (DigitalOcean.com)" }, - - ] - - for (var server of demoServers) { - html += ( - `
- - ${server.title} -
-
- ` - ); - } - } - - return html; -} - -function setMyNetdataMenu(html) { - const el = document.getElementById('my-netdata-dropdown-content') - el.innerHTML = html; -} - -function clearMyNetdataMenu() { - setMyNetdataMenu(`
- - Loading, please wait... -
-
`); -} - -function errorMyNetdataMenu() { - setMyNetdataMenu(`
- - Cannot load known Netdata agents from Netdata Cloud! Please make sure you have the latest version of Netdata. -
`); -} - -function restrictMyNetdataMenu() { - setMyNetdataMenu(`
- Please sign in to netdata.cloud to view your nodes! -
-
`); -} - -function openAuthenticatedUrl(url) { - if (isSignedIn()) { - window.open(url); - } else { - window.open(`${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}&redirect_uri=${encodeURIComponent(window.location.origin + "/" + url)}`); - } -} - -function renderMyNetdataMenu(machinesArray) { - const el = document.getElementById('my-netdata-dropdown-content'); - el.classList.add(`theme-${netdataTheme}`); - - if (machinesArray == registryAgents) { - console.log("Rendering my-netdata menu from registry"); - } else { - console.log("Rendering my-netdata menu from netdata.cloud", machinesArray); - } - - let html = ''; - - if (!isSignedIn()) { - if (!NETDATA.registry.isRegistryEnabled()) { - html += ( - `` - ); - } - } - - if (isSignedIn()) { - html += ( - `
- - -
-
` - ); - } - - // options.hosts = [ - // { - // hostname: "streamed1", - // }, - // { - // hostname: "streamed2", - // }, - // ] - - if (options.hosts.length > 1) { - html += `
${renderStreamedHosts(options)}

`; - } - - if (isSignedIn() || NETDATA.registry.isRegistryEnabled()) { - html += `
${renderMachines(machinesArray)}

`; - } - - if (!isSignedIn()) { - html += ( - `
- - 更换标识 -
-
-
- - 这是什么? -
-
` - ) - } else { - html += ( - `
- - Nodes beta -
-
- -
- - What is this? -
-
` - ) - } - - el.innerHTML = html; - - gotoServerInit(); -} - -function isdemo() { - if (this_is_demo !== null) { - return this_is_demo; - } - this_is_demo = false; - - try { - if (typeof document.location.hostname === 'string') { - if (document.location.hostname.endsWith('.my-netdata.io') || - document.location.hostname.endsWith('.mynetdata.io') || - document.location.hostname.endsWith('.netdata.rocks') || - document.location.hostname.endsWith('.netdata.ai') || - document.location.hostname.endsWith('.netdata.live') || - document.location.hostname.endsWith('.firehol.org') || - document.location.hostname.endsWith('.netdata.online') || - document.location.hostname.endsWith('.netdata.cloud')) { - this_is_demo = true; - } - } - } - catch (error) { - } - return this_is_demo; -} - -function netdataURL(url, forReload) { - if (typeof url === 'undefined') - // url = document.location.toString(); - { - url = ''; - } - - if (url.indexOf('#') !== -1) { - url = url.substring(0, url.indexOf('#')); - } - - var hash = urlOptions.genHash(forReload); - - // console.log('netdataURL: ' + url + hash); - - return url + hash; -} - -function netdataReload(url) { - document.location = verifyURL(netdataURL(url, true)); - - // since we play with hash - // this is needed to reload the page - location.reload(); -} - -function gotoHostedModalHandler(url) { - document.location = verifyURL(url + urlOptions.genHash()); - return false; -} - -var gotoServerValidateRemaining = 0; -var gotoServerMiddleClick = false; -var gotoServerStop = false; - -function gotoServerValidateUrl(id, guid, url) { - var penalty = 0; - var error = 'failed'; - - if (document.location.toString().startsWith('http://') && url.toString().startsWith('https://')) - // we penalize https only if the current url is http - // to allow the user walk through all its servers. - { - penalty = 500; - } else if (document.location.toString().startsWith('https://') && url.toString().startsWith('http://')) { - error = 'can\'t check'; - } - - var finalURL = netdataURL(url); - - setTimeout(function () { - document.getElementById('gotoServerList').innerHTML += '' + escapeUserInputHTML(url) + 'checking...'; - - NETDATA.registry.hello(url, function (data) { - if (typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) { - // console.log('OK ' + id + ' URL: ' + url); - document.getElementById(guid + '-' + id + '-status').innerHTML = "OK"; - - if (!gotoServerStop) { - gotoServerStop = true; - - if (gotoServerMiddleClick) { - window.open(verifyURL(finalURL), '_blank'); - gotoServerMiddleClick = false; - document.getElementById('gotoServerResponse').innerHTML = 'Opening new window to ' + NETDATA.registry.machines[guid].name + '
' + escapeUserInputHTML(url) + '

(check your pop-up blocker if it fails)'; - } else { - document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:
' + escapeUserInputHTML(url) + ''; - document.location = verifyURL(finalURL); - $('#gotoServerModal').modal('hide'); - } - } - } else { - if (typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid !== guid) { - error = 'wrong machine'; - } - - document.getElementById(guid + '-' + id + '-status').innerHTML = error; - gotoServerValidateRemaining--; - if (gotoServerValidateRemaining <= 0) { - gotoServerMiddleClick = false; - document.getElementById('gotoServerResponse').innerHTML = 'Sorry! I cannot find any operational URL for this server'; - } - } - }); - }, (id * 50) + penalty); -} - -function gotoServerModalHandler(guid) { - // console.log('goto server: ' + guid); - - gotoServerStop = false; - var checked = {}; - var len = NETDATA.registry.machines[guid].alternate_urls.length; - var count = 0; - - document.getElementById('gotoServerResponse').innerHTML = ''; - document.getElementById('gotoServerList').innerHTML = ''; - document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name; - $('#gotoServerModal').modal('show'); - - gotoServerValidateRemaining = len; - while (len--) { - var url = NETDATA.registry.machines[guid].alternate_urls[len]; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); - } - - if (!isSignedIn()) { - // When the registry is enabled, if the user's known URLs are not working - // we consult the registry to get additional URLs. - setTimeout(function () { - if (gotoServerStop === false) { - document.getElementById('gotoServerResponse').innerHTML = 'Added all the known URLs for this machine.'; - NETDATA.registry.search(guid, function (data) { - // console.log(data); - len = data.urls.length; - while (len--) { - var url = data.urls[len][1]; - // console.log(url); - if (typeof checked[url] === 'undefined') { - gotoServerValidateRemaining++; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); - } - } - }); - } - }, 2000); - } - - return false; -} - -function gotoServerInit() { - $(".registry_link").on('click', function (e) { - if (e.which === 2) { - e.preventDefault(); - gotoServerMiddleClick = true; - } else { - gotoServerMiddleClick = false; - } - - return true; - }); -} - -function switchRegistryModalHandler() { - document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid; - document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server; - document.getElementById('switchRegistryResponse').innerHTML = ''; - $('#switchRegistryModal').modal('show'); -} - -function notifyForSwitchRegistry() { - var n = document.getElementById('switchRegistryPersonGUID').value; - - if (n !== '' && n.length === 36) { - NETDATA.registry.switch(n, function (result) { - if (result !== null) { - $('#switchRegistryModal').modal('hide'); - NETDATA.registry.init(); - } else { - document.getElementById('switchRegistryResponse').innerHTML = "Sorry! The registry rejected your request."; - } - }); - } else { - document.getElementById('switchRegistryResponse').innerHTML = "The ID you have entered is not a GUID."; - } -} - -var deleteRegistryGuid = null; -var deleteRegistryUrl = null; - -function deleteRegistryModalHandler(guid, name, url) { - // void (guid); - - deleteRegistryGuid = guid; - deleteRegistryUrl = url; - - document.getElementById('deleteRegistryServerName').innerHTML = name; - document.getElementById('deleteRegistryServerName2').innerHTML = name; - document.getElementById('deleteRegistryServerURL').innerHTML = url; - document.getElementById('deleteRegistryResponse').innerHTML = ''; - - $('#deleteRegistryModal').modal('show'); -} - -function notifyForDeleteRegistry() { - const responseEl = document.getElementById('deleteRegistryResponse'); - - if (deleteRegistryUrl) { - if (isSignedIn()) { - deleteCloudAgentURL(deleteRegistryGuid, deleteRegistryUrl) - .then((count) => { - if (!count) { - responseEl.innerHTML = "Sorry, this command was rejected by netdata.cloud!"; - return; - } - NETDATA.registry.delete(deleteRegistryUrl, function (result) { - if (result === null) { - console.log("Received error from registry", result); - } - - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - }); - }); - } else { - NETDATA.registry.delete(deleteRegistryUrl, function (result) { - if (result !== null) { - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - } else { - responseEl.innerHTML = "Sorry, this command was rejected by the registry server!"; - } - }); - } - } -} - -var options = { - menus: {}, - submenu_names: {}, - data: null, - hostname: 'netdata_server', // will be overwritten by the netdata server - version: 'unknown', - release_channel: 'unknown', - hosts: [], - - duration: 0, // the default duration of the charts - update_every: 1, - - chartsPerRow: 0, - // chartsMinWidth: 1450, - chartsHeight: 180, -}; - -function chartsPerRow(total) { - void (total); - - if (options.chartsPerRow === 0) { - return 1; - //var width = Math.floor(total / options.chartsMinWidth); - //if(width === 0) width = 1; - //return width; - } else { - return options.chartsPerRow; - } -} - -function prioritySort(a, b) { - if (a.priority < b.priority) { - return -1; - } - if (a.priority > b.priority) { - return 1; - } - return naturalSortCompare(a.name, b.name); -} - -function sortObjectByPriority(object) { - var idx = {}; - var sorted = []; - - for (var i in object) { - if (!object.hasOwnProperty(i)) { - continue; - } - - if (typeof idx[i] === 'undefined') { - idx[i] = object[i]; - sorted.push(i); - } - } - - sorted.sort(function (a, b) { - if (idx[a].priority < idx[b].priority) { - return -1; - } - if (idx[a].priority > idx[b].priority) { - return 1; - } - return naturalSortCompare(a, b); - }); - - return sorted; -} - -// ---------------------------------------------------------------------------- -// scroll to a section, without changing the browser history - -function scrollToId(hash) { - if (hash && hash !== '' && document.getElementById(hash) !== null) { - var offset = $('#' + hash).offset(); - if (typeof offset !== 'undefined') { - //console.log('scrolling to ' + hash + ' at ' + offset.top.toString()); - $('html, body').animate({ scrollTop: offset.top - 30 }, 0); - } - } - - // we must return false to prevent the default action - return false; -} - -// ---------------------------------------------------------------------------- - -// user editable information -var customDashboard = { - menu: {}, - submenu: {}, - context: {} -}; - -// netdata standard information -var netdataDashboard = { - sparklines_registry: {}, - os: 'unknown', - - menu: {}, - submenu: {}, - context: {}, - - // generate a sparkline - // used in the documentation - sparkline: function (prefix, chart, dimension, units, suffix) { - if (options.data === null || typeof options.data.charts === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart] === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart].dimensions === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart].dimensions[dimension] === 'undefined') { - return ''; - } - - var key = chart + '.' + dimension; - - if (typeof units === 'undefined') { - units = ''; - } - - if (typeof this.sparklines_registry[key] === 'undefined') { - this.sparklines_registry[key] = { count: 1 }; - } else { - this.sparklines_registry[key].count++; - } - - key = key + '.' + this.sparklines_registry[key].count; - - return prefix + '
(X' + units + ')' + suffix; - }, - - gaugeChart: function (title, width, dimensions, colors) { - if (typeof colors === 'undefined') { - colors = ''; - } - - if (typeof dimensions === 'undefined') { - dimensions = ''; - } - - return '
'; - }, - - anyAttribute: function (obj, attr, key, def) { - if (typeof (obj[key]) !== 'undefined') { - var x = obj[key][attr]; - - if (typeof (x) === 'undefined') { - return def; - } - - if (typeof (x) === 'function') { - return x(netdataDashboard.os); - } - - return x; - } - - return def; - }, - - menuTitle: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() - + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); - } - - return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); - }, - - menuIcon: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '').toString(); - } - - return this.anyAttribute(this.menu, 'icon', chart.menu, ''); - }, - - menuInfo: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null); - } - - return this.anyAttribute(this.menu, 'info', chart.menu, null); - }, - - menuHeight: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0); - } - - return this.anyAttribute(this.menu, 'height', chart.menu, 1.0); - }, - - submenuTitle: function (menu, submenu) { - var key = menu + '.' + submenu; - // console.log(key); - var title = this.anyAttribute(this.submenu, 'title', key, submenu).toString().replace(/_/g, ' '); - if (title.length > 28) { - var a = title.substring(0, 13); - var b = title.substring(title.length - 12, title.length); - return a + '...' + b; - } - return title; - }, - - submenuInfo: function (menu, submenu) { - var key = menu + '.' + submenu; - return this.anyAttribute(this.submenu, 'info', key, null); - }, - - submenuHeight: function (menu, submenu, relative) { - var key = menu + '.' + submenu; - return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative; - }, - - contextInfo: function (id) { - var x = this.anyAttribute(this.context, 'info', id, null); - - if (x !== null) { - return '
' + x + '
'; - } else { - return ''; - } - }, - - contextValueRange: function (id) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined') { - return this.context[id].valueRange; - } else { - return '[null, null]'; - } - }, - - contextHeight: function (id, def) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined') { - return def * this.context[id].height; - } else { - return def; - } - }, - - contextDecimalDigits: function (id, def) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].decimalDigits !== 'undefined') { - return this.context[id].decimalDigits; - } else { - return def; - } - } -}; - -// ---------------------------------------------------------------------------- - -// enrich the data structure returned by netdata -// to reflect our menu system and content -// TODO: this is a shame - we should fix charts naming (issue #807) -function enrichChartData(chart) { - var parts = chart.type.split('_'); - var tmp = parts[0]; - - switch (tmp) { - case 'ap': - case 'net': - case 'disk': - case 'powersupply': - case 'statsd': - chart.menu = tmp; - break; - - case 'apache': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'cache') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'bind': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'rndc') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'cgroup': - chart.menu = chart.type; - if (chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) { - chart.menu_pattern = 'cgqemu'; - } else { - chart.menu_pattern = 'cgroup'; - } - break; - - case 'go': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'expvar') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'mount': - if (parts.length > 2) { - chart.menu = tmp + '_' + parts[1]; - } else { - chart.menu = tmp; - } - break; - - case 'isc': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'dhcpd') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'ovpn': - chart.menu = chart.type; - if (parts.length > 3 && parts[1] === 'status' && parts[2] === 'log') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'smartd': - case 'web': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'log') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'tc': - chart.menu = tmp; - - // find a name for this device from fireqos info - // we strip '_(in|out)' or '(in|out)_' - if (chart.context === 'tc.qos' && (typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)) { - var n = chart.name.split('.')[1]; - if (n.endsWith('_in')) { - options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in')); - } else if (n.endsWith('_out')) { - options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out')); - } else if (n.startsWith('in_')) { - options.submenu_names[chart.family] = n.slice(3, n.length); - } else if (n.startsWith('out_')) { - options.submenu_names[chart.family] = n.slice(4, n.length); - } else { - options.submenu_names[chart.family] = n; - } - } - - // increase the priority of IFB devices - // to have inbound appear before outbound - if (chart.id.match(/.*-ifb$/)) { - chart.priority--; - } - - break; - - default: - chart.menu = chart.type; - if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - } - - chart.submenu = chart.family; -} - -// ---------------------------------------------------------------------------- - -function headMain(os, charts, duration) { - void (os); - - if (urlOptions.mode === 'print') { - return ''; - } - - var head = ''; - - if (typeof charts['system.swap'] !== 'undefined') { - head += '
'; - } - - if (typeof charts['system.io'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.pgpgio'] !== 'undefined') { - head += '
'; - - head += '
'; - } - - if (typeof charts['system.cpu'] !== 'undefined') { - head += '
'; - } - - if (typeof charts['system.net'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ip'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ipv4'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ipv6'] !== 'undefined') { - head += '
'; - - head += '
'; - } - - if (typeof charts['system.ram'] !== 'undefined') { - head += '
'; - } - - return head; -} - -function generateHeadCharts(type, chart, duration) { - if (urlOptions.mode === 'print') { - return ''; - } - - var head = ''; - var hcharts = netdataDashboard.anyAttribute(netdataDashboard.context, type, chart.context, []); - if (hcharts.length > 0) { - var hi = 0, hlen = hcharts.length; - while (hi < hlen) { - if (typeof hcharts[hi] === 'function') { - head += hcharts[hi](netdataDashboard.os, chart.id).replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id); - } else { - head += hcharts[hi].replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id); - } - hi++; - } - } - return head; -} - -function renderPage(menus, data) { - var div = document.getElementById('charts_div'); - var pcent_width = Math.floor(100 / chartsPerRow($(div).width())); - - // find the proper duration for per-second updates - var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60; - options.duration = duration; - options.update_every = data.update_every; - - var html = ''; - var sidebar = ''; - div.innerHTML = html; - document.getElementById('sidebar').innerHTML = sidebar; - - if (urlOptions.highlight === true) { - NETDATA.globalChartUnderlay.init(null - , urlOptions.highlight_after - , urlOptions.highlight_before - , (urlOptions.after > 0) ? urlOptions.after : null - , (urlOptions.before > 0) ? urlOptions.before : null - ); - } else { - NETDATA.globalChartUnderlay.clear(); - } - - if (urlOptions.mode === 'print') { - printPage(); - } else { - finalizePage(); - } -} - -function renderChartsAndMenu(data) { - options.menus = {}; - options.submenu_names = {}; - - var menus = options.menus; - var charts = data.charts; - var m, menu_key; - - for (var c in charts) { - if (!charts.hasOwnProperty(c)) { - continue; - } - - var chart = charts[c]; - enrichChartData(chart); - m = chart.menu; - - // create the menu - if (typeof menus[m] === 'undefined') { - menus[m] = { - menu_pattern: chart.menu_pattern, - priority: chart.priority, - submenus: {}, - title: netdataDashboard.menuTitle(chart), - icon: netdataDashboard.menuIcon(chart), - info: netdataDashboard.menuInfo(chart), - height: netdataDashboard.menuHeight(chart) * options.chartsHeight - }; - } else { - if (typeof (menus[m].menu_pattern) === 'undefined') { - menus[m].menu_pattern = chart.menu_pattern; - } - - if (chart.priority < menus[m].priority) { - menus[m].priority = chart.priority; - } - } - - menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m; - - // create the submenu - if (typeof menus[m].submenus[chart.submenu] === 'undefined') { - menus[m].submenus[chart.submenu] = { - priority: chart.priority, - charts: [], - title: null, - info: netdataDashboard.submenuInfo(menu_key, chart.submenu), - height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height) - }; - } else { - if (chart.priority < menus[m].submenus[chart.submenu].priority) { - menus[m].submenus[chart.submenu].priority = chart.priority; - } - } - - // index the chart in the menu/submenu - menus[m].submenus[chart.submenu].charts.push(chart); - } - - // propagate the descriptive subname given to QoS - // to all the other submenus with the same name - for (var m in menus) { - if (!menus.hasOwnProperty(m)) { - continue; - } - - for (var s in menus[m].submenus) { - if (!menus[m].submenus.hasOwnProperty(s)) { - continue; - } - - // set the family using a name - if (typeof options.submenu_names[s] !== 'undefined') { - menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; - } else { - menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m; - menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s); - } - } - } - - renderPage(menus, data); -} - -// ---------------------------------------------------------------------------- - -function loadJs(url, callback) { - $.ajax({ - url: url.startsWith("http") ? url : transformWithOldSuffix(url), - cache: true, - dataType: "script", - xhrFields: { withCredentials: true } // required for the cookie - }) - .fail(function () { - alert('Cannot load required JS library: ' + url); - }) - .always(function () { - if (typeof callback === 'function') { - callback(); - } - }) -} - -var clipboardLoaded = false; - -function loadClipboard(callback) { - if (clipboardLoaded === false) { - clipboardLoaded = true; - loadJs('lib/clipboard-polyfill-be05dad.js', callback); - } else { - callback(); - } -} - -var bootstrapTableLoaded = false; - -function loadBootstrapTable(callback) { - if (bootstrapTableLoaded === false) { - bootstrapTableLoaded = true; - loadJs('lib/bootstrap-table-1.11.0.min.js', function () { - loadJs('lib/bootstrap-table-export-1.11.0.min.js', function () { - loadJs('lib/tableExport-1.6.0.min.js', callback); - }) - }); - } else { - callback(); - } -} - -var bootstrapSliderLoaded = false; - -function loadBootstrapSlider(callback) { - if (bootstrapSliderLoaded === false) { - bootstrapSliderLoaded = true; - loadJs('lib/bootstrap-slider-10.0.0.min.js', function () { - NETDATA._loadCSS(transformWithOldSuffix("css/bootstrap-slider-10.0.0.min.css")); - callback(); - }); - } else { - callback(); - } -} - -var lzStringLoaded = false; - -function loadLzString(callback) { - if (lzStringLoaded === false) { - lzStringLoaded = true; - loadJs('lib/lz-string-1.4.4.min.js', callback); - } else { - callback(); - } -} - -var pakoLoaded = false; - -function loadPako(callback) { - if (pakoLoaded === false) { - pakoLoaded = true; - loadJs('lib/pako-1.0.6.min.js', callback); - } else { - callback(); - } -} - -// ---------------------------------------------------------------------------- - -function clipboardCopy(text) { - clipboard.writeText(text); -} - -function clipboardCopyBadgeEmbed(url) { - clipboard.writeText(''); -} - -// ---------------------------------------------------------------------------- - -function alarmsUpdateModal() { - var active = '

触发警报

'; - var all = '

所有作用中的警报

'; - var footer = '
netdata badges 会自动重新整理。不同颜色分表代表的警报状态: 红色  表示重大, 橘色  表示警告, 绿色  表示良好, 灰色  表示未定义 (例如无资料或无状态), 黑色  表示尚未初始化。您可以复制这里的网址并将它们嵌入到任一个网页。
netdata 能够发送这些警报通知。请参阅 这个设定档 了解更多资讯。'; - - loadClipboard(function () { - }); - - NETDATA.alarms.get('all', function (data) { - options.alarm_families = []; - - alarmsCallback(data); - - if (data === null) { - document.getElementById('alarms_active').innerHTML = - document.getElementById('alarms_all').innerHTML = - document.getElementById('alarms_log').innerHTML = - 'failed to load alarm data!'; - return; - } - - function alarmid4human(id) { - if (id === 0) { - return '-'; - } - - return id.toString(); - } - - function timestamp4human(timestamp, space) { - if (timestamp === 0) { - return '-'; - } - - if (typeof space === 'undefined') { - space = ' '; - } - - var t = new Date(timestamp * 1000); - var now = new Date(); - - if (t.toDateString() === now.toDateString()) { - return t.toLocaleTimeString(); - } - - return t.toLocaleDateString() + space + t.toLocaleTimeString(); - } - - function alarm_lookup_explain(alarm, chart) { - var dimensions = ' of all values '; - - if (chart.dimensions.length > 1) { - dimensions = ' of the sum of all dimensions '; - } - - if (typeof alarm.lookup_dimensions !== 'undefined') { - var d = alarm.lookup_dimensions.replace(/|/g, ','); - var x = d.split(','); - if (x.length > 1) { - dimensions = 'of the sum of dimensions ' + alarm.lookup_dimensions + ' '; - } else { - dimensions = 'of all values of dimension ' + alarm.lookup_dimensions + ' '; - } - } - - return '' + alarm.lookup_method + ' ' - + dimensions + ', of chart ' + alarm.chart + '' - + ', starting ' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, { space: ' ' }) + ' and up to ' + NETDATA.seconds4human(alarm.lookup_before, { space: ' ' }) + '' - + ((alarm.lookup_options) ? (', with options ' + alarm.lookup_options.replace(/ /g, ', ') + '') : '') - + '.'; - } - - function alarm_to_html(alarm, full) { - var chart = options.data.charts[alarm.chart]; - if (typeof (chart) === 'undefined') { - chart = options.data.charts_by_name[alarm.chart]; - if (typeof (chart) === 'undefined') { - // this means the charts loaded are incomplete - // probably netdata was restarted and more alarms - // are now available. - console.log('Cannot find chart ' + alarm.chart + ', you probably need to refresh the page.'); - return ''; - } - } - - var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined'); - var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto'; - - var action_buttons = '
 
role: ' + alarm.recipient + '
 
' - + '
' - + '
' - + '
'; - - var html = '
' - + ''; - - return html; - } - - function alarm_family_show(id) { - var html = '
' + alarm.chart + '
 

 
' + alarm.info + '' + action_buttons + '
' - + ((typeof alarm.warn !== 'undefined') ? ('') : '') - + ((typeof alarm.crit !== 'undefined') ? ('') : ''); - - if (full === true) { - var units = chart.units; - if (units === '%') { - units = '%'; - } - - html += ((typeof alarm.lookup_after !== 'undefined') ? ('') : '') - + ((typeof alarm.calc !== 'undefined') ? ('') : '') - + ((chart.green !== null) ? ('') : '') - + ((chart.red !== null) ? ('') : ''); - } - - if (alarm.warn_repeat_every > 0) { - html += ''; - } - - if (alarm.crit_repeat_every > 0) { - html += ''; - } - - var delay = ''; - if ((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) { - if (alarm.delay_up_duration === alarm.delay_down_duration) { - delay += '
hysteresis ' + NETDATA.seconds4human(alarm.delay_up_duration, { - space: ' ', - negative_suffix: '' - }); - } else { - delay = '
hysteresis '; - if (alarm.delay_up_duration > 0) { - delay += 'on escalation ' + NETDATA.seconds4human(alarm.delay_up_duration, { - space: ' ', - negative_suffix: '' - }) + ', '; - } - if (alarm.delay_down_duration > 0) { - delay += 'on recovery ' + NETDATA.seconds4human(alarm.delay_down_duration, { - space: ' ', - negative_suffix: '' - }) + ', '; - } - } - if (alarm.delay_multiplier !== 1.0) { - delay += 'multiplied by ' + alarm.delay_multiplier.toString() + ''; - delay += ', up to ' + NETDATA.seconds4human(alarm.delay_max_duration, { - space: ' ', - negative_suffix: '' - }) + ''; - } - delay += '
'; - } - - html += '
' - + ((has_alarm === true) ? ('') : '') - + '' - + '
warning when' + alarm.warn + '
critical when' + alarm.crit + '
db lookup' + alarm_lookup_explain(alarm, chart) + '
calculation' + alarm.calc + '
green threshold' + chart.green + ' ' + units + '
red threshold' + chart.red + ' ' + units + '
repeat warning' + NETDATA.seconds4human(alarm.warn_repeat_every) + '
repeat critical' + NETDATA.seconds4human(alarm.crit_repeat_every) + '
check every' + NETDATA.seconds4human(alarm.update_every, { - space: ' ', - negative_suffix: '' - }) + '
execute' + alarm.exec + '' + delay + '
source' + alarm.source + '
'; - var family = options.alarm_families[id]; - var len = family.arr.length; - while (len--) { - var alarm = family.arr[len]; - html += alarm_to_html(alarm, true); - } - html += '
'; - - $('#alarm_all_' + id.toString()).html(html); - enableTooltipsAndPopovers(); - } - - // find the proper family of each alarm - var x, family, alarm; - var count_active = 0; - var count_all = 0; - var families = {}; - var families_sort = []; - for (x in data.alarms) { - if (!data.alarms.hasOwnProperty(x)) { - continue; - } - - alarm = data.alarms[x]; - family = alarm.family; - - // find the chart - var chart = options.data.charts[alarm.chart]; - if (typeof chart === 'undefined') { - chart = options.data.charts_by_name[alarm.chart]; - } - - // not found - this should never happen! - if (typeof chart === 'undefined') { - console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.'); - chart = { priority: 9999999 }; - } - else if (typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined') - // the family based on the chart - { - family = chart.menu + ' - ' + chart.submenu; - } - - if (typeof families[family] === 'undefined') { - families[family] = { - name: family, - arr: [], - priority: chart.priority - }; - - families_sort.push(families[family]); - } - - if (chart.priority < families[family].priority) { - families[family].priority = chart.priority; - } - - families[family].arr.unshift(alarm); - } - - // sort the families, like the dashboard menu does - var families_sorted = families_sort.sort(function (a, b) { - if (a.priority < b.priority) { - return -1; - } - if (a.priority > b.priority) { - return 1; - } - return naturalSortCompare(a.name, b.name); - }); - - var i = 0; - var fc = 0; - var len = families_sorted.length; - while (len--) { - family = families_sorted[i++].name; - var active_family_added = false; - var expanded = 'true'; - var collapsed = ''; - var cin = 'in'; - - if (fc !== 0) { - all += ""; - expanded = 'false'; - collapsed = 'class="collapsed"'; - cin = ''; - } - - all += '
'; - - options.alarm_families[fc] = families[family]; - - fc++; - - var arr = families[family].arr; - var c = arr.length; - while (c--) { - alarm = arr[c]; - if (alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { - if (!active_family_added) { - active_family_added = true; - active += '

' + family + '

'; - } - count_active++; - active += alarm_to_html(alarm, true); - } - - count_all++; - } - } - active += ""; - if (families_sorted.length > 0) { - all += "
"; - } - all += ""; - - if (!count_active) { - active += '

Everything is normal. No raised alarms.
'; - } else { - active += footer; - } - - if (!count_all) { - all += "

此系统中没有运行警报。

"; - } else { - all += footer; - } - - document.getElementById('alarms_active').innerHTML = active; - document.getElementById('alarms_all').innerHTML = all; - enableTooltipsAndPopovers(); - - if (families_sorted.length > 0) { - alarm_family_show(0); - } - - // register bootstrap events - var $accordion = $('#alarms_all_accordion'); - $accordion.on('show.bs.collapse', function (d) { - var target = $(d.target); - var id = $(target).data('alarm-id'); - alarm_family_show(id); - }); - $accordion.on('hidden.bs.collapse', function (d) { - var target = $(d.target); - var id = $(target).data('alarm-id'); - $('#alarm_all_' + id.toString()).html(''); - }); - - document.getElementById('alarms_log').innerHTML = '

警报记录

'; - - loadBootstrapTable(function () { - $('#alarms_log_table').bootstrapTable({ - url: NETDATA.alarms.server + '/api/v1/alarm_log?all', - cache: false, - pagination: true, - pageSize: 10, - showPaginationSwitch: false, - search: true, - searchTimeOut: 300, - searchAlign: 'left', - showColumns: true, - showExport: true, - exportDataType: 'basic', - exportOptions: { - fileName: 'netdata_alarm_log' - }, - onClickRow: function (row, $element,field) { - void (field); - void ($element); - let main_url; - let common_url = "&host=" + encodeURIComponent(row['hostname']) + "&chart=" + encodeURIComponent(row['chart']) + "&family=" + encodeURIComponent(row['family']) + "&alarm=" + encodeURIComponent(row['name']) + "&alarm_unique_id=" + row['unique_id'] + "&alarm_id=" + row['alarm_id'] + "&alarm_event_id=" + row['alarm_event_id'] + "&alarm_when=" + row['when']; - if (NETDATA.registry.isUsingGlobalRegistry() && NETDATA.registry.machine_guid != null) { - main_url = "https://netdata.cloud/alarms/redirect?agentID=" + NETDATA.registry.machine_guid + common_url; - } else { - main_url = NETDATA.registry.server + "/goto-host-from-alarm.html?" + common_url ; - } - window.open(main_url,"_blank"); - }, - rowStyle: function (row, index) { - void (index); - - switch (row.status) { - case 'CRITICAL': - return { classes: 'danger' }; - break; - case 'WARNING': - return { classes: 'warning' }; - break; - case 'UNDEFINED': - return { classes: 'info' }; - break; - case 'CLEAR': - return { classes: 'success' }; - break; - } - return {}; - }, - showFooter: false, - showHeader: true, - showRefresh: true, - showToggle: false, - sortable: true, - silentSort: false, - columns: [ - { - field: 'when', - title: '事件日期', - valign: 'middle', - titleTooltip: 'The date and time the even took place', - formatter: function (value, row, index) { - void (row); - void (index); - return timestamp4human(value, ' '); - }, - align: 'center', - switchable: false, - sortable: true - }, - { - field: 'hostname', - title: '主机', - valign: 'middle', - titleTooltip: 'The host that generated this event', - align: 'center', - visible: false, - sortable: true - }, - { - field: 'unique_id', - title: '唯一 ID', - titleTooltip: 'The host unique ID for this event', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_id', - title: '警报 ID', - titleTooltip: 'The ID of the alarm that generated this event', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_event_id', - title: '警报事件 ID', - titleTooltip: 'The incremental ID of this event for the given alarm', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'chart', - title: '图表', - titleTooltip: 'The chart the alarm is attached to', - align: 'center', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'family', - title: 'Family', - titleTooltip: 'The family of the chart the alarm is attached to', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'name', - title: '警报', - titleTooltip: 'The alarm name that generated this event', - formatter: function (value, row, index) { - void (row); - void (index); - return value.toString().replace(/_/g, ' '); - }, - align: 'center', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'value_string', - title: 'Friendly Value', - titleTooltip: 'The value of the alarm, that triggered this event', - align: 'right', - valign: 'middle', - sortable: true - }, - { - field: 'old_value_string', - title: 'Friendly Old Value', - titleTooltip: 'The value of the alarm, just before this event', - align: 'right', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'old_value', - title: 'Old Value', - titleTooltip: 'The value of the alarm, just before this event', - formatter: function (value, row, index) { - void (row); - void (index); - return ((value !== null) ? Math.round(value * 100) / 100 : 'NaN').toString(); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'value', - title: 'Value', - titleTooltip: 'The value of the alarm, that triggered this event', - formatter: function (value, row, index) { - void (row); - void (index); - return ((value !== null) ? Math.round(value * 100) / 100 : 'NaN').toString(); - }, - align: 'right', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'units', - title: '单位', - titleTooltip: 'The units of the value of the alarm', - align: 'left', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'old_status', - title: '先前状态', - titleTooltip: 'The status of the alarm, just before this event', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'status', - title: '状态', - titleTooltip: 'The status of the alarm, that was set due to this event', - align: 'center', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'duration', - title: '上次持续时间', - titleTooltip: 'The duration the alarm was at its previous state, just before this event', - formatter: function (value, row, index) { - void (row); - void (index); - return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'non_clear_duration', - title: '提升的持续时间', - titleTooltip: 'The duration the alarm was raised, just before this event', - formatter: function (value, row, index) { - void (row); - void (index); - return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'recipient', - title: '收件人', - titleTooltip: 'The recipient of this event', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'processed', - title: '已处理状态', - titleTooltip: 'True when this event is processed', - formatter: function (value, row, index) { - void (row); - void (index); - - if (value === true) { - return 'DONE'; - } else { - return 'PENDING'; - } - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updated', - title: '更新的状态', - titleTooltip: 'True when this event has been updated by another event', - formatter: function (value, row, index) { - void (row); - void (index); - - if (value === true) { - return 'UPDATED'; - } else { - return 'CURRENT'; - } - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updated_by_id', - title: '由ID更新', - titleTooltip: 'The unique ID of the event that obsoleted this one', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updates_id', - title: '更新ID', - titleTooltip: 'The unique ID of the event obsoleted because of this event', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec', - title: '脚本', - titleTooltip: 'The script to handle the event notification', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec_run', - title: '脚本运行时间', - titleTooltip: 'The date and time the script has been ran', - formatter: function (value, row, index) { - void (row); - void (index); - return timestamp4human(value, ' '); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec_code', - title: '脚本返回值', - titleTooltip: 'The return code of the script', - formatter: function (value, row, index) { - void (row); - void (index); - - if (value === 0) { - return 'OK (returned 0)'; - } else { - return 'FAILED (with code ' + value.toString() + ')'; - } - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'delay', - title: '脚本延迟', - titleTooltip: 'The hysteresis of the notification', - formatter: function (value, row, index) { - void (row); - void (index); - - return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'delay_up_to_timestamp', - title: '脚本延迟运行时间', - titleTooltip: 'The date and time the script should be run, after hysteresis', - formatter: function (value, row, index) { - void (row); - void (index); - return timestamp4human(value, ' '); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'info', - title: '描述', - titleTooltip: 'A short description of the alarm', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'source', - title: '报警源', - titleTooltip: 'The source of configuration of the alarm', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - } - ] - }); - // console.log($('#alarms_log_table').bootstrapTable('getOptions')); - }); - }); -} - -function alarmsCallback(data) { - var count = 0, x; - for (x in data.alarms) { - if (!data.alarms.hasOwnProperty(x)) { - continue; - } - - var alarm = data.alarms[x]; - if (alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { - count++; - } - } - - if (count > 0) { - document.getElementById('alarms_count_badge').innerHTML = count.toString(); - } else { - document.getElementById('alarms_count_badge').innerHTML = ''; - } -} - -function initializeDynamicDashboardWithData(data) { - if (data !== null) { - options.hostname = data.hostname; - options.data = data; - options.version = data.version; - options.release_channel = data.release_channel; - netdataDashboard.os = data.os; - - if (typeof data.hosts !== 'undefined') { - options.hosts = data.hosts; - } - - // update the dashboard hostname - document.getElementById('hostname').innerHTML = '' + options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString() + '  '; - document.getElementById('hostname').href = NETDATA.serverDefault; - document.getElementById('netdataVersion').innerHTML = options.version; - - if (netdataSnapshotData !== null) { - $('#alarmsButton').hide(); - $('#updateButton').hide(); - // $('#loadButton').hide(); - $('#saveButton').hide(); - $('#printButton').hide(); - } - - // update the dashboard title - document.title = options.hostname + ' netdata 仪表板'; - - // close the splash screen - $("#loadOverlay").css("display", "none"); - - // create a chart_by_name index - data.charts_by_name = {}; - var charts = data.charts; - var x; - for (x in charts) { - if (!charts.hasOwnProperty(x)) { - continue; - } - - var chart = charts[x]; - data.charts_by_name[chart.name] = chart; - } - - // render all charts - renderChartsAndMenu(data); - - // Ensure MyNetdata menu is rendered with latest host info #5370 - renderMyNetdataMenu(isSignedIn() ? cloudAgents : registryAgents); - } -} - -// an object to keep initialization configuration -// needed due to the async nature of the XSS modal -var initializeConfig = { - url: null, - custom_info: true, -}; - -function loadCustomDashboardInfo(url, callback) { - loadJs(url, function () { - $.extend(true, netdataDashboard, customDashboard); - callback(); - }); -} - -function initializeChartsAndCustomInfo() { - NETDATA.alarms.callback = alarmsCallback; - - // download all the charts the server knows - NETDATA.chartRegistry.downloadAll(initializeConfig.url, function (data) { - if (data !== null) { - if (initializeConfig.custom_info === true && typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) { - //console.log('loading custom dashboard decorations from server ' + initializeConfig.url); - loadCustomDashboardInfo(NETDATA.serverDefault + data.custom_info, function () { - initializeDynamicDashboardWithData(data); - }); - } else { - //console.log('not loading custom dashboard decorations from server ' + initializeConfig.url); - initializeDynamicDashboardWithData(data); - } - } - }); -} - -function xssModalDisableXss() { - //console.log('disabling xss checks'); - NETDATA.xss.enabled = false; - NETDATA.xss.enabled_for_data = false; - initializeConfig.custom_info = true; - initializeChartsAndCustomInfo(); - return false; -} - -function xssModalKeepXss() { - //console.log('keeping xss checks'); - NETDATA.xss.enabled = true; - NETDATA.xss.enabled_for_data = true; - initializeConfig.custom_info = false; - initializeChartsAndCustomInfo(); - return false; -} - -function initializeDynamicDashboard(netdata_url) { - if (typeof netdata_url === 'undefined' || netdata_url === null) { - netdata_url = NETDATA.serverDefault; - } - - initializeConfig.url = netdata_url; - - // initialize clickable alarms - NETDATA.alarms.chart_div_offset = -50; - NETDATA.alarms.chart_div_id_prefix = 'chart_'; - NETDATA.alarms.chart_div_animation_duration = 0; - - NETDATA.pause(function () { - if (typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true) { - //$("#loadOverlay").css("display","none"); - document.getElementById('netdataXssModalServer').innerText = initializeConfig.url; - $('#xssModal').modal('show'); - } else { - initializeChartsAndCustomInfo(); - } - }); -} - -// ---------------------------------------------------------------------------- - -function versionLog(msg) { - document.getElementById('versionCheckLog').innerHTML = msg; -} - -// New way of checking for updates, based only on versions - -function versionsMatch(v1, v2) { - if (v1 == v2) { - return true; - } else { - let s1 = v1.split('.'); - let s2 = v2.split('.'); - // Check major version - let n1 = parseInt(s1[0].substring(1, 2), 10); - let n2 = parseInt(s2[0].substring(1, 2), 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - // Check minor version - n1 = parseInt(s1[1], 10); - n2 = parseInt(s2[1], 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - // Split patch: format could be e.g. 0-22-nightly - s1 = s1[2].split('-'); - s2 = s2[2].split('-'); - - n1 = parseInt(s1[0], 10); - n2 = parseInt(s2[0], 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - n1 = (s1.length > 1) ? parseInt(s1[1], 10) : 0; - n2 = (s2.length > 1) ? parseInt(s2[1], 10) : 0; - if (n1 < n2) return false; - else return true; - } -} - -function getGithubLatestVersion(callback) { - versionLog('正在从 github 下载最新版本 ID...'); - - $.ajax({ - url: 'https://api.github.com/repos/netdata/netdata/releases/latest', - async: true, - cache: false - }) - .done(function (data) { - data = data.tag_name.replace(/(\r\n|\n|\r| |\t)/gm, ""); - versionLog('从 github 取得最新版本是 ' + data); - callback(data); - }) - .fail(function () { - versionLog('从 github 下载最新版本 ID 失败。'); - callback(null); - }); -} - -function getGCSLatestVersion(callback) { - versionLog('Downloading latest version id from GCS...'); - $.ajax({ - url: "https://www.googleapis.com/storage/v1/b/netdata-nightlies/o/latest-version.txt", - async: true, - cache: false - }) - .done(function (response) { - $.ajax({ - url: response.mediaLink, - async: true, - cache: false - }) - .done(function (data) { - data = data.replace(/(\r\n|\n|\r| |\t)/gm, ""); - versionLog('Latest nightly version from GCS is ' + data); - callback(data); - }) - .fail(function (xhr, textStatus, errorThrown) { - versionLog('Failed to download the latest nightly version id from GCS!'); - callback(null); - }); - }) - .fail(function (xhr, textStatus, errorThrown) { - versionLog('Failed to download the latest nightly version from GCS!'); - callback(null); - }); -} - - -function checkForUpdateByVersion(force, callback) { - if (options.release_channel === 'stable') { - getGithubLatestVersion(function (sha2) { - callback(options.version, sha2); - }); - } else { - getGCSLatestVersion(function (sha2) { - callback(options.version, sha2); - }); - } - return null; -} - -function notifyForUpdate(force) { - versionLog('

正在检查更新...

'); - - var now = Date.now(); - - if (typeof force === 'undefined' || force !== true) { - var last = loadLocalStorage('last_update_check'); - - if (typeof last === 'string') { - last = parseInt(last); - } else { - last = 0; - } - - if (now - last < 3600000 * 8) { - // no need to check it - too soon - return; - } - } - - checkForUpdateByVersion(force, function (sha1, sha2) { - var save = false; - - if (sha1 === null) { - save = false; - versionLog('

取得您的 netdata 版本失败!

'); - else { - save = true; - versionLog('

您已经是最新版本的 netdata!

'); - } - - if (save) { - saveLocalStorage('last_update_check', now.toString()); - } - }); -} - -// ---------------------------------------------------------------------------- -// printing dashboards - -function showPageFooter() { - document.getElementById('footer').style.display = 'block'; -} - -function printPreflight() { - var url = document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString() + urlOptions.genHash() + ';mode=print'; - var width = 990; - var height = screen.height * 90 / 100; - //console.log(url); - //console.log(document.location); - window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes'); - $('#printPreflightModal').modal('hide'); -} - -function printPage() { - var print_is_rendering = true; - - $('#printModal').on('hide.bs.modal', function (e) { - if (print_is_rendering === true) { - e.preventDefault(); - return false; - } - - return true; - }); - - $('#printModal').on('show.bs.modal', function () { - var print_options = { - stop_updates_when_focus_is_lost: false, - update_only_visible: false, - sync_selection: false, - eliminate_zero_dimensions: false, - pan_and_zoom_data_padding: false, - show_help: false, - legend_toolbox: false, - resize_charts: false, - pixels_per_point: 1 - }; - - var x; - for (x in print_options) { - if (print_options.hasOwnProperty(x)) { - NETDATA.options.current[x] = print_options[x]; - } - } - - NETDATA.parseDom(); - showPageFooter(); - - NETDATA.globalSelectionSync.stop(); - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); - // NETDATA.onresize(); - - var el = document.getElementById('printModalProgressBar'); - var eltxt = document.getElementById('printModalProgressBarText'); - - function update_chart(idx) { - var state = NETDATA.options.targets[--idx]; - - var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length; - $(el).css('width', pcent + '%').attr('aria-valuenow', pcent); - eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id; - - setTimeout(function () { - state.updateChart(function () { - NETDATA.options.targets[idx].resizeForPrint(); - - if (idx > 0) { - update_chart(idx); - } else { - print_is_rendering = false; - $('#printModal').modal('hide'); - window.print(); - window.close(); - } - }) - }, 0); - } - - print_is_rendering = true; - update_chart(NETDATA.options.targets.length); - }); - - $('#printModal').modal('show'); -} - -// -------------------------------------------------------------------- - -function jsonStringifyFn(obj) { - return JSON.stringify(obj, function (key, value) { - return (typeof value === 'function') ? value.toString() : value; - }); -} - -function jsonParseFn(str) { - return JSON.parse(str, function (key, value) { - if (typeof value != 'string') { - return value; - } - return (value.substring(0, 8) == 'function') ? eval('(' + value + ')') : value; - }); -} - -// -------------------------------------------------------------------- - -var snapshotOptions = { - bytes_per_chart: 2048, - compressionDefault: 'pako.deflate.base64', - - compressions: { - 'none': { - bytes_per_point_memory: 5.2, - bytes_per_point_disk: 5.6, - - compress: function (s) { - return s; - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return s; - } - }, - - 'pako.deflate.base64': { - bytes_per_point_memory: 1.8, - bytes_per_point_disk: 1.9, - - compress: function (s) { - return btoa(pako.deflate(s, { to: 'string' })); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return pako.inflate(atob(s), { to: 'string' }); - } - }, - - 'pako.deflate': { - bytes_per_point_memory: 1.4, - bytes_per_point_disk: 3.2, - - compress: function (s) { - return pako.deflate(s, { to: 'string' }); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return pako.inflate(s, { to: 'string' }); - } - }, - - 'lzstring.utf16': { - bytes_per_point_memory: 1.7, - bytes_per_point_disk: 2.6, - - compress: function (s) { - return LZString.compressToUTF16(s); - }, - - compressed_length: function (s) { - return s.length * 2; - }, - - uncompress: function (s) { - return LZString.decompressFromUTF16(s); - } - }, - - 'lzstring.base64': { - bytes_per_point_memory: 2.1, - bytes_per_point_disk: 2.3, - - compress: function (s) { - return LZString.compressToBase64(s); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return LZString.decompressFromBase64(s); - } - }, - - 'lzstring.uri': { - bytes_per_point_memory: 2.1, - bytes_per_point_disk: 2.3, - - compress: function (s) { - return LZString.compressToEncodedURIComponent(s); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return LZString.decompressFromEncodedURIComponent(s); - } - } - } -}; - -// -------------------------------------------------------------------- -// loading snapshots - -function loadSnapshotModalLog(priority, msg) { - document.getElementById('loadSnapshotStatus').className = "alert alert-" + priority; - document.getElementById('loadSnapshotStatus').innerHTML = msg; -} - -var tmpSnapshotData = null; - -function loadSnapshot() { - $('#loadSnapshotImport').addClass('disabled'); - - if (tmpSnapshotData === null) { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'no data have been loaded'); - return; - } - - loadPako(function () { - loadLzString(function () { - loadSnapshotModalLog('info', 'Please wait, activating snapshot...'); - $('#loadSnapshotModal').modal('hide'); - - netdataShowAlarms = false; - netdataRegistry = false; - netdataServer = tmpSnapshotData.server; - NETDATA.serverDefault = netdataServer; - - document.getElementById('charts_div').innerHTML = ''; - document.getElementById('sidebar').innerHTML = ''; - NETDATA.globalReset(); - - if (typeof tmpSnapshotData.hash !== 'undefined') { - urlOptions.hash = tmpSnapshotData.hash; - } else { - urlOptions.hash = '#'; - } - - if (typeof tmpSnapshotData.info !== 'undefined') { - var info = jsonParseFn(tmpSnapshotData.info); - if (typeof info.menu !== 'undefined') { - netdataDashboard.menu = info.menu; - } - - if (typeof info.submenu !== 'undefined') { - netdataDashboard.submenu = info.submenu; - } - - if (typeof info.context !== 'undefined') { - netdataDashboard.context = info.context; - } - } - - if (typeof tmpSnapshotData.compression !== 'string') { - tmpSnapshotData.compression = 'none'; - } - - if (typeof snapshotOptions.compressions[tmpSnapshotData.compression] === 'undefined') { - alert('unknown compression method: ' + tmpSnapshotData.compression); - tmpSnapshotData.compression = 'none'; - } - - tmpSnapshotData.uncompress = snapshotOptions.compressions[tmpSnapshotData.compression].uncompress; - netdataSnapshotData = tmpSnapshotData; - - urlOptions.after = tmpSnapshotData.after_ms; - urlOptions.before = tmpSnapshotData.before_ms; - - if (typeof tmpSnapshotData.highlight_after_ms !== 'undefined' - && tmpSnapshotData.highlight_after_ms !== null - && tmpSnapshotData.highlight_after_ms > 0 - && typeof tmpSnapshotData.highlight_before_ms !== 'undefined' - && tmpSnapshotData.highlight_before_ms !== null - && tmpSnapshotData.highlight_before_ms > 0 - ) { - urlOptions.highlight_after = tmpSnapshotData.highlight_after_ms; - urlOptions.highlight_before = tmpSnapshotData.highlight_before_ms; - urlOptions.highlight = true; - } else { - urlOptions.highlight_after = 0; - urlOptions.highlight_before = 0; - urlOptions.highlight = false; - } - - netdataCheckXSS = false; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded - NETDATA.xss.enabled = true; // we should not do any remote requests, but if we do, check them - NETDATA.xss.enabled_for_data = true; // check also snapshot data - that have been excluded from the initial check, due to compression - loadSnapshotPreflightEmpty(); - initializeDynamicDashboard(); - }); - }); -}; - -function loadSnapshotPreflightFile(file) { - var filename = NETDATA.xss.string(file.name); - var fr = new FileReader(); - fr.onload = function (e) { - document.getElementById('loadSnapshotFilename').innerHTML = filename; - var result = null; - try { - result = NETDATA.xss.checkAlways('snapshot', JSON.parse(e.target.result), /^(snapshot\.info|snapshot\.data)$/); - - //console.log(result); - var date_after = new Date(result.after_ms); - var date_before = new Date(result.before_ms); - - if (typeof result.charts_ok === 'undefined') { - result.charts_ok = 'unknown'; - } - - if (typeof result.charts_failed === 'undefined') { - result.charts_failed = 0; - } - - if (typeof result.compression === 'undefined') { - result.compression = 'none'; - } - - if (typeof result.data_size === 'undefined') { - result.data_size = 0; - } - - document.getElementById('loadSnapshotFilename').innerHTML = '' + filename + ''; - document.getElementById('loadSnapshotHostname').innerHTML = '' + result.hostname + ', netdata version: ' + result.netdata_version.toString() + ''; - document.getElementById('loadSnapshotURL').innerHTML = result.url; - document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point'; - document.getElementById('loadSnapshotInfo').innerHTML = 'version: ' + result.snapshot_version.toString() + ', includes ' + result.charts_ok.toString() + ' unique chart data queries ' + ((result.charts_failed > 0) ? ('' + result.charts_failed.toString() + ' failed') : '').toString() + ', compressed with ' + result.compression.toString() + ', data size ' + (Math.round(result.data_size * 100 / 1024 / 1024) / 100).toString() + ' MB'; - document.getElementById('loadSnapshotTimeRange').innerHTML = '' + NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + ' to ' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before) + ''; - document.getElementById('loadSnapshotComments').innerHTML = ((result.comments) ? result.comments : '').toString(); - loadSnapshotModalLog('success', 'File loaded, click Import to render it!'); - $('#loadSnapshotImport').removeClass('disabled'); - - tmpSnapshotData = result; - } - catch (e) { - console.log(e); - document.getElementById('loadSnapshotStatus').className = "alert alert-danger"; - document.getElementById('loadSnapshotStatus').innerHTML = "Failed to parse this file!"; - $('#loadSnapshotImport').addClass('disabled'); - } - } - - //console.log(file); - fr.readAsText(file); -}; - -function loadSnapshotPreflightEmpty() { - document.getElementById('loadSnapshotFilename').innerHTML = ''; - document.getElementById('loadSnapshotHostname').innerHTML = ''; - document.getElementById('loadSnapshotURL').innerHTML = ''; - document.getElementById('loadSnapshotCharts').innerHTML = ''; - document.getElementById('loadSnapshotInfo').innerHTML = ''; - document.getElementById('loadSnapshotTimeRange').innerHTML = ''; - document.getElementById('loadSnapshotComments').innerHTML = ''; - loadSnapshotModalLog('success', 'Browse for a snapshot file (or drag it and drop it here), then click Import to render it.'); - $('#loadSnapshotImport').addClass('disabled'); -}; - -var loadSnapshotDragAndDropInitialized = false; - -function loadSnapshotDragAndDropSetup() { - if (loadSnapshotDragAndDropInitialized === false) { - loadSnapshotDragAndDropInitialized = true; - $('#loadSnapshotDragAndDrop') - .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) { - e.preventDefault(); - e.stopPropagation(); - }) - .on('drop', function (e) { - if (e.originalEvent.dataTransfer.files.length) { - loadSnapshotPreflightFile(e.originalEvent.dataTransfer.files.item(0)); - } else { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'No file selected'); - } - }); - } -}; - -function loadSnapshotPreflight() { - var files = document.getElementById('loadSnapshotSelectFiles').files; - if (files.length <= 0) { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'No file selected'); - return; - } - - loadSnapshotModalLog('info', 'Loading file...'); - - loadSnapshotPreflightFile(files.item(0)); -} - -// -------------------------------------------------------------------- -// saving snapshots - -var saveSnapshotStop = false; - -function saveSnapshotCancel() { - saveSnapshotStop = true; -} - -var saveSnapshotModalInitialized = false; - -function saveSnapshotModalSetup() { - if (saveSnapshotModalInitialized === false) { - saveSnapshotModalInitialized = true; - $('#saveSnapshotModal') - .on('hide.bs.modal', saveSnapshotCancel) - .on('show.bs.modal', saveSnapshotModalInit) - .on('shown.bs.modal', function () { - $('#saveSnapshotResolutionSlider').find(".slider-handle:first").attr("tabindex", 1); - document.getElementById('saveSnapshotComments').focus(); - }); - } -}; - -function saveSnapshotModalLog(priority, msg) { - document.getElementById('saveSnapshotStatus').className = "alert alert-" + priority; - document.getElementById('saveSnapshotStatus').innerHTML = msg; -} - -function saveSnapshotModalShowExpectedSize() { - var points = Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint); - var priority = 'info'; - var msg = 'A moderate snapshot.'; - - var sizemb = Math.round( - (options.data.charts_count * snapshotOptions.bytes_per_chart - + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_disk) - * 10 / 1024 / 1024) / 10; - - var memmb = Math.round( - (options.data.charts_count * snapshotOptions.bytes_per_chart - + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_memory) - * 10 / 1024 / 1024) / 10; - - if (sizemb < 10) { - priority = 'success'; - msg = 'A nice small snapshot!'; - } - if (sizemb > 50) { - priority = 'warning'; - msg = 'Will stress your browser...'; - } - if (sizemb > 100) { - priority = 'danger'; - msg = 'Hm... good luck...'; - } - - saveSnapshotModalLog(priority, 'The snapshot will have ' + points.toString() + ' points per dimension. Expected size on disk ' + sizemb + ' MB, at browser memory ' + memmb + ' MB.
' + msg); -} - -var saveSnapshotCompression = snapshotOptions.compressionDefault; - -function saveSnapshotSetCompression(name) { - saveSnapshotCompression = name; - document.getElementById('saveSnapshotCompressionName').innerHTML = saveSnapshotCompression; - saveSnapshotModalShowExpectedSize(); -} - -var saveSnapshotSlider = null; -var saveSnapshotSelectedSecondsPerPoint = 1; -var saveSnapshotViewDuration = 1; - -function saveSnapshotModalInit() { - $('#saveSnapshotModalProgressSection').hide(); - $('#saveSnapshotResolutionRadio').show(); - saveSnapshotModalLog('info', 'Select resolution and click Save'); - $('#saveSnapshotExport').removeClass('disabled'); - - loadBootstrapSlider(function () { - saveSnapshotViewDuration = options.duration; - var start_ms = Math.round(Date.now() - saveSnapshotViewDuration * 1000); - - if (NETDATA.globalPanAndZoom.isActive() === true) { - saveSnapshotViewDuration = Math.round((NETDATA.globalPanAndZoom.force_before_ms - NETDATA.globalPanAndZoom.force_after_ms) / 1000); - start_ms = NETDATA.globalPanAndZoom.force_after_ms; - } - - var start_date = new Date(start_ms); - var yyyymmddhhssmm = start_date.getFullYear() + NETDATA.zeropad(start_date.getMonth() + 1) + NETDATA.zeropad(start_date.getDate()) + '-' + NETDATA.zeropad(start_date.getHours()) + NETDATA.zeropad(start_date.getMinutes()) + NETDATA.zeropad(start_date.getSeconds()); - - document.getElementById('saveSnapshotFilename').value = 'netdata-' + options.hostname.toString() + '-' + yyyymmddhhssmm.toString() + '-' + saveSnapshotViewDuration.toString() + '.snapshot'; - saveSnapshotSetCompression(saveSnapshotCompression); - - var min = options.update_every; - var max = Math.round(saveSnapshotViewDuration / 100); - - if (NETDATA.globalPanAndZoom.isActive() === false) { - max = Math.round(saveSnapshotViewDuration / 50); - } - - var view = Math.round(saveSnapshotViewDuration / Math.round($(document.getElementById('charts_div')).width() / 2)); - - // console.log('view duration: ' + saveSnapshotViewDuration + ', min: ' + min + ', max: ' + max + ', view: ' + view); - - if (max < 10) { - max = 10; - } - if (max < min) { - max = min; - } - if (view < min) { - view = min; - } - if (view > max) { - view = max; - } - - if (saveSnapshotSlider !== null) { - saveSnapshotSlider.destroy(); - } - - saveSnapshotSlider = new Slider('#saveSnapshotResolutionSlider', { - ticks: [min, view, max], - min: min, - max: max, - step: options.update_every, - value: view, - scale: (max > 100) ? 'logarithmic' : 'linear', - tooltip: 'always', - formatter: function (value) { - if (value < 1) { - value = 1; - } - - if (value < options.data.update_every) { - value = options.data.update_every; - } - - saveSnapshotSelectedSecondsPerPoint = value; - saveSnapshotModalShowExpectedSize(); - - var seconds = ' seconds '; - if (value === 1) { - seconds = ' second '; - } - - return value + seconds + 'per point' + ((value === options.data.update_every) ? ', server default' : '').toString(); - } - }); - }); -} - -function saveSnapshot() { - loadPako(function () { - loadLzString(function () { - saveSnapshotStop = false; - $('#saveSnapshotModalProgressSection').show(); - $('#saveSnapshotResolutionRadio').hide(); - $('#saveSnapshotExport').addClass('disabled'); - - var filename = document.getElementById('saveSnapshotFilename').value; - // console.log(filename); - saveSnapshotModalLog('info', 'Generating snapshot as ' + filename.toString() + ''); - - var save_options = { - stop_updates_when_focus_is_lost: false, - update_only_visible: false, - sync_selection: false, - eliminate_zero_dimensions: true, - pan_and_zoom_data_padding: false, - show_help: false, - legend_toolbox: false, - resize_charts: false, - pixels_per_point: 1 - }; - var backedup_options = {}; - - var x; - for (x in save_options) { - if (save_options.hasOwnProperty(x)) { - backedup_options[x] = NETDATA.options.current[x]; - NETDATA.options.current[x] = save_options[x]; - } - } - - var el = document.getElementById('saveSnapshotModalProgressBar'); - var eltxt = document.getElementById('saveSnapshotModalProgressBarText'); - - options.data.charts_by_name = null; - - var saveData = { - hostname: options.hostname, - server: NETDATA.serverDefault, - netdata_version: options.data.version, - snapshot_version: 1, - after_ms: Date.now() - options.duration * 1000, - before_ms: Date.now(), - highlight_after_ms: urlOptions.highlight_after, - highlight_before_ms: urlOptions.highlight_before, - duration_ms: options.duration * 1000, - update_every_ms: options.update_every * 1000, - data_points: 0, - url: ((urlOptions.server !== null) ? urlOptions.server : document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString()).toString(), - comments: document.getElementById('saveSnapshotComments').value.toString(), - hash: urlOptions.hash, - charts: options.data, - info: jsonStringifyFn({ - menu: netdataDashboard.menu, - submenu: netdataDashboard.submenu, - context: netdataDashboard.context - }), - charts_ok: 0, - charts_failed: 0, - compression: saveSnapshotCompression, - data_size: 0, - data: {} - }; - - if (typeof snapshotOptions.compressions[saveData.compression] === 'undefined') { - alert('unknown compression method: ' + saveData.compression); - saveData.compression = 'none'; - } - - var compress = snapshotOptions.compressions[saveData.compression].compress; - var compressed_length = snapshotOptions.compressions[saveData.compression].compressed_length; - - function pack_api1_v1_chart_data(state) { - if (state.library_name === null || state.data === null) { - return; - } - - var data = state.data; - state.data = null; - data.state = null; - var str = JSON.stringify(data); - - if (typeof str === 'string') { - var cstr = compress(str); - saveData.data[state.chartDataUniqueID()] = cstr; - return compressed_length(cstr); - } else { - return 0; - } - } - - var clearPanAndZoom = false; - if (NETDATA.globalPanAndZoom.isActive() === false) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveData.after_ms, saveData.before_ms); - clearPanAndZoom = true; - } - - saveData.after_ms = NETDATA.globalPanAndZoom.force_after_ms; - saveData.before_ms = NETDATA.globalPanAndZoom.force_before_ms; - saveData.duration_ms = saveData.before_ms - saveData.after_ms; - saveData.data_points = Math.round((saveData.before_ms - saveData.after_ms) / (saveSnapshotSelectedSecondsPerPoint * 1000)); - saveSnapshotModalLog('info', 'Generating snapshot with ' + saveData.data_points.toString() + ' data points per dimension...'); - - var charts_count = 0; - var charts_ok = 0; - var charts_failed = 0; - - function saveSnapshotRestore() { - $('#saveSnapshotModal').modal('hide'); - - // restore the options - var x; - for (x in backedup_options) { - if (backedup_options.hasOwnProperty(x)) { - NETDATA.options.current[x] = backedup_options[x]; - } - } - - $(el).css('width', '0%').attr('aria-valuenow', 0); - eltxt.innerText = '0%'; - - if (clearPanAndZoom) { - NETDATA.globalPanAndZoom.clearMaster(); - } - - NETDATA.options.force_data_points = 0; - NETDATA.options.fake_chart_rendering = false; - NETDATA.onscroll_updater_enabled = true; - NETDATA.onresize(); - NETDATA.unpause(); - - $('#saveSnapshotExport').removeClass('disabled'); - } - - NETDATA.globalSelectionSync.stop(); - NETDATA.options.force_data_points = saveData.data_points; - NETDATA.options.fake_chart_rendering = true; - NETDATA.onscroll_updater_enabled = false; - NETDATA.abortAllRefreshes(); - - var size = 0; - var info = ' Resolution: ' + saveSnapshotSelectedSecondsPerPoint.toString() + ((saveSnapshotSelectedSecondsPerPoint === 1) ? ' second ' : ' seconds ').toString() + 'per point.'; - - function update_chart(idx) { - if (saveSnapshotStop === true) { - saveSnapshotModalLog('info', 'Cancelled!'); - saveSnapshotRestore(); - return; - } - - var state = NETDATA.options.targets[--idx]; - - var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length; - $(el).css('width', pcent + '%').attr('aria-valuenow', pcent); - eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id; - - setTimeout(function () { - charts_count++; - state.isVisible(true); - state.current.force_after_ms = saveData.after_ms; - state.current.force_before_ms = saveData.before_ms; - - state.updateChart(function (status, reason) { - state.current.force_after_ms = null; - state.current.force_before_ms = null; - - if (status === true) { - charts_ok++; - // state.log('ok'); - size += pack_api1_v1_chart_data(state); - } else { - charts_failed++; - state.log('failed to be updated: ' + reason); - } - - saveSnapshotModalLog((charts_failed) ? 'danger' : 'info', 'Generated snapshot data size ' + (Math.round(size * 100 / 1024 / 1024) / 100).toString() + ' MB. ' + ((charts_failed) ? (charts_failed.toString() + ' charts have failed to be downloaded') : '').toString() + info); - - if (idx > 0) { - update_chart(idx); - } else { - saveData.charts_ok = charts_ok; - saveData.charts_failed = charts_failed; - saveData.data_size = size; - // console.log(saveData.compression + ': ' + (size / (options.data.dimensions_count * Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint))).toString()); - - // save it - // console.log(saveData); - saveObjectToClient(saveData, filename); - - if (charts_failed > 0) { - alert(charts_failed.toString() + ' failed to be downloaded'); - } - - saveSnapshotRestore(); - saveData = null; - } - }) - }, 0); - } - - update_chart(NETDATA.options.targets.length); - }); - }); -} - -// -------------------------------------------------------------------- -// activate netdata on the page - -function dashboardSettingsSetup() { - var update_options_modal = function () { - // console.log('update_options_modal'); - - var sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== NETDATA.getOption(option)) { - // console.log('switching ' + option.toString()); - self.bootstrapToggle(NETDATA.getOption(option) ? 'on' : 'off'); - } - }; - - var theme_sync_option = function (option) { - var self = $('#' + option); - - self.bootstrapToggle(netdataTheme === 'slate' ? 'on' : 'off'); - }; - var units_sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== (NETDATA.getOption('units') === 'auto')) { - self.bootstrapToggle(NETDATA.getOption('units') === 'auto' ? 'on' : 'off'); - } - - if (self.prop('checked') === true) { - $('#settingsLocaleTempRow').show(); - $('#settingsLocaleTimeRow').show(); - } else { - $('#settingsLocaleTempRow').hide(); - $('#settingsLocaleTimeRow').hide(); - } - }; - var temp_sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== (NETDATA.getOption('temperature') === 'celsius')) { - self.bootstrapToggle(NETDATA.getOption('temperature') === 'celsius' ? 'on' : 'off'); - } - }; - var timezone_sync_option = function (option) { - var self = $('#' + option); - - document.getElementById('browser_timezone').innerText = NETDATA.options.browser_timezone; - document.getElementById('server_timezone').innerText = NETDATA.options.server_timezone; - document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone; - - if (self.prop('checked') === NETDATA.dateTime.using_timezone) { - self.bootstrapToggle(NETDATA.dateTime.using_timezone ? 'off' : 'on'); - } - }; - - sync_option('eliminate_zero_dimensions'); - sync_option('destroy_on_hide'); - sync_option('async_on_scroll'); - sync_option('parallel_refresher'); - sync_option('concurrent_refreshes'); - sync_option('sync_selection'); - sync_option('sync_pan_and_zoom'); - sync_option('stop_updates_when_focus_is_lost'); - sync_option('smooth_plot'); - sync_option('pan_and_zoom_data_padding'); - sync_option('show_help'); - sync_option('seconds_as_time'); - theme_sync_option('netdata_theme_control'); - units_sync_option('units_conversion'); - temp_sync_option('units_temp'); - timezone_sync_option('local_timezone'); - - if (NETDATA.getOption('parallel_refresher') === false) { - $('#concurrent_refreshes_row').hide(); - } else { - $('#concurrent_refreshes_row').show(); - } - }; - NETDATA.setOption('setOptionCallback', update_options_modal); - - // handle options changes - $('#eliminate_zero_dimensions').change(function () { - NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); - }); - $('#destroy_on_hide').change(function () { - NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); - }); - $('#async_on_scroll').change(function () { - NETDATA.setOption('async_on_scroll', $(this).prop('checked')); - }); - $('#parallel_refresher').change(function () { - NETDATA.setOption('parallel_refresher', $(this).prop('checked')); - }); - $('#concurrent_refreshes').change(function () { - NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); - }); - $('#sync_selection').change(function () { - NETDATA.setOption('sync_selection', $(this).prop('checked')); - }); - $('#sync_pan_and_zoom').change(function () { - NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); - }); - $('#stop_updates_when_focus_is_lost').change(function () { - urlOptions.update_always = !$(this).prop('checked'); - urlOptions.hashUpdate(); - - NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always); - }); - $('#smooth_plot').change(function () { - NETDATA.setOption('smooth_plot', $(this).prop('checked')); - }); - $('#pan_and_zoom_data_padding').change(function () { - NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); - }); - $('#seconds_as_time').change(function () { - NETDATA.setOption('seconds_as_time', $(this).prop('checked')); - }); - $('#local_timezone').change(function () { - if ($(this).prop('checked')) { - selected_server_timezone('default', true); - } else { - selected_server_timezone('default', false); - } - }); - - $('#units_conversion').change(function () { - NETDATA.setOption('units', $(this).prop('checked') ? 'auto' : 'original'); - }); - $('#units_temp').change(function () { - NETDATA.setOption('temperature', $(this).prop('checked') ? 'celsius' : 'fahrenheit'); - }); - - $('#show_help').change(function () { - urlOptions.help = $(this).prop('checked'); - urlOptions.hashUpdate(); - - NETDATA.setOption('show_help', urlOptions.help); - netdataReload(); - }); - - // this has to be the last - // it reloads the page - $('#netdata_theme_control').change(function () { - urlOptions.theme = $(this).prop('checked') ? 'slate' : 'white'; - urlOptions.hashUpdate(); - - if (setTheme(urlOptions.theme)) { - netdataReload(); - } - }); -} - -function scrollDashboardTo() { - if (netdataSnapshotData !== null && typeof netdataSnapshotData.hash !== 'undefined') { - //console.log(netdataSnapshotData.hash); - scrollToId(netdataSnapshotData.hash.replace('#', '')); - } else { - // check if we have to jump to a specific section - scrollToId(urlOptions.hash.replace('#', '')); - - if (urlOptions.chart !== null) { - NETDATA.alarms.scrollToChart(urlOptions.chart); - //urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu); - //urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart); - //console.log('hash = ' + urlOptions.hash); - } - } -} - -var modalHiddenCallback = null; - -function scrollToChartAfterHidingModal(chart, alarmDate, alarmStatus) { - modalHiddenCallback = function () { - NETDATA.alarms.scrollToChart(chart, alarmDate); - - if (['WARNING', 'CRITICAL'].includes(alarmStatus)) { - const currentChartState = NETDATA.options.targets.find( - (chartState) => chartState.id === chart, - ) - const twoMinutes = 2 * 60 * 1000 - NETDATA.globalPanAndZoom.setMaster( - currentChartState, - alarmDate - twoMinutes, - alarmDate + twoMinutes, - ) - } - }; -} - -// ---------------------------------------------------------------------------- - -function enableTooltipsAndPopovers() { - $('[data-toggle="tooltip"]').tooltip({ - animated: 'fade', - trigger: 'hover', - html: true, - delay: { show: 500, hide: 0 }, - container: 'body' - }); - $('[data-toggle="popover"]').popover(); -} - -// ---------------------------------------------------------------------------- - -var runOnceOnDashboardLastRun = 0; - -function runOnceOnDashboardWithjQuery() { - if (runOnceOnDashboardLastRun !== 0) { - scrollDashboardTo(); - - // restore the scrollspy at the proper position - $(document.body).scrollspy('refresh'); - $(document.body).scrollspy('process'); - - return; - } - - runOnceOnDashboardLastRun = Date.now(); - - // ------------------------------------------------------------------------ - // bootstrap modals - - // prevent bootstrap modals from scrolling the page - // maintains the current scroll position - // https://stackoverflow.com/a/34754029/4525767 - - var scrollPos = 0; - var modal_depth = 0; // how many modals are currently open - var modal_shown = false; // set to true, if a modal is shown - var netdata_paused_on_modal = false; // set to true, if the modal paused netdata - var scrollspyOffset = $(window).height() / 3; // will be updated below - the offset of scrollspy to select an item - - $('.modal') - .on('show.bs.modal', function () { - if (modal_depth === 0) { - scrollPos = window.scrollY; - - $('body').css({ - overflow: 'hidden', - position: 'fixed', - top: -scrollPos - }); - - modal_shown = true; - - if (NETDATA.options.pauseCallback === null) { - NETDATA.pause(function () { - }); - netdata_paused_on_modal = true; - } else { - netdata_paused_on_modal = false; - } - } - - modal_depth++; - //console.log(urlOptions.after); - - }) - .on('hide.bs.modal', function () { - - modal_depth--; - - if (modal_depth <= 0) { - modal_depth = 0; - - $('body') - .css({ - overflow: '', - position: '', - top: '' - }); - - // scroll to the position we had open before the modal - $('html, body') - .animate({ scrollTop: scrollPos }, 0); - - // unpause netdata, if we paused it - if (netdata_paused_on_modal === true) { - NETDATA.unpause(); - netdata_paused_on_modal = false; - } - - // restore the scrollspy at the proper position - $(document.body).scrollspy('process'); - } - //console.log(urlOptions.after); - }) - .on('hidden.bs.modal', function () { - if (modal_depth === 0) { - modal_shown = false; - } - - if (typeof modalHiddenCallback === 'function') { - modalHiddenCallback(); - } - - modalHiddenCallback = null; - //console.log(urlOptions.after); - }); - - // ------------------------------------------------------------------------ - // sidebar / affix - - if (shouldShowSignInBanner()) { - const el = document.getElementById("sign-in-banner"); - if (el) { - el.style.display = "initial"; - el.classList.add(`theme-${netdataTheme}`); - } - } - - $('#sidebar') - .affix({ - offset: { - top: (isdemo()) ? 150 : 0, - bottom: 0 - } - }) - .on('affixed.bs.affix', function () { - // fix scrolling of very long affix lists - // http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long - - $(this).removeAttr('style'); - }) - .on('affix-top.bs.affix', function () { - // fix bootstrap affix click bug - // https://stackoverflow.com/a/37847981/4525767 - - if (modal_shown) { - return false; - } - }) - .on('activate.bs.scrollspy', function (e) { - // change the URL based on the current position of the screen - - if (modal_shown === false) { - var el = $(e.target); - var hash = el.find('a').attr('href'); - if (typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { - urlOptions.hash = hash; - urlOptions.hashUpdate(); - } - } - }); - - Ps.initialize(document.getElementById('sidebar'), { - wheelSpeed: 0.5, - wheelPropagation: true, - swipePropagation: true, - minScrollbarLength: null, - maxScrollbarLength: null, - useBothWheelAxes: false, - suppressScrollX: true, - suppressScrollY: false, - scrollXMarginOffset: 0, - scrollYMarginOffset: 0, - theme: 'default' - }); - - // ------------------------------------------------------------------------ - // scrollspy - - if (scrollspyOffset > 250) { - scrollspyOffset = 250; - } - if (scrollspyOffset < 75) { - scrollspyOffset = 75; - } - document.body.setAttribute('data-offset', scrollspyOffset); - - // scroll the dashboard, before activating the scrollspy, so that our - // hash will not be updated before we got the chance to scroll to it - scrollDashboardTo(); - - $(document.body).scrollspy({ - target: '#sidebar', - offset: scrollspyOffset // controls the diff of the element to the top, to select it - }); - - // ------------------------------------------------------------------------ - // my-netdata menu - - Ps.initialize(document.getElementById('my-netdata-dropdown-content'), { - wheelSpeed: 1, - wheelPropagation: false, - swipePropagation: false, - minScrollbarLength: null, - maxScrollbarLength: null, - useBothWheelAxes: false, - suppressScrollX: true, - suppressScrollY: false, - scrollXMarginOffset: 0, - scrollYMarginOffset: 0, - theme: 'default' - }); - - $('#myNetdataDropdownParent') - .on('show.bs.dropdown', function () { - var hash = urlOptions.genHash(); - $('.registry_link').each(function (idx) { - this.setAttribute('href', this.getAttribute("href").replace(/#.*$/, hash)); - }); - - NETDATA.pause(function () { - }); - }) - .on('shown.bs.dropdown', function () { - Ps.update(document.getElementById('my-netdata-dropdown-content')); - myNetdataMenuDidShow(); - }) - .on('hidden.bs.dropdown', function () { - NETDATA.unpause(); - }); - - $('#deleteRegistryModal') - .on('hidden.bs.modal', function () { - deleteRegistryGuid = null; - }); - - // ------------------------------------------------------------------------ - // update modal - - $('#updateModal') - .on('show.bs.modal', function () { - versionLog('checking, please wait...'); - }) - .on('shown.bs.modal', function () { - }); - - // ------------------------------------------------------------------------ - // alarms modal - - $('#alarmsModal') - .on('shown.bs.modal', function () { - alarmsUpdateModal(); - }) - .on('hidden.bs.modal', function () { - document.getElementById('alarms_active').innerHTML = - document.getElementById('alarms_all').innerHTML = - document.getElementById('alarms_log').innerHTML = - 'loading...'; - }); - - // ------------------------------------------------------------------------ - - dashboardSettingsSetup(); - loadSnapshotDragAndDropSetup(); - saveSnapshotModalSetup(); - showPageFooter(); - - // ------------------------------------------------------------------------ - // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js - - $.fn.shorten = function (settings) { - "use strict"; - - var config = { - showChars: 750, - minHideChars: 10, - ellipsesText: "...", - moreText: ' show more information', - lessText: ' show less information', - onLess: function () { - NETDATA.onscroll(); - }, - onMore: function () { - NETDATA.onscroll(); - }, - errMsg: null, - force: false - }; - - if (settings) { - $.extend(config, settings); - } - - if ($(this).data('jquery.shorten') && !config.force) { - return false; - } - $(this).data('jquery.shorten', true); - - $(document).off("click", '.morelink'); - - $(document).on({ - click: function () { - - var $this = $(this); - if ($this.hasClass('less')) { - $this.removeClass('less'); - $this.html(config.moreText); - $this.parent().prev().animate({ 'height': '0' + '%' }, 0, function () { - $this.parent().prev().prev().show(); - }).hide(0, function () { - config.onLess(); - }); - } else { - $this.addClass('less'); - $this.html(config.lessText); - $this.parent().prev().animate({ 'height': '100' + '%' }, 0, function () { - $this.parent().prev().prev().hide(); - }).show(0, function () { - config.onMore(); - }); - } - return false; - } - }, '.morelink'); - - return this.each(function () { - var $this = $(this); - - var content = $this.html(); - var contentlen = $this.text().length; - if (contentlen > config.showChars + config.minHideChars) { - var c = content.substr(0, config.showChars); - if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it - { - var inTag = false; // I'm in a tag? - var bag = ''; // Put the characters to be shown here - var countChars = 0; // Current bag size - var openTags = []; // Stack for opened tags, so I can close them later - var tagName = null; - - for (var i = 0, r = 0; r <= config.showChars; i++) { - if (content[i] === '<' && !inTag) { - inTag = true; - - // This could be "tag" or "/tag" - tagName = content.substring(i + 1, content.indexOf('>', i)); - - // If its a closing tag - if (tagName[0] === '/') { - - if (tagName !== ('/' + openTags[0])) { - config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes'; - } else { - openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!) - } - - } else { - // There are some nasty tags that don't have a close tag like
- if (tagName.toLowerCase() !== 'br') { - openTags.unshift(tagName); // Add to start the name of the tag that opens - } - } - } - - if (inTag && content[i] === '>') { - inTag = false; - } - - if (inTag) { - bag += content.charAt(i); - } else { - // Add tag name chars to the result - r++; - if (countChars <= config.showChars) { - bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the [] - countChars++; - } else { - // Now I have the characters needed - if (openTags.length > 0) { - // I have unclosed tags - - //console.log('They were open tags'); - //console.log(openTags); - for (var j = 0; j < openTags.length; j++) { - //console.log('Cierro tag ' + openTags[j]); - bag += ''; // Close all tags that were opened - - // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags - } - break; - } - } - } - } - c = $('
').html(bag + '' + config.ellipsesText + '').html(); - } else { - c += config.ellipsesText; - } - - var html = '
' + c + - '
' + content + - '
' + config.moreText + ''; - - $this.html(html); - $this.find(".allcontent").hide(); // Hide all text - $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened - } - }); - }; -} - -function finalizePage() { - // resize all charts - without starting the background thread - // this has to be done while NETDATA is paused - // if we omit this, the affix menu will be wrong, since all - // the Dom elements are initially zero-sized - NETDATA.parseDom(); - - // ------------------------------------------------------------------------ - - NETDATA.globalPanAndZoom.callback = null; - NETDATA.globalChartUnderlay.callback = null; - - if (urlOptions.pan_and_zoom === true && NETDATA.options.targets.length > 0) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); - } - - // callback for us to track PanAndZoom operations - NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback; - NETDATA.globalChartUnderlay.callback = urlOptions.netdataHighlightCallback; - - // ------------------------------------------------------------------------ - - // let it run (update the charts) - NETDATA.unpause(); - - runOnceOnDashboardWithjQuery(); - $(".shorten").shorten(); - enableTooltipsAndPopovers(); - - if (isdemo()) { - // do not to give errors on netdata demo servers for 60 seconds - NETDATA.options.current.retries_on_data_failures = 60; - - // google analytics when this is used for the home page of the demo sites - // this does not run on user's installations - setTimeout(function () { - (function (i, s, o, g, r, a, m) { - i['GoogleAnalyticsObject'] = r; - i[r] = i[r] || function () { - (i[r].q = i[r].q || []).push(arguments) - }, i[r].l = 1 * new Date(); - a = s.createElement(o), - m = s.getElementsByTagName(o)[0]; - a.async = 1; - a.src = g; - m.parentNode.insertBefore(a, m) - })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); - - ga('create', 'UA-64295674-3', 'auto'); - ga('send', 'pageview', '/demosite/' + window.location.host); - }, 2000); - } else { - } - - if (urlOptions.show_alarms === true) { - setTimeout(function () { - $('#alarmsModal').modal('show'); - }, 1000); - } - - NETDATA.onresizeCallback = function () { - Ps.update(document.getElementById('sidebar')); - Ps.update(document.getElementById('my-netdata-dropdown-content')); - }; - NETDATA.onresizeCallback(); - - if (netdataSnapshotData !== null) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], netdataSnapshotData.after_ms, netdataSnapshotData.before_ms); - } - - //if (urlOptions.nowelcome !== true) { - // setTimeout(function () { - // $('#welcomeModal').modal(); - // }, 2000); - //} - - // var netdataEnded = performance.now(); - // console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms'); -} - -function resetDashboardOptions() { - var help = NETDATA.options.current.show_help; - - NETDATA.resetOptions(); - if (setTheme('slate')) { - netdataReload(); - } - - if (help !== NETDATA.options.current.show_help) { - netdataReload(); - } -} - -// callback to add the dashboard info to the -// parallel javascript downloader in netdata -var netdataPrepCallback = function () { - NETDATA.requiredCSS.push({ - url: NETDATA.serverStatic + 'css/bootstrap-toggle-2.2.2.min.css', - isAlreadyLoaded: function () { - return false; - } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverStatic + 'lib/bootstrap-toggle-2.2.2.min.js', - isAlreadyLoaded: function () { - return false; - } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverStatic + 'dashboard_info.js?v20181019-1', - async: false, - isAlreadyLoaded: function () { - return false; - } - }); - - if (isdemo()) { - document.getElementById('masthead').style.display = 'block'; - } else { - if (urlOptions.update_always === true) { - NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always); - } - } -}; - -var selected_server_timezone = function (timezone, status) { - //console.log('called with timezone: ' + timezone + ", status: " + ((typeof status === 'undefined')?'undefined':status).toString()); - - // clear the error - document.getElementById('timezone_error_message').innerHTML = ''; - - if (typeof status === 'undefined') { - // the user selected a timezone from the menu - - NETDATA.setOption('user_set_server_timezone', timezone); - - if (NETDATA.dateTime.init(timezone) === false) { - NETDATA.dateTime.init(); - - if (!$('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('on'); - } - - document.getElementById('timezone_error_message').innerHTML = 'Ooops! That timezone was not accepted by your browser. Please open a github issue to help us fix it.'; - NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone); - } else { - if ($('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('off'); - } - } - } else if (status === true) { - // the user wants the browser default timezone to be activated - - NETDATA.dateTime.init(); - } else { - // the user wants the server default timezone to be activated - //console.log('found ' + NETDATA.options.current.user_set_server_timezone); - - if (NETDATA.options.current.user_set_server_timezone === 'default') { - NETDATA.options.current.user_set_server_timezone = NETDATA.options.server_timezone; - } - - timezone = NETDATA.options.current.user_set_server_timezone; - - if (NETDATA.dateTime.init(timezone) === false) { - NETDATA.dateTime.init(); - - if (!$('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('on'); - } - - document.getElementById('timezone_error_message').innerHTML = 'Sorry. The timezone "' + timezone.toString() + '" is not accepted by your browser. Please select one from the list.'; - NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone); - } - } - - document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone; - return false; -}; - -// our entry point -// var netdataStarted = performance.now(); - -var netdataCallback = initializeDynamicDashboard; - -// ================================================================================================= -// netdata.cloud - -let registryAgents = []; - -let cloudAgents = []; - -let myNetdataMenuFilterValue = ""; - -let cloudAccountID = null; - -let cloudAccountName = null; - -let cloudToken = null; - -/// Enforces a maximum string length while retaining the prefix and the postfix of -/// the string. -function truncateString(str, maxLength) { - if (str.length <= maxLength) { - return str; - } - - const spanLength = Math.floor((maxLength - 3) / 2); - return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`; -} - -// ------------------------------------------------------------------------------------------------- -// netdata.cloud API Client -// ------------------------------------------------------------------------------------------------- - -function isValidAgent(a) { - return a.urls != null && a.urls.length > 0; -} - -// https://github.com/netdata/hub/issues/146 -function getCloudAccountAgents() { - if (!isSignedIn()) { - return []; - } - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, - { - method: "GET", - mode: "cors", - headers: { - "Authorization": `Bearer ${cloudToken}` - } - } - ).then((response) => { - if (!response.ok) { - throw Error("Cannot fetch known accounts"); - } - return response.json(); - }).then((payload) => { - const agents = payload.result ? payload.result.agents : null; - - if (!agents) { - return []; - } - - return agents.filter((a) => isValidAgent(a)).map((a) => { - return { - "guid": a.id, - "name": a.name, - "url": a.urls[0], - "alternate_urls": a.urls - } - }) - }).catch(function (error) { - console.log(error); - return null; - }); -} - -/** Updates the lastAccessTime and accessCount properties of the agent for the account. */ -function touchAgent() { - if (!isSignedIn()) { - return []; - } - - const touchUrl = `${NETDATA.registry.cloudBaseURL}/api/v1/agents/${NETDATA.registry.machine_guid}/touch?account_id=${cloudAccountID}`; - return fetch( - touchUrl, - { - method: "post", - body: "", - mode: "cors", - headers: { - "Authorization": `Bearer ${cloudToken}` - } - } - ).then((response) => { - if (!response.ok) { - throw Error("Cannot touch agent" + JSON.stringify(response)); - } - return response.json(); - }).then((payload) => { - - }).catch(function (error) { - console.log(error); - return null; - }); -} - -// https://github.com/netdata/hub/issues/128 -function postCloudAccountAgents(agentsToSync) { - if (!isSignedIn()) { - return []; - } - - const maskedURL = NETDATA.registry.MASKED_DATA; - - const agents = agentsToSync.map((a) => { - const urls = a.alternate_urls.filter((url) => url != maskedURL); - - return { - "id": a.guid, - "name": a.name, - "urls": urls - } - }).filter((a) => isValidAgent(a)) - - const payload = { - "accountID": cloudAccountID, - "agents": agents, - "merge": false, - }; - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, - { - method: "POST", - mode: "cors", - headers: { - "Content-Type": "application/json; charset=utf-8", - "Authorization": `Bearer ${cloudToken}` - }, - body: JSON.stringify(payload) - } - ).then((response) => { - return response.json(); - }).then((payload) => { - const agents = payload.result ? payload.result.agents : null; - - if (!agents) { - return []; - } - - return agents.filter((a) => isValidAgent(a)).map((a) => { - return { - "guid": a.id, - "name": a.name, - "url": a.urls[0], - "alternate_urls": a.urls - } - }) - }); -} - -function deleteCloudAgentURL(agentID, url) { - if (!isSignedIn()) { - return []; - } - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents/${agentID}/url?value=${encodeURIComponent(url)}`, - { - method: "DELETE", - mode: "cors", - headers: { - "Content-Type": "application/json; charset=utf-8", - "Authorization": `Bearer ${cloudToken}` - }, - } - ).then((response) => { - return response.json(); - }).then((payload) => { - const count = payload.result ? payload.result.count : 0; - return count; - }); -} - -// ------------------------------------------------------------------------------------------------- - -function signInDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - - if (!NETDATA.registry.isUsingGlobalRegistry()) { - // If user is using a private registry, request his consent for - // synchronizing with cloud. - showSignInModal(); - return; - } - - signIn(); -} - -function shouldShowSignInBanner() { - return false; -} - -function closeSignInBanner() { - localStorage.setItem("signInBannerClosed", "true"); - const el = document.getElementById("sign-in-banner"); - if (el) { - el.style.display = "none"; - } -} - -function closeSignInBannerDidClick(e) { - closeSignInBanner(); -} - -function signOutDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - signOut(); -} - -// ------------------------------------------------------------------------------------------------- - -function updateMyNetdataAfterFilterChange() { - const machinesEl = document.getElementById("my-netdata-menu-machines") - machinesEl.innerHTML = renderMachines(cloudAgents); - - if (options.hosts.length > 1) { - const streamedEl = document.getElementById("my-netdata-menu-streamed") - streamedEl.innerHTML = renderStreamedHosts(options); - } -} - -function myNetdataMenuDidShow() { - const filterEl = document.getElementById("my-netdata-menu-filter-input"); - if (filterEl) { - filterEl.focus(); - } -} - -function myNetdataFilterDidChange(e) { - const inputEl = e.target; - setTimeout(() => { - myNetdataMenuFilterValue = inputEl.value; - updateMyNetdataAfterFilterChange(); - }, 1); -} - -function myNetdataFilterClearDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - - const inputEl = document.getElementById("my-netdata-menu-filter-input"); - inputEl.value = ""; - myNetdataMenuFilterValue = ""; - - updateMyNetdataAfterFilterChange(); - - inputEl.focus(); -} - -// ------------------------------------------------------------------------------------------------- - -function clearCloudVariables() { - cloudAccountID = null; - cloudAccountName = null; - cloudToken = null; -} - -function clearCloudLocalStorageItems() { - localStorage.removeItem("cloud.baseURL"); - localStorage.removeItem("cloud.agentID"); - localStorage.removeItem("cloud.sync"); -} - -function signIn() { - const url = `${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}`; - window.open(url); -} - -function signOut() { - cloudSSOSignOut(); -} - -function handleMessage(e) { - switch (e.data.type) { - case "sign-in": - handleSignInMessage(e); - break; - - case "sign-out": - handleSignOutMessage(e); - break; - - default: - return; - } -} - -function handleSignInMessage(e) { - closeSignInBanner(); - localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); - - cloudAccountID = e.data.accountID; - cloudAccountName = e.data.accountName; - cloudToken = e.data.token; - - netdataRegistryCallback(registryAgents); - if (e.data.redirectURI && !window.location.href.includes(e.data.redirectURI)) { - // lgtm false-positive - redirectURI does not come from user input, but from iframe callback - window.location.replace(e.data.redirectURI); // lgtm[js/client-side-unvalidated-url-redirection] - } -} - -function handleSignOutMessage(e) { - clearCloudVariables(); - renderMyNetdataMenu(registryAgents); -} - -function isSignedIn() { - return cloudToken != null && cloudAccountID != null; -} - -function sortedArraysEqual(a, b) { - if (a.length != b.length) return false; - - for (var i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false; - } - - return true; -} - -// If merging is needed returns the merged agents set, otherwise returns null. -function mergeAgents(cloud, local) { - let dirty = false; - - const union = new Map(); - - for (const cagent of cloud) { - union.set(cagent.guid, cagent); - } - - for (const lagent of local) { - const cagent = union.get(lagent.guid); - if (cagent) { - for (const u of lagent.alternate_urls) { - if (u === NETDATA.registry.MASKED_DATA) { // TODO: temp until registry is updated. - continue; - } - - if (!cagent.alternate_urls.includes(u)) { - dirty = true; - cagent.alternate_urls.push(u); - } - } - } else { - dirty = true; - union.set(lagent.guid, lagent); - } - } - - if (dirty) { - return Array.from(union.values()); - } - - return null; -} - -function showSignInModal() { - document.getElementById("sim-registry").innerHTML = NETDATA.registry.server; - $("#signInModal").modal("show"); -} - -function explicitlySignIn() { - $("#signInModal").modal("hide"); - signIn(); -} - -function showSyncModal() { - document.getElementById("sync-registry-modal-registry").innerHTML = NETDATA.registry.server; - $("#syncRegistryModal").modal("show"); -} - -function explicitlySyncAgents() { - $("#syncRegistryModal").modal("hide"); - - const json = localStorage.getItem("cloud.sync"); - const sync = json ? JSON.parse(json) : {}; - delete sync[cloudAccountID]; - localStorage.setItem("cloud.sync", JSON.stringify(sync)); - - NETDATA.registry.init(); -} - -function syncAgents(callback) { - const json = localStorage.getItem("cloud.sync"); - const sync = json ? JSON.parse(json) : {}; - - const currentAgent = { - guid: NETDATA.registry.machine_guid, - name: NETDATA.registry.hostname, - url: NETDATA.serverDefault, - alternate_urls: [NETDATA.serverDefault], - } - - const localAgents = sync[cloudAccountID] - ? [currentAgent] - : registryAgents.concat([currentAgent]); - - console.log("Checking if sync is needed.", localAgents); - - const agentsToSync = mergeAgents(cloudAgents, localAgents); - - if ((!sync[cloudAccountID]) || agentsToSync) { - sync[cloudAccountID] = new Date().getTime(); - localStorage.setItem("cloud.sync", JSON.stringify(sync)); - } - - if (agentsToSync) { - console.log("Synchronizing with netdata.cloud."); - - postCloudAccountAgents(agentsToSync).then((agents) => { - // TODO: clear syncTime on error! - cloudAgents = agents; - callback(cloudAgents); - }); - - return - } - - callback(cloudAgents); -} - -let isCloudSSOInitialized = false; - -function cloudSSOInit() { - const iframeEl = document.getElementById("ssoifrm"); - const url = `${NETDATA.registry.cloudBaseURL}/account/sso-agent?id=${NETDATA.registry.machine_guid}`; - iframeEl.src = url; - isCloudSSOInitialized = true; -} - -function cloudSSOSignOut() { - const iframe = document.getElementById("ssoifrm"); - const url = `${NETDATA.registry.cloudBaseURL}/account/sign-out-agent`; - iframe.src = url; -} - -function initCloud() { - if (!NETDATA.registry.isCloudEnabled) { - clearCloudVariables(); - clearCloudLocalStorageItems(); - return; - } - - if (NETDATA.registry.cloudBaseURL != localStorage.getItem("cloud.baseURL")) { - clearCloudVariables(); - clearCloudLocalStorageItems(); - if (NETDATA.registry.cloudBaseURL) { - localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); - } - } - - if (!isCloudSSOInitialized) { - cloudSSOInit(); - } - - touchAgent(); -} - -// This callback is called after NETDATA.registry is initialized. -function netdataRegistryCallback(machinesArray) { - localStorage.setItem("cloud.agentID", NETDATA.registry.machine_guid); - - initCloud(); - - registryAgents = machinesArray; - - if (isSignedIn()) { - // We call getCloudAccountAgents() here because it requires that - // NETDATA.registry is initialized. - clearMyNetdataMenu(); - getCloudAccountAgents().then((agents) => { - if (!agents) { - errorMyNetdataMenu(); - return; - } - cloudAgents = agents; - syncAgents((agents) => { - const agentsMap = {} - for (const agent of agents) { - agentsMap[agent.guid] = agent; - } - - NETDATA.registry.machines = agentsMap; - NETDATA.registry.machines_array = agents; - - renderMyNetdataMenu(agents); - }); - }); - } else { - renderMyNetdataMenu(machinesArray) - } -}; - -// If we know the cloudBaseURL and agentID from local storage render (eagerly) -// the account ui before receiving the definitive response from the web server. -// This improves the perceived performance. -function tryFastInitCloud() { - const baseURL = localStorage.getItem("cloud.baseURL"); - const agentID = localStorage.getItem("cloud.agentID"); - - if (baseURL && agentID) { - NETDATA.registry.cloudBaseURL = baseURL; - NETDATA.registry.machine_guid = agentID; - NETDATA.registry.isCloudEnabled = true; - - initCloud(); - } -} - -function initializeApp() { - window.addEventListener("message", handleMessage, false); - - // tryFastInitCloud(); -} - -if (document.readyState === "complete") { - initializeApp(); -} else { - document.addEventListener("readystatechange", () => { - if (document.readyState === "complete") { - initializeApp(); - } - }); -} diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/netdata b/luci-app-netdata/root/usr/share/netdata/webcn/netdata deleted file mode 100644 index 99691c0..0000000 --- a/luci-app-netdata/root/usr/share/netdata/webcn/netdata +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 -USE_PROCD=1 - -APPBINARY=/usr/sbin/netdata -CONFIGFILE=/etc/netdata/netdata.conf - -get_config() { - config_get_bool enabled $1 enabled 1 - config_get_bool logger $1 logger 1 - config_get port $1 port 19999 -} - -netdata_prepare() { - pgrep -f /usr/sbin/netdata | xargs kill -9 >/dev/null 2>&1 - logger -t netdata -p warn "netdata is stop." - echo "netdata is stop." -} - - -stop_service() { - netdata_prepare -} - -start_service() { - netdata_prepare - config_load netdata - config_foreach get_config netdata - [ x$enabled == x1 ] || return 1 - mkdir -m 0755 -p /var/cache/netdata - logger -t netdata -p warn "netdata is start." - echo "netdata is start." - chown nobody /var/cache/netdata - mkdir -m 0755 -p /var/lib/netdata - chown nobody /var/lib/netdata - mkdir -m 0755 -p /var/log/netdata - chown nobody /var/log/netdata - procd_open_instance - procd_set_param command $APPBINARY -D -c $CONFIGFILE -p $port:$port - [ "x$logger" == x1 ] && procd_set_param stderr 1 - procd_set_param file $CONFIGFILE - procd_set_param respawn - procd_close_instance -} - -service_triggers() { - procd_add_reload_trigger "netdata" -} diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf b/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf deleted file mode 100644 index 9b2c9d2..0000000 --- a/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf +++ /dev/null @@ -1,52 +0,0 @@ -# Full configuration can be retrieved from the running -# server at http://localhost:19999/netdata.conf -# -# Example: -# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf -# - -[global] - update every = 2 - memory deduplication (ksm) = no - debug log = syslog - error log = syslog - access log = none - run as user = root - -[web] - allow connections from = * - allow dashboard from = * - -[plugins] - cgroups = no - apps = no - charts.d = no - fping = no - node.d = no - python.d = no - -[plugin:proc] - ipc =no - /proc/sysvipc/shm = no - /sys/devices/system/edac/mc = no - /sys/devices/system/node = no - /proc/net/sockstat = no - /proc/net/netstat = no - /proc/net/snmp = no - /proc/net/softnet_stat = no - /proc/net/sctp/snmp = no - /proc/net/ip_vs/stats = no - /proc/net/stat/synproxy = no - /sys/kernel/mm/ksm = no - /dev/mapper = no - semaphore totals = no - -[plugin:proc:/proc/diskstats] - path to /dev/vx/dsk = - path to /dev/disk/by-label = - -[health] - enabled = no - -[plugin:proc:ipc] - shared memory totals = no diff --git a/luci-app-netdata/root/usr/share/rpcd/acl.d/luci-app-netdata.json b/luci-app-netdata/root/usr/share/rpcd/acl.d/luci-app-netdata.json deleted file mode 100644 index 6f19d2f..0000000 --- a/luci-app-netdata/root/usr/share/rpcd/acl.d/luci-app-netdata.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-nedata": { - "description": "Grant UCI access for luci-app-netdata", - "read": { - "uci": [ "netdata" ] - }, - "write": { - "uci": [ "netdata" ] - } - } -}