summaryrefslogtreecommitdiffstats
path: root/web/gui/dashboard.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/gui/dashboard.js')
-rw-r--r--web/gui/dashboard.js9512
1 files changed, 9512 insertions, 0 deletions
diff --git a/web/gui/dashboard.js b/web/gui/dashboard.js
new file mode 100644
index 0000000000..16fbf88d0a
--- /dev/null
+++ b/web/gui/dashboard.js
@@ -0,0 +1,9512 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// ----------------------------------------------------------------------------
+// You can set the following variables before loading this script:
+
+/*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
+
+var NETDATA = window.NETDATA || {};
+
+(function(window, document, $, undefined) {
+
+ NETDATA.encodeURIComponent = function(s) {
+ if(typeof(s) === 'string')
+ return encodeURIComponent(s);
+
+ return s;
+ };
+
+ // ------------------------------------------------------------------------
+ // 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;
+ };
+ }
+
+ NETDATA.name2id = function(s) {
+ return s
+ .replace(/ /g, '_')
+ .replace(/\(/g, '_')
+ .replace(/\)/g, '_')
+ .replace(/\./g, '_')
+ .replace(/\//g, '_');
+ };
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // 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, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#39;');
+ },
+
+ object: function(name, obj, ignore_regex) {
+ if(typeof ignore_regex !== 'undefined' && ignore_regex.test(name) === true) {
+ // console.log('XSS: ignoring "' + name + '"');
+ return obj;
+ }
+
+ switch (typeof(obj)) {
+ case 'string':
+ var 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) === true) {
+ // console.log('checking array "' + name + '"');
+
+ var 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 === true) {
+ //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 === true) {
+ //console.log('XSS: checking data "' + name + '"...');
+ return this.object(name, obj, ignore_regex);
+ }
+ return obj;
+ }
+ };
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // 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() {
+ var script = null;
+
+ if(typeof document.currentScript !== 'undefined') {
+ script = document.currentScript;
+ }
+ else {
+ var 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;
+ };
+
+ if(typeof netdataServer !== 'undefined')
+ NETDATA.serverDefault = netdataServer;
+ else {
+ var 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;
+ }
+
+
+ // 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';
+
+ NETDATA.themes = {
+ white: {
+ bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-3.3.7.css',
+ dashboard_css: NETDATA.serverStatic + 'dashboard.css?v20180210-1',
+ 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?v20180210-1',
+ 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' ];
+
+ NETDATA.icons = {
+ left: '<i class="fas fa-backward"></i>',
+ reset: '<i class="fas fa-play"></i>',
+ right: '<i class="fas fa-forward"></i>',
+ zoomIn: '<i class="fas fa-plus"></i>',
+ zoomOut: '<i class="fas fa-minus"></i>',
+ resize: '<i class="fas fa-sort"></i>',
+ lineChart: '<i class="fas fa-chart-line"></i>',
+ areaChart: '<i class="fas fa-chart-area"></i>',
+ noChart: '<i class="fas fa-chart-area"></i>',
+ loading: '<i class="fas fa-sync-alt"></i>',
+ noData: '<i class="fas fa-exclamation-triangle"></i>'
+ };
+
+ if(typeof netdataIcons === 'object') {
+ for(var icon in NETDATA.icons) {
+ if(NETDATA.icons.hasOwnProperty(icon) && 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 = 1500;
+
+ if(typeof netdataRegistry === 'undefined') {
+ // backward compatibility
+ netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false);
+ }
+ if(netdataRegistry === false && typeof netdataRegistryCallback === 'function')
+ netdataRegistry = true;
+
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // detect if this is probably a slow device
+
+ var isSlowDeviceResult = undefined;
+ var isSlowDevice = function() {
+ if(isSlowDeviceResult !== undefined)
+ return isSlowDeviceResult;
+
+ try {
+ var ua = navigator.userAgent.toLowerCase();
+
+ var iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream;
+ var android = /android/.test(ua) && !window.MSStream;
+ isSlowDeviceResult = (iOS === true || android === true);
+ }
+ catch (e) {
+ isSlowDeviceResult = false;
+ }
+
+ return isSlowDeviceResult;
+ };
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // 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() === false), // enable parallel refresh of charts
+
+ concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
+
+ destroy_on_hide: (isSlowDevice() === true), // 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() === false), // 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: false, // 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
+ };
+
+
+ // ----------------------------------------------------------------------------------------------------------------
+
+ 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() {
+ var 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 === true) {
+ // we have installed custom .step() / .clear() functions
+ // overwrite the .set() too
+
+ this.set = function(callback, delay) {
+ var that = this;
+
+ var start = Date.now(),
+ handle = new Object();
+
+ function loop() {
+ var current = Date.now(),
+ delta = current - start;
+
+ if(delta >= delay) {
+ callback.call();
+ }
+ else {
+ handle.value = that.step(loop);
+ }
+ }
+
+ handle.value = that.step(loop);
+ return handle;
+ };
+ }
+ }
+ };
+
+ NETDATA.timeout.init();
+
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // 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') {
+ var 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) {
+ var 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() === true) {
+ 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() === true) {
+ // 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) {
+ var keys = Object.keys(obj);
+ var len = keys.length;
+ while(len--) {
+ var 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) {
+ var 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.toSt