// 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 ":