// 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 *//* boolen, 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-2.2.4.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 ":