1 // You can set the following variables before loading this script:
3 // var netdataNoDygraphs = true; // do not use dygraph
4 // var netdataNoSparklines = true; // do not use sparkline
5 // var netdataNoPeitys = true; // do not use peity
6 // var netdataNoGoogleCharts = true; // do not use google
7 // var netdataNoMorris = true; // do not use morris
8 // var netdataNoEasyPieChart = true; // do not use easy pie chart
9 // var netdataNoGauge = true; // do not use gauge.js
10 // var netdataNoD3 = true; // do not use D3
11 // var netdataNoC3 = true; // do not use C3
12 // var netdataNoBootstrap = true; // do not load bootstrap
13 // var netdataDontStart = true; // do not start the thread to process the charts
14 // var netdataErrorCallback = null; // Callback function that will be invoked upon error
15 // var netdataRegistry = true; // Update the registry (default disabled)
16 // var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
17 // the URLs from the registry
18 // var netdataShowHelp = false; // enable/disable help (default enabled)
19 // var netdataShowAlarms = true; // enable/disable alarms checks and notifications (default disabled)
21 // var netdataRegistryAfterMs = 1500 // the time to consult to registry on startup
23 // var netdataCallback = null; // a function to call when netdata is ready
24 // // netdata will be running while this is called (call NETDATA.pause to stop it)
25 // var netdataPrepCallback = null; // a callback to be called before netdata does anything else
27 // You can also set the default netdata server, using the following.
28 // When this variable is not set, we assume the page is hosted on your
29 // netdata server already.
30 // var netdataServer = "http://yourhost:19999"; // set your NetData server
32 //(function(window, document, undefined) {
34 // ------------------------------------------------------------------------
35 // compatibility fixes
37 // fix IE issue with console
38 if(!window.console) { window.console = { log: function(){} }; }
40 // if string.endsWith is not defined, define it
41 if(typeof String.prototype.endsWith !== 'function') {
42 String.prototype.endsWith = function(s) {
43 if(s.length > this.length) return false;
44 return this.slice(-s.length) === s;
48 // if string.startsWith is not defined, define it
49 if(typeof String.prototype.startsWith !== 'function') {
50 String.prototype.startsWith = function(s) {
51 if(s.length > this.length) return false;
52 return this.slice(s.length) === s;
57 var NETDATA = window.NETDATA || {};
59 NETDATA.name2id = function(s) {
68 // ----------------------------------------------------------------------------------------------------------------
69 // Detect the netdata server
71 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
72 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
73 NETDATA._scriptSource = function() {
76 if(typeof document.currentScript !== 'undefined') {
77 script = document.currentScript;
80 var all_scripts = document.getElementsByTagName('script');
81 script = all_scripts[all_scripts.length - 1];
84 if (typeof script.getAttribute.length !== 'undefined')
87 script = script.getAttribute('src', -1);
92 if(typeof netdataServer !== 'undefined')
93 NETDATA.serverDefault = netdataServer;
95 var s = NETDATA._scriptSource();
96 if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
98 console.log('WARNING: Cannot detect the URL of the netdata server.');
99 NETDATA.serverDefault = null;
103 if(NETDATA.serverDefault === null)
104 NETDATA.serverDefault = '';
105 else if(NETDATA.serverDefault.slice(-1) !== '/')
106 NETDATA.serverDefault += '/';
108 // default URLs for all the external files we need
109 // make them RELATIVE so that the whole thing can also be
110 // installed under a web server
111 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-3.1.1.min.js';
112 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
113 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
114 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
115 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge.min.js';
116 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-f6ec7be.js';
117 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
118 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
119 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
120 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3.min.js';
121 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3.min.js';
122 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3.min.css';
123 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
124 NETDATA.google_js = 'https://www.google.com/jsapi';
128 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css',
129 dashboard_css: NETDATA.serverDefault + 'dashboard.css?v57',
130 background: '#FFFFFF',
131 foreground: '#000000',
134 colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477',
135 '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6',
136 '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707',
137 '#329262', '#3B3EAC' ],
138 easypiechart_track: '#f0f0f0',
139 easypiechart_scale: '#dfe0e0',
140 gauge_pointer: '#C0C0C0',
141 gauge_stroke: '#F0F0F0',
142 gauge_gradient: false
145 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
146 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v57',
147 background: '#272b30',
148 foreground: '#C8C8C8',
151 /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00',
152 '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0',
153 '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a',
154 '#a6a479', '#a66da8' ],
156 colors: [ '#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00',
157 '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700',
158 '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737',
159 '#329262', '#3B3EFF' ],
160 easypiechart_track: '#373b40',
161 easypiechart_scale: '#373b40',
162 gauge_pointer: '#474b50',
163 gauge_stroke: '#373b40',
164 gauge_gradient: false
168 if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
169 NETDATA.themes.current = NETDATA.themes[netdataTheme];
171 NETDATA.themes.current = NETDATA.themes.white;
173 NETDATA.colors = NETDATA.themes.current.colors;
175 // these are the colors Google Charts are using
176 // we have them here to attempt emulate their look and feel on the other chart libraries
177 // http://there4.io/2012/05/02/google-chart-color-list/
178 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
179 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
180 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
182 // an alternative set
183 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
184 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
185 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
187 if(typeof netdataShowHelp === 'undefined')
188 netdataShowHelp = true;
190 if(typeof netdataShowAlarms === 'undefined')
191 netdataShowAlarms = false;
193 if(typeof netdataRegistryAfterMs !== 'number' || netdataRegistryAfterMs < 0)
194 netdataRegistryAfterMs = 1500;
196 if(typeof netdataRegistry === 'undefined') {
197 // backward compatibility
198 if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false)
199 netdataRegistry = true;
201 netdataRegistry = false;
203 if(netdataRegistry === false && typeof netdataRegistryCallback === 'function')
204 netdataRegistry = true;
206 // ----------------------------------------------------------------------------------------------------------------
207 // the defaults for all charts
209 // if the user does not specify any of these, the following will be used
211 NETDATA.chartDefaults = {
212 host: NETDATA.serverDefault, // the server to get data from
213 width: '100%', // the chart width - can be null
214 height: '100%', // the chart height - can be null
215 min_width: null, // the chart minimum width - can be null
216 library: 'dygraph', // the graphing library to use
217 method: 'average', // the grouping method
218 before: 0, // panning
219 after: -600, // panning
220 pixels_per_point: 1, // the detail of the chart
221 fill_luminance: 0.8 // luminance of colors in solit areas
224 // ----------------------------------------------------------------------------------------------------------------
228 pauseCallback: null, // a callback when we are really paused
230 pause: false, // when enabled we don't auto-refresh the charts
232 targets: null, // an array of all the state objects that are
233 // currently active (independently of their
234 // viewport visibility)
236 updated_dom: true, // when true, the DOM has been updated with
237 // new elements we have to check.
239 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
240 // rendering charts continiously.
241 // used with .current.fast_render_timeframe
243 page_is_visible: true, // when true, this page is visible
245 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
246 // auto-refresher for some time (when a chart is
247 // performing pan or zoom, we need to stop refreshing
248 // all other charts, to have the maximum speed for
249 // rendering the chart that is panned or zoomed).
250 // Used with .current.global_pan_sync_time
252 last_resized: new Date().getTime(), // the timestamp of the last resize request
254 last_page_scroll: 0, // the timestamp the last time the page was scrolled
256 // the current profile
257 // we may have many...
259 pixels_per_point: 1, // the minimum pixels per point for all charts
260 // increase this to speed javascript up
261 // each chart library has its own limit too
262 // the max of this and the chart library is used
263 // the final is calculated every time, so a change
264 // here will have immediate effect on the next chart
267 idle_between_charts: 100, // ms - how much time to wait between chart updates
269 fast_render_timeframe: 200, // ms - render continously until this time of continious
270 // rendering has been reached
271 // this setting is used to make it render e.g. 10
272 // charts at once, sleep idle_between_charts time
273 // and continue for another 10 charts.
275 idle_between_loops: 500, // ms - if all charts have been updated, wait this
276 // time before starting again.
278 idle_parallel_loops: 100, // ms - the time between parallel refresher updates
280 idle_lost_focus: 500, // ms - when the window does not have focus, check
281 // if focus has been regained, every this time
283 global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
284 // autorefreshing of charts is paused for this amount
287 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
288 // of time before setting up synchronized selections
291 sync_selection: true, // enable or disable selection sync
293 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
295 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
297 pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
299 update_only_visible: true, // enable or disable visibility management
301 parallel_refresher: true, // enable parallel refresh of charts
303 concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
305 destroy_on_hide: false, // destroy charts when they are not visible
307 show_help: netdataShowHelp, // when enabled the charts will show some help
308 show_help_delay_show_ms: 500,
309 show_help_delay_hide_ms: 0,
311 eliminate_zero_dimensions: true, // do not show dimensions with just zeros
313 stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
314 stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts
316 double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap
318 smooth_plot: true, // enable smooth plot, where possible
320 charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
322 color_fill_opacity_line: 1.0,
323 color_fill_opacity_area: 0.2,
324 color_fill_opacity_stacked: 0.8,
326 pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
327 pan_and_zoom_factor_multiplier_control: 2.0,
328 pan_and_zoom_factor_multiplier_shift: 3.0,
329 pan_and_zoom_factor_multiplier_alt: 4.0,
331 abort_ajax_on_scroll: false, // kill pending ajax page scroll
332 async_on_scroll: false, // sync/async onscroll handler
333 onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler
335 setOptionCallback: function() { ; }
343 chart_data_url: false,
344 chart_errors: false, // FIXME
352 NETDATA.statistics = {
355 refreshes_active_max: 0
359 // ----------------------------------------------------------------------------------------------------------------
360 // local storage options
362 NETDATA.localStorage = {
365 callback: {} // only used for resetting back to defaults
368 NETDATA.localStorageGet = function(key, def, callback) {
371 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
372 NETDATA.localStorage.default[key.toString()] = def;
373 NETDATA.localStorage.callback[key.toString()] = callback;
376 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
378 // console.log('localStorage: loading "' + key.toString() + '"');
379 ret = localStorage.getItem(key.toString());
380 // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
381 if(ret === null || ret === 'undefined') {
382 // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
383 localStorage.setItem(key.toString(), JSON.stringify(def));
387 // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
388 ret = JSON.parse(ret);
389 // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
393 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
398 if(typeof ret === 'undefined' || ret === 'undefined') {
399 console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
403 NETDATA.localStorage.current[key.toString()] = ret;
407 NETDATA.localStorageSet = function(key, value, callback) {
408 if(typeof value === 'undefined' || value === 'undefined') {
409 console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
412 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
413 NETDATA.localStorage.default[key.toString()] = value;
414 NETDATA.localStorage.current[key.toString()] = value;
415 NETDATA.localStorage.callback[key.toString()] = callback;
418 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
419 // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
421 localStorage.setItem(key.toString(), JSON.stringify(value));
424 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
428 NETDATA.localStorage.current[key.toString()] = value;
432 NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
434 if(typeof obj[i] === 'object') {
435 //console.log('object ' + prefix + '.' + i.toString());
436 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
440 obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
444 NETDATA.setOption = function(key, value) {
445 if(key.toString() === 'setOptionCallback') {
446 if(typeof NETDATA.options.current.setOptionCallback === 'function') {
447 NETDATA.options.current[key.toString()] = value;
448 NETDATA.options.current.setOptionCallback();
451 else if(NETDATA.options.current[key.toString()] !== value) {
452 var name = 'options.' + key.toString();
454 if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
455 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
457 //console.log(NETDATA.localStorage);
458 //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
459 //console.log(NETDATA.options);
460 NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
462 if(typeof NETDATA.options.current.setOptionCallback === 'function')
463 NETDATA.options.current.setOptionCallback();
469 NETDATA.getOption = function(key) {
470 return NETDATA.options.current[key.toString()];
473 // read settings from local storage
474 NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
476 // always start with this option enabled.
477 NETDATA.setOption('stop_updates_when_focus_is_lost', true);
479 NETDATA.resetOptions = function() {
480 for(var i in NETDATA.localStorage.default) {
481 var a = i.split('.');
483 if(a[0] === 'options') {
484 if(a[1] === 'setOptionCallback') continue;
485 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
486 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
488 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
490 else if(a[0] === 'chart_heights') {
491 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
492 NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
498 // ----------------------------------------------------------------------------------------------------------------
500 if(NETDATA.options.debug.main_loop === true)
501 console.log('welcome to NETDATA');
503 NETDATA.onresize = function() {
504 NETDATA.options.last_resized = new Date().getTime();
508 NETDATA.onscroll_updater_count = 0;
509 NETDATA.onscroll_updater_running = false;
510 NETDATA.onscroll_updater_last_run = 0;
511 NETDATA.onscroll_updater_watchdog = null;
512 NETDATA.onscroll_updater_max_duration = 0;
513 NETDATA.onscroll_updater_above_threshold_count = 0;
514 NETDATA.onscroll_updater = function() {
515 NETDATA.onscroll_updater_running = true;
516 NETDATA.onscroll_updater_count++;
517 var start = new Date().getTime();
519 var targets = NETDATA.options.targets;
520 var len = targets.length;
522 // when the user scrolls he sees that we have
523 // hidden all the not-visible charts
524 // using this little function we try to switch
525 // the charts back to visible quickly
528 if(NETDATA.options.abort_ajax_on_scroll === true) {
529 // we have to cancel pending requests too
532 if (targets[len]._updating === true) {
533 if (typeof targets[len].xhr !== 'undefined') {
534 targets[len].xhr.abort();
535 targets[len].running = false;
536 targets[len]._updating = false;
538 targets[len].isVisible();
543 // just find which chart is visible
546 targets[len].isVisible();
549 var end = new Date().getTime();
550 // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms');
552 if(NETDATA.options.current.async_on_scroll === false) {
553 var dt = end - start;
554 if(dt > NETDATA.onscroll_updater_max_duration) {
555 // console.log('max onscroll event handler duration increased to ' + dt);
556 NETDATA.onscroll_updater_max_duration = dt;
559 if(dt > NETDATA.options.current.onscroll_worker_duration_threshold) {
560 // console.log('slow: ' + dt);
561 NETDATA.onscroll_updater_above_threshold_count++;
563 if(NETDATA.onscroll_updater_above_threshold_count > 2 && NETDATA.onscroll_updater_above_threshold_count * 100 / NETDATA.onscroll_updater_count > 2) {
564 NETDATA.options.current.async_on_scroll = true;
565 console.log('NETDATA: your browser is slow - enabling asynchronous onscroll event handler.');
570 NETDATA.onscroll_updater_last_run = start;
571 NETDATA.onscroll_updater_running = false;
574 NETDATA.onscroll = function() {
575 // console.log('onscroll');
577 NETDATA.options.last_page_scroll = new Date().getTime();
578 NETDATA.options.auto_refresher_stop_until = 0;
580 if(NETDATA.options.targets === null) return;
582 if(NETDATA.options.current.async_on_scroll === true) {
584 if(NETDATA.onscroll_updater_running === false) {
585 NETDATA.onscroll_updater_running = true;
586 setTimeout(NETDATA.onscroll_updater, 0);
589 if(NETDATA.onscroll_updater_watchdog !== null)
590 clearTimeout(NETDATA.onscroll_updater_watchdog);
592 NETDATA.onscroll_updater_watchdog = setTimeout(function() {
593 if(NETDATA.onscroll_updater_running === false && NETDATA.options.last_page_scroll > NETDATA.onscroll_updater_last_run) {
594 // console.log('watchdog');
595 NETDATA.onscroll_updater();
598 NETDATA.onscroll_updater_watchdog = null;
604 NETDATA.onscroll_updater();
608 window.onresize = NETDATA.onresize;
609 window.onscroll = NETDATA.onscroll;
611 // ----------------------------------------------------------------------------------------------------------------
614 NETDATA.errorCodes = {
615 100: { message: "Cannot load chart library", alert: true },
616 101: { message: "Cannot load jQuery", alert: true },
617 402: { message: "Chart library not found", alert: false },
618 403: { message: "Chart library not enabled/is failed", alert: false },
619 404: { message: "Chart not found", alert: false },
620 405: { message: "Cannot download charts index from server", alert: true },
621 406: { message: "Invalid charts index downloaded from server", alert: true },
622 407: { message: "Cannot HELLO netdata server", alert: false },
623 408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
624 409: { message: "Cannot ACCESS netdata registry", alert: false },
625 410: { message: "Netdata registry ACCESS failed", alert: false },
626 411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
627 412: { message: "Netdata registry DELETE failed", alert: false },
628 413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
629 414: { message: "Netdata registry SWITCH failed", alert: false },
630 415: { message: "Netdata alarms download failed", alert: false },
631 416: { message: "Netdata alarms log download failed", alert: false },
632 417: { message: "Netdata registry server send invalid response to SEARCH ", alert: false },
633 418: { message: "Netdata registry SEARCH failed", alert: false }
635 NETDATA.errorLast = {
641 NETDATA.error = function(code, msg) {
642 NETDATA.errorLast.code = code;
643 NETDATA.errorLast.message = msg;
644 NETDATA.errorLast.datetime = new Date().getTime();
646 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
649 if(typeof netdataErrorCallback === 'function') {
650 ret = netdataErrorCallback('system', code, msg);
653 if(ret && NETDATA.errorCodes[code].alert)
654 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
657 NETDATA.errorReset = function() {
658 NETDATA.errorLast.code = 0;
659 NETDATA.errorLast.message = "You are doing fine!";
660 NETDATA.errorLast.datetime = 0;
663 // ----------------------------------------------------------------------------------------------------------------
666 // When multiple charts need the same chart, we avoid downloading it
667 // multiple times (and having it in browser memory multiple time)
668 // by using this registry.
670 // Every time we download a chart definition, we save it here with .add()
671 // Then we try to get it back with .get(). If that fails, we download it.
673 NETDATA.chartRegistry = {
676 fixid: function(id) {
677 return id.replace(/:/g, "_").replace(/\//g, "_");
680 add: function(host, id, data) {
681 host = this.fixid(host);
684 if(typeof this.charts[host] === 'undefined')
685 this.charts[host] = {};
687 //console.log('added ' + host + '/' + id);
688 this.charts[host][id] = data;
691 get: function(host, id) {
692 host = this.fixid(host);
695 if(typeof this.charts[host] === 'undefined')
698 if(typeof this.charts[host][id] === 'undefined')
701 //console.log('cached ' + host + '/' + id);
702 return this.charts[host][id];
705 downloadAll: function(host, callback) {
706 while(host.slice(-1) === '/')
707 host = host.substring(0, host.length - 1);
712 url: host + '/api/v1/charts',
715 xhrFields: { withCredentials: true } // required for the cookie
717 .done(function(data) {
719 var h = NETDATA.chartRegistry.fixid(host);
720 self.charts[h] = data.charts;
722 else NETDATA.error(406, host + '/api/v1/charts');
724 if(typeof callback === 'function')
728 NETDATA.error(405, host + '/api/v1/charts');
730 if(typeof callback === 'function')
736 // ----------------------------------------------------------------------------------------------------------------
737 // Global Pan and Zoom on charts
739 // Using this structure are synchronize all the charts, so that
740 // when you pan or zoom one, all others are automatically refreshed
741 // to the same timespan.
743 NETDATA.globalPanAndZoom = {
744 seq: 0, // timestamp ms
745 // every time a chart is panned or zoomed
746 // we set the timestamp here
747 // then we use it as a sequence number
748 // to find if other charts are syncronized
751 master: null, // the master chart (state), to which all others
754 force_before_ms: null, // the timespan to sync all other charts
755 force_after_ms: null,
760 setMaster: function(state, after, before) {
761 if(NETDATA.options.current.sync_pan_and_zoom === false)
764 if(this.master !== null && this.master !== state)
765 this.master.resetChart(true, true);
767 var now = new Date().getTime();
770 this.force_after_ms = after;
771 this.force_before_ms = before;
772 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
774 if(typeof this.callback === 'function')
775 this.callback(true, after, before);
779 clearMaster: function() {
780 if(this.master !== null) {
781 var st = this.master;
788 this.force_after_ms = null;
789 this.force_before_ms = null;
790 NETDATA.options.auto_refresher_stop_until = 0;
792 if(typeof this.callback === 'function')
793 this.callback(false, 0, 0);
796 // is the given state the master of the global
797 // pan and zoom sync?
798 isMaster: function(state) {
799 if(this.master === state) return true;
803 // are we currently have a global pan and zoom sync?
804 isActive: function() {
805 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
809 // check if a chart, other than the master
810 // needs to be refreshed, due to the global pan and zoom
811 shouldBeAutoRefreshed: function(state) {
812 if(this.master === null || this.seq === 0)
815 //if(state.needsRecreation())
818 if(state.tm.pan_and_zoom_seq === this.seq)
825 // ----------------------------------------------------------------------------------------------------------------
826 // dimensions selection
829 // move color assignment to dimensions, here
831 dimensionStatus = function(parent, label, name_div, value_div, color) {
832 this.enabled = false;
833 this.parent = parent;
835 this.name_div = null;
836 this.value_div = null;
837 this.color = NETDATA.themes.current.foreground;
839 if(parent.selected_count > parent.unselected_count)
840 this.selected = true;
842 this.selected = false;
844 this.setOptions(name_div, value_div, color);
847 dimensionStatus.prototype.invalidate = function() {
848 this.name_div = null;
849 this.value_div = null;
850 this.enabled = false;
853 dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
856 if(this.name_div != name_div) {
857 this.name_div = name_div;
858 this.name_div.title = this.label;
859 this.name_div.style.color = this.color;
860 if(this.selected === false)
861 this.name_div.className = 'netdata-legend-name not-selected';
863 this.name_div.className = 'netdata-legend-name selected';
866 if(this.value_div != value_div) {
867 this.value_div = value_div;
868 this.value_div.title = this.label;
869 this.value_div.style.color = this.color;
870 if(this.selected === false)
871 this.value_div.className = 'netdata-legend-value not-selected';
873 this.value_div.className = 'netdata-legend-value selected';
880 dimensionStatus.prototype.setHandler = function() {
881 if(this.enabled === false) return;
885 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
886 this.name_div.onclick = this.value_div.onclick = function(e) {
888 if(ds.isSelected()) {
890 if(e.shiftKey === true || e.ctrlKey === true) {
891 // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
894 if(ds.parent.countSelected() === 0)
895 ds.parent.selectAll();
898 // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
899 if(ds.parent.countSelected() === 1) {
900 ds.parent.selectAll();
903 ds.parent.selectNone();
909 // this is not selected
910 if(e.shiftKey === true || e.ctrlKey === true) {
911 // control or shift key is pressed -> select this too
915 // no key is pressed -> select only this
916 ds.parent.selectNone();
921 ds.parent.state.redrawChart();
925 dimensionStatus.prototype.select = function() {
926 if(this.enabled === false) return;
928 this.name_div.className = 'netdata-legend-name selected';
929 this.value_div.className = 'netdata-legend-value selected';
930 this.selected = true;
933 dimensionStatus.prototype.unselect = function() {
934 if(this.enabled === false) return;
936 this.name_div.className = 'netdata-legend-name not-selected';
937 this.value_div.className = 'netdata-legend-value hidden';
938 this.selected = false;
941 dimensionStatus.prototype.isSelected = function() {
942 return(this.enabled === true && this.selected === true);
945 // ----------------------------------------------------------------------------------------------------------------
947 dimensionsVisibility = function(state) {
950 this.dimensions = {};
951 this.selected_count = 0;
952 this.unselected_count = 0;
955 dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
956 if(typeof this.dimensions[label] === 'undefined') {
958 this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
961 this.dimensions[label].setOptions(name_div, value_div, color);
963 return this.dimensions[label];
966 dimensionsVisibility.prototype.dimensionGet = function(label) {
967 return this.dimensions[label];
970 dimensionsVisibility.prototype.invalidateAll = function() {
971 for(var d in this.dimensions)
972 this.dimensions[d].invalidate();
975 dimensionsVisibility.prototype.selectAll = function() {
976 for(var d in this.dimensions)
977 this.dimensions[d].select();
980 dimensionsVisibility.prototype.countSelected = function() {
982 for(var d in this.dimensions)
983 if(this.dimensions[d].isSelected()) i++;
988 dimensionsVisibility.prototype.selectNone = function() {
989 for(var d in this.dimensions)
990 this.dimensions[d].unselect();
993 dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
994 var ret = new Array();
995 this.selected_count = 0;
996 this.unselected_count = 0;
998 for(var i = 0, len = array.length; i < len ; i++) {
999 var ds = this.dimensions[array[i]];
1000 if(typeof ds === 'undefined') {
1001 // console.log(array[i] + ' is not found');
1006 if(ds.isSelected()) {
1008 this.selected_count++;
1012 this.unselected_count++;
1016 if(this.selected_count === 0 && this.unselected_count !== 0) {
1018 return this.selected2BooleanArray(array);
1025 // ----------------------------------------------------------------------------------------------------------------
1026 // global selection sync
1028 NETDATA.globalSelectionSync = {
1030 dont_sync_before: 0,
1035 if(this.state !== null)
1036 this.state.globalSelectionSyncStop();
1040 if(this.state !== null) {
1041 this.state.globalSelectionSyncDelay();
1046 // ----------------------------------------------------------------------------------------------------------------
1047 // Our state object, where all per-chart values are stored
1049 chartState = function(element) {
1050 var self = $(element);
1051 this.element = element;
1054 // all private functions should use 'that', instead of 'this'
1057 /* error() - private
1058 * show an error instead of the chart
1060 var error = function(msg) {
1063 if(typeof netdataErrorCallback === 'function') {
1064 ret = netdataErrorCallback('chart', that.id, msg);
1068 that.element.innerHTML = that.id + ': ' + msg;
1069 that.enabled = false;
1070 that.current = that.pan;
1074 // GUID - a unique identifier for the chart
1075 this.uuid = NETDATA.guid();
1077 // string - the name of chart
1078 this.id = self.data('netdata');
1080 // string - the key for localStorage settings
1081 this.settings_id = self.data('id') || null;
1083 // the user given dimensions of the element
1084 this.width = self.data('width') || NETDATA.chartDefaults.width;
1085 this.height = self.data('height') || NETDATA.chartDefaults.height;
1087 if(this.settings_id !== null) {
1088 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
1089 // this is the callback that will be called
1090 // if and when the user resets all localStorage variables
1091 // to their defaults
1093 resizeChartToHeight(height);
1097 // string - the netdata server URL, without any path
1098 this.host = self.data('host') || NETDATA.chartDefaults.host;
1100 // make sure the host does not end with /
1101 // all netdata API requests use absolute paths
1102 while(this.host.slice(-1) === '/')
1103 this.host = this.host.substring(0, this.host.length - 1);
1105 // string - the grouping method requested by the user
1106 this.method = self.data('method') || NETDATA.chartDefaults.method;
1108 // the time-range requested by the user
1109 this.after = self.data('after') || NETDATA.chartDefaults.after;
1110 this.before = self.data('before') || NETDATA.chartDefaults.before;
1112 // the pixels per point requested by the user
1113 this.pixels_per_point = self.data('pixels-per-point') || 1;
1114 this.points = self.data('points') || null;
1116 // the dimensions requested by the user
1117 this.dimensions = self.data('dimensions') || null;
1119 // the chart library requested by the user
1120 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
1122 // object - the chart library used
1123 this.library = null;
1127 this.colors_assigned = {};
1128 this.colors_available = null;
1130 // the element already created by the user
1131 this.element_message = null;
1133 // the element with the chart
1134 this.element_chart = null;
1136 // the element with the legend of the chart (if created by us)
1137 this.element_legend = null;
1138 this.element_legend_childs = {
1148 this.chart_url = null; // string - the url to download chart info
1149 this.chart = null; // object - the chart as downloaded from the server
1151 this.title = self.data('title') || null; // the title of the chart
1152 this.units = self.data('units') || null; // the units of the chart dimensions
1153 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1155 this.running = false; // boolean - true when the chart is being refreshed now
1156 this.validated = false; // boolean - has the chart been validated?
1157 this.enabled = true; // boolean - is the chart enabled for refresh?
1158 this.paused = false; // boolean - is the chart paused for any reason?
1159 this.selected = false; // boolean - is the chart shown a selection?
1160 this.debug = false; // boolean - console.log() debug info about this chart
1162 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1163 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1164 this.requested_after = null; // milliseconds - the timestamp of the request after param
1165 this.requested_before = null; // milliseconds - the timestamp of the request before param
1166 this.requested_padding = null;
1167 this.view_after = 0;
1168 this.view_before = 0;
1173 force_update_at: 0, // the timestamp to force the update at
1174 force_before_ms: null,
1175 force_after_ms: null
1180 force_update_at: 0, // the timestamp to force the update at
1181 force_before_ms: null,
1182 force_after_ms: null
1187 force_update_at: 0, // the timestamp to force the update at
1188 force_before_ms: null,
1189 force_after_ms: null
1192 // this is a pointer to one of the sub-classes below
1194 this.current = this.auto;
1196 // check the requested library is available
1197 // we don't initialize it here - it will be initialized when
1198 // this chart will be first used
1199 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1200 NETDATA.error(402, that.library_name);
1201 error('chart library "' + that.library_name + '" is not found');
1204 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1205 NETDATA.error(403, that.library_name);
1206 error('chart library "' + that.library_name + '" is not enabled');
1210 that.library = NETDATA.chartLibraries[that.library_name];
1212 // milliseconds - the time the last refresh took
1213 this.refresh_dt_ms = 0;
1215 // if we need to report the rendering speed
1216 // find the element that needs to be updated
1217 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1219 if(refresh_dt_element_name !== null)
1220 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1222 this.refresh_dt_element = null;
1224 this.dimensions_visibility = new dimensionsVisibility(this);
1226 this._updating = false;
1228 // ============================================================================================================
1229 // PRIVATE FUNCTIONS
1231 var createDOM = function() {
1232 if(that.enabled === false) return;
1234 if(that.element_message !== null) that.element_message.innerHTML = '';
1235 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1236 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1238 that.element.innerHTML = '';
1240 that.element_message = document.createElement('div');
1241 that.element_message.className = ' netdata-message hidden';
1242 that.element.appendChild(that.element_message);
1244 that.element_chart = document.createElement('div');
1245 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1246 that.element.appendChild(that.element_chart);
1248 if(that.hasLegend() === true) {
1249 that.element.className = "netdata-container-with-legend";
1250 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1252 that.element_legend = document.createElement('div');
1253 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1254 that.element.appendChild(that.element_legend);
1257 that.element.className = "netdata-container";
1258 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1260 that.element_legend = null;
1262 that.element_legend_childs.series = null;
1264 if(typeof(that.width) === 'string')
1265 $(that.element).css('width', that.width);
1266 else if(typeof(that.width) === 'number')
1267 $(that.element).css('width', that.width + 'px');
1269 if(typeof(that.library.aspect_ratio) === 'undefined') {
1270 if(typeof(that.height) === 'string')
1271 $(that.element).css('height', that.height);
1272 else if(typeof(that.height) === 'number')
1273 $(that.element).css('height', that.height + 'px');
1276 var w = that.element.offsetWidth;
1277 if(w === null || w === 0) {
1278 // the div is hidden
1279 // this will resize the chart when next viewed
1280 that.tm.last_resized = 0;
1283 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1286 if(NETDATA.chartDefaults.min_width !== null)
1287 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1289 that.tm.last_dom_created = new Date().getTime();
1295 * initialize state variables
1296 * destroy all (possibly) created state elements
1297 * create the basic DOM for a chart
1299 var init = function() {
1300 if(that.enabled === false) return;
1302 that.paused = false;
1303 that.selected = false;
1305 that.chart_created = false; // boolean - is the library.create() been called?
1306 that.updates_counter = 0; // numeric - the number of refreshes made so far
1307 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1308 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1311 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1312 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1313 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1315 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1316 last_updated: 0, // the timestamp the chart last updated with data
1317 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1319 // Used with NETDATA.globalPanAndZoom.seq
1320 last_visible_check: 0, // the time we last checked if it is visible
1321 last_resized: 0, // the time the chart was resized
1322 last_hidden: 0, // the time the chart was hidden
1323 last_unhidden: 0, // the time the chart was unhidden
1324 last_autorefreshed: 0 // the time the chart was last refreshed
1327 that.data = null; // the last data as downloaded from the netdata server
1328 that.data_url = 'invalid://'; // string - the last url used to update the chart
1329 that.data_points = 0; // number - the number of points returned from netdata
1330 that.data_after = 0; // milliseconds - the first timestamp of the data
1331 that.data_before = 0; // milliseconds - the last timestamp of the data
1332 that.data_update_every = 0; // milliseconds - the frequency to update the data
1334 that.tm.last_initialized = new Date().getTime();
1337 that.setMode('auto');
1340 var maxMessageFontSize = function() {
1341 // normally we want a font size, as tall as the element
1342 var h = that.element_message.clientHeight;
1344 // but give it some air, 20% let's say, or 5 pixels min
1345 var lost = Math.max(h * 0.2, 5);
1348 // center the text, vertically
1349 var paddingTop = (lost - 5) / 2;
1351 // but check the width too
1352 // it should fit 10 characters in it
1353 var w = that.element_message.clientWidth / 10;
1355 paddingTop += (h - w) / 2;
1359 // and don't make it too huge
1360 // 5% of the screen size is good
1361 if(h > screen.height / 20) {
1362 paddingTop += (h - (screen.height / 20)) / 2;
1363 h = screen.height / 20;
1367 that.element_message.style.fontSize = h.toString() + 'px';
1368 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1371 var showMessage = function(msg) {
1372 that.element_message.className = 'netdata-message';
1373 that.element_message.innerHTML = msg;
1374 that.element_message.style.fontSize = 'x-small';
1375 that.element_message.style.paddingTop = '0px';
1376 that.___messageHidden___ = undefined;
1379 var showMessageIcon = function(icon) {
1380 that.element_message.innerHTML = icon;
1381 that.element_message.className = 'netdata-message icon';
1382 maxMessageFontSize();
1383 that.___messageHidden___ = undefined;
1386 var hideMessage = function() {
1387 if(typeof that.___messageHidden___ === 'undefined') {
1388 that.___messageHidden___ = true;
1389 that.element_message.className = 'netdata-message hidden';
1393 var showRendering = function() {
1395 if(that.chart !== null) {
1396 if(that.chart.chart_type === 'line')
1397 icon = '<i class="fa fa-line-chart"></i>';
1399 icon = '<i class="fa fa-area-chart"></i>';
1402 icon = '<i class="fa fa-area-chart"></i>';
1404 showMessageIcon(icon + ' netdata');
1407 var showLoading = function() {
1408 if(that.chart_created === false) {
1409 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1415 var isHidden = function() {
1416 if(typeof that.___chartIsHidden___ !== 'undefined')
1422 // hide the chart, when it is not visible - called from isVisible()
1423 var hideChart = function() {
1424 // hide it, if it is not already hidden
1425 if(isHidden() === true) return;
1427 if(that.chart_created === true) {
1428 if(NETDATA.options.current.destroy_on_hide === true) {
1429 // we should destroy it
1434 that.element_chart.style.display = 'none';
1435 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1436 that.tm.last_hidden = new Date().getTime();
1439 // This works, but I not sure there are no corner cases somewhere
1440 // so it is commented - if the user has memory issues he can
1441 // set Destroy on Hide for all charts
1442 // that.data = null;
1446 that.___chartIsHidden___ = true;
1449 // unhide the chart, when it is visible - called from isVisible()
1450 var unhideChart = function() {
1451 if(isHidden() === false) return;
1453 that.___chartIsHidden___ = undefined;
1454 that.updates_since_last_unhide = 0;
1456 if(that.chart_created === false) {
1457 // we need to re-initialize it, to show our background
1458 // logo in bootstrap tabs, until the chart loads
1462 that.tm.last_unhidden = new Date().getTime();
1463 that.element_chart.style.display = '';
1464 if(that.element_legend !== null) that.element_legend.style.display = '';
1470 var canBeRendered = function() {
1471 if(isHidden() === true || that.isVisible(true) === false)
1477 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1478 var callChartLibraryUpdateSafely = function(data) {
1481 if(canBeRendered() === false)
1484 if(NETDATA.options.debug.chart_errors === true)
1485 status = that.library.update(that, data);
1488 status = that.library.update(that, data);
1495 if(status === false) {
1496 error('chart failed to be updated as ' + that.library_name);
1503 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1504 var callChartLibraryCreateSafely = function(data) {
1507 if(canBeRendered() === false)
1510 if(NETDATA.options.debug.chart_errors === true)
1511 status = that.library.create(that, data);
1514 status = that.library.create(that, data);
1521 if(status === false) {
1522 error('chart failed to be created as ' + that.library_name);
1526 that.chart_created = true;
1527 that.updates_since_last_creation = 0;
1531 // ----------------------------------------------------------------------------------------------------------------
1534 // resizeChart() - private
1535 // to be called just before the chart library to make sure that
1536 // a properly sized dom is available
1537 var resizeChart = function() {
1538 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1539 if(that.chart_created === false) return;
1541 if(that.needsRecreation()) {
1544 else if(typeof that.library.resize === 'function') {
1545 that.library.resize(that);
1547 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1548 $(that.element_legend_childs.nano).nanoScroller();
1550 maxMessageFontSize();
1553 that.tm.last_resized = new Date().getTime();
1557 // this is the actual chart resize algorithm
1559 // - resize the entire container
1560 // - update the internal states
1561 // - resize the chart as the div changes height
1562 // - update the scrollbar of the legend
1563 var resizeChartToHeight = function(h) {
1565 that.element.style.height = h;
1567 if(that.settings_id !== null)
1568 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1570 var now = new Date().getTime();
1571 NETDATA.options.last_page_scroll = now;
1572 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1575 that.tm.last_resized = 0;
1579 this.resizeHandler = function(e) {
1582 if(typeof this.event_resize === 'undefined'
1583 || this.event_resize.chart_original_w === 'undefined'
1584 || this.event_resize.chart_original_h === 'undefined')
1585 this.event_resize = {
1586 chart_original_w: this.element.clientWidth,
1587 chart_original_h: this.element.clientHeight,
1591 if(e.type === 'touchstart') {
1592 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1593 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1596 this.event_resize.mouse_start_x = e.clientX;
1597 this.event_resize.mouse_start_y = e.clientY;
1600 this.event_resize.chart_start_w = this.element.clientWidth;
1601 this.event_resize.chart_start_h = this.element.clientHeight;
1602 this.event_resize.chart_last_w = this.element.clientWidth;
1603 this.event_resize.chart_last_h = this.element.clientHeight;
1605 var now = new Date().getTime();
1606 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1607 // double click / double tap event
1609 // the optimal height of the chart
1610 // showing the entire legend
1611 var optimal = this.event_resize.chart_last_h
1612 + this.element_legend_childs.content.scrollHeight
1613 - this.element_legend_childs.content.clientHeight;
1615 // if we are not optimal, be optimal
1616 if(this.event_resize.chart_last_h != optimal)
1617 resizeChartToHeight(optimal.toString() + 'px');
1619 // else if we do not have the original height
1620 // reset to the original height
1621 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1622 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1625 this.event_resize.last = now;
1627 // process movement event
1628 document.onmousemove =
1629 document.ontouchmove =
1630 this.element_legend_childs.resize_handler.onmousemove =
1631 this.element_legend_childs.resize_handler.ontouchmove =
1636 case 'mousemove': y = e.clientY; break;
1637 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1641 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1643 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1644 resizeChartToHeight(newH.toString() + 'px');
1645 that.event_resize.chart_last_h = newH;
1650 // process end event
1651 document.onmouseup =
1652 document.ontouchend =
1653 this.element_legend_childs.resize_handler.onmouseup =
1654 this.element_legend_childs.resize_handler.ontouchend =
1656 // remove all the hooks
1657 document.onmouseup =
1658 document.onmousemove =
1659 document.ontouchmove =
1660 document.ontouchend =
1661 that.element_legend_childs.resize_handler.onmousemove =
1662 that.element_legend_childs.resize_handler.ontouchmove =
1663 that.element_legend_childs.resize_handler.onmouseout =
1664 that.element_legend_childs.resize_handler.onmouseup =
1665 that.element_legend_childs.resize_handler.ontouchend =
1668 // allow auto-refreshes
1669 NETDATA.options.auto_refresher_stop_until = 0;
1675 var noDataToShow = function() {
1676 showMessageIcon('<i class="fa fa-warning"></i> empty');
1677 that.legendUpdateDOM();
1678 that.tm.last_autorefreshed = new Date().getTime();
1679 // that.data_update_every = 30 * 1000;
1680 //that.element_chart.style.display = 'none';
1681 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1682 //that.___chartIsHidden___ = true;
1685 // ============================================================================================================
1688 this.error = function(msg) {
1692 this.setMode = function(m) {
1693 if(this.current !== null && this.current.name === m) return;
1696 this.current = this.auto;
1697 else if(m === 'pan')
1698 this.current = this.pan;
1699 else if(m === 'zoom')
1700 this.current = this.zoom;
1702 this.current = this.auto;
1704 this.current.force_update_at = 0;
1705 this.current.force_before_ms = null;
1706 this.current.force_after_ms = null;
1708 this.tm.last_mode_switch = new Date().getTime();
1711 // ----------------------------------------------------------------------------------------------------------------
1712 // global selection sync
1714 // prevent to global selection sync for some time
1715 this.globalSelectionSyncDelay = function(ms) {
1716 if(NETDATA.options.current.sync_selection === false)
1719 if(typeof ms === 'number')
1720 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1722 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1725 // can we globally apply selection sync?
1726 this.globalSelectionSyncAbility = function() {
1727 if(NETDATA.options.current.sync_selection === false)
1730 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1736 this.globalSelectionSyncIsMaster = function() {
1737 if(NETDATA.globalSelectionSync.state === this)
1743 // this chart is the master of the global selection sync
1744 this.globalSelectionSyncBeMaster = function() {
1746 if(this.globalSelectionSyncIsMaster()) {
1747 if(this.debug === true)
1748 this.log('sync: I am the master already.');
1753 if(NETDATA.globalSelectionSync.state) {
1754 if(this.debug === true)
1755 this.log('sync: I am not the sync master. Resetting global sync.');
1757 this.globalSelectionSyncStop();
1760 // become the master
1761 if(this.debug === true)
1762 this.log('sync: becoming sync master.');
1764 this.selected = true;
1765 NETDATA.globalSelectionSync.state = this;
1767 // find the all slaves
1768 var targets = NETDATA.options.targets;
1769 var len = targets.length;
1774 if(this.debug === true)
1775 st.log('sync: not adding me to sync');
1777 else if(st.globalSelectionSyncIsEligible()) {
1778 if(this.debug === true)
1779 st.log('sync: adding to sync as slave');
1781 st.globalSelectionSyncBeSlave();
1785 // this.globalSelectionSyncDelay(100);
1788 // can the chart participate to the global selection sync as a slave?
1789 this.globalSelectionSyncIsEligible = function() {
1790 if(this.enabled === true
1791 && this.library !== null
1792 && typeof this.library.setSelection === 'function'
1793 && this.isVisible() === true
1794 && this.chart_created === true)
1800 // this chart becomes a slave of the global selection sync
1801 this.globalSelectionSyncBeSlave = function() {
1802 if(NETDATA.globalSelectionSync.state !== this)
1803 NETDATA.globalSelectionSync.slaves.push(this);
1806 // sync all the visible charts to the given time
1807 // this is to be called from the chart libraries
1808 this.globalSelectionSync = function(t) {
1809 if(this.globalSelectionSyncAbility() === false) {
1810 if(this.debug === true)
1811 this.log('sync: cannot sync (yet?).');
1816 if(this.globalSelectionSyncIsMaster() === false) {
1817 if(this.debug === true)
1818 this.log('sync: trying to be sync master.');
1820 this.globalSelectionSyncBeMaster();
1822 if(this.globalSelectionSyncAbility() === false) {
1823 if(this.debug === true)
1824 this.log('sync: cannot sync (yet?).');
1830 NETDATA.globalSelectionSync.last_t = t;
1831 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1836 // stop syncing all charts to the given time
1837 this.globalSelectionSyncStop = function() {
1838 if(NETDATA.globalSelectionSync.slaves.length) {
1839 if(this.debug === true)
1840 this.log('sync: cleaning up...');
1842 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1844 if(that.debug === true)
1845 st.log('sync: not adding me to sync stop');
1848 if(that.debug === true)
1849 st.log('sync: removed slave from sync');
1851 st.clearSelection();
1855 NETDATA.globalSelectionSync.last_t = 0;
1856 NETDATA.globalSelectionSync.slaves = [];
1857 NETDATA.globalSelectionSync.state = null;
1860 this.clearSelection();
1863 this.setSelection = function(t) {
1864 if(typeof this.library.setSelection === 'function') {
1865 if(this.library.setSelection(this, t) === true)
1866 this.selected = true;
1868 this.selected = false;
1870 else this.selected = true;
1872 if(this.selected === true && this.debug === true)
1873 this.log('selection set to ' + t.toString());
1875 return this.selected;
1878 this.clearSelection = function() {
1879 if(this.selected === true) {
1880 if(typeof this.library.clearSelection === 'function') {
1881 if(this.library.clearSelection(this) === true)
1882 this.selected = false;
1884 this.selected = true;
1886 else this.selected = false;
1888 if(this.selected === false && this.debug === true)
1889 this.log('selection cleared');
1894 return this.selected;
1897 // find if a timestamp (ms) is shown in the current chart
1898 this.timeIsVisible = function(t) {
1899 if(t >= this.data_after && t <= this.data_before)
1904 this.calculateRowForTime = function(t) {
1905 if(this.timeIsVisible(t) === false) return -1;
1906 return Math.floor((t - this.data_after) / this.data_update_every);
1909 // ----------------------------------------------------------------------------------------------------------------
1912 this.log = function(msg) {
1913 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1916 this.pauseChart = function() {
1917 if(this.paused === false) {
1918 if(this.debug === true)
1919 this.log('pauseChart()');
1925 this.unpauseChart = function() {
1926 if(this.paused === true) {
1927 if(this.debug === true)
1928 this.log('unpauseChart()');
1930 this.paused = false;
1934 this.resetChart = function(dont_clear_master, dont_update) {
1935 if(this.debug === true)
1936 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1938 if(typeof dont_clear_master === 'undefined')
1939 dont_clear_master = false;
1941 if(typeof dont_update === 'undefined')
1942 dont_update = false;
1944 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1945 if(this.debug === true)
1946 this.log('resetChart() diverting to clearMaster().');
1947 // this will call us back with master === true
1948 NETDATA.globalPanAndZoom.clearMaster();
1952 this.clearSelection();
1954 this.tm.pan_and_zoom_seq = 0;
1956 this.setMode('auto');
1957 this.current.force_update_at = 0;
1958 this.current.force_before_ms = null;
1959 this.current.force_after_ms = null;
1960 this.tm.last_autorefreshed = 0;
1961 this.paused = false;
1962 this.selected = false;
1963 this.enabled = true;
1964 // this.debug = false;
1966 // do not update the chart here
1967 // or the chart will flip-flop when it is the master
1968 // of a selection sync and another chart becomes
1971 if(dont_update !== true && this.isVisible() === true) {
1976 this.updateChartPanOrZoom = function(after, before) {
1977 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1980 if(this.debug === true)
1983 if(before < after) {
1984 if(this.debug === true)
1985 this.log(logme + 'flipped parameters, rejecting it.');
1990 if(typeof this.fixed_min_duration === 'undefined')
1991 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1993 var min_duration = this.fixed_min_duration;
1994 var current_duration = Math.round(this.view_before - this.view_after);
1996 // round the numbers
1997 after = Math.round(after);
1998 before = Math.round(before);
2000 // align them to update_every
2001 // stretching them further away
2002 after -= after % this.data_update_every;
2003 before += this.data_update_every - (before % this.data_update_every);
2005 // the final wanted duration
2006 var wanted_duration = before - after;
2008 // to allow panning, accept just a point below our minimum
2009 if((current_duration - this.data_update_every) < min_duration)
2010 min_duration = current_duration - this.data_update_every;
2012 // we do it, but we adjust to minimum size and return false
2013 // when the wanted size is below the current and the minimum
2015 if(wanted_duration < current_duration && wanted_duration < min_duration) {
2016 if(this.debug === true)
2017 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
2019 min_duration = this.fixed_min_duration;
2021 var dt = (min_duration - wanted_duration) / 2;
2024 wanted_duration = before - after;
2028 var tolerance = this.data_update_every * 2;
2029 var movement = Math.abs(before - this.view_before);
2031 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
2032 if(this.debug === true)
2033 this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
2037 if(this.current.name === 'auto') {
2038 this.log(logme + 'caller called me with mode: ' + this.current.name);
2039 this.setMode('pan');
2042 if(this.debug === true)
2043 this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
2045 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
2046 this.current.force_after_ms = after;
2047 this.current.force_before_ms = before;
2048 NETDATA.globalPanAndZoom.setMaster(this, after, before);
2052 this.legendFormatValue = function(value) {
2053 if(value === null || value === 'undefined') return '-';
2054 if(typeof value !== 'number') return value;
2056 var abs = Math.abs(value);
2057 if(abs >= 1000) return (Math.round(value)).toLocaleString();
2058 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
2059 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
2060 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
2061 return (Math.round(value * 10000) / 10000).toLocaleString();
2064 this.legendSetLabelValue = function(label, value) {
2065 var series = this.element_legend_childs.series[label];
2066 if(typeof series === 'undefined') return;
2067 if(series.value === null && series.user === null) return;
2069 // if the value has not changed, skip DOM update
2070 //if(series.last === value) return;
2073 if(typeof value === 'number') {
2074 var v = Math.abs(value);
2075 s = r = this.legendFormatValue(value);
2077 if(typeof series.last === 'number') {
2078 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2079 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2080 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2082 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2087 series.last = value;
2090 if(series.value !== null) series.value.innerHTML = s;
2091 if(series.user !== null) series.user.innerHTML = r;
2094 this.legendSetDate = function(ms) {
2095 if(typeof ms !== 'number') {
2096 this.legendShowUndefined();
2100 var d = new Date(ms);
2102 if(this.element_legend_childs.title_date)
2103 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
2105 if(this.element_legend_childs.title_time)
2106 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
2108 if(this.element_legend_childs.title_units)
2109 this.element_legend_childs.title_units.innerHTML = this.units;
2112 this.legendShowUndefined = function() {
2113 if(this.element_legend_childs.title_date)
2114 this.element_legend_childs.title_date.innerHTML = ' ';
2116 if(this.element_legend_childs.title_time)
2117 this.element_legend_childs.title_time.innerHTML = this.chart.name;
2119 if(this.element_legend_childs.title_units)
2120 this.element_legend_childs.title_units.innerHTML = ' ';
2122 if(this.data && this.element_legend_childs.series !== null) {
2123 var labels = this.data.dimension_names;
2124 var i = labels.length;
2126 var label = labels[i];
2128 if(typeof label === 'undefined') continue;
2129 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2130 this.legendSetLabelValue(label, null);
2135 this.legendShowLatestValues = function() {
2136 if(this.chart === null) return;
2137 if(this.selected) return;
2139 if(this.data === null || this.element_legend_childs.series === null) {
2140 this.legendShowUndefined();
2144 var show_undefined = true;
2145 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
2146 show_undefined = false;
2148 if(show_undefined) {
2149 this.legendShowUndefined();
2153 this.legendSetDate(this.view_before);
2155 var labels = this.data.dimension_names;
2156 var i = labels.length;
2158 var label = labels[i];
2160 if(typeof label === 'undefined') continue;
2161 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2164 this.legendSetLabelValue(label, null);
2166 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2170 this.legendReset = function() {
2171 this.legendShowLatestValues();
2174 // this should be called just ONCE per dimension per chart
2175 this._chartDimensionColor = function(label) {
2176 if(this.colors === null) this.chartColors();
2178 if(typeof this.colors_assigned[label] === 'undefined') {
2179 if(this.colors_available.length === 0) {
2180 for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2181 this.colors_available.push(NETDATA.themes.current.colors[i]);
2184 this.colors_assigned[label] = this.colors_available.shift();
2186 if(this.debug === true)
2187 this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
2190 if(this.debug === true)
2191 this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
2194 this.colors.push(this.colors_assigned[label]);
2195 return this.colors_assigned[label];
2198 this.chartColors = function() {
2199 if(this.colors !== null) return this.colors;
2201 this.colors = new Array();
2202 this.colors_available = new Array();
2205 var c = $(this.element).data('colors');
2206 // this.log('read colors: ' + c);
2207 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2208 if(typeof c !== 'string') {
2209 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2216 for(i = 0, len = c.length; i < len ; i++) {
2218 this.colors_available.push(c[i]);
2219 // this.log('adding color: ' + c[i]);
2225 // push all the standard colors too
2226 for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2227 this.colors_available.push(NETDATA.themes.current.colors[i]);
2232 this.legendUpdateDOM = function() {
2235 // check that the legend DOM is up to date for the downloaded dimensions
2236 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2237 // this.log('the legend does not have any series - requesting legend update');
2240 else if(this.data === null) {
2241 // this.log('the chart does not have any data - requesting legend update');
2244 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2248 var labels = this.data.dimension_names.toString();
2249 if(labels !== this.element_legend_childs.series.labels_key) {
2252 if(this.debug === true)
2253 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2257 if(needed === false) {
2258 // make sure colors available
2261 // do we have to update the current values?
2262 // we do this, only when the visible chart is current
2263 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2264 if(this.debug === true)
2265 this.log('chart is in latest position... updating values on legend...');
2267 //var labels = this.data.dimension_names;
2268 //var i = labels.length;
2270 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2274 if(this.colors === null) {
2275 // this is the first time we update the chart
2276 // let's assign colors to all dimensions
2277 if(this.library.track_colors() === true)
2278 for(var dim in this.chart.dimensions)
2279 this._chartDimensionColor(this.chart.dimensions[dim].name);
2281 // we will re-generate the colors for the chart
2282 // based on the selected dimensions
2285 if(this.debug === true)
2286 this.log('updating Legend DOM');
2288 // mark all dimensions as invalid
2289 this.dimensions_visibility.invalidateAll();
2291 var genLabel = function(state, parent, dim, name, count) {
2292 var color = state._chartDimensionColor(name);
2294 var user_element = null;
2295 var user_id = self.data('show-value-of-' + dim + '-at') || null;
2296 if(user_id !== null) {
2297 user_element = document.getElementById(user_id) || null;
2298 if(user_element === null)
2299 state.log('Cannot find element with id: ' + user_id);
2302 state.element_legend_childs.series[name] = {
2303 name: document.createElement('span'),
2304 value: document.createElement('span'),
2309 var label = state.element_legend_childs.series[name];
2311 // create the dimension visibility tracking for this label
2312 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2314 var rgb = NETDATA.colorHex2Rgb(color);
2315 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2316 + state.chart.chart_type
2317 + '" style="background-color: '
2318 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2319 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2321 var text = document.createTextNode(' ' + name);
2322 label.name.appendChild(text);
2325 parent.appendChild(document.createElement('br'));
2327 parent.appendChild(label.name);
2328 parent.appendChild(label.value);
2331 var content = document.createElement('div');
2333 if(this.hasLegend()) {
2334 this.element_legend_childs = {
2336 resize_handler: document.createElement('div'),
2337 toolbox: document.createElement('div'),
2338 toolbox_left: document.createElement('div'),
2339 toolbox_right: document.createElement('div'),
2340 toolbox_reset: document.createElement('div'),
2341 toolbox_zoomin: document.createElement('div'),
2342 toolbox_zoomout: document.createElement('div'),
2343 toolbox_volume: document.createElement('div'),
2344 title_date: document.createElement('span'),
2345 title_time: document.createElement('span'),
2346 title_units: document.createElement('span'),
2347 nano: document.createElement('div'),
2349 paneClass: 'netdata-legend-series-pane',
2350 sliderClass: 'netdata-legend-series-slider',
2351 contentClass: 'netdata-legend-series-content',
2352 enabledClass: '__enabled',
2353 flashedClass: '__flashed',
2354 activeClass: '__active',
2356 alwaysVisible: true,
2362 this.element_legend.innerHTML = '';
2364 if(this.library.toolboxPanAndZoom !== null) {
2366 function get_pan_and_zoom_step(event) {
2368 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2370 else if (event.shiftKey)
2371 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2373 else if (event.altKey)
2374 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2377 return NETDATA.options.current.pan_and_zoom_factor;
2380 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2381 this.element.appendChild(this.element_legend_childs.toolbox);
2383 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2384 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2385 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2386 this.element_legend_childs.toolbox_left.onclick = function(e) {
2389 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2390 var before = that.view_before - step;
2391 var after = that.view_after - step;
2392 if(after >= that.netdata_first)
2393 that.library.toolboxPanAndZoom(that, after, before);
2395 if(NETDATA.options.current.show_help === true)
2396 $(this.element_legend_childs.toolbox_left).popover({
2401 placement: 'bottom',
2402 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2404 content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2408 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2409 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2410 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2411 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2413 NETDATA.resetAllCharts(that);
2415 if(NETDATA.options.current.show_help === true)
2416 $(this.element_legend_childs.toolbox_reset).popover({
2421 placement: 'bottom',
2422 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2423 title: 'Chart Reset',
2424 content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2427 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2428 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2429 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2430 this.element_legend_childs.toolbox_right.onclick = function(e) {
2432 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2433 var before = that.view_before + step;
2434 var after = that.view_after + step;
2435 if(before <= that.netdata_last)
2436 that.library.toolboxPanAndZoom(that, after, before);
2438 if(NETDATA.options.current.show_help === true)
2439 $(this.element_legend_childs.toolbox_right).popover({
2444 placement: 'bottom',
2445 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2447 content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2451 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2452 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2453 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2454 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2456 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2457 var before = that.view_before - dt;
2458 var after = that.view_after + dt;
2459 that.library.toolboxPanAndZoom(that, after, before);
2461 if(NETDATA.options.current.show_help === true)
2462 $(this.element_legend_childs.toolbox_zoomin).popover({
2467 placement: 'bottom',
2468 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2469 title: 'Chart Zoom In',
2470 content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2473 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2474 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2475 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2476 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2478 var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
2479 var before = that.view_before + dt;
2480 var after = that.view_after - dt;
2482 that.library.toolboxPanAndZoom(that, after, before);
2484 if(NETDATA.options.current.show_help === true)
2485 $(this.element_legend_childs.toolbox_zoomout).popover({
2490 placement: 'bottom',
2491 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2492 title: 'Chart Zoom Out',
2493 content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2496 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2497 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2498 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2499 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2500 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2501 //e.preventDefault();
2502 //alert('clicked toolbox_volume on ' + that.id);
2506 this.element_legend_childs.toolbox = null;
2507 this.element_legend_childs.toolbox_left = null;
2508 this.element_legend_childs.toolbox_reset = null;
2509 this.element_legend_childs.toolbox_right = null;
2510 this.element_legend_childs.toolbox_zoomin = null;
2511 this.element_legend_childs.toolbox_zoomout = null;
2512 this.element_legend_childs.toolbox_volume = null;
2515 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2516 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2517 this.element.appendChild(this.element_legend_childs.resize_handler);
2518 if(NETDATA.options.current.show_help === true)
2519 $(this.element_legend_childs.resize_handler).popover({
2524 placement: 'bottom',
2525 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2526 title: 'Chart Resize',
2527 content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
2531 this.element_legend_childs.resize_handler.onmousedown =
2533 that.resizeHandler(e);
2537 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2538 that.resizeHandler(e);
2541 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2542 this.element_legend.appendChild(this.element_legend_childs.title_date);
2544 this.element_legend.appendChild(document.createElement('br'));
2546 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2547 this.element_legend.appendChild(this.element_legend_childs.title_time);
2549 this.element_legend.appendChild(document.createElement('br'));
2551 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2552 this.element_legend.appendChild(this.element_legend_childs.title_units);
2554 this.element_legend.appendChild(document.createElement('br'));
2556 this.element_legend_childs.nano.className = 'netdata-legend-series';
2557 this.element_legend.appendChild(this.element_legend_childs.nano);
2559 content.className = 'netdata-legend-series-content';
2560 this.element_legend_childs.nano.appendChild(content);
2562 if(NETDATA.options.current.show_help === true)
2563 $(content).popover({
2568 placement: 'bottom',
2569 title: 'Chart Legend',
2570 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2571 content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
2575 this.element_legend_childs = {
2577 resize_handler: null,
2580 toolbox_right: null,
2581 toolbox_reset: null,
2582 toolbox_zoomin: null,
2583 toolbox_zoomout: null,
2584 toolbox_volume: null,
2595 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2596 if(this.debug === true)
2597 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2599 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2600 genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
2604 var tmp = new Array();
2605 for(var dim in this.chart.dimensions) {
2606 tmp.push(this.chart.dimensions[dim].name);
2607 genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
2609 this.element_legend_childs.series.labels_key = tmp.toString();
2610 if(this.debug === true)
2611 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2614 // create a hidden div to be used for hidding
2615 // the original legend of the chart library
2616 var el = document.createElement('div');
2617 if(this.element_legend !== null)
2618 this.element_legend.appendChild(el);
2619 el.style.display = 'none';
2621 this.element_legend_childs.hidden = document.createElement('div');
2622 el.appendChild(this.element_legend_childs.hidden);
2624 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2625 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2627 this.legendShowLatestValues();
2630 this.hasLegend = function() {
2631 if(typeof this.___hasLegendCache___ !== 'undefined')
2632 return this.___hasLegendCache___;
2635 if(this.library && this.library.legend(this) === 'right-side') {
2636 var legend = $(this.element).data('legend') || 'yes';
2637 if(legend === 'yes') leg = true;
2640 this.___hasLegendCache___ = leg;
2644 this.legendWidth = function() {
2645 return (this.hasLegend())?140:0;
2648 this.legendHeight = function() {
2649 return $(this.element).height();
2652 this.chartWidth = function() {
2653 return $(this.element).width() - this.legendWidth();
2656 this.chartHeight = function() {
2657 return $(this.element).height();
2660 this.chartPixelsPerPoint = function() {
2661 // force an options provided detail
2662 var px = this.pixels_per_point;
2664 if(this.library && px < this.library.pixels_per_point(this))
2665 px = this.library.pixels_per_point(this);
2667 if(px < NETDATA.options.current.pixels_per_point)
2668 px = NETDATA.options.current.pixels_per_point;
2673 this.needsRecreation = function() {
2675 this.chart_created === true
2677 && this.library.autoresize() === false
2678 && this.tm.last_resized < NETDATA.options.last_resized
2682 this.chartURL = function() {
2683 var after, before, points_multiplier = 1;
2684 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2685 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2687 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2688 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2689 this.view_after = after * 1000;
2690 this.view_before = before * 1000;
2692 this.requested_padding = null;
2693 points_multiplier = 1;
2695 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2696 this.tm.pan_and_zoom_seq = 0;
2698 before = Math.round(this.current.force_before_ms / 1000);
2699 after = Math.round(this.current.force_after_ms / 1000);
2700 this.view_after = after * 1000;
2701 this.view_before = before * 1000;
2703 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2704 this.requested_padding = Math.round((before - after) / 2);
2705 after -= this.requested_padding;
2706 before += this.requested_padding;
2707 this.requested_padding *= 1000;
2708 points_multiplier = 2;
2711 this.current.force_before_ms = null;
2712 this.current.force_after_ms = null;
2715 this.tm.pan_and_zoom_seq = 0;
2717 before = this.before;
2719 this.view_after = after * 1000;
2720 this.view_before = before * 1000;
2722 this.requested_padding = null;
2723 points_multiplier = 1;
2726 this.requested_after = after * 1000;
2727 this.requested_before = before * 1000;
2729 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2731 // build the data URL
2732 this.data_url = this.host + this.chart.data_url;
2733 this.data_url += "&format=" + this.library.format();
2734 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2735 this.data_url += "&group=" + this.method;
2736 this.data_url += "&options=" + this.library.options(this);
2737 this.data_url += '|jsonwrap';
2739 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2740 this.data_url += '|nonzero';
2742 if(this.append_options !== null)
2743 this.data_url += '|' + this.append_options.toString();
2746 this.data_url += "&after=" + after.toString();
2749 this.data_url += "&before=" + before.toString();
2752 this.data_url += "&dimensions=" + this.dimensions;
2754 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2755 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2758 this.redrawChart = function() {
2759 if(this.data !== null)
2760 this.updateChartWithData(this.data);
2763 this.updateChartWithData = function(data) {
2764 if(this.debug === true)
2765 this.log('updateChartWithData() called.');
2767 // this may force the chart to be re-created
2771 this.updates_counter++;
2772 this.updates_since_last_unhide++;
2773 this.updates_since_last_creation++;
2775 var started = new Date().getTime();
2777 // if the result is JSON, find the latest update-every
2778 this.data_update_every = data.view_update_every * 1000;
2779 this.data_after = data.after * 1000;
2780 this.data_before = data.before * 1000;
2781 this.netdata_first = data.first_entry * 1000;
2782 this.netdata_last = data.last_entry * 1000;
2783 this.data_points = data.points;
2786 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2787 if(this.view_after < this.data_after) {
2788 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2789 this.view_after = this.data_after;
2792 if(this.view_before > this.data_before) {
2793 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2794 this.view_before = this.data_before;
2798 this.view_after = this.data_after;
2799 this.view_before = this.data_before;
2802 if(this.debug === true) {
2803 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2805 if(this.current.force_after_ms)
2806 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2808 this.log('STATUS: forced : unset');
2810 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2811 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2812 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2813 this.log('STATUS: points : ' + (this.data_points).toString());
2816 if(this.data_points === 0) {
2821 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2822 if(this.debug === true)
2823 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2825 this.chart_created = false;
2828 // check and update the legend
2829 this.legendUpdateDOM();
2831 if(this.chart_created === true
2832 && typeof this.library.update === 'function') {
2834 if(this.debug === true)
2835 this.log('updating chart...');
2837 if(callChartLibraryUpdateSafely(data) === false)
2841 if(this.debug === true)
2842 this.log('creating chart...');
2844 if(callChartLibraryCreateSafely(data) === false)
2848 this.legendShowLatestValues();
2849 if(this.selected === true)
2850 NETDATA.globalSelectionSync.stop();
2852 // update the performance counters
2853 var now = new Date().getTime();
2854 this.tm.last_updated = now;
2856 // don't update last_autorefreshed if this chart is
2857 // forced to be updated with global PanAndZoom
2858 if(NETDATA.globalPanAndZoom.isActive())
2859 this.tm.last_autorefreshed = 0;
2861 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2862 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2864 this.tm.last_autorefreshed = now;
2867 this.refresh_dt_ms = now - started;
2868 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2870 if(this.refresh_dt_element !== null)
2871 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2874 this.updateChart = function(callback) {
2875 if(this.debug === true)
2876 this.log('updateChart() called.');
2878 if(this._updating === true) {
2879 if(this.debug === true)
2880 this.log('I am already updating...');
2882 if(typeof callback === 'function') callback();
2886 // due to late initialization of charts and libraries
2887 // we need to check this too
2888 if(this.enabled === false) {
2889 if(this.debug === true)
2890 this.log('I am not enabled');
2892 if(typeof callback === 'function') callback();
2896 if(canBeRendered() === false) {
2897 if(typeof callback === 'function') callback();
2901 if(this.chart === null) {
2902 this.getChart(function() { that.updateChart(callback); });
2906 if(this.library.initialized === false) {
2907 if(this.library.enabled === true) {
2908 this.library.initialize(function() { that.updateChart(callback); });
2912 error('chart library "' + this.library_name + '" is not available.');
2913 if(typeof callback === 'function') callback();
2918 this.clearSelection();
2921 if(this.debug === true)
2922 this.log('updating from ' + this.data_url);
2924 NETDATA.statistics.refreshes_total++;
2925 NETDATA.statistics.refreshes_active++;
2927 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2928 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2930 this._updating = true;
2932 this.xhr = $.ajax( {
2936 xhrFields: { withCredentials: true } // required for the cookie
2938 .done(function(data) {
2939 that.xhr = undefined;
2941 if(that.debug === true)
2942 that.log('data received. updating chart.');
2944 that.updateChartWithData(data);
2946 .fail(function(msg) {
2947 that.xhr = undefined;
2949 if(msg.statusText !== 'abort')
2950 error('data download failed for url: ' + that.data_url);
2952 .always(function() {
2953 that.xhr = undefined;
2955 NETDATA.statistics.refreshes_active--;
2956 that._updating = false;
2957 if(typeof callback === 'function') callback();
2963 this.isVisible = function(nocache) {
2964 if(typeof nocache === 'undefined')
2967 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2969 // caching - we do not evaluate the charts visibility
2970 // if the page has not been scrolled since the last check
2971 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2972 return this.___isVisible___;
2974 this.tm.last_visible_check = new Date().getTime();
2976 var wh = window.innerHeight;
2977 var x = this.element.getBoundingClientRect();
2981 if(x.width === 0 || x.height === 0) {
2983 this.___isVisible___ = false;
2984 return this.___isVisible___;
2987 if(x.top < 0 && -x.top > x.height) {
2988 // the chart is entirely above
2989 ret = -x.top - x.height;
2991 else if(x.top > wh) {
2992 // the chart is entirely below
2996 if(ret > tolerance) {
2997 // the chart is too far
3000 this.___isVisible___ = false;
3001 return this.___isVisible___;
3004 // the chart is inside or very close
3007 this.___isVisible___ = true;
3008 return this.___isVisible___;
3012 this.isAutoRefreshable = function() {
3013 return (this.current.autorefresh);
3016 this.canBeAutoRefreshed = function() {
3017 var now = new Date().getTime();
3019 if(this.running === true) {
3020 if(this.debug === true)
3021 this.log('I am already running');
3026 if(this.enabled === false) {
3027 if(this.debug === true)
3028 this.log('I am not enabled');
3033 if(this.library === null || this.library.enabled === false) {
3034 error('charting library "' + this.library_name + '" is not available');
3035 if(this.debug === true)
3036 this.log('My chart library ' + this.library_name + ' is not available');
3041 if(this.isVisible() === false) {
3042 if(NETDATA.options.debug.visibility === true || this.debug === true)
3043 this.log('I am not visible');
3048 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
3049 if(this.debug === true)
3050 this.log('timed force update detected - allowing this update');
3052 this.current.force_update_at = 0;
3056 if(this.isAutoRefreshable() === true) {
3057 // allow the first update, even if the page is not visible
3058 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
3059 if(NETDATA.options.debug.focus === true || this.debug === true)
3060 this.log('canBeAutoRefreshed(): page does not have focus');
3065 if(this.needsRecreation() === true) {
3066 if(this.debug === true)
3067 this.log('canBeAutoRefreshed(): needs re-creation.');
3072 // options valid only for autoRefresh()
3073 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
3074 if(NETDATA.globalPanAndZoom.isActive()) {
3075 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
3076 if(this.debug === true)
3077 this.log('canBeAutoRefreshed(): global panning: I need an update.');
3082 if(this.debug === true)
3083 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
3089 if(this.selected === true) {
3090 if(this.debug === true)
3091 this.log('canBeAutoRefreshed(): I have a selection in place.');
3096 if(this.paused === true) {
3097 if(this.debug === true)
3098 this.log('canBeAutoRefreshed(): I am paused.');
3103 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
3104 if(this.debug === true)
3105 this.log('canBeAutoRefreshed(): It is time to update me.');
3115 this.autoRefresh = function(callback) {
3116 if(this.canBeAutoRefreshed() === true && this.running === false) {
3119 state.running = true;
3120 state.updateChart(function() {
3121 state.running = false;
3123 if(typeof callback !== 'undefined')
3128 if(typeof callback !== 'undefined')
3133 this._defaultsFromDownloadedChart = function(chart) {
3135 this.chart_url = chart.url;
3136 this.data_update_every = chart.update_every * 1000;
3137 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
3138 this.tm.last_info_downloaded = new Date().getTime();
3140 if(this.title === null)
3141 this.title = chart.title;
3143 if(this.units === null)
3144 this.units = chart.units;
3147 // fetch the chart description from the netdata server
3148 this.getChart = function(callback) {
3149 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
3151 this._defaultsFromDownloadedChart(this.chart);
3152 if(typeof callback === 'function') callback();
3155 this.chart_url = "/api/v1/chart?chart=" + this.id;
3157 if(this.debug === true)
3158 this.log('downloading ' + this.chart_url);
3161 url: this.host + this.chart_url,
3164 xhrFields: { withCredentials: true } // required for the cookie
3166 .done(function(chart) {
3167 chart.url = that.chart_url;
3168 that._defaultsFromDownloadedChart(chart);
3169 NETDATA.chartRegistry.add(that.host, that.id, chart);
3172 NETDATA.error(404, that.chart_url);
3173 error('chart not found on url "' + that.chart_url + '"');
3175 .always(function() {
3176 if(typeof callback === 'function') callback();
3181 // ============================================================================================================
3187 NETDATA.resetAllCharts = function(state) {
3188 // first clear the global selection sync
3189 // to make sure no chart is in selected state
3190 state.globalSelectionSyncStop();
3192 // there are 2 possibilities here
3193 // a. state is the global Pan and Zoom master
3194 // b. state is not the global Pan and Zoom master
3196 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3199 // clear the global Pan and Zoom
3200 // this will also refresh the master
3201 // and unblock any charts currently mirroring the master
3202 NETDATA.globalPanAndZoom.clearMaster();
3204 // if we were not the master, reset our status too
3205 // this is required because most probably the mouse
3206 // is over this chart, blocking it from auto-refreshing
3207 if(master === false && (state.paused === true || state.selected === true))
3211 // get or create a chart state, given a DOM element
3212 NETDATA.chartState = function(element) {
3213 var state = $(element).data('netdata-state-object') || null;
3214 if(state === null) {
3215 state = new chartState(element);
3216 $(element).data('netdata-state-object', state);
3221 // ----------------------------------------------------------------------------------------------------------------
3222 // Library functions
3224 // Load a script without jquery
3225 // This is used to load jquery - after it is loaded, we use jquery
3226 NETDATA._loadjQuery = function(callback) {
3227 if(typeof jQuery === 'undefined') {
3228 if(NETDATA.options.debug.main_loop === true)
3229 console.log('loading ' + NETDATA.jQuery);
3231 var script = document.createElement('script');
3232 script.type = 'text/javascript';
3233 script.async = true;
3234 script.src = NETDATA.jQuery;
3236 // script.onabort = onError;
3237 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3238 if(typeof callback === "function")
3239 script.onload = callback;
3241 var s = document.getElementsByTagName('script')[0];
3242 s.parentNode.insertBefore(script, s);
3244 else if(typeof callback === "function")
3248 NETDATA._loadCSS = function(filename) {
3249 // don't use jQuery here
3250 // styles are loaded before jQuery
3251 // to eliminate showing an unstyled page to the user
3253 var fileref = document.createElement("link");
3254 fileref.setAttribute("rel", "stylesheet");
3255 fileref.setAttribute("type", "text/css");
3256 fileref.setAttribute("href", filename);
3258 if (typeof fileref !== 'undefined')
3259 document.getElementsByTagName("head")[0].appendChild(fileref);
3262 NETDATA.colorHex2Rgb = function(hex) {
3263 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3264 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3265 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3266 return r + r + g + g + b + b;
3269 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3271 r: parseInt(result[1], 16),
3272 g: parseInt(result[2], 16),
3273 b: parseInt(result[3], 16)
3277 NETDATA.colorLuminance = function(hex, lum) {
3278 // validate hex string
3279 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3281 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3285 // convert to decimal and change luminosity
3286 var rgb = "#", c, i;
3287 for (i = 0; i < 3; i++) {
3288 c = parseInt(hex.substr(i*2,2), 16);
3289 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3290 rgb += ("00"+c).substr(c.length);
3296 NETDATA.guid = function() {
3298 return Math.floor((1 + Math.random()) * 0x10000)
3303 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3306 NETDATA.zeropad = function(x) {
3307 if(x > -10 && x < 10) return '0' + x.toString();
3308 else return x.toString();
3311 // user function to signal us the DOM has been
3313 NETDATA.updatedDom = function() {
3314 NETDATA.options.updated_dom = true;
3317 NETDATA.ready = function(callback) {
3318 NETDATA.options.pauseCallback = callback;
3321 NETDATA.pause = function(callback) {
3322 if(NETDATA.options.pause === true)
3325 NETDATA.options.pauseCallback = callback;
3328 NETDATA.unpause = function() {
3329 NETDATA.options.pauseCallback = null;
3330 NETDATA.options.updated_dom = true;
3331 NETDATA.options.pause = false;
3334 // ----------------------------------------------------------------------------------------------------------------
3336 // this is purely sequencial charts refresher
3337 // it is meant to be autonomous
3338 NETDATA.chartRefresherNoParallel = function(index) {
3339 if(NETDATA.options.debug.mail_loop === true)
3340 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3342 if(NETDATA.options.updated_dom === true) {
3343 // the dom has been updated
3344 // get the dom parts again
3345 NETDATA.parseDom(NETDATA.chartRefresher);
3348 if(index >= NETDATA.options.targets.length) {
3349 if(NETDATA.options.debug.main_loop === true)
3350 console.log('waiting to restart main loop...');
3352 NETDATA.options.auto_refresher_fast_weight = 0;
3354 setTimeout(function() {
3355 NETDATA.chartRefresher();
3356 }, NETDATA.options.current.idle_between_loops);
3359 var state = NETDATA.options.targets[index];
3361 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3362 if(NETDATA.options.debug.main_loop === true)
3363 console.log('fast rendering...');
3365 state.autoRefresh(function() {
3366 NETDATA.chartRefresherNoParallel(++index);
3370 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3371 NETDATA.options.auto_refresher_fast_weight = 0;
3373 setTimeout(function() {
3374 state.autoRefresh(function() {
3375 NETDATA.chartRefresherNoParallel(++index);
3377 }, NETDATA.options.current.idle_between_charts);
3382 // this is part of the parallel refresher
3383 // its cause is to refresh sequencially all the charts
3384 // that depend on chart library initialization
3385 // it will call the parallel refresher back
3386 // as soon as it sees a chart that its chart library
3388 NETDATA.chartRefresher_uninitialized = function() {
3389 if(NETDATA.options.updated_dom === true) {
3390 // the dom has been updated
3391 // get the dom parts again
3392 NETDATA.parseDom(NETDATA.chartRefresher);
3396 if(NETDATA.options.sequencial.length === 0)
3397 NETDATA.chartRefresher();
3399 var state = NETDATA.options.sequencial.pop();
3400 if(state.library.initialized === true)
3401 NETDATA.chartRefresher();
3403 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3407 NETDATA.chartRefresherWaitTime = function() {
3408 return NETDATA.options.current.idle_parallel_loops;
3411 // the default refresher
3412 // it will create 2 sets of charts:
3413 // - the ones that can be refreshed in parallel
3414 // - the ones that depend on something else
3415 // the first set will be executed in parallel
3416 // the second will be given to NETDATA.chartRefresher_uninitialized()
3417 NETDATA.chartRefresher = function() {
3418 // console.log('auto-refresher...');
3420 if(NETDATA.options.pause === true) {
3421 // console.log('auto-refresher is paused');
3422 setTimeout(NETDATA.chartRefresher,
3423 NETDATA.chartRefresherWaitTime());
3427 if(typeof NETDATA.options.pauseCallback === 'function') {
3428 // console.log('auto-refresher is calling pauseCallback');
3429 NETDATA.options.pause = true;
3430 NETDATA.options.pauseCallback();
3431 NETDATA.chartRefresher();
3435 if(NETDATA.options.current.parallel_refresher === false) {
3436 // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
3437 NETDATA.chartRefresherNoParallel(0);
3441 if(NETDATA.options.updated_dom === true) {
3442 // the dom has been updated
3443 // get the dom parts again
3444 // console.log('auto-refresher is calling parseDom()');
3445 NETDATA.parseDom(NETDATA.chartRefresher);
3449 var parallel = new Array();
3450 var targets = NETDATA.options.targets;
3451 var len = targets.length;
3454 state = targets[len];
3455 if(state.isVisible() === false || state.running === true)
3458 if(state.library.initialized === false) {
3459 if(state.library.enabled === true) {
3460 state.library.initialize(NETDATA.chartRefresher);
3464 state.error('chart library "' + state.library_name + '" is not enabled.');
3468 parallel.unshift(state);
3471 if(parallel.length > 0) {
3472 // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
3473 // this will execute the jobs in parallel
3474 $(parallel).each(function() {
3479 // console.log('auto-refresher nothing to do');
3482 // run the next refresh iteration
3483 setTimeout(NETDATA.chartRefresher,
3484 NETDATA.chartRefresherWaitTime());
3487 NETDATA.parseDom = function(callback) {
3488 NETDATA.options.last_page_scroll = new Date().getTime();
3489 NETDATA.options.updated_dom = false;
3491 var targets = $('div[data-netdata]'); //.filter(':visible');
3493 if(NETDATA.options.debug.main_loop === true)
3494 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3496 NETDATA.options.targets = new Array();
3497 var len = targets.length;
3499 // the initialization will take care of sizing
3500 // and the "loading..." message
3501 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3504 if(typeof callback === 'function') callback();
3507 // this is the main function - where everything starts
3508 NETDATA.start = function() {
3509 // this should be called only once
3511 NETDATA.options.page_is_visible = true;
3513 $(window).blur(function() {
3514 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3515 NETDATA.options.page_is_visible = false;
3516 if(NETDATA.options.debug.focus === true)
3517 console.log('Lost Focus!');
3521 $(window).focus(function() {
3522 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3523 NETDATA.options.page_is_visible = true;
3524 if(NETDATA.options.debug.focus === true)
3525 console.log('Focus restored!');
3529 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3530 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3531 NETDATA.options.page_is_visible = false;
3532 if(NETDATA.options.debug.focus === true)
3533 console.log('Document has no focus!');
3537 // bootstrap tab switching
3538 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3540 // bootstrap modal switching
3541 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3542 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3544 // bootstrap collapse switching
3545 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3546 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3548 NETDATA.parseDom(NETDATA.chartRefresher);
3550 // Alarms initialization
3551 setTimeout(NETDATA.alarms.init, 1000);
3553 // Registry initialization
3554 setTimeout(NETDATA.registry.init, netdataRegistryAfterMs);
3556 if(typeof netdataCallback === 'function')
3560 // ----------------------------------------------------------------------------------------------------------------
3563 NETDATA.peityInitialize = function(callback) {
3564 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3566 url: NETDATA.peity_js,
3569 xhrFields: { withCredentials: true } // required for the cookie
3572 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3575 NETDATA.chartLibraries.peity.enabled = false;
3576 NETDATA.error(100, NETDATA.peity_js);
3578 .always(function() {
3579 if(typeof callback === "function")
3584 NETDATA.chartLibraries.peity.enabled = false;
3585 if(typeof callback === "function")
3590 NETDATA.peityChartUpdate = function(state, data) {
3591 state.peity_instance.innerHTML = data.result;
3593 if(state.peity_options.stroke !== state.chartColors()[0]) {
3594 state.peity_options.stroke = state.chartColors()[0];
3595 if(state.chart.chart_type === 'line')
3596 state.peity_options.fill = NETDATA.themes.current.background;
3598 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3601 $(state.peity_instance).peity('line', state.peity_options);
3605 NETDATA.peityChartCreate = function(state, data) {
3606 state.peity_instance = document.createElement('div');
3607 state.element_chart.appendChild(state.peity_instance);
3609 var self = $(state.element);
3610 state.peity_options = {
3611 stroke: NETDATA.themes.current.foreground,
3612 strokeWidth: self.data('peity-strokewidth') || 1,
3613 width: state.chartWidth(),
3614 height: state.chartHeight(),
3615 fill: NETDATA.themes.current.foreground
3618 NETDATA.peityChartUpdate(state, data);
3622 // ----------------------------------------------------------------------------------------------------------------
3625 NETDATA.sparklineInitialize = function(callback) {
3626 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3628 url: NETDATA.sparkline_js,
3631 xhrFields: { withCredentials: true } // required for the cookie
3634 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3637 NETDATA.chartLibraries.sparkline.enabled = false;
3638 NETDATA.error(100, NETDATA.sparkline_js);
3640 .always(function() {
3641 if(typeof callback === "function")
3646 NETDATA.chartLibraries.sparkline.enabled = false;
3647 if(typeof callback === "function")
3652 NETDATA.sparklineChartUpdate = function(state, data) {
3653 state.sparkline_options.width = state.chartWidth();
3654 state.sparkline_options.height = state.chartHeight();
3656 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3660 NETDATA.sparklineChartCreate = function(state, data) {
3661 var self = $(state.element);
3662 var type = self.data('sparkline-type') || 'line';
3663 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3664 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3665 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3666 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3667 var composite = self.data('sparkline-composite') || undefined;
3668 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3669 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3670 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3671 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3672 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3673 var spotColor = self.data('sparkline-spotcolor') || undefined;
3674 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3675 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3676 var spotRadius = self.data('sparkline-spotradius') || undefined;
3677 var valueSpots = self.data('sparkline-valuespots') || undefined;
3678 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3679 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3680 var lineWidth = self.data('sparkline-linewidth') || undefined;
3681 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3682 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3683 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3684 var xvalues = self.data('sparkline-xvalues') || undefined;
3685 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3686 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3687 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3688 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3689 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3690 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3691 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3692 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3693 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3694 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3695 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3696 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3697 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3698 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3699 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3700 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3701 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3702 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3703 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3704 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3705 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3706 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3708 if(spotColor === 'disable') spotColor='';
3709 if(minSpotColor === 'disable') minSpotColor='';
3710 if(maxSpotColor === 'disable') maxSpotColor='';
3712 state.sparkline_options = {
3714 lineColor: lineColor,
3715 fillColor: fillColor,
3716 chartRangeMin: chartRangeMin,
3717 chartRangeMax: chartRangeMax,
3718 composite: composite,
3719 enableTagOptions: enableTagOptions,
3720 tagOptionPrefix: tagOptionPrefix,
3721 tagValuesAttribute: tagValuesAttribute,
3722 disableHiddenCheck: disableHiddenCheck,
3723 defaultPixelsPerValue: defaultPixelsPerValue,
3724 spotColor: spotColor,
3725 minSpotColor: minSpotColor,
3726 maxSpotColor: maxSpotColor,
3727 spotRadius: spotRadius,
3728 valueSpots: valueSpots,
3729 highlightSpotColor: highlightSpotColor,
3730 highlightLineColor: highlightLineColor,
3731 lineWidth: lineWidth,
3732 normalRangeMin: normalRangeMin,
3733 normalRangeMax: normalRangeMax,
3734 drawNormalOnTop: drawNormalOnTop,
3736 chartRangeClip: chartRangeClip,
3737 chartRangeMinX: chartRangeMinX,
3738 chartRangeMaxX: chartRangeMaxX,
3739 disableInteraction: disableInteraction,
3740 disableTooltips: disableTooltips,
3741 disableHighlight: disableHighlight,
3742 highlightLighten: highlightLighten,
3743 highlightColor: highlightColor,
3744 tooltipContainer: tooltipContainer,
3745 tooltipClassname: tooltipClassname,
3746 tooltipChartTitle: state.title,
3747 tooltipFormat: tooltipFormat,
3748 tooltipPrefix: tooltipPrefix,
3749 tooltipSuffix: tooltipSuffix,
3750 tooltipSkipNull: tooltipSkipNull,
3751 tooltipValueLookups: tooltipValueLookups,
3752 tooltipFormatFieldlist: tooltipFormatFieldlist,
3753 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3754 numberFormatter: numberFormatter,
3755 numberDigitGroupSep: numberDigitGroupSep,
3756 numberDecimalMark: numberDecimalMark,
3757 numberDigitGroupCount: numberDigitGroupCount,
3758 animatedZooms: animatedZooms,
3759 width: state.chartWidth(),
3760 height: state.chartHeight()
3763 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3767 // ----------------------------------------------------------------------------------------------------------------
3774 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3775 if(after < state.netdata_first)
3776 after = state.netdata_first;
3778 if(before > state.netdata_last)
3779 before = state.netdata_last;
3781 state.setMode('zoom');
3782 state.globalSelectionSyncStop();
3783 state.globalSelectionSyncDelay();
3784 state.dygraph_user_action = true;
3785 state.dygraph_force_zoom = true;
3786 state.updateChartPanOrZoom(after, before);
3787 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3790 NETDATA.dygraphSetSelection = function(state, t) {
3791 if(typeof state.dygraph_instance !== 'undefined') {
3792 var r = state.calculateRowForTime(t);
3794 state.dygraph_instance.setSelection(r);
3796 state.dygraph_instance.clearSelection();
3797 state.legendShowUndefined();
3804 NETDATA.dygraphClearSelection = function(state, t) {
3805 if(typeof state.dygraph_instance !== 'undefined') {
3806 state.dygraph_instance.clearSelection();
3811 NETDATA.dygraphSmoothInitialize = function(callback) {
3813 url: NETDATA.dygraph_smooth_js,
3816 xhrFields: { withCredentials: true } // required for the cookie
3819 NETDATA.dygraph.smooth = true;
3820 smoothPlotter.smoothing = 0.3;
3823 NETDATA.dygraph.smooth = false;
3825 .always(function() {
3826 if(typeof callback === "function")
3831 NETDATA.dygraphInitialize = function(callback) {
3832 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3834 url: NETDATA.dygraph_js,
3837 xhrFields: { withCredentials: true } // required for the cookie
3840 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3843 NETDATA.chartLibraries.dygraph.enabled = false;
3844 NETDATA.error(100, NETDATA.dygraph_js);
3846 .always(function() {
3847 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3848 NETDATA.dygraphSmoothInitialize(callback);
3849 else if(typeof callback === "function")
3854 NETDATA.chartLibraries.dygraph.enabled = false;
3855 if(typeof callback === "function")
3860 NETDATA.dygraphChartUpdate = function(state, data) {
3861 var dygraph = state.dygraph_instance;
3863 if(typeof dygraph === 'undefined')
3864 return NETDATA.dygraphChartCreate(state, data);
3866 // when the chart is not visible, and hidden
3867 // if there is a window resize, dygraph detects
3868 // its element size as 0x0.
3869 // this will make it re-appear properly
3871 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3875 file: data.result.data,
3876 colors: state.chartColors(),
3877 labels: data.result.labels,
3878 labelsDivWidth: state.chartWidth() - 70,
3879 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3882 if(state.dygraph_force_zoom === true) {
3883 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3884 state.log('dygraphChartUpdate() forced zoom update');
3886 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3887 options.valueRange = state.dygraph_options.valueRange;
3888 options.isZoomedIgnoreProgrammaticZoom = true;
3889 state.dygraph_force_zoom = false;
3891 else if(state.current.name !== 'auto') {
3892 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3893 state.log('dygraphChartUpdate() loose update');
3895 options.valueRange = state.dygraph_options.valueRange;
3898 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3899 state.log('dygraphChartUpdate() strict update');
3901 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3902 options.valueRange = state.dygraph_options.valueRange;
3903 options.isZoomedIgnoreProgrammaticZoom = true;
3906 if(state.dygraph_smooth_eligible === true) {
3907 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3908 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3909 NETDATA.dygraphChartCreate(state, data);
3914 dygraph.updateOptions(options);
3916 state.dygraph_last_rendered = new Date().getTime();
3920 NETDATA.dygraphChartCreate = function(state, data) {
3921 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3922 state.log('dygraphChartCreate()');
3924 var self = $(state.element);
3926 var chart_type = state.chart.chart_type;
3927 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3928 chart_type = self.data('dygraph-type') || chart_type;
3930 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3931 smooth = self.data('dygraph-smooth') || smooth;
3933 if(NETDATA.dygraph.smooth === false)
3936 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3937 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3939 state.dygraph_options = {
3940 colors: self.data('dygraph-colors') || state.chartColors(),
3942 // leave a few pixels empty on the right of the chart
3943 rightGap: self.data('dygraph-rightgap') || 5,
3944 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3945 showRoller: self.data('dygraph-showroller') || false,
3947 title: self.data('dygraph-title') || state.title,
3948 titleHeight: self.data('dygraph-titleheight') || 19,
3950 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3951 labels: data.result.labels,
3952 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3953 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3954 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3955 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3956 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3959 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3960 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3962 includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
3963 xRangePad: self.data('dygraph-xrangepad') || 0,
3964 // yRangePad: self.data('dygraph-yrangepad') || 1,
3966 valueRange: self.data('dygraph-valuerange') || null,
3968 ylabel: state.units,
3969 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3971 // the function to plot the chart
3974 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3975 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3976 strokePattern: self.data('dygraph-strokepattern') || undefined,
3978 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3979 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3980 drawPoints: self.data('dygraph-drawpoints') || false,
3982 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3983 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3985 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3986 pointSize: self.data('dygraph-pointsize') || 1,
3988 // enabling this makes the chart with little square lines
3989 stepPlot: self.data('dygraph-stepplot') || false,
3991 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3992 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3993 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3995 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3996 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3997 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
3998 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
4000 drawAxis: self.data('dygraph-drawaxis') || true,
4001 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
4002 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
4003 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
4005 drawGrid: self.data('dygraph-drawgrid') || true,
4006 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
4007 drawYGrid: self.data('dygraph-drawygrid') || undefined,
4008 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
4009 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4,
4010 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
4012 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
4013 sigFigs: self.data('dygraph-sigfigs') || null,
4014 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
4015 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
4017 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
4018 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
4019 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
4021 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
4022 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
4026 ticker: Dygraph.dateTicker,
4027 axisLabelFormatter: function (d, gran) {
4028 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4030 valueFormatter: function (ms) {
4031 var d = new Date(ms);
4032 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
4033 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4038 valueFormatter: function (x) {
4039 // we format legends with the state object
4040 // no need to do anything here
4041 // return (Math.round(x*100) / 100).toLocaleString();
4042 // return state.legendFormatValue(x);
4047 legendFormatter: function(data) {
4048 var elements = state.element_legend_childs;
4050 // if the hidden div is not there
4051 // we are not managing the legend
4052 if(elements.hidden === null) return;
4054 if (typeof data.x !== 'undefined') {
4055 state.legendSetDate(data.x);
4056 var i = data.series.length;
4058 var series = data.series[i];
4059 if(!series.isVisible) continue;
4060 state.legendSetLabelValue(series.label, series.y);
4066 drawCallback: function(dygraph, is_initial) {
4067 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
4068 state.dygraph_user_action = false;
4070 var x_range = dygraph.xAxisRange();
4071 var after = Math.round(x_range[0]);
4072 var before = Math.round(x_range[1]);
4074 if(NETDATA.options.debug.dygraph === true)
4075 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
4077 if(before <= state.netdata_last && after >= state.netdata_first)
4078 state.updateChartPanOrZoom(after, before);
4081 zoomCallback: function(minDate, maxDate, yRanges) {
4082 if(NETDATA.options.debug.dygraph === true)
4083 state.log('dygraphZoomCallback()');
4085 state.globalSelectionSyncStop();
4086 state.globalSelectionSyncDelay();
4087 state.setMode('zoom');
4089 // refresh it to the greatest possible zoom level
4090 state.dygraph_user_action = true;
4091 state.dygraph_force_zoom = true;
4092 state.updateChartPanOrZoom(minDate, maxDate);
4094 highlightCallback: function(event, x, points, row, seriesName) {
4095 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4096 state.log('dygraphHighlightCallback()');
4100 // there is a bug in dygraph when the chart is zoomed enough
4101 // the time it thinks is selected is wrong
4102 // here we calculate the time t based on the row number selected
4104 var t = state.data_after + row * state.data_update_every;
4105 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
4107 state.globalSelectionSync(x);
4109 // fix legend zIndex using the internal structures of dygraph legend module
4110 // this works, but it is a hack!
4111 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
4113 unhighlightCallback: function(event) {
4114 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4115 state.log('dygraphUnhighlightCallback()');
4117 state.unpauseChart();
4118 state.globalSelectionSyncStop();
4120 interactionModel : {
4121 mousedown: function(event, dygraph, context) {
4122 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4123 state.log('interactionModel.mousedown()');
4125 state.dygraph_user_action = true;
4126 state.globalSelectionSyncStop();
4128 if(NETDATA.options.debug.dygraph === true)
4129 state.log('dygraphMouseDown()');
4131 // Right-click should not initiate a zoom.
4132 if(event.button && event.button === 2) return;
4134 context.initializeMouseDown(event, dygraph, context);
4136 if(event.button && event.button === 1) {
4137 if (event.altKey || event.shiftKey) {
4138 state.setMode('pan');
4139 state.globalSelectionSyncDelay();
4140 Dygraph.startPan(event, dygraph, context);
4143 state.setMode('zoom');
4144 state.globalSelectionSyncDelay();
4145 Dygraph.startZoom(event, dygraph, context);
4149 if (event.altKey || event.shiftKey) {
4150 state.setMode('zoom');
4151 state.globalSelectionSyncDelay();
4152 Dygraph.startZoom(event, dygraph, context);
4155 state.setMode('pan');
4156 state.globalSelectionSyncDelay();
4157 Dygraph.startPan(event, dygraph, context);
4161 mousemove: function(event, dygraph, context) {
4162 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4163 state.log('interactionModel.mousemove()');
4165 if(context.isPanning) {
4166 state.dygraph_user_action = true;
4167 state.globalSelectionSyncStop();
4168 state.globalSelectionSyncDelay();
4169 state.setMode('pan');
4170 Dygraph.movePan(event, dygraph, context);
4172 else if(context.isZooming) {
4173 state.dygraph_user_action = true;
4174 state.globalSelectionSyncStop();
4175 state.globalSelectionSyncDelay();
4176 state.setMode('zoom');
4177 Dygraph.moveZoom(event, dygraph, context);
4180 mouseup: function(event, dygraph, context) {
4181 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4182 state.log('interactionModel.mouseup()');
4184 if (context.isPanning) {
4185 state.dygraph_user_action = true;
4186 state.globalSelectionSyncDelay();
4187 Dygraph.endPan(event, dygraph, context);
4189 else if (context.isZooming) {
4190 state.dygraph_user_action = true;
4191 state.globalSelectionSyncDelay();
4192 Dygraph.endZoom(event, dygraph, context);
4195 click: function(event, dygraph, context) {
4196 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4197 state.log('interactionModel.click()');
4199 event.preventDefault();
4201 dblclick: function(event, dygraph, context) {
4202 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4203 state.log('interactionModel.dblclick()');
4204 NETDATA.resetAllCharts(state);
4206 mousewheel: function(event, dygraph, context) {
4207 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4208 state.log('interactionModel.mousewheel()');
4210 // Take the offset of a mouse event on the dygraph canvas and
4211 // convert it to a pair of percentages from the bottom left.
4212 // (Not top left, bottom is where the lower value is.)
4213 function offsetToPercentage(g, offsetX, offsetY) {
4214 // This is calculating the pixel offset of the leftmost date.
4215 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4216 var yar0 = g.yAxisRange(0);
4218 // This is calculating the pixel of the higest value. (Top pixel)
4219 var yOffset = g.toDomCoords(null, yar0[1])[1];
4221 // x y w and h are relative to the corner of the drawing area,
4222 // so that the upper corner of the drawing area is (0, 0).
4223 var x = offsetX - xOffset;
4224 var y = offsetY - yOffset;
4226 // This is computing the rightmost pixel, effectively defining the
4228 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4230 // This is computing the lowest pixel, effectively defining the height.
4231 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4233 // Percentage from the left.
4234 var xPct = w === 0 ? 0 : (x / w);
4235 // Percentage from the top.
4236 var yPct = h === 0 ? 0 : (y / h);
4238 // The (1-) part below changes it from "% distance down from the top"
4239 // to "% distance up from the bottom".
4240 return [xPct, (1-yPct)];
4243 // Adjusts [x, y] toward each other by zoomInPercentage%
4244 // Split it so the left/bottom axis gets xBias/yBias of that change and
4245 // tight/top gets (1-xBias)/(1-yBias) of that change.
4247 // If a bias is missing it splits it down the middle.
4248 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4249 xBias = xBias || 0.5;
4250 yBias = yBias || 0.5;
4252 function adjustAxis(axis, zoomInPercentage, bias) {
4253 var delta = axis[1] - axis[0];
4254 var increment = delta * zoomInPercentage;
4255 var foo = [increment * bias, increment * (1-bias)];
4257 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4260 var yAxes = g.yAxisRanges();
4262 for (var i = 0; i < yAxes.length; i++) {
4263 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4266 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4269 if(event.altKey || event.shiftKey) {
4270 state.dygraph_user_action = true;
4272 state.globalSelectionSyncStop();
4273 state.globalSelectionSyncDelay();
4275 // http://dygraphs.com/gallery/interaction-api.js
4276 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4277 var percentage = normal / 50;
4279 if (!(event.offsetX && event.offsetY)){
4280 event.offsetX = event.layerX - event.target.offsetLeft;
4281 event.offsetY = event.layerY - event.target.offsetTop;
4284 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4285 var xPct = percentages[0];
4286 var yPct = percentages[1];
4288 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4290 var after = new_x_range[0];
4291 var before = new_x_range[1];
4293 var first = state.netdata_first + state.data_update_every;
4294 var last = state.netdata_last + state.data_update_every;
4297 after -= (before - last);
4304 state.setMode('zoom');
4305 if(state.updateChartPanOrZoom(after, before) === true)
4306 dygraph.updateOptions({ dateWindow: [ after, before ] });
4308 event.preventDefault();
4311 touchstart: function(event, dygraph, context) {
4312 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4313 state.log('interactionModel.touchstart()');
4315 state.dygraph_user_action = true;
4316 state.setMode('zoom');
4319 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4321 // we overwrite the touch directions at the end, to overwrite
4322 // the internal default of dygraphs
4323 context.touchDirections = { x: true, y: false };
4325 state.dygraph_last_touch_start = new Date().getTime();
4326 state.dygraph_last_touch_move = 0;
4328 if(typeof event.touches[0].pageX === 'number')
4329 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4331 state.dygraph_last_touch_page_x = 0;
4333 touchmove: function(event, dygraph, context) {
4334 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4335 state.log('interactionModel.touchmove()');
4337 state.dygraph_user_action = true;
4338 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4340 state.dygraph_last_touch_move = new Date().getTime();
4342 touchend: function(event, dygraph, context) {
4343 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4344 state.log('interactionModel.touchend()');
4346 state.dygraph_user_action = true;
4347 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4349 // if it didn't move, it is a selection
4350 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4351 // internal api of dygraphs
4352 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4353 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4354 if(NETDATA.dygraphSetSelection(state, t) === true)
4355 state.globalSelectionSync(t);
4358 // if it was double tap within double click time, reset the charts
4359 var now = new Date().getTime();
4360 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4361 if(state.dygraph_last_touch_move === 0) {
4362 var dt = now - state.dygraph_last_touch_end;
4363 if(dt <= NETDATA.options.current.double_click_speed)
4364 NETDATA.resetAllCharts(state);
4368 // remember the timestamp of the last touch end
4369 state.dygraph_last_touch_end = now;
4374 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4375 state.dygraph_options.drawGrid = false;
4376 state.dygraph_options.drawAxis = false;
4377 state.dygraph_options.title = undefined;
4378 state.dygraph_options.units = undefined;
4379 state.dygraph_options.ylabel = undefined;
4380 state.dygraph_options.yLabelWidth = 0;
4381 state.dygraph_options.labelsDivWidth = 120;
4382 state.dygraph_options.labelsDivStyles.width = '120px';
4383 state.dygraph_options.labelsSeparateLines = true;
4384 state.dygraph_options.rightGap = 0;
4385 state.dygraph_options.yRangePad = 1;
4388 if(smooth === true) {
4389 state.dygraph_smooth_eligible = true;
4391 if(NETDATA.options.current.smooth_plot === true)
4392 state.dygraph_options.plotter = smoothPlotter;
4394 else state.dygraph_smooth_eligible = false;
4396 state.dygraph_instance = new Dygraph(state.element_chart,
4397 data.result.data, state.dygraph_options);
4399 state.dygraph_force_zoom = false;
4400 state.dygraph_user_action = false;
4401 state.dygraph_last_rendered = new Date().getTime();
4405 // ----------------------------------------------------------------------------------------------------------------
4408 NETDATA.morrisInitialize = function(callback) {
4409 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4411 // morris requires raphael
4412 if(!NETDATA.chartLibraries.raphael.initialized) {
4413 if(NETDATA.chartLibraries.raphael.enabled) {
4414 NETDATA.raphaelInitialize(function() {
4415 NETDATA.morrisInitialize(callback);
4419 NETDATA.chartLibraries.morris.enabled = false;
4420 if(typeof callback === "function")
4425 NETDATA._loadCSS(NETDATA.morris_css);
4428 url: NETDATA.morris_js,
4431 xhrFields: { withCredentials: true } // required for the cookie
4434 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4437 NETDATA.chartLibraries.morris.enabled = false;
4438 NETDATA.error(100, NETDATA.morris_js);
4440 .always(function() {
4441 if(typeof callback === "function")
4447 NETDATA.chartLibraries.morris.enabled = false;
4448 if(typeof callback === "function")
4453 NETDATA.morrisChartUpdate = function(state, data) {
4454 state.morris_instance.setData(data.result.data);
4458 NETDATA.morrisChartCreate = function(state, data) {
4460 state.morris_options = {
4461 element: state.element_chart.id,
4462 data: data.result.data,
4464 ykeys: data.dimension_names,
4465 labels: data.dimension_names,
4471 continuousLine: false,
4472 behaveLikeLine: false
4475 if(state.chart.chart_type === 'line')
4476 state.morris_instance = new Morris.Line(state.morris_options);
4478 else if(state.chart.chart_type === 'area') {
4479 state.morris_options.behaveLikeLine = true;
4480 state.morris_instance = new Morris.Area(state.morris_options);
4483 state.morris_instance = new Morris.Area(state.morris_options);
4488 // ----------------------------------------------------------------------------------------------------------------
4491 NETDATA.raphaelInitialize = function(callback) {
4492 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4494 url: NETDATA.raphael_js,
4497 xhrFields: { withCredentials: true } // required for the cookie
4500 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4503 NETDATA.chartLibraries.raphael.enabled = false;
4504 NETDATA.error(100, NETDATA.raphael_js);
4506 .always(function() {
4507 if(typeof callback === "function")
4512 NETDATA.chartLibraries.raphael.enabled = false;
4513 if(typeof callback === "function")
4518 NETDATA.raphaelChartUpdate = function(state, data) {
4519 $(state.element_chart).raphael(data.result, {
4520 width: state.chartWidth(),
4521 height: state.chartHeight()
4527 NETDATA.raphaelChartCreate = function(state, data) {
4528 $(state.element_chart).raphael(data.result, {
4529 width: state.chartWidth(),
4530 height: state.chartHeight()
4536 // ----------------------------------------------------------------------------------------------------------------
4539 NETDATA.c3Initialize = function(callback) {
4540 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4543 if(!NETDATA.chartLibraries.d3.initialized) {
4544 if(NETDATA.chartLibraries.d3.enabled) {
4545 NETDATA.d3Initialize(function() {
4546 NETDATA.c3Initialize(callback);
4550 NETDATA.chartLibraries.c3.enabled = false;
4551 if(typeof callback === "function")
4556 NETDATA._loadCSS(NETDATA.c3_css);
4562 xhrFields: { withCredentials: true } // required for the cookie
4565 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4568 NETDATA.chartLibraries.c3.enabled = false;
4569 NETDATA.error(100, NETDATA.c3_js);
4571 .always(function() {
4572 if(typeof callback === "function")
4578 NETDATA.chartLibraries.c3.enabled = false;
4579 if(typeof callback === "function")
4584 NETDATA.c3ChartUpdate = function(state, data) {
4585 state.c3_instance.destroy();
4586 return NETDATA.c3ChartCreate(state, data);
4588 //state.c3_instance.load({
4589 // rows: data.result,
4596 NETDATA.c3ChartCreate = function(state, data) {
4598 state.element_chart.id = 'c3-' + state.uuid;
4599 // console.log('id = ' + state.element_chart.id);
4601 state.c3_instance = c3.generate({
4602 bindto: '#' + state.element_chart.id,
4604 width: state.chartWidth(),
4605 height: state.chartHeight()
4608 pattern: state.chartColors()
4613 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4619 format: function(x) {
4620 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4647 // console.log(state.c3_instance);
4652 // ----------------------------------------------------------------------------------------------------------------
4655 NETDATA.d3Initialize = function(callback) {
4656 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4661 xhrFields: { withCredentials: true } // required for the cookie
4664 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4667 NETDATA.chartLibraries.d3.enabled = false;
4668 NETDATA.error(100, NETDATA.d3_js);
4670 .always(function() {
4671 if(typeof callback === "function")
4676 NETDATA.chartLibraries.d3.enabled = false;
4677 if(typeof callback === "function")
4682 NETDATA.d3ChartUpdate = function(state, data) {
4686 NETDATA.d3ChartCreate = function(state, data) {
4690 // ----------------------------------------------------------------------------------------------------------------
4693 NETDATA.googleInitialize = function(callback) {
4694 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4696 url: NETDATA.google_js,
4699 xhrFields: { withCredentials: true } // required for the cookie
4702 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4703 google.load('visualization', '1.1', {
4704 'packages': ['corechart', 'controls'],
4705 'callback': callback
4709 NETDATA.chartLibraries.google.enabled = false;
4710 NETDATA.error(100, NETDATA.google_js);
4711 if(typeof callback === "function")
4716 NETDATA.chartLibraries.google.enabled = false;
4717 if(typeof callback === "function")
4722 NETDATA.googleChartUpdate = function(state, data) {
4723 var datatable = new google.visualization.DataTable(data.result);
4724 state.google_instance.draw(datatable, state.google_options);
4728 NETDATA.googleChartCreate = function(state, data) {
4729 var datatable = new google.visualization.DataTable(data.result);
4731 state.google_options = {
4732 colors: state.chartColors(),
4734 // do not set width, height - the chart resizes itself
4735 //width: state.chartWidth(),
4736 //height: state.chartHeight(),
4741 // title: "Time of Day",
4742 // format:'HH:mm:ss',
4743 viewWindowMode: 'maximized',
4755 viewWindowMode: 'pretty',
4770 focusTarget: 'category',
4777 titlePosition: 'out',
4788 curveType: 'function',
4793 switch(state.chart.chart_type) {
4795 state.google_options.vAxis.viewWindowMode = 'maximized';
4796 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4797 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4801 state.google_options.isStacked = true;
4802 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4803 state.google_options.vAxis.viewWindowMode = 'maximized';
4804 state.google_options.vAxis.minValue = null;
4805 state.google_options.vAxis.maxValue = null;
4806 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4811 state.google_options.lineWidth = 2;
4812 state.google_instance = new google.visualization.LineChart(state.element_chart);
4816 state.google_instance.draw(datatable, state.google_options);
4820 // ----------------------------------------------------------------------------------------------------------------
4822 NETDATA.percentFromValueMax = function(value, max) {
4823 if(value === null) value = 0;
4824 if(max < value) max = value;
4828 pcent = Math.round(value * 100 / max);
4829 if(pcent === 0 && value > 0) pcent = 1;
4835 // ----------------------------------------------------------------------------------------------------------------
4838 NETDATA.easypiechartInitialize = function(callback) {
4839 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4841 url: NETDATA.easypiechart_js,
4844 xhrFields: { withCredentials: true } // required for the cookie
4847 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4850 NETDATA.chartLibraries.easypiechart.enabled = false;
4851 NETDATA.error(100, NETDATA.easypiechart_js);
4853 .always(function() {
4854 if(typeof callback === "function")
4859 NETDATA.chartLibraries.easypiechart.enabled = false;
4860 if(typeof callback === "function")
4865 NETDATA.easypiechartClearSelection = function(state) {
4866 if(typeof state.easyPieChartEvent !== 'undefined') {
4867 if(state.easyPieChartEvent.timer !== null)
4868 clearTimeout(state.easyPieChartEvent.timer);
4870 state.easyPieChartEvent.timer = null;
4873 if(state.isAutoRefreshable() === true && state.data !== null) {
4874 NETDATA.easypiechartChartUpdate(state, state.data);
4877 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4878 state.easyPieChart_instance.update(0);
4880 state.easyPieChart_instance.enableAnimation();
4885 NETDATA.easypiechartSetSelection = function(state, t) {
4886 if(state.timeIsVisible(t) !== true)
4887 return NETDATA.easypiechartClearSelection(state);
4889 var slot = state.calculateRowForTime(t);
4890 if(slot < 0 || slot >= state.data.result.length)
4891 return NETDATA.easypiechartClearSelection(state);
4893 if(typeof state.easyPieChartEvent === 'undefined') {
4894 state.easyPieChartEvent = {
4901 var value = state.data.result[state.data.result.length - 1 - slot];
4902 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4903 var pcent = NETDATA.percentFromValueMax(value, max);
4905 state.easyPieChartEvent.value = value;
4906 state.easyPieChartEvent.pcent = pcent;
4907 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4909 if(state.easyPieChartEvent.timer === null) {
4910 state.easyPieChart_instance.disableAnimation();
4912 state.easyPieChartEvent.timer = setTimeout(function() {
4913 state.easyPieChartEvent.timer = null;
4914 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4915 }, NETDATA.options.current.charts_selection_animation_delay);
4921 NETDATA.easypiechartChartUpdate = function(state, data) {
4922 var value, max, pcent;
4924 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4930 value = data.result[0];
4931 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4932 pcent = NETDATA.percentFromValueMax(value, max);
4935 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4936 state.easyPieChart_instance.update(pcent);
4940 NETDATA.easypiechartChartCreate = function(state, data) {
4941 var self = $(state.element);
4942 var chart = $(state.element_chart);
4944 var value = data.result[0];
4945 var max = self.data('easypiechart-max-value') || null;
4946 var adjust = self.data('easypiechart-adjust') || null;
4950 state.easyPieChartMax = null;
4953 state.easyPieChartMax = max;
4955 var pcent = NETDATA.percentFromValueMax(value, max);
4957 chart.data('data-percent', pcent);
4961 case 'width': size = state.chartHeight(); break;
4962 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4963 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4965 default: size = state.chartWidth(); break;
4967 state.element.style.width = size + 'px';
4968 state.element.style.height = size + 'px';
4970 var stroke = Math.floor(size / 22);
4971 if(stroke < 3) stroke = 2;
4973 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4974 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4975 state.easyPieChartLabel = document.createElement('span');
4976 state.easyPieChartLabel.className = 'easyPieChartLabel';
4977 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4978 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4979 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4980 state.element_chart.appendChild(state.easyPieChartLabel);
4982 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4983 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4984 state.easyPieChartTitle = document.createElement('span');
4985 state.easyPieChartTitle.className = 'easyPieChartTitle';
4986 state.easyPieChartTitle.innerHTML = state.title;
4987 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4988 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4989 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4990 state.element_chart.appendChild(state.easyPieChartTitle);
4992 var unitfontsize = Math.round(titlefontsize * 0.9);
4993 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4994 state.easyPieChartUnits = document.createElement('span');
4995 state.easyPieChartUnits.className = 'easyPieChartUnits';
4996 state.easyPieChartUnits.innerHTML = state.units;
4997 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4998 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4999 state.element_chart.appendChild(state.easyPieChartUnits);
5001 chart.easyPieChart({
5002 barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
5003 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
5004 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
5005 scaleLength: self.data('easypiechart-scalelength') || 5,
5006 lineCap: self.data('easypiechart-linecap') || 'round',
5007 lineWidth: self.data('easypiechart-linewidth') || stroke,
5008 trackWidth: self.data('easypiechart-trackwidth') || undefined,
5009 size: self.data('easypiechart-size') || size,
5010 rotate: self.data('easypiechart-rotate') || 0,
5011 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
5012 easing: self.data('easypiechart-easing') || undefined
5015 // when we just re-create the chart
5016 // do not animate the first update
5018 if(typeof state.easyPieChart_instance !== 'undefined')
5021 state.easyPieChart_instance = chart.data('easyPieChart');
5022 if(animate === false) state.easyPieChart_instance.disableAnimation();
5023 state.easyPieChart_instance.update(pcent);
5024 if(animate === false) state.easyPieChart_instance.enableAnimation();
5028 // ----------------------------------------------------------------------------------------------------------------
5031 NETDATA.gaugeInitialize = function(callback) {
5032 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
5034 url: NETDATA.gauge_js,
5037 xhrFields: { withCredentials: true } // required for the cookie
5040 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
5043 NETDATA.chartLibraries.gauge.enabled = false;
5044 NETDATA.error(100, NETDATA.gauge_js);
5046 .always(function() {
5047 if(typeof callback === "function")
5052 NETDATA.chartLibraries.gauge.enabled = false;
5053 if(typeof callback === "function")
5058 NETDATA.gaugeAnimation = function(state, status) {
5061 if(typeof status === 'boolean' && status === false)
5063 else if(typeof status === 'number')
5066 state.gauge_instance.animationSpeed = speed;
5067 state.___gaugeOld__.speed = speed;
5070 NETDATA.gaugeSet = function(state, value, min, max) {
5071 if(typeof value !== 'number') value = 0;
5072 if(typeof min !== 'number') min = 0;
5073 if(typeof max !== 'number') max = 0;
5074 if(value > max) max = value;
5075 if(value < min) min = value;
5084 // gauge.js has an issue if the needle
5085 // is smaller than min or larger than max
5086 // when we set the new values
5087 // the needle will go crazy
5089 // to prevent it, we always feed it
5090 // with a percentage, so that the needle
5091 // is always between min and max
5092 var pcent = (value - min) * 100 / (max - min);
5094 // these should never happen
5095 if(pcent < 0) pcent = 0;
5096 if(pcent > 100) pcent = 100;
5098 state.gauge_instance.set(pcent);
5100 state.___gaugeOld__.value = value;
5101 state.___gaugeOld__.min = min;
5102 state.___gaugeOld__.max = max;
5105 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5106 if(state.___gaugeOld__.valueLabel !== value) {
5107 state.___gaugeOld__.valueLabel = value;
5108 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5110 if(state.___gaugeOld__.minLabel !== min) {
5111 state.___gaugeOld__.minLabel = min;
5112 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5114 if(state.___gaugeOld__.maxLabel !== max) {
5115 state.___gaugeOld__.maxLabel = max;
5116 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5120 NETDATA.gaugeClearSelection = function(state) {
5121 if(typeof state.gaugeEvent !== 'undefined') {
5122 if(state.gaugeEvent.timer !== null)
5123 clearTimeout(state.gaugeEvent.timer);
5125 state.gaugeEvent.timer = null;
5128 if(state.isAutoRefreshable() === true && state.data !== null) {
5129 NETDATA.gaugeChartUpdate(state, state.data);
5132 NETDATA.gaugeAnimation(state, false);
5133 NETDATA.gaugeSet(state, null, null, null);
5134 NETDATA.gaugeSetLabels(state, null, null, null);
5137 NETDATA.gaugeAnimation(state, true);
5141 NETDATA.gaugeSetSelection = function(state, t) {
5142 if(state.timeIsVisible(t) !== true)
5143 return NETDATA.gaugeClearSelection(state);
5145 var slot = state.calculateRowForTime(t);
5146 if(slot < 0 || slot >= state.data.result.length)
5147 return NETDATA.gaugeClearSelection(state);
5149 if(typeof state.gaugeEvent === 'undefined') {
5150 state.gaugeEvent = {
5158 var value = state.data.result[state.data.result.length - 1 - slot];
5159 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5162 state.gaugeEvent.value = value;
5163 state.gaugeEvent.max = max;
5164 state.gaugeEvent.min = min;
5165 NETDATA.gaugeSetLabels(state, value, min, max);
5167 if(state.gaugeEvent.timer === null) {
5168 NETDATA.gaugeAnimation(state, false);
5170 state.gaugeEvent.timer = setTimeout(function() {
5171 state.gaugeEvent.timer = null;
5172 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5173 }, NETDATA.options.current.charts_selection_animation_delay);
5179 NETDATA.gaugeChartUpdate = function(state, data) {
5180 var value, min, max;
5182 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5186 NETDATA.gaugeSetLabels(state, null, null, null);
5189 value = data.result[0];
5191 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5192 if(value > max) max = value;
5193 NETDATA.gaugeSetLabels(state, value, min, max);
5196 NETDATA.gaugeSet(state, value, min, max);
5200 NETDATA.gaugeChartCreate = function(state, data) {
5201 var self = $(state.element);
5202 // var chart = $(state.element_chart);
5204 var value = data.result[0];
5205 var max = self.data('gauge-max-value') || null;
5206 var adjust = self.data('gauge-adjust') || null;
5207 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5208 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5209 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5210 var stopColor = self.data('gauge-stop-color') || void 0;
5211 var generateGradient = self.data('gauge-generate-gradient') || false;
5215 state.gaugeMax = null;
5218 state.gaugeMax = max;
5220 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5222 // case 'width': width = height * ratio; break;
5224 // default: height = width / ratio; break;
5226 //state.element.style.width = width.toString() + 'px';
5227 //state.element.style.height = height.toString() + 'px';
5232 lines: 12, // The number of lines to draw
5233 angle: 0.15, // The length of each line
5234 lineWidth: 0.44, // 0.44 The line thickness
5236 length: 0.8, // 0.9 The radius of the inner circle
5237 strokeWidth: 0.035, // The rotation offset
5238 color: pointerColor // Fill color
5240 colorStart: startColor, // Colors
5241 colorStop: stopColor, // just experiment with them
5242 strokeColor: strokeColor, // to see which ones work best for you
5244 generateGradient: (generateGradient === true)?true:false,
5248 if (generateGradient.constructor === Array) {
5250 // data-gauge-generate-gradient="[0, 50, 100]"
5251 // data-gauge-gradient-percent-color-0="#FFFFFF"
5252 // data-gauge-gradient-percent-color-50="#999900"
5253 // data-gauge-gradient-percent-color-100="#000000"
5255 options.percentColors = new Array();
5256 var len = generateGradient.length;
5258 var pcent = generateGradient[len];
5259 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5260 if(color !== false) {
5261 var a = new Array();
5264 options.percentColors.unshift(a);
5267 if(options.percentColors.length === 0)
5268 delete options.percentColors;
5270 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5271 options.percentColors = [
5272 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5273 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5274 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5275 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5276 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5277 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5278 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5279 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5280 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5281 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5282 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5285 state.gauge_canvas = document.createElement('canvas');
5286 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5287 state.gauge_canvas.className = 'gaugeChart';
5288 state.gauge_canvas.width = width;
5289 state.gauge_canvas.height = height;
5290 state.element_chart.appendChild(state.gauge_canvas);
5292 var valuefontsize = Math.floor(height / 6);
5293 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5294 state.gaugeChartLabel = document.createElement('span');
5295 state.gaugeChartLabel.className = 'gaugeChartLabel';
5296 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5297 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5298 state.element_chart.appendChild(state.gaugeChartLabel);
5300 var titlefontsize = Math.round(valuefontsize / 2);
5302 state.gaugeChartTitle = document.createElement('span');
5303 state.gaugeChartTitle.className = 'gaugeChartTitle';
5304 state.gaugeChartTitle.innerHTML = state.title;
5305 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5306 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5307 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5308 state.element_chart.appendChild(state.gaugeChartTitle);
5310 var unitfontsize = Math.round(titlefontsize * 0.9);
5311 state.gaugeChartUnits = document.createElement('span');
5312 state.gaugeChartUnits.className = 'gaugeChartUnits';
5313 state.gaugeChartUnits.innerHTML = state.units;
5314 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5315 state.element_chart.appendChild(state.gaugeChartUnits);
5317 state.gaugeChartMin = document.createElement('span');
5318 state.gaugeChartMin.className = 'gaugeChartMin';
5319 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5320 state.element_chart.appendChild(state.gaugeChartMin);
5322 state.gaugeChartMax = document.createElement('span');
5323 state.gaugeChartMax.className = 'gaugeChartMax';
5324 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5325 state.element_chart.appendChild(state.gaugeChartMax);
5327 // when we just re-create the chart
5328 // do not animate the first update
5330 if(typeof state.gauge_instance !== 'undefined')
5333 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5335 state.___gaugeOld__ = {
5344 // we will always feed a percentage
5345 state.gauge_instance.minValue = 0;
5346 state.gauge_instance.maxValue = 100;
5348 NETDATA.gaugeAnimation(state, animate);
5349 NETDATA.gaugeSet(state, value, 0, max);
5350 NETDATA.gaugeSetLabels(state, value, 0, max);
5351 NETDATA.gaugeAnimation(state, true);
5355 // ----------------------------------------------------------------------------------------------------------------
5356 // Charts Libraries Registration
5358 NETDATA.chartLibraries = {
5360 initialize: NETDATA.dygraphInitialize,
5361 create: NETDATA.dygraphChartCreate,
5362 update: NETDATA.dygraphChartUpdate,
5363 resize: function(state) {
5364 if(typeof state.dygraph_instance.resize === 'function')
5365 state.dygraph_instance.resize();
5367 setSelection: NETDATA.dygraphSetSelection,
5368 clearSelection: NETDATA.dygraphClearSelection,
5369 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5372 format: function(state) { return 'json'; },
5373 options: function(state) { return 'ms|flip'; },
5374 legend: function(state) {
5375 if(this.isSparkline(state) === false)
5376 return 'right-side';
5380 autoresize: function(state) { return true; },
5381 max_updates_to_recreate: function(state) { return 5000; },
5382 track_colors: function(state) { return true; },
5383 pixels_per_point: function(state) {
5384 if(this.isSparkline(state) === false)
5390 isSparkline: function(state) {
5391 if(typeof state.dygraph_sparkline === 'undefined') {
5392 var t = $(state.element).data('dygraph-theme');
5393 if(t === 'sparkline')
5394 state.dygraph_sparkline = true;
5396 state.dygraph_sparkline = false;
5398 return state.dygraph_sparkline;
5402 initialize: NETDATA.sparklineInitialize,
5403 create: NETDATA.sparklineChartCreate,
5404 update: NETDATA.sparklineChartUpdate,
5406 setSelection: undefined, // function(state, t) { return true; },
5407 clearSelection: undefined, // function(state) { return true; },
5408 toolboxPanAndZoom: null,
5411 format: function(state) { return 'array'; },
5412 options: function(state) { return 'flip|abs'; },
5413 legend: function(state) { return null; },
5414 autoresize: function(state) { return false; },
5415 max_updates_to_recreate: function(state) { return 5000; },
5416 track_colors: function(state) { return false; },
5417 pixels_per_point: function(state) { return 3; }
5420 initialize: NETDATA.peityInitialize,
5421 create: NETDATA.peityChartCreate,
5422 update: NETDATA.peityChartUpdate,
5424 setSelection: undefined, // function(state, t) { return true; },
5425 clearSelection: undefined, // function(state) { return true; },
5426 toolboxPanAndZoom: null,
5429 format: function(state) { return 'ssvcomma'; },
5430 options: function(state) { return 'null2zero|flip|abs'; },
5431 legend: function(state) { return null; },
5432 autoresize: function(state) { return false; },
5433 max_updates_to_recreate: function(state) { return 5000; },
5434 track_colors: function(state) { return false; },
5435 pixels_per_point: function(state) { return 3; }
5438 initialize: NETDATA.morrisInitialize,
5439 create: NETDATA.morrisChartCreate,
5440 update: NETDATA.morrisChartUpdate,
5442 setSelection: undefined, // function(state, t) { return true; },
5443 clearSelection: undefined, // function(state) { return true; },
5444 toolboxPanAndZoom: null,
5447 format: function(state) { return 'json'; },
5448 options: function(state) { return 'objectrows|ms'; },
5449 legend: function(state) { return null; },
5450 autoresize: function(state) { return false; },
5451 max_updates_to_recreate: function(state) { return 50; },
5452 track_colors: function(state) { return false; },
5453 pixels_per_point: function(state) { return 15; }
5456 initialize: NETDATA.googleInitialize,
5457 create: NETDATA.googleChartCreate,
5458 update: NETDATA.googleChartUpdate,
5460 setSelection: undefined, //function(state, t) { return true; },
5461 clearSelection: undefined, //function(state) { return true; },
5462 toolboxPanAndZoom: null,
5465 format: function(state) { return 'datatable'; },
5466 options: function(state) { return ''; },
5467 legend: function(state) { return null; },
5468 autoresize: function(state) { return false; },
5469 max_updates_to_recreate: function(state) { return 300; },
5470 track_colors: function(state) { return false; },
5471 pixels_per_point: function(state) { return 4; }
5474 initialize: NETDATA.raphaelInitialize,
5475 create: NETDATA.raphaelChartCreate,
5476 update: NETDATA.raphaelChartUpdate,
5478 setSelection: undefined, // function(state, t) { return true; },
5479 clearSelection: undefined, // function(state) { return true; },
5480 toolboxPanAndZoom: null,
5483 format: function(state) { return 'json'; },
5484 options: function(state) { return ''; },
5485 legend: function(state) { return null; },
5486 autoresize: function(state) { return false; },
5487 max_updates_to_recreate: function(state) { return 5000; },
5488 track_colors: function(state) { return false; },
5489 pixels_per_point: function(state) { return 3; }
5492 initialize: NETDATA.c3Initialize,
5493 create: NETDATA.c3ChartCreate,
5494 update: NETDATA.c3ChartUpdate,
5496 setSelection: undefined, // function(state, t) { return true; },
5497 clearSelection: undefined, // function(state) { return true; },
5498 toolboxPanAndZoom: null,
5501 format: function(state) { return 'csvjsonarray'; },
5502 options: function(state) { return 'milliseconds'; },
5503 legend: function(state) { return null; },
5504 autoresize: function(state) { return false; },
5505 max_updates_to_recreate: function(state) { return 5000; },
5506 track_colors: function(state) { return false; },
5507 pixels_per_point: function(state) { return 15; }
5510 initialize: NETDATA.d3Initialize,
5511 create: NETDATA.d3ChartCreate,
5512 update: NETDATA.d3ChartUpdate,
5514 setSelection: undefined, // function(state, t) { return true; },
5515 clearSelection: undefined, // function(state) { return true; },
5516 toolboxPanAndZoom: null,
5519 format: function(state) { return 'json'; },
5520 options: function(state) { return ''; },
5521 legend: function(state) { return null; },
5522 autoresize: function(state) { return false; },
5523 max_updates_to_recreate: function(state) { return 5000; },
5524 track_colors: function(state) { return false; },
5525 pixels_per_point: function(state) { return 3; }
5528 initialize: NETDATA.easypiechartInitialize,
5529 create: NETDATA.easypiechartChartCreate,
5530 update: NETDATA.easypiechartChartUpdate,
5532 setSelection: NETDATA.easypiechartSetSelection,
5533 clearSelection: NETDATA.easypiechartClearSelection,
5534 toolboxPanAndZoom: null,
5537 format: function(state) { return 'array'; },
5538 options: function(state) { return 'absolute'; },
5539 legend: function(state) { return null; },
5540 autoresize: function(state) { return false; },
5541 max_updates_to_recreate: function(state) { return 5000; },
5542 track_colors: function(state) { return true; },
5543 pixels_per_point: function(state) { return 3; },
5547 initialize: NETDATA.gaugeInitialize,
5548 create: NETDATA.gaugeChartCreate,
5549 update: NETDATA.gaugeChartUpdate,
5551 setSelection: NETDATA.gaugeSetSelection,
5552 clearSelection: NETDATA.gaugeClearSelection,
5553 toolboxPanAndZoom: null,
5556 format: function(state) { return 'array'; },
5557 options: function(state) { return 'absolute'; },
5558 legend: function(state) { return null; },
5559 autoresize: function(state) { return false; },
5560 max_updates_to_recreate: function(state) { return 5000; },
5561 track_colors: function(state) { return true; },
5562 pixels_per_point: function(state) { return 3; },
5567 NETDATA.registerChartLibrary = function(library, url) {
5568 if(NETDATA.options.debug.libraries === true)
5569 console.log("registering chart library: " + library);
5571 NETDATA.chartLibraries[library].url = url;
5572 NETDATA.chartLibraries[library].initialized = true;
5573 NETDATA.chartLibraries[library].enabled = true;
5576 // ----------------------------------------------------------------------------------------------------------------
5577 // Load required JS libraries and CSS
5579 NETDATA.requiredJs = [
5581 url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js',
5583 isAlreadyLoaded: function() {
5584 // check if bootstrap is loaded
5585 if(typeof $().emulateTransitionEnd == 'function')
5588 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5596 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
5597 isAlreadyLoaded: function() { return false; }
5601 NETDATA.requiredCSS = [
5603 url: NETDATA.themes.current.bootstrap_css,
5604 isAlreadyLoaded: function() {
5605 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5612 url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
5613 isAlreadyLoaded: function() { return false; }
5616 url: NETDATA.themes.current.dashboard_css,
5617 isAlreadyLoaded: function() { return false; }
5620 url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
5621 isAlreadyLoaded: function() { return false; }
5625 NETDATA.loadedRequiredJs = 0;
5626 NETDATA.loadRequiredJs = function(index, callback) {
5627 if(index >= NETDATA.requiredJs.length) {
5628 if(typeof callback === 'function')
5633 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5634 NETDATA.loadedRequiredJs++;
5635 NETDATA.loadRequiredJs(++index, callback);
5639 if(NETDATA.options.debug.main_loop === true)
5640 console.log('loading ' + NETDATA.requiredJs[index].url);
5643 if(typeof NETDATA.requiredJs[index].async !== 'undefined' && NETDATA.requiredJs[index].async === false)
5647 url: NETDATA.requiredJs[index].url,
5650 xhrFields: { withCredentials: true } // required for the cookie
5653 if(NETDATA.options.debug.main_loop === true)
5654 console.log('loaded ' + NETDATA.requiredJs[index].url);
5657 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5659 .always(function() {
5660 NETDATA.loadedRequiredJs++;
5663 NETDATA.loadRequiredJs(++index, callback);
5667 NETDATA.loadRequiredJs(++index, callback);
5670 NETDATA.loadRequiredCSS = function(index) {
5671 if(index >= NETDATA.requiredCSS.length)
5674 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5675 NETDATA.loadRequiredCSS(++index);
5679 if(NETDATA.options.debug.main_loop === true)
5680 console.log('loading ' + NETDATA.requiredCSS[index].url);
5682 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5683 NETDATA.loadRequiredCSS(++index);
5687 // ----------------------------------------------------------------------------------------------------------------
5688 // Registry of netdata hosts
5691 onclick: null, // the callback to handle the click - it will be called with the alarm log entry
5692 chart_div_offset: 100, // give that space above the chart when scrolling to it
5693 chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id))
5694 chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart
5696 ms_penalty: 0, // the time penalty of the next alarm
5697 ms_between_notifications: 500, // firefox moves the alarms off-screen (above, outside the top of the screen)
5698 // if alarms are shown faster than: one per 500ms
5700 notifications: false, // when true, the browser supports notifications (may not be granted though)
5701 last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5702 first_notification_id: 0, // the id of the first alarm_log entry for this session
5703 // this is used to prevent CLEAR notifications for past events
5704 // notifications_shown: new Array(),
5706 server: null, // the server to connect to for fetching alarms
5707 current: null, // the list of raised alarms - updated in the background
5708 callback: null, // a callback function to call every time the list of raised alarms is refreshed
5710 notify: function(entry) {
5711 // console.log('alarm ' + entry.unique_id);
5713 if(entry.updated === true) {
5714 // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5718 var value = entry.value;
5719 if(NETDATA.alarms.current !== null) {
5720 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
5721 if(typeof t !== 'undefined' && entry.status == t.status)
5725 var name = entry.name.replace(/_/g, ' ');
5726 var status = entry.status.toLowerCase();
5727 var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
5728 var tag = entry.alarm_id;
5729 var icon = 'images/seo-performance-128.png';
5730 var interaction = false;
5734 // console.log('alarm ' + entry.unique_id + ' ' + entry.chart + '.' + entry.name + ' is ' + entry.status);
5736 switch(entry.status) {
5744 case 'UNINITIALIZED':
5748 if(entry.unique_id < NETDATA.alarms.first_notification_id) {
5749 // console.log('alarm ' + entry.unique_id + ' is not current');
5752 if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5753 // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5756 title = name + ' back to normal';
5757 icon = 'images/check-mark-2-128-green.png'
5758 interaction = false;
5762 if(entry.old_status === 'CRITICAL')
5763 status = 'demoted to ' + entry.status.toLowerCase();
5765 icon = 'images/alert-128-orange.png';
5766 interaction = false;
5770 if(entry.old_status === 'WARNING')
5771 status = 'escalated to ' + entry.status.toLowerCase();
5773 icon = 'images/alert-128-red.png'
5778 console.log('invalid alarm status ' + entry.status);
5783 // cleanup old notifications with the same alarm_id as this one
5784 // FIXME: it does not seem to work on any web browser!
5785 var len = NETDATA.alarms.notifications_shown.length;
5787 var n = NETDATA.alarms.notifications_shown[len];
5788 if(n.data.alarm_id === entry.alarm_id) {
5789 console.log('removing old alarm ' + n.data.unique_id);
5791 // close the notification
5794 // remove it from the array
5795 NETDATA.alarms.notifications_shown.splice(len, 1);
5796 len = NETDATA.alarms.notifications_shown.length;
5803 setTimeout(function() {
5804 // show this notification
5805 // console.log('new notification: ' + title);
5806 var n = new Notification(title, {
5807 body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info,
5809 requireInteraction: interaction,
5810 icon: NETDATA.serverDefault + icon,
5814 n.onclick = function(event) {
5815 event.preventDefault();
5816 NETDATA.alarms.onclick(event.target.data);
5820 // NETDATA.alarms.notifications_shown.push(n);
5821 // console.log(entry);
5822 }, NETDATA.alarms.ms_penalty);
5824 NETDATA.alarms.ms_penalty += NETDATA.alarms.ms_between_notifications;
5828 scrollToChart: function(chart_id) {
5829 if(typeof chart_id === 'string') {
5830 var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset();
5831 if(typeof offset !== 'undefined') {
5832 $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration);
5839 scrollToAlarm: function(alarm) {
5840 if(typeof alarm === 'object') {
5841 var ret = NETDATA.alarms.scrollToChart(alarm.chart);
5843 if(ret === true && NETDATA.options.page_is_visible === false)
5845 // alert('netdata dashboard will now scroll to chart: ' + alarm.chart + '\n\nThis alarm opened to bring the browser window in front of the screen. Click on the dashboard to prevent it from appearing again.');
5850 notifyAll: function() {
5851 // console.log('FETCHING ALARM LOG');
5852 NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5853 // console.log('ALARM LOG FETCHED');
5855 if(data === null || typeof data !== 'object') {
5856 console.log('invalid alarms log response');
5860 if(data.length === 0) {
5861 console.log('received empty alarm log');
5865 // console.log('received alarm log of ' + data.length + ' entries, from ' + data[data.length - 1].unique_id.toString() + ' to ' + data[0].unique_id.toString());
5867 data.sort(function(a, b) {
5868 if(a.unique_id > b.unique_id) return -1;
5869 if(a.unique_id < b.unique_id) return 1;
5873 NETDATA.alarms.ms_penalty = 0;
5875 var len = data.length;
5877 if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5878 NETDATA.alarms.notify(data[len]);
5881 // console.log('ignoring alarm (older) with id ' + data[len].unique_id.toString());
5884 NETDATA.alarms.last_notification_id = data[0].unique_id;
5885 NETDATA.localStorageSet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5886 // console.log('last notification id = ' + NETDATA.alarms.last_notification_id);
5890 check_notifications: function() {
5891 // returns true if we should fire 1+ notifications
5893 if(NETDATA.alarms.notifications !== true) {
5894 // console.log('notifications not available');
5898 if(Notification.permission !== 'granted') {
5899 // console.log('notifications not granted');
5903 if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5904 // console.log('can do alarms: old id = ' + NETDATA.alarms.last_notification_id + ' new id = ' + NETDATA.alarms.current.latest_alarm_log_unique_id);
5906 if(NETDATA.alarms.current.latest_alarm_log_unique_id > NETDATA.alarms.last_notification_id) {
5907 // console.log('new alarms detected');
5910 //else console.log('no new alarms');
5912 // else console.log('cannot process alarms');
5917 get: function(what, callback) {
5919 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5922 xhrFields: { withCredentials: true } // required for the cookie
5924 .done(function(data) {
5925 if(NETDATA.alarms.first_notification_id === 0 && typeof data.latest_alarm_log_unique_id === 'number')
5926 NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
5928 if(typeof callback === 'function')
5932 NETDATA.error(415, NETDATA.alarms.server);
5934 if(typeof callback === 'function')
5939 update_forever: function() {
5940 NETDATA.alarms.get('active', function(data) {
5942 NETDATA.alarms.current = data;
5944 if(NETDATA.alarms.check_notifications() === true) {
5945 NETDATA.alarms.notifyAll();
5948 if (typeof NETDATA.alarms.callback === 'function') {
5949 NETDATA.alarms.callback(data);
5952 // Health monitoring is disabled on this netdata
5953 if(data.status === false) return;
5956 setTimeout(NETDATA.alarms.update_forever, 10000);
5960 get_log: function(last_id, callback) {
5961 // console.log('fetching all log after ' + last_id.toString());
5963 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5966 xhrFields: { withCredentials: true } // required for the cookie
5968 .done(function(data) {
5969 if(typeof callback === 'function')
5973 NETDATA.error(416, NETDATA.alarms.server);
5975 if(typeof callback === 'function')
5981 var host = NETDATA.serverDefault;
5982 while(host.slice(-1) === '/')
5983 host = host.substring(0, host.length - 1);
5984 NETDATA.alarms.server = host;
5986 NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5988 if(NETDATA.alarms.onclick === null)
5989 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
5991 if(netdataShowAlarms === true) {
5992 NETDATA.alarms.update_forever();
5994 if('Notification' in window) {
5995 // console.log('notifications available');
5996 NETDATA.alarms.notifications = true;
5998 if(Notification.permission === 'default')
5999 Notification.requestPermission();
6005 // ----------------------------------------------------------------------------------------------------------------
6006 // Registry of netdata hosts
6008 NETDATA.registry = {
6009 server: null, // the netdata registry server
6010 person_guid: null, // the unique ID of this browser / user
6011 machine_guid: null, // the unique ID the netdata server that served dashboard.js
6012 hostname: null, // the hostname of the netdata server that served dashboard.js
6013 machines: null, // the user's other URLs
6014 machines_array: null, // the user's other URLs in an array
6017 parsePersonUrls: function(person_urls) {
6018 // console.log(person_urls);
6019 NETDATA.registry.person_urls = person_urls;
6022 NETDATA.registry.machines = {};
6023 NETDATA.registry.machines_array = new Array();
6025 var now = new Date().getTime();
6026 var apu = person_urls;
6029 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
6030 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6036 accesses: apu[i][3],
6038 alternate_urls: new Array()
6040 obj.alternate_urls.push(apu[i][1]);
6042 NETDATA.registry.machines[apu[i][0]] = obj;
6043 NETDATA.registry.machines_array.push(obj);
6046 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6048 var pu = NETDATA.registry.machines[apu[i][0]];
6049 if(pu.last_t < apu[i][2]) {
6051 pu.last_t = apu[i][2];
6052 pu.name = apu[i][4];
6054 pu.accesses += apu[i][3];
6055 pu.alternate_urls.push(apu[i][1]);
6060 if(typeof netdataRegistryCallback === 'function')
6061 netdataRegistryCallback(NETDATA.registry.machines_array);
6065 if(netdataRegistry !== true) return;
6067 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
6069 NETDATA.registry.server = data.registry;
6070 NETDATA.registry.machine_guid = data.machine_guid;
6071 NETDATA.registry.hostname = data.hostname;
6073 NETDATA.registry.access(2, function (person_urls) {
6074 NETDATA.registry.parsePersonUrls(person_urls);
6081 hello: function(host, callback) {
6082 while(host.slice(-1) === '/')
6083 host = host.substring(0, host.length - 1);
6085 // send HELLO to a netdata server:
6086 // 1. verifies the server is reachable
6087 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
6089 url: host + '/api/v1/registry?action=hello',
6092 xhrFields: { withCredentials: true } // required for the cookie
6094 .done(function(data) {
6095 if(typeof data.status !== 'string' || data.status !== 'ok') {
6096 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
6100 if(typeof callback === 'function')
6104 NETDATA.error(407, host);
6106 if(typeof callback === 'function')
6111 access: function(max_redirects, callback) {
6112 // send ACCESS to a netdata registry:
6113 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
6114 // 2. it responds with a list of netdata servers we know
6115 // the registry identifies us using a cookie it sets the first time we access it
6116 // the registry may respond with a redirect URL to send us to another registry
6118 url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location),
6121 xhrFields: { withCredentials: true } // required for the cookie
6123 .done(function(data) {
6124 var redirect = null;
6125 if(typeof data.registry === 'string')
6126 redirect = data.registry;
6128 if(typeof data.status !== 'string' || data.status !== 'ok') {
6129 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6134 if(redirect !== null && max_redirects > 0) {
6135 NETDATA.registry.server = redirect;
6136 NETDATA.registry.access(max_redirects - 1, callback);
6139 if(typeof callback === 'function')
6144 if(typeof data.person_guid === 'string')
6145 NETDATA.registry.person_guid = data.person_guid;
6147 if(typeof callback === 'function')
6148 callback(data.urls);
6152 NETDATA.error(410, NETDATA.registry.server);
6154 if(typeof callback === 'function')
6159 delete: function(delete_url, callback) {
6160 // send DELETE to a netdata registry:
6162 url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url),
6165 xhrFields: { withCredentials: true } // required for the cookie
6167 .done(function(data) {
6168 if(typeof data.status !== 'string' || data.status !== 'ok') {
6169 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6173 if(typeof callback === 'function')
6177 NETDATA.error(412, NETDATA.registry.server);
6179 if(typeof callback === 'function')
6184 search: function(machine_guid, callback) {
6185 // SEARCH for the URLs of a machine:
6187 url: NETDATA.registry.server + '/api/v1/registry?action=search&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&for=' + machine_guid,
6190 xhrFields: { withCredentials: true } // required for the cookie
6192 .done(function(data) {
6193 if(typeof data.status !== 'string' || data.status !== 'ok') {
6194 NETDATA.error(417, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6198 if(typeof callback === 'function')
6202 NETDATA.error(418, NETDATA.registry.server);
6204 if(typeof callback === 'function')
6209 switch: function(new_person_guid, callback) {
6212 url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid,
6215 xhrFields: { withCredentials: true } // required for the cookie
6217 .done(function(data) {
6218 if(typeof data.status !== 'string' || data.status !== 'ok') {
6219 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6223 if(typeof callback === 'function')
6227 NETDATA.error(414, NETDATA.registry.server);
6229 if(typeof callback === 'function')
6235 // ----------------------------------------------------------------------------------------------------------------
6238 if(typeof netdataPrepCallback === 'function')
6239 netdataPrepCallback();
6241 NETDATA.errorReset();
6242 NETDATA.loadRequiredCSS(0);
6244 NETDATA._loadjQuery(function() {
6245 NETDATA.loadRequiredJs(0, function() {
6246 if(typeof $().emulateTransitionEnd !== 'function') {
6247 // bootstrap is not available
6248 NETDATA.options.current.show_help = false;
6251 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
6252 if(NETDATA.options.debug.main_loop === true)
6253 console.log('starting chart refresh thread');
6260 // window.NETDATA = NETDATA;
6261 // })(window, document);