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-2.2.4.min.js';
112 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js';
113 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js';
114 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js';
115 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-d5260c3.min.js';
116 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js';
117 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js';
118 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js';
119 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3-0.4.11.min.js';
120 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3-0.4.11.min.css';
121 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3-3.5.17.min.js';
122 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris-0.5.1.min.js';
123 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris-0.5.1.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?v20161002-1',
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?v20161002-1',
146 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-1',
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.setOption('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.unselected_count === 0)
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 var len = array.length;
1000 var ds = this.dimensions[array[len]];
1001 if(typeof ds === 'undefined') {
1002 // console.log(array[i] + ' is not found');
1005 else if(ds.isSelected()) {
1007 this.selected_count++;
1011 this.unselected_count++;
1015 if(this.selected_count === 0 && this.unselected_count !== 0) {
1017 return this.selected2BooleanArray(array);
1024 // ----------------------------------------------------------------------------------------------------------------
1025 // global selection sync
1027 NETDATA.globalSelectionSync = {
1029 dont_sync_before: 0,
1034 if(this.state !== null)
1035 this.state.globalSelectionSyncStop();
1039 if(this.state !== null) {
1040 this.state.globalSelectionSyncDelay();
1045 // ----------------------------------------------------------------------------------------------------------------
1046 // Our state object, where all per-chart values are stored
1048 chartState = function(element) {
1049 var self = $(element);
1050 this.element = element;
1053 // all private functions should use 'that', instead of 'this'
1056 /* error() - private
1057 * show an error instead of the chart
1059 var error = function(msg) {
1062 if(typeof netdataErrorCallback === 'function') {
1063 ret = netdataErrorCallback('chart', that.id, msg);
1067 that.element.innerHTML = that.id + ': ' + msg;
1068 that.enabled = false;
1069 that.current = that.pan;
1073 // GUID - a unique identifier for the chart
1074 this.uuid = NETDATA.guid();
1076 // string - the name of chart
1077 this.id = self.data('netdata');
1079 // string - the key for localStorage settings
1080 this.settings_id = self.data('id') || null;
1082 // the user given dimensions of the element
1083 this.width = self.data('width') || NETDATA.chartDefaults.width;
1084 this.height = self.data('height') || NETDATA.chartDefaults.height;
1086 if(this.settings_id !== null) {
1087 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
1088 // this is the callback that will be called
1089 // if and when the user resets all localStorage variables
1090 // to their defaults
1092 resizeChartToHeight(height);
1096 // string - the netdata server URL, without any path
1097 this.host = self.data('host') || NETDATA.chartDefaults.host;
1099 // make sure the host does not end with /
1100 // all netdata API requests use absolute paths
1101 while(this.host.slice(-1) === '/')
1102 this.host = this.host.substring(0, this.host.length - 1);
1104 // string - the grouping method requested by the user
1105 this.method = self.data('method') || NETDATA.chartDefaults.method;
1107 // the time-range requested by the user
1108 this.after = self.data('after') || NETDATA.chartDefaults.after;
1109 this.before = self.data('before') || NETDATA.chartDefaults.before;
1111 // the pixels per point requested by the user
1112 this.pixels_per_point = self.data('pixels-per-point') || 1;
1113 this.points = self.data('points') || null;
1115 // the dimensions requested by the user
1116 this.dimensions = self.data('dimensions') || null;
1118 // the chart library requested by the user
1119 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
1121 // object - the chart library used
1122 this.library = null;
1126 this.colors_assigned = {};
1127 this.colors_available = null;
1129 // the element already created by the user
1130 this.element_message = null;
1132 // the element with the chart
1133 this.element_chart = null;
1135 // the element with the legend of the chart (if created by us)
1136 this.element_legend = null;
1137 this.element_legend_childs = {
1147 this.chart_url = null; // string - the url to download chart info
1148 this.chart = null; // object - the chart as downloaded from the server
1150 this.title = self.data('title') || null; // the title of the chart
1151 this.units = self.data('units') || null; // the units of the chart dimensions
1152 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1154 this.running = false; // boolean - true when the chart is being refreshed now
1155 this.validated = false; // boolean - has the chart been validated?
1156 this.enabled = true; // boolean - is the chart enabled for refresh?
1157 this.paused = false; // boolean - is the chart paused for any reason?
1158 this.selected = false; // boolean - is the chart shown a selection?
1159 this.debug = false; // boolean - console.log() debug info about this chart
1161 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1162 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1163 this.requested_after = null; // milliseconds - the timestamp of the request after param
1164 this.requested_before = null; // milliseconds - the timestamp of the request before param
1165 this.requested_padding = null;
1166 this.view_after = 0;
1167 this.view_before = 0;
1172 force_update_at: 0, // the timestamp to force the update at
1173 force_before_ms: null,
1174 force_after_ms: null
1179 force_update_at: 0, // the timestamp to force the update at
1180 force_before_ms: null,
1181 force_after_ms: null
1186 force_update_at: 0, // the timestamp to force the update at
1187 force_before_ms: null,
1188 force_after_ms: null
1191 // this is a pointer to one of the sub-classes below
1193 this.current = this.auto;
1195 // check the requested library is available
1196 // we don't initialize it here - it will be initialized when
1197 // this chart will be first used
1198 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1199 NETDATA.error(402, that.library_name);
1200 error('chart library "' + that.library_name + '" is not found');
1203 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1204 NETDATA.error(403, that.library_name);
1205 error('chart library "' + that.library_name + '" is not enabled');
1209 that.library = NETDATA.chartLibraries[that.library_name];
1211 // milliseconds - the time the last refresh took
1212 this.refresh_dt_ms = 0;
1214 // if we need to report the rendering speed
1215 // find the element that needs to be updated
1216 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1218 if(refresh_dt_element_name !== null)
1219 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1221 this.refresh_dt_element = null;
1223 this.dimensions_visibility = new dimensionsVisibility(this);
1225 this._updating = false;
1227 // ============================================================================================================
1228 // PRIVATE FUNCTIONS
1230 var createDOM = function() {
1231 if(that.enabled === false) return;
1233 if(that.element_message !== null) that.element_message.innerHTML = '';
1234 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1235 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1237 that.element.innerHTML = '';
1239 that.element_message = document.createElement('div');
1240 that.element_message.className = ' netdata-message hidden';
1241 that.element.appendChild(that.element_message);
1243 that.element_chart = document.createElement('div');
1244 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1245 that.element.appendChild(that.element_chart);
1247 if(that.hasLegend() === true) {
1248 that.element.className = "netdata-container-with-legend";
1249 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1251 that.element_legend = document.createElement('div');
1252 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1253 that.element.appendChild(that.element_legend);
1256 that.element.className = "netdata-container";
1257 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1259 that.element_legend = null;
1261 that.element_legend_childs.series = null;
1263 if(typeof(that.width) === 'string')
1264 $(that.element).css('width', that.width);
1265 else if(typeof(that.width) === 'number')
1266 $(that.element).css('width', that.width + 'px');
1268 if(typeof(that.library.aspect_ratio) === 'undefined') {
1269 if(typeof(that.height) === 'string')
1270 $(that.element).css('height', that.height);
1271 else if(typeof(that.height) === 'number')
1272 $(that.element).css('height', that.height + 'px');
1275 var w = that.element.offsetWidth;
1276 if(w === null || w === 0) {
1277 // the div is hidden
1278 // this will resize the chart when next viewed
1279 that.tm.last_resized = 0;
1282 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1285 if(NETDATA.chartDefaults.min_width !== null)
1286 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1288 that.tm.last_dom_created = new Date().getTime();
1294 * initialize state variables
1295 * destroy all (possibly) created state elements
1296 * create the basic DOM for a chart
1298 var init = function() {
1299 if(that.enabled === false) return;
1301 that.paused = false;
1302 that.selected = false;
1304 that.chart_created = false; // boolean - is the library.create() been called?
1305 that.updates_counter = 0; // numeric - the number of refreshes made so far
1306 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1307 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1310 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1311 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1312 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1314 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1315 last_updated: 0, // the timestamp the chart last updated with data
1316 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1318 // Used with NETDATA.globalPanAndZoom.seq
1319 last_visible_check: 0, // the time we last checked if it is visible
1320 last_resized: 0, // the time the chart was resized
1321 last_hidden: 0, // the time the chart was hidden
1322 last_unhidden: 0, // the time the chart was unhidden
1323 last_autorefreshed: 0 // the time the chart was last refreshed
1326 that.data = null; // the last data as downloaded from the netdata server
1327 that.data_url = 'invalid://'; // string - the last url used to update the chart
1328 that.data_points = 0; // number - the number of points returned from netdata
1329 that.data_after = 0; // milliseconds - the first timestamp of the data
1330 that.data_before = 0; // milliseconds - the last timestamp of the data
1331 that.data_update_every = 0; // milliseconds - the frequency to update the data
1333 that.tm.last_initialized = new Date().getTime();
1336 that.setMode('auto');
1339 var maxMessageFontSize = function() {
1340 // normally we want a font size, as tall as the element
1341 var h = that.element_message.clientHeight;
1343 // but give it some air, 20% let's say, or 5 pixels min
1344 var lost = Math.max(h * 0.2, 5);
1347 // center the text, vertically
1348 var paddingTop = (lost - 5) / 2;
1350 // but check the width too
1351 // it should fit 10 characters in it
1352 var w = that.element_message.clientWidth / 10;
1354 paddingTop += (h - w) / 2;
1358 // and don't make it too huge
1359 // 5% of the screen size is good
1360 if(h > screen.height / 20) {
1361 paddingTop += (h - (screen.height / 20)) / 2;
1362 h = screen.height / 20;
1366 that.element_message.style.fontSize = h.toString() + 'px';
1367 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1370 var showMessage = function(msg) {
1371 that.element_message.className = 'netdata-message';
1372 that.element_message.innerHTML = msg;
1373 that.element_message.style.fontSize = 'x-small';
1374 that.element_message.style.paddingTop = '0px';
1375 that.___messageHidden___ = undefined;
1378 var showMessageIcon = function(icon) {
1379 that.element_message.innerHTML = icon;
1380 that.element_message.className = 'netdata-message icon';
1381 maxMessageFontSize();
1382 that.___messageHidden___ = undefined;
1385 var hideMessage = function() {
1386 if(typeof that.___messageHidden___ === 'undefined') {
1387 that.___messageHidden___ = true;
1388 that.element_message.className = 'netdata-message hidden';
1392 var showRendering = function() {
1394 if(that.chart !== null) {
1395 if(that.chart.chart_type === 'line')
1396 icon = '<i class="fa fa-line-chart"></i>';
1398 icon = '<i class="fa fa-area-chart"></i>';
1401 icon = '<i class="fa fa-area-chart"></i>';
1403 showMessageIcon(icon + ' netdata');
1406 var showLoading = function() {
1407 if(that.chart_created === false) {
1408 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1414 var isHidden = function() {
1415 if(typeof that.___chartIsHidden___ !== 'undefined')
1421 // hide the chart, when it is not visible - called from isVisible()
1422 var hideChart = function() {
1423 // hide it, if it is not already hidden
1424 if(isHidden() === true) return;
1426 if(that.chart_created === true) {
1427 if(NETDATA.options.current.destroy_on_hide === true) {
1428 // we should destroy it
1433 that.element_chart.style.display = 'none';
1434 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1435 that.tm.last_hidden = new Date().getTime();
1438 // This works, but I not sure there are no corner cases somewhere
1439 // so it is commented - if the user has memory issues he can
1440 // set Destroy on Hide for all charts
1441 // that.data = null;
1445 that.___chartIsHidden___ = true;
1448 // unhide the chart, when it is visible - called from isVisible()
1449 var unhideChart = function() {
1450 if(isHidden() === false) return;
1452 that.___chartIsHidden___ = undefined;
1453 that.updates_since_last_unhide = 0;
1455 if(that.chart_created === false) {
1456 // we need to re-initialize it, to show our background
1457 // logo in bootstrap tabs, until the chart loads
1461 that.tm.last_unhidden = new Date().getTime();
1462 that.element_chart.style.display = '';
1463 if(that.element_legend !== null) that.element_legend.style.display = '';
1469 var canBeRendered = function() {
1470 if(isHidden() === true || that.isVisible(true) === false)
1476 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1477 var callChartLibraryUpdateSafely = function(data) {
1480 if(canBeRendered() === false)
1483 if(NETDATA.options.debug.chart_errors === true)
1484 status = that.library.update(that, data);
1487 status = that.library.update(that, data);
1494 if(status === false) {
1495 error('chart failed to be updated as ' + that.library_name);
1502 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1503 var callChartLibraryCreateSafely = function(data) {
1506 if(canBeRendered() === false)
1509 if(NETDATA.options.debug.chart_errors === true)
1510 status = that.library.create(that, data);
1513 status = that.library.create(that, data);
1520 if(status === false) {
1521 error('chart failed to be created as ' + that.library_name);
1525 that.chart_created = true;
1526 that.updates_since_last_creation = 0;
1530 // ----------------------------------------------------------------------------------------------------------------
1533 // resizeChart() - private
1534 // to be called just before the chart library to make sure that
1535 // a properly sized dom is available
1536 var resizeChart = function() {
1537 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1538 if(that.chart_created === false) return;
1540 if(that.needsRecreation()) {
1543 else if(typeof that.library.resize === 'function') {
1544 that.library.resize(that);
1546 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1547 $(that.element_legend_childs.nano).nanoScroller();
1549 maxMessageFontSize();
1552 that.tm.last_resized = new Date().getTime();
1556 // this is the actual chart resize algorithm
1558 // - resize the entire container
1559 // - update the internal states
1560 // - resize the chart as the div changes height
1561 // - update the scrollbar of the legend
1562 var resizeChartToHeight = function(h) {
1564 that.element.style.height = h;
1566 if(that.settings_id !== null)
1567 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1569 var now = new Date().getTime();
1570 NETDATA.options.last_page_scroll = now;
1571 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1574 that.tm.last_resized = 0;
1578 this.resizeHandler = function(e) {
1581 if(typeof this.event_resize === 'undefined'
1582 || this.event_resize.chart_original_w === 'undefined'
1583 || this.event_resize.chart_original_h === 'undefined')
1584 this.event_resize = {
1585 chart_original_w: this.element.clientWidth,
1586 chart_original_h: this.element.clientHeight,
1590 if(e.type === 'touchstart') {
1591 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1592 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1595 this.event_resize.mouse_start_x = e.clientX;
1596 this.event_resize.mouse_start_y = e.clientY;
1599 this.event_resize.chart_start_w = this.element.clientWidth;
1600 this.event_resize.chart_start_h = this.element.clientHeight;
1601 this.event_resize.chart_last_w = this.element.clientWidth;
1602 this.event_resize.chart_last_h = this.element.clientHeight;
1604 var now = new Date().getTime();
1605 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1606 // double click / double tap event
1608 // the optimal height of the chart
1609 // showing the entire legend
1610 var optimal = this.event_resize.chart_last_h
1611 + this.element_legend_childs.content.scrollHeight
1612 - this.element_legend_childs.content.clientHeight;
1614 // if we are not optimal, be optimal
1615 if(this.event_resize.chart_last_h != optimal)
1616 resizeChartToHeight(optimal.toString() + 'px');
1618 // else if we do not have the original height
1619 // reset to the original height
1620 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1621 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1624 this.event_resize.last = now;
1626 // process movement event
1627 document.onmousemove =
1628 document.ontouchmove =
1629 this.element_legend_childs.resize_handler.onmousemove =
1630 this.element_legend_childs.resize_handler.ontouchmove =
1635 case 'mousemove': y = e.clientY; break;
1636 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1640 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1642 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1643 resizeChartToHeight(newH.toString() + 'px');
1644 that.event_resize.chart_last_h = newH;
1649 // process end event
1650 document.onmouseup =
1651 document.ontouchend =
1652 this.element_legend_childs.resize_handler.onmouseup =
1653 this.element_legend_childs.resize_handler.ontouchend =
1655 // remove all the hooks
1656 document.onmouseup =
1657 document.onmousemove =
1658 document.ontouchmove =
1659 document.ontouchend =
1660 that.element_legend_childs.resize_handler.onmousemove =
1661 that.element_legend_childs.resize_handler.ontouchmove =
1662 that.element_legend_childs.resize_handler.onmouseout =
1663 that.element_legend_childs.resize_handler.onmouseup =
1664 that.element_legend_childs.resize_handler.ontouchend =
1667 // allow auto-refreshes
1668 NETDATA.options.auto_refresher_stop_until = 0;
1674 var noDataToShow = function() {
1675 showMessageIcon('<i class="fa fa-warning"></i> empty');
1676 that.legendUpdateDOM();
1677 that.tm.last_autorefreshed = new Date().getTime();
1678 // that.data_update_every = 30 * 1000;
1679 //that.element_chart.style.display = 'none';
1680 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1681 //that.___chartIsHidden___ = true;
1684 // ============================================================================================================
1687 this.error = function(msg) {
1691 this.setMode = function(m) {
1692 if(this.current !== null && this.current.name === m) return;
1695 this.current = this.auto;
1696 else if(m === 'pan')
1697 this.current = this.pan;
1698 else if(m === 'zoom')
1699 this.current = this.zoom;
1701 this.current = this.auto;
1703 this.current.force_update_at = 0;
1704 this.current.force_before_ms = null;
1705 this.current.force_after_ms = null;
1707 this.tm.last_mode_switch = new Date().getTime();
1710 // ----------------------------------------------------------------------------------------------------------------
1711 // global selection sync
1713 // prevent to global selection sync for some time
1714 this.globalSelectionSyncDelay = function(ms) {
1715 if(NETDATA.options.current.sync_selection === false)
1718 if(typeof ms === 'number')
1719 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1721 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1724 // can we globally apply selection sync?
1725 this.globalSelectionSyncAbility = function() {
1726 if(NETDATA.options.current.sync_selection === false)
1729 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1735 this.globalSelectionSyncIsMaster = function() {
1736 if(NETDATA.globalSelectionSync.state === this)
1742 // this chart is the master of the global selection sync
1743 this.globalSelectionSyncBeMaster = function() {
1745 if(this.globalSelectionSyncIsMaster()) {
1746 if(this.debug === true)
1747 this.log('sync: I am the master already.');
1752 if(NETDATA.globalSelectionSync.state) {
1753 if(this.debug === true)
1754 this.log('sync: I am not the sync master. Resetting global sync.');
1756 this.globalSelectionSyncStop();
1759 // become the master
1760 if(this.debug === true)
1761 this.log('sync: becoming sync master.');
1763 this.selected = true;
1764 NETDATA.globalSelectionSync.state = this;
1766 // find the all slaves
1767 var targets = NETDATA.options.targets;
1768 var len = targets.length;
1773 if(this.debug === true)
1774 st.log('sync: not adding me to sync');
1776 else if(st.globalSelectionSyncIsEligible()) {
1777 if(this.debug === true)
1778 st.log('sync: adding to sync as slave');
1780 st.globalSelectionSyncBeSlave();
1784 // this.globalSelectionSyncDelay(100);
1787 // can the chart participate to the global selection sync as a slave?
1788 this.globalSelectionSyncIsEligible = function() {
1789 if(this.enabled === true
1790 && this.library !== null
1791 && typeof this.library.setSelection === 'function'
1792 && this.isVisible() === true
1793 && this.chart_created === true)
1799 // this chart becomes a slave of the global selection sync
1800 this.globalSelectionSyncBeSlave = function() {
1801 if(NETDATA.globalSelectionSync.state !== this)
1802 NETDATA.globalSelectionSync.slaves.push(this);
1805 // sync all the visible charts to the given time
1806 // this is to be called from the chart libraries
1807 this.globalSelectionSync = function(t) {
1808 if(this.globalSelectionSyncAbility() === false) {
1809 if(this.debug === true)
1810 this.log('sync: cannot sync (yet?).');
1815 if(this.globalSelectionSyncIsMaster() === false) {
1816 if(this.debug === true)
1817 this.log('sync: trying to be sync master.');
1819 this.globalSelectionSyncBeMaster();
1821 if(this.globalSelectionSyncAbility() === false) {
1822 if(this.debug === true)
1823 this.log('sync: cannot sync (yet?).');
1829 NETDATA.globalSelectionSync.last_t = t;
1830 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1835 // stop syncing all charts to the given time
1836 this.globalSelectionSyncStop = function() {
1837 if(NETDATA.globalSelectionSync.slaves.length) {
1838 if(this.debug === true)
1839 this.log('sync: cleaning up...');
1841 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1843 if(that.debug === true)
1844 st.log('sync: not adding me to sync stop');
1847 if(that.debug === true)
1848 st.log('sync: removed slave from sync');
1850 st.clearSelection();
1854 NETDATA.globalSelectionSync.last_t = 0;
1855 NETDATA.globalSelectionSync.slaves = [];
1856 NETDATA.globalSelectionSync.state = null;
1859 this.clearSelection();
1862 this.setSelection = function(t) {
1863 if(typeof this.library.setSelection === 'function') {
1864 if(this.library.setSelection(this, t) === true)
1865 this.selected = true;
1867 this.selected = false;
1869 else this.selected = true;
1871 if(this.selected === true && this.debug === true)
1872 this.log('selection set to ' + t.toString());
1874 return this.selected;
1877 this.clearSelection = function() {
1878 if(this.selected === true) {
1879 if(typeof this.library.clearSelection === 'function') {
1880 if(this.library.clearSelection(this) === true)
1881 this.selected = false;
1883 this.selected = true;
1885 else this.selected = false;
1887 if(this.selected === false && this.debug === true)
1888 this.log('selection cleared');
1893 return this.selected;
1896 // find if a timestamp (ms) is shown in the current chart
1897 this.timeIsVisible = function(t) {
1898 if(t >= this.data_after && t <= this.data_before)
1903 this.calculateRowForTime = function(t) {
1904 if(this.timeIsVisible(t) === false) return -1;
1905 return Math.floor((t - this.data_after) / this.data_update_every);
1908 // ----------------------------------------------------------------------------------------------------------------
1911 this.log = function(msg) {
1912 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1915 this.pauseChart = function() {
1916 if(this.paused === false) {
1917 if(this.debug === true)
1918 this.log('pauseChart()');
1924 this.unpauseChart = function() {
1925 if(this.paused === true) {
1926 if(this.debug === true)
1927 this.log('unpauseChart()');
1929 this.paused = false;
1933 this.resetChart = function(dont_clear_master, dont_update) {
1934 if(this.debug === true)
1935 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1937 if(typeof dont_clear_master === 'undefined')
1938 dont_clear_master = false;
1940 if(typeof dont_update === 'undefined')
1941 dont_update = false;
1943 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1944 if(this.debug === true)
1945 this.log('resetChart() diverting to clearMaster().');
1946 // this will call us back with master === true
1947 NETDATA.globalPanAndZoom.clearMaster();
1951 this.clearSelection();
1953 this.tm.pan_and_zoom_seq = 0;
1955 this.setMode('auto');
1956 this.current.force_update_at = 0;
1957 this.current.force_before_ms = null;
1958 this.current.force_after_ms = null;
1959 this.tm.last_autorefreshed = 0;
1960 this.paused = false;
1961 this.selected = false;
1962 this.enabled = true;
1963 // this.debug = false;
1965 // do not update the chart here
1966 // or the chart will flip-flop when it is the master
1967 // of a selection sync and another chart becomes
1970 if(dont_update !== true && this.isVisible() === true) {
1975 this.updateChartPanOrZoom = function(after, before) {
1976 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1979 if(this.debug === true)
1982 if(before < after) {
1983 if(this.debug === true)
1984 this.log(logme + 'flipped parameters, rejecting it.');
1989 if(typeof this.fixed_min_duration === 'undefined')
1990 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1992 var min_duration = this.fixed_min_duration;
1993 var current_duration = Math.round(this.view_before - this.view_after);
1995 // round the numbers
1996 after = Math.round(after);
1997 before = Math.round(before);
1999 // align them to update_every
2000 // stretching them further away
2001 after -= after % this.data_update_every;
2002 before += this.data_update_every - (before % this.data_update_every);
2004 // the final wanted duration
2005 var wanted_duration = before - after;
2007 // to allow panning, accept just a point below our minimum
2008 if((current_duration - this.data_update_every) < min_duration)
2009 min_duration = current_duration - this.data_update_every;
2011 // we do it, but we adjust to minimum size and return false
2012 // when the wanted size is below the current and the minimum
2014 if(wanted_duration < current_duration && wanted_duration < min_duration) {
2015 if(this.debug === true)
2016 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
2018 min_duration = this.fixed_min_duration;
2020 var dt = (min_duration - wanted_duration) / 2;
2023 wanted_duration = before - after;
2027 var tolerance = this.data_update_every * 2;
2028 var movement = Math.abs(before - this.view_before);
2030 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
2031 if(this.debug === true)
2032 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);
2036 if(this.current.name === 'auto') {
2037 this.log(logme + 'caller called me with mode: ' + this.current.name);
2038 this.setMode('pan');
2041 if(this.debug === true)
2042 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);
2044 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
2045 this.current.force_after_ms = after;
2046 this.current.force_before_ms = before;
2047 NETDATA.globalPanAndZoom.setMaster(this, after, before);
2051 this.legendFormatValue = function(value) {
2052 if(value === null || value === 'undefined') return '-';
2053 if(typeof value !== 'number') return value;
2055 var abs = Math.abs(value);
2056 if(abs >= 1000) return (Math.round(value)).toLocaleString();
2057 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
2058 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
2059 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
2060 return (Math.round(value * 10000) / 10000).toLocaleString();
2063 this.legendSetLabelValue = function(label, value) {
2064 var series = this.element_legend_childs.series[label];
2065 if(typeof series === 'undefined') return;
2066 if(series.value === null && series.user === null) return;
2068 // if the value has not changed, skip DOM update
2069 //if(series.last === value) return;
2072 if(typeof value === 'number') {
2073 var v = Math.abs(value);
2074 s = r = this.legendFormatValue(value);
2076 if(typeof series.last === 'number') {
2077 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2078 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2079 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2081 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2086 series.last = value;
2089 if(series.value !== null) series.value.innerHTML = s;
2090 if(series.user !== null) series.user.innerHTML = r;
2093 this.legendSetDate = function(ms) {
2094 if(typeof ms !== 'number') {
2095 this.legendShowUndefined();
2099 var d = new Date(ms);
2101 if(this.element_legend_childs.title_date)
2102 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
2104 if(this.element_legend_childs.title_time)
2105 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
2107 if(this.element_legend_childs.title_units)
2108 this.element_legend_childs.title_units.innerHTML = this.units;
2111 this.legendShowUndefined = function() {
2112 if(this.element_legend_childs.title_date)
2113 this.element_legend_childs.title_date.innerHTML = ' ';
2115 if(this.element_legend_childs.title_time)
2116 this.element_legend_childs.title_time.innerHTML = this.chart.name;
2118 if(this.element_legend_childs.title_units)
2119 this.element_legend_childs.title_units.innerHTML = ' ';
2121 if(this.data && this.element_legend_childs.series !== null) {
2122 var labels = this.data.dimension_names;
2123 var i = labels.length;
2125 var label = labels[i];
2127 if(typeof label === 'undefined') continue;
2128 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2129 this.legendSetLabelValue(label, null);
2134 this.legendShowLatestValues = function() {
2135 if(this.chart === null) return;
2136 if(this.selected) return;
2138 if(this.data === null || this.element_legend_childs.series === null) {
2139 this.legendShowUndefined();
2143 var show_undefined = true;
2144 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
2145 show_undefined = false;
2147 if(show_undefined) {
2148 this.legendShowUndefined();
2152 this.legendSetDate(this.view_before);
2154 var labels = this.data.dimension_names;
2155 var i = labels.length;
2157 var label = labels[i];
2159 if(typeof label === 'undefined') continue;
2160 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2163 this.legendSetLabelValue(label, null);
2165 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2169 this.legendReset = function() {
2170 this.legendShowLatestValues();
2173 // this should be called just ONCE per dimension per chart
2174 this._chartDimensionColor = function(label) {
2175 if(this.colors === null) this.chartColors();
2177 if(typeof this.colors_assigned[label] === 'undefined') {
2178 if(this.colors_available.length === 0) {
2179 var len = NETDATA.themes.current.colors.length;
2181 this.colors_available.unshift(NETDATA.themes.current.colors[len]);
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();
2204 // add the standard colors
2205 var len = NETDATA.themes.current.colors.length;
2207 this.colors_available.unshift(NETDATA.themes.current.colors[len]);
2209 // add the user supplied colors
2210 var c = $(this.element).data('colors');
2211 // this.log('read colors: ' + c);
2212 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2213 if(typeof c !== 'string') {
2214 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2224 this.colors_available.unshift(c[len]);
2225 // this.log('adding color: ' + c[len]);
2234 this.legendUpdateDOM = function() {
2237 // check that the legend DOM is up to date for the downloaded dimensions
2238 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2239 // this.log('the legend does not have any series - requesting legend update');
2242 else if(this.data === null) {
2243 // this.log('the chart does not have any data - requesting legend update');
2246 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2250 var labels = this.data.dimension_names.toString();
2251 if(labels !== this.element_legend_childs.series.labels_key) {
2254 if(this.debug === true)
2255 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2259 if(needed === false) {
2260 // make sure colors available
2263 // do we have to update the current values?
2264 // we do this, only when the visible chart is current
2265 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2266 if(this.debug === true)
2267 this.log('chart is in latest position... updating values on legend...');
2269 //var labels = this.data.dimension_names;
2270 //var i = labels.length;
2272 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2276 if(this.colors === null) {
2277 // this is the first time we update the chart
2278 // let's assign colors to all dimensions
2279 if(this.library.track_colors() === true)
2280 for(var dim in this.chart.dimensions)
2281 this._chartDimensionColor(this.chart.dimensions[dim].name);
2283 // we will re-generate the colors for the chart
2284 // based on the selected dimensions
2287 if(this.debug === true)
2288 this.log('updating Legend DOM');
2290 // mark all dimensions as invalid
2291 this.dimensions_visibility.invalidateAll();
2293 var genLabel = function(state, parent, dim, name, count) {
2294 var color = state._chartDimensionColor(name);
2296 var user_element = null;
2297 var user_id = self.data('show-value-of-' + dim + '-at') || null;
2298 if(user_id !== null) {
2299 user_element = document.getElementById(user_id) || null;
2300 if(user_element === null)
2301 state.log('Cannot find element with id: ' + user_id);
2304 state.element_legend_childs.series[name] = {
2305 name: document.createElement('span'),
2306 value: document.createElement('span'),
2311 var label = state.element_legend_childs.series[name];
2313 // create the dimension visibility tracking for this label
2314 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2316 var rgb = NETDATA.colorHex2Rgb(color);
2317 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2318 + state.chart.chart_type
2319 + '" style="background-color: '
2320 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2321 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2323 var text = document.createTextNode(' ' + name);
2324 label.name.appendChild(text);
2327 parent.appendChild(document.createElement('br'));
2329 parent.appendChild(label.name);
2330 parent.appendChild(label.value);
2333 var content = document.createElement('div');
2335 if(this.hasLegend()) {
2336 this.element_legend_childs = {
2338 resize_handler: document.createElement('div'),
2339 toolbox: document.createElement('div'),
2340 toolbox_left: document.createElement('div'),
2341 toolbox_right: document.createElement('div'),
2342 toolbox_reset: document.createElement('div'),
2343 toolbox_zoomin: document.createElement('div'),
2344 toolbox_zoomout: document.createElement('div'),
2345 toolbox_volume: document.createElement('div'),
2346 title_date: document.createElement('span'),
2347 title_time: document.createElement('span'),
2348 title_units: document.createElement('span'),
2349 nano: document.createElement('div'),
2351 paneClass: 'netdata-legend-series-pane',
2352 sliderClass: 'netdata-legend-series-slider',
2353 contentClass: 'netdata-legend-series-content',
2354 enabledClass: '__enabled',
2355 flashedClass: '__flashed',
2356 activeClass: '__active',
2358 alwaysVisible: true,
2364 this.element_legend.innerHTML = '';
2366 if(this.library.toolboxPanAndZoom !== null) {
2368 function get_pan_and_zoom_step(event) {
2370 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2372 else if (event.shiftKey)
2373 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2375 else if (event.altKey)
2376 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2379 return NETDATA.options.current.pan_and_zoom_factor;
2382 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2383 this.element.appendChild(this.element_legend_childs.toolbox);
2385 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2386 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2387 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2388 this.element_legend_childs.toolbox_left.onclick = function(e) {
2391 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2392 var before = that.view_before - step;
2393 var after = that.view_after - step;
2394 if(after >= that.netdata_first)
2395 that.library.toolboxPanAndZoom(that, after, before);
2397 if(NETDATA.options.current.show_help === true)
2398 $(this.element_legend_childs.toolbox_left).popover({
2403 placement: 'bottom',
2404 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2406 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>'
2410 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2411 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2412 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2413 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2415 NETDATA.resetAllCharts(that);
2417 if(NETDATA.options.current.show_help === true)
2418 $(this.element_legend_childs.toolbox_reset).popover({
2423 placement: 'bottom',
2424 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2425 title: 'Chart Reset',
2426 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>'
2429 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2430 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2431 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2432 this.element_legend_childs.toolbox_right.onclick = function(e) {
2434 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2435 var before = that.view_before + step;
2436 var after = that.view_after + step;
2437 if(before <= that.netdata_last)
2438 that.library.toolboxPanAndZoom(that, after, before);
2440 if(NETDATA.options.current.show_help === true)
2441 $(this.element_legend_childs.toolbox_right).popover({
2446 placement: 'bottom',
2447 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2449 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>'
2453 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2454 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2455 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2456 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2458 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2459 var before = that.view_before - dt;
2460 var after = that.view_after + dt;
2461 that.library.toolboxPanAndZoom(that, after, before);
2463 if(NETDATA.options.current.show_help === true)
2464 $(this.element_legend_childs.toolbox_zoomin).popover({
2469 placement: 'bottom',
2470 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2471 title: 'Chart Zoom In',
2472 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>'
2475 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2476 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2477 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2478 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2480 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);
2481 var before = that.view_before + dt;
2482 var after = that.view_after - dt;
2484 that.library.toolboxPanAndZoom(that, after, before);
2486 if(NETDATA.options.current.show_help === true)
2487 $(this.element_legend_childs.toolbox_zoomout).popover({
2492 placement: 'bottom',
2493 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2494 title: 'Chart Zoom Out',
2495 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>'
2498 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2499 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2500 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2501 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2502 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2503 //e.preventDefault();
2504 //alert('clicked toolbox_volume on ' + that.id);
2508 this.element_legend_childs.toolbox = null;
2509 this.element_legend_childs.toolbox_left = null;
2510 this.element_legend_childs.toolbox_reset = null;
2511 this.element_legend_childs.toolbox_right = null;
2512 this.element_legend_childs.toolbox_zoomin = null;
2513 this.element_legend_childs.toolbox_zoomout = null;
2514 this.element_legend_childs.toolbox_volume = null;
2517 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2518 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2519 this.element.appendChild(this.element_legend_childs.resize_handler);
2520 if(NETDATA.options.current.show_help === true)
2521 $(this.element_legend_childs.resize_handler).popover({
2526 placement: 'bottom',
2527 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2528 title: 'Chart Resize',
2529 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>'
2533 this.element_legend_childs.resize_handler.onmousedown =
2535 that.resizeHandler(e);
2539 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2540 that.resizeHandler(e);
2543 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2544 this.element_legend.appendChild(this.element_legend_childs.title_date);
2546 this.element_legend.appendChild(document.createElement('br'));
2548 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2549 this.element_legend.appendChild(this.element_legend_childs.title_time);
2551 this.element_legend.appendChild(document.createElement('br'));
2553 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2554 this.element_legend.appendChild(this.element_legend_childs.title_units);
2556 this.element_legend.appendChild(document.createElement('br'));
2558 this.element_legend_childs.nano.className = 'netdata-legend-series';
2559 this.element_legend.appendChild(this.element_legend_childs.nano);
2561 content.className = 'netdata-legend-series-content';
2562 this.element_legend_childs.nano.appendChild(content);
2564 if(NETDATA.options.current.show_help === true)
2565 $(content).popover({
2570 placement: 'bottom',
2571 title: 'Chart Legend',
2572 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2573 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>'
2577 this.element_legend_childs = {
2579 resize_handler: null,
2582 toolbox_right: null,
2583 toolbox_reset: null,
2584 toolbox_zoomin: null,
2585 toolbox_zoomout: null,
2586 toolbox_volume: null,
2597 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2598 if(this.debug === true)
2599 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2601 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2602 genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
2606 var tmp = new Array();
2607 for(var dim in this.chart.dimensions) {
2608 tmp.push(this.chart.dimensions[dim].name);
2609 genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
2611 this.element_legend_childs.series.labels_key = tmp.toString();
2612 if(this.debug === true)
2613 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2616 // create a hidden div to be used for hidding
2617 // the original legend of the chart library
2618 var el = document.createElement('div');
2619 if(this.element_legend !== null)
2620 this.element_legend.appendChild(el);
2621 el.style.display = 'none';
2623 this.element_legend_childs.hidden = document.createElement('div');
2624 el.appendChild(this.element_legend_childs.hidden);
2626 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2627 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2629 this.legendShowLatestValues();
2632 this.hasLegend = function() {
2633 if(typeof this.___hasLegendCache___ !== 'undefined')
2634 return this.___hasLegendCache___;
2637 if(this.library && this.library.legend(this) === 'right-side') {
2638 var legend = $(this.element).data('legend') || 'yes';
2639 if(legend === 'yes') leg = true;
2642 this.___hasLegendCache___ = leg;
2646 this.legendWidth = function() {
2647 return (this.hasLegend())?140:0;
2650 this.legendHeight = function() {
2651 return $(this.element).height();
2654 this.chartWidth = function() {
2655 return $(this.element).width() - this.legendWidth();
2658 this.chartHeight = function() {
2659 return $(this.element).height();
2662 this.chartPixelsPerPoint = function() {
2663 // force an options provided detail
2664 var px = this.pixels_per_point;
2666 if(this.library && px < this.library.pixels_per_point(this))
2667 px = this.library.pixels_per_point(this);
2669 if(px < NETDATA.options.current.pixels_per_point)
2670 px = NETDATA.options.current.pixels_per_point;
2675 this.needsRecreation = function() {
2677 this.chart_created === true
2679 && this.library.autoresize() === false
2680 && this.tm.last_resized < NETDATA.options.last_resized
2684 this.chartURL = function() {
2685 var after, before, points_multiplier = 1;
2686 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2687 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2689 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2690 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2691 this.view_after = after * 1000;
2692 this.view_before = before * 1000;
2694 this.requested_padding = null;
2695 points_multiplier = 1;
2697 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2698 this.tm.pan_and_zoom_seq = 0;
2700 before = Math.round(this.current.force_before_ms / 1000);
2701 after = Math.round(this.current.force_after_ms / 1000);
2702 this.view_after = after * 1000;
2703 this.view_before = before * 1000;
2705 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2706 this.requested_padding = Math.round((before - after) / 2);
2707 after -= this.requested_padding;
2708 before += this.requested_padding;
2709 this.requested_padding *= 1000;
2710 points_multiplier = 2;
2713 this.current.force_before_ms = null;
2714 this.current.force_after_ms = null;
2717 this.tm.pan_and_zoom_seq = 0;
2719 before = this.before;
2721 this.view_after = after * 1000;
2722 this.view_before = before * 1000;
2724 this.requested_padding = null;
2725 points_multiplier = 1;
2728 this.requested_after = after * 1000;
2729 this.requested_before = before * 1000;
2731 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2733 // build the data URL
2734 this.data_url = this.host + this.chart.data_url;
2735 this.data_url += "&format=" + this.library.format();
2736 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2737 this.data_url += "&group=" + this.method;
2738 this.data_url += "&options=" + this.library.options(this);
2739 this.data_url += '|jsonwrap';
2741 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2742 this.data_url += '|nonzero';
2744 if(this.append_options !== null)
2745 this.data_url += '|' + this.append_options.toString();
2748 this.data_url += "&after=" + after.toString();
2751 this.data_url += "&before=" + before.toString();
2754 this.data_url += "&dimensions=" + this.dimensions;
2756 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2757 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2760 this.redrawChart = function() {
2761 if(this.data !== null)
2762 this.updateChartWithData(this.data);
2765 this.updateChartWithData = function(data) {
2766 if(this.debug === true)
2767 this.log('updateChartWithData() called.');
2769 // this may force the chart to be re-created
2773 this.updates_counter++;
2774 this.updates_since_last_unhide++;
2775 this.updates_since_last_creation++;
2777 var started = new Date().getTime();
2779 // if the result is JSON, find the latest update-every
2780 this.data_update_every = data.view_update_every * 1000;
2781 this.data_after = data.after * 1000;
2782 this.data_before = data.before * 1000;
2783 this.netdata_first = data.first_entry * 1000;
2784 this.netdata_last = data.last_entry * 1000;
2785 this.data_points = data.points;
2788 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2789 if(this.view_after < this.data_after) {
2790 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2791 this.view_after = this.data_after;
2794 if(this.view_before > this.data_before) {
2795 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2796 this.view_before = this.data_before;
2800 this.view_after = this.data_after;
2801 this.view_before = this.data_before;
2804 if(this.debug === true) {
2805 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2807 if(this.current.force_after_ms)
2808 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2810 this.log('STATUS: forced : unset');
2812 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2813 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2814 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2815 this.log('STATUS: points : ' + (this.data_points).toString());
2818 if(this.data_points === 0) {
2823 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2824 if(this.debug === true)
2825 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2827 this.chart_created = false;
2830 // check and update the legend
2831 this.legendUpdateDOM();
2833 if(this.chart_created === true
2834 && typeof this.library.update === 'function') {
2836 if(this.debug === true)
2837 this.log('updating chart...');
2839 if(callChartLibraryUpdateSafely(data) === false)
2843 if(this.debug === true)
2844 this.log('creating chart...');
2846 if(callChartLibraryCreateSafely(data) === false)
2850 this.legendShowLatestValues();
2851 if(this.selected === true)
2852 NETDATA.globalSelectionSync.stop();
2854 // update the performance counters
2855 var now = new Date().getTime();
2856 this.tm.last_updated = now;
2858 // don't update last_autorefreshed if this chart is
2859 // forced to be updated with global PanAndZoom
2860 if(NETDATA.globalPanAndZoom.isActive())
2861 this.tm.last_autorefreshed = 0;
2863 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2864 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2866 this.tm.last_autorefreshed = now;
2869 this.refresh_dt_ms = now - started;
2870 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2872 if(this.refresh_dt_element !== null)
2873 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2876 this.updateChart = function(callback) {
2877 if(this.debug === true)
2878 this.log('updateChart() called.');
2880 if(this._updating === true) {
2881 if(this.debug === true)
2882 this.log('I am already updating...');
2884 if(typeof callback === 'function') callback();
2888 // due to late initialization of charts and libraries
2889 // we need to check this too
2890 if(this.enabled === false) {
2891 if(this.debug === true)
2892 this.log('I am not enabled');
2894 if(typeof callback === 'function') callback();
2898 if(canBeRendered() === false) {
2899 if(typeof callback === 'function') callback();
2903 if(this.chart === null) {
2904 this.getChart(function() { that.updateChart(callback); });
2908 if(this.library.initialized === false) {
2909 if(this.library.enabled === true) {
2910 this.library.initialize(function() { that.updateChart(callback); });
2914 error('chart library "' + this.library_name + '" is not available.');
2915 if(typeof callback === 'function') callback();
2920 this.clearSelection();
2923 if(this.debug === true)
2924 this.log('updating from ' + this.data_url);
2926 NETDATA.statistics.refreshes_total++;
2927 NETDATA.statistics.refreshes_active++;
2929 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2930 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2932 this._updating = true;
2934 this.xhr = $.ajax( {
2938 xhrFields: { withCredentials: true } // required for the cookie
2940 .done(function(data) {
2941 that.xhr = undefined;
2943 if(that.debug === true)
2944 that.log('data received. updating chart.');
2946 that.updateChartWithData(data);
2948 .fail(function(msg) {
2949 that.xhr = undefined;
2951 if(msg.statusText !== 'abort')
2952 error('data download failed for url: ' + that.data_url);
2954 .always(function() {
2955 that.xhr = undefined;
2957 NETDATA.statistics.refreshes_active--;
2958 that._updating = false;
2959 if(typeof callback === 'function') callback();
2965 this.isVisible = function(nocache) {
2966 if(typeof nocache === 'undefined')
2969 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2971 // caching - we do not evaluate the charts visibility
2972 // if the page has not been scrolled since the last check
2973 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2974 return this.___isVisible___;
2976 this.tm.last_visible_check = new Date().getTime();
2978 var wh = window.innerHeight;
2979 var x = this.element.getBoundingClientRect();
2983 if(x.width === 0 || x.height === 0) {
2985 this.___isVisible___ = false;
2986 return this.___isVisible___;
2989 if(x.top < 0 && -x.top > x.height) {
2990 // the chart is entirely above
2991 ret = -x.top - x.height;
2993 else if(x.top > wh) {
2994 // the chart is entirely below
2998 if(ret > tolerance) {
2999 // the chart is too far
3002 this.___isVisible___ = false;
3003 return this.___isVisible___;
3006 // the chart is inside or very close
3009 this.___isVisible___ = true;
3010 return this.___isVisible___;
3014 this.isAutoRefreshable = function() {
3015 return (this.current.autorefresh);
3018 this.canBeAutoRefreshed = function() {
3019 var now = new Date().getTime();
3021 if(this.running === true) {
3022 if(this.debug === true)
3023 this.log('I am already running');
3028 if(this.enabled === false) {
3029 if(this.debug === true)
3030 this.log('I am not enabled');
3035 if(this.library === null || this.library.enabled === false) {
3036 error('charting library "' + this.library_name + '" is not available');
3037 if(this.debug === true)
3038 this.log('My chart library ' + this.library_name + ' is not available');
3043 if(this.isVisible() === false) {
3044 if(NETDATA.options.debug.visibility === true || this.debug === true)
3045 this.log('I am not visible');
3050 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
3051 if(this.debug === true)
3052 this.log('timed force update detected - allowing this update');
3054 this.current.force_update_at = 0;
3058 if(this.isAutoRefreshable() === true) {
3059 // allow the first update, even if the page is not visible
3060 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
3061 if(NETDATA.options.debug.focus === true || this.debug === true)
3062 this.log('canBeAutoRefreshed(): page does not have focus');
3067 if(this.needsRecreation() === true) {
3068 if(this.debug === true)
3069 this.log('canBeAutoRefreshed(): needs re-creation.');
3074 // options valid only for autoRefresh()
3075 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
3076 if(NETDATA.globalPanAndZoom.isActive()) {
3077 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
3078 if(this.debug === true)
3079 this.log('canBeAutoRefreshed(): global panning: I need an update.');
3084 if(this.debug === true)
3085 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
3091 if(this.selected === true) {
3092 if(this.debug === true)
3093 this.log('canBeAutoRefreshed(): I have a selection in place.');
3098 if(this.paused === true) {
3099 if(this.debug === true)
3100 this.log('canBeAutoRefreshed(): I am paused.');
3105 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
3106 if(this.debug === true)
3107 this.log('canBeAutoRefreshed(): It is time to update me.');
3117 this.autoRefresh = function(callback) {
3118 if(this.canBeAutoRefreshed() === true && this.running === false) {
3121 state.running = true;
3122 state.updateChart(function() {
3123 state.running = false;
3125 if(typeof callback !== 'undefined')
3130 if(typeof callback !== 'undefined')
3135 this._defaultsFromDownloadedChart = function(chart) {
3137 this.chart_url = chart.url;
3138 this.data_update_every = chart.update_every * 1000;
3139 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
3140 this.tm.last_info_downloaded = new Date().getTime();
3142 if(this.title === null)
3143 this.title = chart.title;
3145 if(this.units === null)
3146 this.units = chart.units;
3149 // fetch the chart description from the netdata server
3150 this.getChart = function(callback) {
3151 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
3153 this._defaultsFromDownloadedChart(this.chart);
3154 if(typeof callback === 'function') callback();
3157 this.chart_url = "/api/v1/chart?chart=" + this.id;
3159 if(this.debug === true)
3160 this.log('downloading ' + this.chart_url);
3163 url: this.host + this.chart_url,
3166 xhrFields: { withCredentials: true } // required for the cookie
3168 .done(function(chart) {
3169 chart.url = that.chart_url;
3170 that._defaultsFromDownloadedChart(chart);
3171 NETDATA.chartRegistry.add(that.host, that.id, chart);
3174 NETDATA.error(404, that.chart_url);
3175 error('chart not found on url "' + that.chart_url + '"');
3177 .always(function() {
3178 if(typeof callback === 'function') callback();
3183 // ============================================================================================================
3189 NETDATA.resetAllCharts = function(state) {
3190 // first clear the global selection sync
3191 // to make sure no chart is in selected state
3192 state.globalSelectionSyncStop();
3194 // there are 2 possibilities here
3195 // a. state is the global Pan and Zoom master
3196 // b. state is not the global Pan and Zoom master
3198 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3201 // clear the global Pan and Zoom
3202 // this will also refresh the master
3203 // and unblock any charts currently mirroring the master
3204 NETDATA.globalPanAndZoom.clearMaster();
3206 // if we were not the master, reset our status too
3207 // this is required because most probably the mouse
3208 // is over this chart, blocking it from auto-refreshing
3209 if(master === false && (state.paused === true || state.selected === true))
3213 // get or create a chart state, given a DOM element
3214 NETDATA.chartState = function(element) {
3215 var state = $(element).data('netdata-state-object') || null;
3216 if(state === null) {
3217 state = new chartState(element);
3218 $(element).data('netdata-state-object', state);
3223 // ----------------------------------------------------------------------------------------------------------------
3224 // Library functions
3226 // Load a script without jquery
3227 // This is used to load jquery - after it is loaded, we use jquery
3228 NETDATA._loadjQuery = function(callback) {
3229 if(typeof jQuery === 'undefined') {
3230 if(NETDATA.options.debug.main_loop === true)
3231 console.log('loading ' + NETDATA.jQuery);
3233 var script = document.createElement('script');
3234 script.type = 'text/javascript';
3235 script.async = true;
3236 script.src = NETDATA.jQuery;
3238 // script.onabort = onError;
3239 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3240 if(typeof callback === "function")
3241 script.onload = callback;
3243 var s = document.getElementsByTagName('script')[0];
3244 s.parentNode.insertBefore(script, s);
3246 else if(typeof callback === "function")
3250 NETDATA._loadCSS = function(filename) {
3251 // don't use jQuery here
3252 // styles are loaded before jQuery
3253 // to eliminate showing an unstyled page to the user
3255 var fileref = document.createElement("link");
3256 fileref.setAttribute("rel", "stylesheet");
3257 fileref.setAttribute("type", "text/css");
3258 fileref.setAttribute("href", filename);
3260 if (typeof fileref !== 'undefined')
3261 document.getElementsByTagName("head")[0].appendChild(fileref);
3264 NETDATA.colorHex2Rgb = function(hex) {
3265 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3266 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3267 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3268 return r + r + g + g + b + b;
3271 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3273 r: parseInt(result[1], 16),
3274 g: parseInt(result[2], 16),
3275 b: parseInt(result[3], 16)
3279 NETDATA.colorLuminance = function(hex, lum) {
3280 // validate hex string
3281 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3283 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3287 // convert to decimal and change luminosity
3288 var rgb = "#", c, i;
3289 for (i = 0; i < 3; i++) {
3290 c = parseInt(hex.substr(i*2,2), 16);
3291 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3292 rgb += ("00"+c).substr(c.length);
3298 NETDATA.guid = function() {
3300 return Math.floor((1 + Math.random()) * 0x10000)
3305 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3308 NETDATA.zeropad = function(x) {
3309 if(x > -10 && x < 10) return '0' + x.toString();
3310 else return x.toString();
3313 // user function to signal us the DOM has been
3315 NETDATA.updatedDom = function() {
3316 NETDATA.options.updated_dom = true;
3319 NETDATA.ready = function(callback) {
3320 NETDATA.options.pauseCallback = callback;
3323 NETDATA.pause = function(callback) {
3324 if(NETDATA.options.pause === true)
3327 NETDATA.options.pauseCallback = callback;
3330 NETDATA.unpause = function() {
3331 NETDATA.options.pauseCallback = null;
3332 NETDATA.options.updated_dom = true;
3333 NETDATA.options.pause = false;
3336 // ----------------------------------------------------------------------------------------------------------------
3338 // this is purely sequencial charts refresher
3339 // it is meant to be autonomous
3340 NETDATA.chartRefresherNoParallel = function(index) {
3341 if(NETDATA.options.debug.mail_loop === true)
3342 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3344 if(NETDATA.options.updated_dom === true) {
3345 // the dom has been updated
3346 // get the dom parts again
3347 NETDATA.parseDom(NETDATA.chartRefresher);
3350 if(index >= NETDATA.options.targets.length) {
3351 if(NETDATA.options.debug.main_loop === true)
3352 console.log('waiting to restart main loop...');
3354 NETDATA.options.auto_refresher_fast_weight = 0;
3356 setTimeout(function() {
3357 NETDATA.chartRefresher();
3358 }, NETDATA.options.current.idle_between_loops);
3361 var state = NETDATA.options.targets[index];
3363 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3364 if(NETDATA.options.debug.main_loop === true)
3365 console.log('fast rendering...');
3367 state.autoRefresh(function() {
3368 NETDATA.chartRefresherNoParallel(++index);
3372 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3373 NETDATA.options.auto_refresher_fast_weight = 0;
3375 setTimeout(function() {
3376 state.autoRefresh(function() {
3377 NETDATA.chartRefresherNoParallel(++index);
3379 }, NETDATA.options.current.idle_between_charts);
3384 // this is part of the parallel refresher
3385 // its cause is to refresh sequencially all the charts
3386 // that depend on chart library initialization
3387 // it will call the parallel refresher back
3388 // as soon as it sees a chart that its chart library
3390 NETDATA.chartRefresher_uninitialized = function() {
3391 if(NETDATA.options.updated_dom === true) {
3392 // the dom has been updated
3393 // get the dom parts again
3394 NETDATA.parseDom(NETDATA.chartRefresher);
3398 if(NETDATA.options.sequencial.length === 0)
3399 NETDATA.chartRefresher();
3401 var state = NETDATA.options.sequencial.pop();
3402 if(state.library.initialized === true)
3403 NETDATA.chartRefresher();
3405 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3409 NETDATA.chartRefresherWaitTime = function() {
3410 return NETDATA.options.current.idle_parallel_loops;
3413 // the default refresher
3414 // it will create 2 sets of charts:
3415 // - the ones that can be refreshed in parallel
3416 // - the ones that depend on something else
3417 // the first set will be executed in parallel
3418 // the second will be given to NETDATA.chartRefresher_uninitialized()
3419 NETDATA.chartRefresher = function() {
3420 // console.log('auto-refresher...');
3422 if(NETDATA.options.pause === true) {
3423 // console.log('auto-refresher is paused');
3424 setTimeout(NETDATA.chartRefresher,
3425 NETDATA.chartRefresherWaitTime());
3429 if(typeof NETDATA.options.pauseCallback === 'function') {
3430 // console.log('auto-refresher is calling pauseCallback');
3431 NETDATA.options.pause = true;
3432 NETDATA.options.pauseCallback();
3433 NETDATA.chartRefresher();
3437 if(NETDATA.options.current.parallel_refresher === false) {
3438 // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
3439 NETDATA.chartRefresherNoParallel(0);
3443 if(NETDATA.options.updated_dom === true) {
3444 // the dom has been updated
3445 // get the dom parts again
3446 // console.log('auto-refresher is calling parseDom()');
3447 NETDATA.parseDom(NETDATA.chartRefresher);
3451 var parallel = new Array();
3452 var targets = NETDATA.options.targets;
3453 var len = targets.length;
3456 state = targets[len];
3457 if(state.isVisible() === false || state.running === true)
3460 if(state.library.initialized === false) {
3461 if(state.library.enabled === true) {
3462 state.library.initialize(NETDATA.chartRefresher);
3466 state.error('chart library "' + state.library_name + '" is not enabled.');
3470 parallel.unshift(state);
3473 if(parallel.length > 0) {
3474 // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
3475 // this will execute the jobs in parallel
3476 $(parallel).each(function() {
3481 // console.log('auto-refresher nothing to do');
3484 // run the next refresh iteration
3485 setTimeout(NETDATA.chartRefresher,
3486 NETDATA.chartRefresherWaitTime());
3489 NETDATA.parseDom = function(callback) {
3490 NETDATA.options.last_page_scroll = new Date().getTime();
3491 NETDATA.options.updated_dom = false;
3493 var targets = $('div[data-netdata]'); //.filter(':visible');
3495 if(NETDATA.options.debug.main_loop === true)
3496 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3498 NETDATA.options.targets = new Array();
3499 var len = targets.length;
3501 // the initialization will take care of sizing
3502 // and the "loading..." message
3503 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3506 if(typeof callback === 'function') callback();
3509 // this is the main function - where everything starts
3510 NETDATA.start = function() {
3511 // this should be called only once
3513 NETDATA.options.page_is_visible = true;
3515 $(window).blur(function() {
3516 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3517 NETDATA.options.page_is_visible = false;
3518 if(NETDATA.options.debug.focus === true)
3519 console.log('Lost Focus!');
3523 $(window).focus(function() {
3524 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3525 NETDATA.options.page_is_visible = true;
3526 if(NETDATA.options.debug.focus === true)
3527 console.log('Focus restored!');
3531 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3532 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3533 NETDATA.options.page_is_visible = false;
3534 if(NETDATA.options.debug.focus === true)
3535 console.log('Document has no focus!');
3539 // bootstrap tab switching
3540 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3542 // bootstrap modal switching
3543 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3544 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3546 // bootstrap collapse switching
3547 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3548 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3550 NETDATA.parseDom(NETDATA.chartRefresher);
3552 // Alarms initialization
3553 setTimeout(NETDATA.alarms.init, 1000);
3555 // Registry initialization
3556 setTimeout(NETDATA.registry.init, netdataRegistryAfterMs);
3558 if(typeof netdataCallback === 'function')
3562 // ----------------------------------------------------------------------------------------------------------------
3565 NETDATA.peityInitialize = function(callback) {
3566 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3568 url: NETDATA.peity_js,
3571 xhrFields: { withCredentials: true } // required for the cookie
3574 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3577 NETDATA.chartLibraries.peity.enabled = false;
3578 NETDATA.error(100, NETDATA.peity_js);
3580 .always(function() {
3581 if(typeof callback === "function")
3586 NETDATA.chartLibraries.peity.enabled = false;
3587 if(typeof callback === "function")
3592 NETDATA.peityChartUpdate = function(state, data) {
3593 state.peity_instance.innerHTML = data.result;
3595 if(state.peity_options.stroke !== state.chartColors()[0]) {
3596 state.peity_options.stroke = state.chartColors()[0];
3597 if(state.chart.chart_type === 'line')
3598 state.peity_options.fill = NETDATA.themes.current.background;
3600 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3603 $(state.peity_instance).peity('line', state.peity_options);
3607 NETDATA.peityChartCreate = function(state, data) {
3608 state.peity_instance = document.createElement('div');
3609 state.element_chart.appendChild(state.peity_instance);
3611 var self = $(state.element);
3612 state.peity_options = {
3613 stroke: NETDATA.themes.current.foreground,
3614 strokeWidth: self.data('peity-strokewidth') || 1,
3615 width: state.chartWidth(),
3616 height: state.chartHeight(),
3617 fill: NETDATA.themes.current.foreground
3620 NETDATA.peityChartUpdate(state, data);
3624 // ----------------------------------------------------------------------------------------------------------------
3627 NETDATA.sparklineInitialize = function(callback) {
3628 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3630 url: NETDATA.sparkline_js,
3633 xhrFields: { withCredentials: true } // required for the cookie
3636 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3639 NETDATA.chartLibraries.sparkline.enabled = false;
3640 NETDATA.error(100, NETDATA.sparkline_js);
3642 .always(function() {
3643 if(typeof callback === "function")
3648 NETDATA.chartLibraries.sparkline.enabled = false;
3649 if(typeof callback === "function")
3654 NETDATA.sparklineChartUpdate = function(state, data) {
3655 state.sparkline_options.width = state.chartWidth();
3656 state.sparkline_options.height = state.chartHeight();
3658 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3662 NETDATA.sparklineChartCreate = function(state, data) {
3663 var self = $(state.element);
3664 var type = self.data('sparkline-type') || 'line';
3665 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3666 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3667 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3668 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3669 var composite = self.data('sparkline-composite') || undefined;
3670 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3671 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3672 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3673 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3674 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3675 var spotColor = self.data('sparkline-spotcolor') || undefined;
3676 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3677 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3678 var spotRadius = self.data('sparkline-spotradius') || undefined;
3679 var valueSpots = self.data('sparkline-valuespots') || undefined;
3680 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3681 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3682 var lineWidth = self.data('sparkline-linewidth') || undefined;
3683 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3684 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3685 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3686 var xvalues = self.data('sparkline-xvalues') || undefined;
3687 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3688 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3689 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3690 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3691 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3692 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3693 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3694 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3695 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3696 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3697 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3698 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3699 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3700 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3701 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3702 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3703 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3704 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3705 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3706 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3707 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3708 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3710 if(spotColor === 'disable') spotColor='';
3711 if(minSpotColor === 'disable') minSpotColor='';
3712 if(maxSpotColor === 'disable') maxSpotColor='';
3714 state.sparkline_options = {
3716 lineColor: lineColor,
3717 fillColor: fillColor,
3718 chartRangeMin: chartRangeMin,
3719 chartRangeMax: chartRangeMax,
3720 composite: composite,
3721 enableTagOptions: enableTagOptions,
3722 tagOptionPrefix: tagOptionPrefix,
3723 tagValuesAttribute: tagValuesAttribute,
3724 disableHiddenCheck: disableHiddenCheck,
3725 defaultPixelsPerValue: defaultPixelsPerValue,
3726 spotColor: spotColor,
3727 minSpotColor: minSpotColor,
3728 maxSpotColor: maxSpotColor,
3729 spotRadius: spotRadius,
3730 valueSpots: valueSpots,
3731 highlightSpotColor: highlightSpotColor,
3732 highlightLineColor: highlightLineColor,
3733 lineWidth: lineWidth,
3734 normalRangeMin: normalRangeMin,
3735 normalRangeMax: normalRangeMax,
3736 drawNormalOnTop: drawNormalOnTop,
3738 chartRangeClip: chartRangeClip,
3739 chartRangeMinX: chartRangeMinX,
3740 chartRangeMaxX: chartRangeMaxX,
3741 disableInteraction: disableInteraction,
3742 disableTooltips: disableTooltips,
3743 disableHighlight: disableHighlight,
3744 highlightLighten: highlightLighten,
3745 highlightColor: highlightColor,
3746 tooltipContainer: tooltipContainer,
3747 tooltipClassname: tooltipClassname,
3748 tooltipChartTitle: state.title,
3749 tooltipFormat: tooltipFormat,
3750 tooltipPrefix: tooltipPrefix,
3751 tooltipSuffix: tooltipSuffix,
3752 tooltipSkipNull: tooltipSkipNull,
3753 tooltipValueLookups: tooltipValueLookups,
3754 tooltipFormatFieldlist: tooltipFormatFieldlist,
3755 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3756 numberFormatter: numberFormatter,
3757 numberDigitGroupSep: numberDigitGroupSep,
3758 numberDecimalMark: numberDecimalMark,
3759 numberDigitGroupCount: numberDigitGroupCount,
3760 animatedZooms: animatedZooms,
3761 width: state.chartWidth(),
3762 height: state.chartHeight()
3765 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3769 // ----------------------------------------------------------------------------------------------------------------
3776 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3777 if(after < state.netdata_first)
3778 after = state.netdata_first;
3780 if(before > state.netdata_last)
3781 before = state.netdata_last;
3783 state.setMode('zoom');
3784 state.globalSelectionSyncStop();
3785 state.globalSelectionSyncDelay();
3786 state.dygraph_user_action = true;
3787 state.dygraph_force_zoom = true;
3788 state.updateChartPanOrZoom(after, before);
3789 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3792 NETDATA.dygraphSetSelection = function(state, t) {
3793 if(typeof state.dygraph_instance !== 'undefined') {
3794 var r = state.calculateRowForTime(t);
3796 state.dygraph_instance.setSelection(r);
3798 state.dygraph_instance.clearSelection();
3799 state.legendShowUndefined();
3806 NETDATA.dygraphClearSelection = function(state, t) {
3807 if(typeof state.dygraph_instance !== 'undefined') {
3808 state.dygraph_instance.clearSelection();
3813 NETDATA.dygraphSmoothInitialize = function(callback) {
3815 url: NETDATA.dygraph_smooth_js,
3818 xhrFields: { withCredentials: true } // required for the cookie
3821 NETDATA.dygraph.smooth = true;
3822 smoothPlotter.smoothing = 0.3;
3825 NETDATA.dygraph.smooth = false;
3827 .always(function() {
3828 if(typeof callback === "function")
3833 NETDATA.dygraphInitialize = function(callback) {
3834 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3836 url: NETDATA.dygraph_js,
3839 xhrFields: { withCredentials: true } // required for the cookie
3842 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3845 NETDATA.chartLibraries.dygraph.enabled = false;
3846 NETDATA.error(100, NETDATA.dygraph_js);
3848 .always(function() {
3849 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3850 NETDATA.dygraphSmoothInitialize(callback);
3851 else if(typeof callback === "function")
3856 NETDATA.chartLibraries.dygraph.enabled = false;
3857 if(typeof callback === "function")
3862 NETDATA.dygraphChartUpdate = function(state, data) {
3863 var dygraph = state.dygraph_instance;
3865 if(typeof dygraph === 'undefined')
3866 return NETDATA.dygraphChartCreate(state, data);
3868 // when the chart is not visible, and hidden
3869 // if there is a window resize, dygraph detects
3870 // its element size as 0x0.
3871 // this will make it re-appear properly
3873 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3877 file: data.result.data,
3878 colors: state.chartColors(),
3879 labels: data.result.labels,
3880 labelsDivWidth: state.chartWidth() - 70,
3881 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3884 if(state.dygraph_force_zoom === true) {
3885 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3886 state.log('dygraphChartUpdate() forced zoom update');
3888 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3889 options.valueRange = state.dygraph_options.valueRange;
3890 options.isZoomedIgnoreProgrammaticZoom = true;
3891 state.dygraph_force_zoom = false;
3893 else if(state.current.name !== 'auto') {
3894 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3895 state.log('dygraphChartUpdate() loose update');
3897 options.valueRange = state.dygraph_options.valueRange;
3900 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3901 state.log('dygraphChartUpdate() strict update');
3903 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3904 options.valueRange = state.dygraph_options.valueRange;
3905 options.isZoomedIgnoreProgrammaticZoom = true;
3908 if(state.dygraph_smooth_eligible === true) {
3909 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3910 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3911 NETDATA.dygraphChartCreate(state, data);
3916 dygraph.updateOptions(options);
3918 state.dygraph_last_rendered = new Date().getTime();
3922 NETDATA.dygraphChartCreate = function(state, data) {
3923 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3924 state.log('dygraphChartCreate()');
3926 var self = $(state.element);
3928 var chart_type = state.chart.chart_type;
3929 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3930 chart_type = self.data('dygraph-type') || chart_type;
3932 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3933 smooth = self.data('dygraph-smooth') || smooth;
3935 if(NETDATA.dygraph.smooth === false)
3938 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3939 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3941 state.dygraph_options = {
3942 colors: self.data('dygraph-colors') || state.chartColors(),
3944 // leave a few pixels empty on the right of the chart
3945 rightGap: self.data('dygraph-rightgap') || 5,
3946 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3947 showRoller: self.data('dygraph-showroller') || false,
3949 title: self.data('dygraph-title') || state.title,
3950 titleHeight: self.data('dygraph-titleheight') || 19,
3952 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3953 labels: data.result.labels,
3954 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3955 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3956 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3957 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3958 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3961 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3962 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3964 includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
3965 xRangePad: self.data('dygraph-xrangepad') || 0,
3966 yRangePad: self.data('dygraph-yrangepad') || 1,
3968 valueRange: self.data('dygraph-valuerange') || null,
3970 ylabel: state.units,
3971 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3973 // the function to plot the chart
3976 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3977 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3978 strokePattern: self.data('dygraph-strokepattern') || undefined,
3980 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3981 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3982 drawPoints: self.data('dygraph-drawpoints') || false,
3984 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3985 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3987 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3988 pointSize: self.data('dygraph-pointsize') || 1,
3990 // enabling this makes the chart with little square lines
3991 stepPlot: self.data('dygraph-stepplot') || false,
3993 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3994 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3995 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3997 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3998 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3999 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
4000 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
4002 drawAxis: self.data('dygraph-drawaxis') || true,
4003 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
4004 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
4005 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
4007 drawGrid: self.data('dygraph-drawgrid') || true,
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.ylabel = undefined;
4379 state.dygraph_options.yLabelWidth = 0;
4380 state.dygraph_options.labelsDivWidth = 120;
4381 state.dygraph_options.labelsDivStyles.width = '120px';
4382 state.dygraph_options.labelsSeparateLines = true;
4383 state.dygraph_options.rightGap = 0;
4384 state.dygraph_options.yRangePad = 1;
4387 if(smooth === true) {
4388 state.dygraph_smooth_eligible = true;
4390 if(NETDATA.options.current.smooth_plot === true)
4391 state.dygraph_options.plotter = smoothPlotter;
4393 else state.dygraph_smooth_eligible = false;
4395 state.dygraph_instance = new Dygraph(state.element_chart,
4396 data.result.data, state.dygraph_options);
4398 state.dygraph_force_zoom = false;
4399 state.dygraph_user_action = false;
4400 state.dygraph_last_rendered = new Date().getTime();
4404 // ----------------------------------------------------------------------------------------------------------------
4407 NETDATA.morrisInitialize = function(callback) {
4408 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4410 // morris requires raphael
4411 if(!NETDATA.chartLibraries.raphael.initialized) {
4412 if(NETDATA.chartLibraries.raphael.enabled) {
4413 NETDATA.raphaelInitialize(function() {
4414 NETDATA.morrisInitialize(callback);
4418 NETDATA.chartLibraries.morris.enabled = false;
4419 if(typeof callback === "function")
4424 NETDATA._loadCSS(NETDATA.morris_css);
4427 url: NETDATA.morris_js,
4430 xhrFields: { withCredentials: true } // required for the cookie
4433 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4436 NETDATA.chartLibraries.morris.enabled = false;
4437 NETDATA.error(100, NETDATA.morris_js);
4439 .always(function() {
4440 if(typeof callback === "function")
4446 NETDATA.chartLibraries.morris.enabled = false;
4447 if(typeof callback === "function")
4452 NETDATA.morrisChartUpdate = function(state, data) {
4453 state.morris_instance.setData(data.result.data);
4457 NETDATA.morrisChartCreate = function(state, data) {
4459 state.morris_options = {
4460 element: state.element_chart.id,
4461 data: data.result.data,
4463 ykeys: data.dimension_names,
4464 labels: data.dimension_names,
4470 continuousLine: false,
4471 behaveLikeLine: false
4474 if(state.chart.chart_type === 'line')
4475 state.morris_instance = new Morris.Line(state.morris_options);
4477 else if(state.chart.chart_type === 'area') {
4478 state.morris_options.behaveLikeLine = true;
4479 state.morris_instance = new Morris.Area(state.morris_options);
4482 state.morris_instance = new Morris.Area(state.morris_options);
4487 // ----------------------------------------------------------------------------------------------------------------
4490 NETDATA.raphaelInitialize = function(callback) {
4491 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4493 url: NETDATA.raphael_js,
4496 xhrFields: { withCredentials: true } // required for the cookie
4499 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4502 NETDATA.chartLibraries.raphael.enabled = false;
4503 NETDATA.error(100, NETDATA.raphael_js);
4505 .always(function() {
4506 if(typeof callback === "function")
4511 NETDATA.chartLibraries.raphael.enabled = false;
4512 if(typeof callback === "function")
4517 NETDATA.raphaelChartUpdate = function(state, data) {
4518 $(state.element_chart).raphael(data.result, {
4519 width: state.chartWidth(),
4520 height: state.chartHeight()
4526 NETDATA.raphaelChartCreate = function(state, data) {
4527 $(state.element_chart).raphael(data.result, {
4528 width: state.chartWidth(),
4529 height: state.chartHeight()
4535 // ----------------------------------------------------------------------------------------------------------------
4538 NETDATA.c3Initialize = function(callback) {
4539 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4542 if(!NETDATA.chartLibraries.d3.initialized) {
4543 if(NETDATA.chartLibraries.d3.enabled) {
4544 NETDATA.d3Initialize(function() {
4545 NETDATA.c3Initialize(callback);
4549 NETDATA.chartLibraries.c3.enabled = false;
4550 if(typeof callback === "function")
4555 NETDATA._loadCSS(NETDATA.c3_css);
4561 xhrFields: { withCredentials: true } // required for the cookie
4564 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4567 NETDATA.chartLibraries.c3.enabled = false;
4568 NETDATA.error(100, NETDATA.c3_js);
4570 .always(function() {
4571 if(typeof callback === "function")
4577 NETDATA.chartLibraries.c3.enabled = false;
4578 if(typeof callback === "function")
4583 NETDATA.c3ChartUpdate = function(state, data) {
4584 state.c3_instance.destroy();
4585 return NETDATA.c3ChartCreate(state, data);
4587 //state.c3_instance.load({
4588 // rows: data.result,
4595 NETDATA.c3ChartCreate = function(state, data) {
4597 state.element_chart.id = 'c3-' + state.uuid;
4598 // console.log('id = ' + state.element_chart.id);
4600 state.c3_instance = c3.generate({
4601 bindto: '#' + state.element_chart.id,
4603 width: state.chartWidth(),
4604 height: state.chartHeight()
4607 pattern: state.chartColors()
4612 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4618 format: function(x) {
4619 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4646 // console.log(state.c3_instance);
4651 // ----------------------------------------------------------------------------------------------------------------
4654 NETDATA.d3Initialize = function(callback) {
4655 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4660 xhrFields: { withCredentials: true } // required for the cookie
4663 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4666 NETDATA.chartLibraries.d3.enabled = false;
4667 NETDATA.error(100, NETDATA.d3_js);
4669 .always(function() {
4670 if(typeof callback === "function")
4675 NETDATA.chartLibraries.d3.enabled = false;
4676 if(typeof callback === "function")
4681 NETDATA.d3ChartUpdate = function(state, data) {
4685 NETDATA.d3ChartCreate = function(state, data) {
4689 // ----------------------------------------------------------------------------------------------------------------
4692 NETDATA.googleInitialize = function(callback) {
4693 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4695 url: NETDATA.google_js,
4698 xhrFields: { withCredentials: true } // required for the cookie
4701 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4702 google.load('visualization', '1.1', {
4703 'packages': ['corechart', 'controls'],
4704 'callback': callback
4708 NETDATA.chartLibraries.google.enabled = false;
4709 NETDATA.error(100, NETDATA.google_js);
4710 if(typeof callback === "function")
4715 NETDATA.chartLibraries.google.enabled = false;
4716 if(typeof callback === "function")
4721 NETDATA.googleChartUpdate = function(state, data) {
4722 var datatable = new google.visualization.DataTable(data.result);
4723 state.google_instance.draw(datatable, state.google_options);
4727 NETDATA.googleChartCreate = function(state, data) {
4728 var datatable = new google.visualization.DataTable(data.result);
4730 state.google_options = {
4731 colors: state.chartColors(),
4733 // do not set width, height - the chart resizes itself
4734 //width: state.chartWidth(),
4735 //height: state.chartHeight(),
4740 // title: "Time of Day",
4741 // format:'HH:mm:ss',
4742 viewWindowMode: 'maximized',
4754 viewWindowMode: 'pretty',
4769 focusTarget: 'category',
4776 titlePosition: 'out',
4787 curveType: 'function',
4792 switch(state.chart.chart_type) {
4794 state.google_options.vAxis.viewWindowMode = 'maximized';
4795 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4796 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4800 state.google_options.isStacked = true;
4801 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4802 state.google_options.vAxis.viewWindowMode = 'maximized';
4803 state.google_options.vAxis.minValue = null;
4804 state.google_options.vAxis.maxValue = null;
4805 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4810 state.google_options.lineWidth = 2;
4811 state.google_instance = new google.visualization.LineChart(state.element_chart);
4815 state.google_instance.draw(datatable, state.google_options);
4819 // ----------------------------------------------------------------------------------------------------------------
4821 NETDATA.percentFromValueMax = function(value, max) {
4822 if(value === null) value = 0;
4823 if(max < value) max = value;
4827 pcent = Math.round(value * 100 / max);
4828 if(pcent === 0 && value > 0) pcent = 1;
4834 // ----------------------------------------------------------------------------------------------------------------
4837 NETDATA.easypiechartInitialize = function(callback) {
4838 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4840 url: NETDATA.easypiechart_js,
4843 xhrFields: { withCredentials: true } // required for the cookie
4846 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4849 NETDATA.chartLibraries.easypiechart.enabled = false;
4850 NETDATA.error(100, NETDATA.easypiechart_js);
4852 .always(function() {
4853 if(typeof callback === "function")
4858 NETDATA.chartLibraries.easypiechart.enabled = false;
4859 if(typeof callback === "function")
4864 NETDATA.easypiechartClearSelection = function(state) {
4865 if(typeof state.easyPieChartEvent !== 'undefined') {
4866 if(state.easyPieChartEvent.timer !== null)
4867 clearTimeout(state.easyPieChartEvent.timer);
4869 state.easyPieChartEvent.timer = null;
4872 if(state.isAutoRefreshable() === true && state.data !== null) {
4873 NETDATA.easypiechartChartUpdate(state, state.data);
4876 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4877 state.easyPieChart_instance.update(0);
4879 state.easyPieChart_instance.enableAnimation();
4884 NETDATA.easypiechartSetSelection = function(state, t) {
4885 if(state.timeIsVisible(t) !== true)
4886 return NETDATA.easypiechartClearSelection(state);
4888 var slot = state.calculateRowForTime(t);
4889 if(slot < 0 || slot >= state.data.result.length)
4890 return NETDATA.easypiechartClearSelection(state);
4892 if(typeof state.easyPieChartEvent === 'undefined') {
4893 state.easyPieChartEvent = {
4900 var value = state.data.result[state.data.result.length - 1 - slot];
4901 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4902 var pcent = NETDATA.percentFromValueMax(value, max);
4904 state.easyPieChartEvent.value = value;
4905 state.easyPieChartEvent.pcent = pcent;
4906 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4908 if(state.easyPieChartEvent.timer === null) {
4909 state.easyPieChart_instance.disableAnimation();
4911 state.easyPieChartEvent.timer = setTimeout(function() {
4912 state.easyPieChartEvent.timer = null;
4913 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4914 }, NETDATA.options.current.charts_selection_animation_delay);
4920 NETDATA.easypiechartChartUpdate = function(state, data) {
4921 var value, max, pcent;
4923 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4929 value = data.result[0];
4930 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4931 pcent = NETDATA.percentFromValueMax(value, max);
4934 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4935 state.easyPieChart_instance.update(pcent);
4939 NETDATA.easypiechartChartCreate = function(state, data) {
4940 var self = $(state.element);
4941 var chart = $(state.element_chart);
4943 var value = data.result[0];
4944 var max = self.data('easypiechart-max-value') || null;
4945 var adjust = self.data('easypiechart-adjust') || null;
4949 state.easyPieChartMax = null;
4952 state.easyPieChartMax = max;
4954 var pcent = NETDATA.percentFromValueMax(value, max);
4956 chart.data('data-percent', pcent);
4960 case 'width': size = state.chartHeight(); break;
4961 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4962 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4964 default: size = state.chartWidth(); break;
4966 state.element.style.width = size + 'px';
4967 state.element.style.height = size + 'px';
4969 var stroke = Math.floor(size / 22);
4970 if(stroke < 3) stroke = 2;
4972 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4973 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4974 state.easyPieChartLabel = document.createElement('span');
4975 state.easyPieChartLabel.className = 'easyPieChartLabel';
4976 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4977 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4978 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4979 state.element_chart.appendChild(state.easyPieChartLabel);
4981 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4982 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4983 state.easyPieChartTitle = document.createElement('span');
4984 state.easyPieChartTitle.className = 'easyPieChartTitle';
4985 state.easyPieChartTitle.innerHTML = state.title;
4986 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4987 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4988 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4989 state.element_chart.appendChild(state.easyPieChartTitle);
4991 var unitfontsize = Math.round(titlefontsize * 0.9);
4992 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4993 state.easyPieChartUnits = document.createElement('span');
4994 state.easyPieChartUnits.className = 'easyPieChartUnits';
4995 state.easyPieChartUnits.innerHTML = state.units;
4996 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4997 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4998 state.element_chart.appendChild(state.easyPieChartUnits);
5000 var barColor = self.data('easypiechart-barcolor');
5001 if(typeof barColor === 'undefined' || barColor === null)
5002 barColor = state.chartColors()[0];
5004 // <div ... data-easypiechart-barcolor="(function(percent){return(percent < 50 ? '#5cb85c' : percent < 85 ? '#f0ad4e' : '#cb3935');})" ...></div>
5005 var tmp = eval(barColor);
5006 if(typeof tmp === 'function')
5010 chart.easyPieChart({
5012 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
5013 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
5014 scaleLength: self.data('easypiechart-scalelength') || 5,
5015 lineCap: self.data('easypiechart-linecap') || 'round',
5016 lineWidth: self.data('easypiechart-linewidth') || stroke,
5017 trackWidth: self.data('easypiechart-trackwidth') || undefined,
5018 size: self.data('easypiechart-size') || size,
5019 rotate: self.data('easypiechart-rotate') || 0,
5020 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
5021 easing: self.data('easypiechart-easing') || undefined
5024 // when we just re-create the chart
5025 // do not animate the first update
5027 if(typeof state.easyPieChart_instance !== 'undefined')
5030 state.easyPieChart_instance = chart.data('easyPieChart');
5031 if(animate === false) state.easyPieChart_instance.disableAnimation();
5032 state.easyPieChart_instance.update(pcent);
5033 if(animate === false) state.easyPieChart_instance.enableAnimation();
5037 // ----------------------------------------------------------------------------------------------------------------
5040 NETDATA.gaugeInitialize = function(callback) {
5041 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
5043 url: NETDATA.gauge_js,
5046 xhrFields: { withCredentials: true } // required for the cookie
5049 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
5052 NETDATA.chartLibraries.gauge.enabled = false;
5053 NETDATA.error(100, NETDATA.gauge_js);
5055 .always(function() {
5056 if(typeof callback === "function")
5061 NETDATA.chartLibraries.gauge.enabled = false;
5062 if(typeof callback === "function")
5067 NETDATA.gaugeAnimation = function(state, status) {
5070 if(typeof status === 'boolean' && status === false)
5072 else if(typeof status === 'number')
5075 // console.log('gauge speed ' + speed);
5076 state.gauge_instance.animationSpeed = speed;
5077 state.___gaugeOld__.speed = speed;
5080 NETDATA.gaugeSet = function(state, value, min, max) {
5081 if(typeof value !== 'number') value = 0;
5082 if(typeof min !== 'number') min = 0;
5083 if(typeof max !== 'number') max = 0;
5084 if(value > max) max = value;
5085 if(value < min) min = value;
5094 // gauge.js has an issue if the needle
5095 // is smaller than min or larger than max
5096 // when we set the new values
5097 // the needle will go crazy
5099 // to prevent it, we always feed it
5100 // with a percentage, so that the needle
5101 // is always between min and max
5102 var pcent = (value - min) * 100 / (max - min);
5104 // these should never happen
5105 if(pcent < 0) pcent = 0;
5106 if(pcent > 100) pcent = 100;
5108 state.gauge_instance.set(pcent);
5109 // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
5111 state.___gaugeOld__.value = value;
5112 state.___gaugeOld__.min = min;
5113 state.___gaugeOld__.max = max;
5116 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5117 if(state.___gaugeOld__.valueLabel !== value) {
5118 state.___gaugeOld__.valueLabel = value;
5119 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5121 if(state.___gaugeOld__.minLabel !== min) {
5122 state.___gaugeOld__.minLabel = min;
5123 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5125 if(state.___gaugeOld__.maxLabel !== max) {
5126 state.___gaugeOld__.maxLabel = max;
5127 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5131 NETDATA.gaugeClearSelection = function(state) {
5132 if(typeof state.gaugeEvent !== 'undefined') {
5133 if(state.gaugeEvent.timer !== null)
5134 clearTimeout(state.gaugeEvent.timer);
5136 state.gaugeEvent.timer = null;
5139 if(state.isAutoRefreshable() === true && state.data !== null) {
5140 NETDATA.gaugeChartUpdate(state, state.data);
5143 NETDATA.gaugeAnimation(state, false);
5144 NETDATA.gaugeSet(state, null, null, null);
5145 NETDATA.gaugeSetLabels(state, null, null, null);
5148 NETDATA.gaugeAnimation(state, true);
5152 NETDATA.gaugeSetSelection = function(state, t) {
5153 if(state.timeIsVisible(t) !== true)
5154 return NETDATA.gaugeClearSelection(state);
5156 var slot = state.calculateRowForTime(t);
5157 if(slot < 0 || slot >= state.data.result.length)
5158 return NETDATA.gaugeClearSelection(state);
5160 if(typeof state.gaugeEvent === 'undefined') {
5161 state.gaugeEvent = {
5169 var value = state.data.result[state.data.result.length - 1 - slot];
5170 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5173 state.gaugeEvent.value = value;
5174 state.gaugeEvent.max = max;
5175 state.gaugeEvent.min = min;
5176 NETDATA.gaugeSetLabels(state, value, min, max);
5178 if(state.gaugeEvent.timer === null) {
5179 NETDATA.gaugeAnimation(state, false);
5181 state.gaugeEvent.timer = setTimeout(function() {
5182 state.gaugeEvent.timer = null;
5183 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5184 }, NETDATA.options.current.charts_selection_animation_delay);
5190 NETDATA.gaugeChartUpdate = function(state, data) {
5191 var value, min, max;
5193 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5197 NETDATA.gaugeSetLabels(state, null, null, null);
5200 value = data.result[0];
5202 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5203 if(value > max) max = value;
5204 NETDATA.gaugeSetLabels(state, value, min, max);
5207 NETDATA.gaugeSet(state, value, min, max);
5211 NETDATA.gaugeChartCreate = function(state, data) {
5212 var self = $(state.element);
5213 // var chart = $(state.element_chart);
5215 var value = data.result[0];
5216 var max = self.data('gauge-max-value') || null;
5217 var adjust = self.data('gauge-adjust') || null;
5218 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5219 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5220 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5221 var stopColor = self.data('gauge-stop-color') || void 0;
5222 var generateGradient = self.data('gauge-generate-gradient') || false;
5226 state.gaugeMax = null;
5229 state.gaugeMax = max;
5231 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5233 // case 'width': width = height * ratio; break;
5235 // default: height = width / ratio; break;
5237 //state.element.style.width = width.toString() + 'px';
5238 //state.element.style.height = height.toString() + 'px';
5243 lines: 12, // The number of lines to draw
5244 angle: 0.15, // The length of each line
5245 lineWidth: 0.44, // 0.44 The line thickness
5247 length: 0.8, // 0.9 The radius of the inner circle
5248 strokeWidth: 0.035, // The rotation offset
5249 color: pointerColor // Fill color
5251 colorStart: startColor, // Colors
5252 colorStop: stopColor, // just experiment with them
5253 strokeColor: strokeColor, // to see which ones work best for you
5255 generateGradient: (generateGradient === true)?true:false,
5259 if (generateGradient.constructor === Array) {
5261 // data-gauge-generate-gradient="[0, 50, 100]"
5262 // data-gauge-gradient-percent-color-0="#FFFFFF"
5263 // data-gauge-gradient-percent-color-50="#999900"
5264 // data-gauge-gradient-percent-color-100="#000000"
5266 options.percentColors = new Array();
5267 var len = generateGradient.length;
5269 var pcent = generateGradient[len];
5270 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5271 if(color !== false) {
5272 var a = new Array();
5275 options.percentColors.unshift(a);
5278 if(options.percentColors.length === 0)
5279 delete options.percentColors;
5281 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5282 options.percentColors = [
5283 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5284 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5285 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5286 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5287 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5288 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5289 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5290 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5291 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5292 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5293 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5296 state.gauge_canvas = document.createElement('canvas');
5297 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5298 state.gauge_canvas.className = 'gaugeChart';
5299 state.gauge_canvas.width = width;
5300 state.gauge_canvas.height = height;
5301 state.element_chart.appendChild(state.gauge_canvas);
5303 var valuefontsize = Math.floor(height / 6);
5304 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5305 state.gaugeChartLabel = document.createElement('span');
5306 state.gaugeChartLabel.className = 'gaugeChartLabel';
5307 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5308 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5309 state.element_chart.appendChild(state.gaugeChartLabel);
5311 var titlefontsize = Math.round(valuefontsize / 2);
5313 state.gaugeChartTitle = document.createElement('span');
5314 state.gaugeChartTitle.className = 'gaugeChartTitle';
5315 state.gaugeChartTitle.innerHTML = state.title;
5316 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5317 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5318 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5319 state.element_chart.appendChild(state.gaugeChartTitle);
5321 var unitfontsize = Math.round(titlefontsize * 0.9);
5322 state.gaugeChartUnits = document.createElement('span');
5323 state.gaugeChartUnits.className = 'gaugeChartUnits';
5324 state.gaugeChartUnits.innerHTML = state.units;
5325 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5326 state.element_chart.appendChild(state.gaugeChartUnits);
5328 state.gaugeChartMin = document.createElement('span');
5329 state.gaugeChartMin.className = 'gaugeChartMin';
5330 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5331 state.element_chart.appendChild(state.gaugeChartMin);
5333 state.gaugeChartMax = document.createElement('span');
5334 state.gaugeChartMax.className = 'gaugeChartMax';
5335 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5336 state.element_chart.appendChild(state.gaugeChartMax);
5338 // when we just re-create the chart
5339 // do not animate the first update
5341 if(typeof state.gauge_instance !== 'undefined')
5344 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5346 state.___gaugeOld__ = {
5355 // we will always feed a percentage
5356 state.gauge_instance.minValue = 0;
5357 state.gauge_instance.maxValue = 100;
5359 NETDATA.gaugeAnimation(state, animate);
5360 NETDATA.gaugeSet(state, value, 0, max);
5361 NETDATA.gaugeSetLabels(state, value, 0, max);
5362 NETDATA.gaugeAnimation(state, true);
5366 // ----------------------------------------------------------------------------------------------------------------
5367 // Charts Libraries Registration
5369 NETDATA.chartLibraries = {
5371 initialize: NETDATA.dygraphInitialize,
5372 create: NETDATA.dygraphChartCreate,
5373 update: NETDATA.dygraphChartUpdate,
5374 resize: function(state) {
5375 if(typeof state.dygraph_instance.resize === 'function')
5376 state.dygraph_instance.resize();
5378 setSelection: NETDATA.dygraphSetSelection,
5379 clearSelection: NETDATA.dygraphClearSelection,
5380 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5383 format: function(state) { return 'json'; },
5384 options: function(state) { return 'ms|flip'; },
5385 legend: function(state) {
5386 if(this.isSparkline(state) === false)
5387 return 'right-side';
5391 autoresize: function(state) { return true; },
5392 max_updates_to_recreate: function(state) { return 5000; },
5393 track_colors: function(state) { return true; },
5394 pixels_per_point: function(state) {
5395 if(this.isSparkline(state) === false)
5401 isSparkline: function(state) {
5402 if(typeof state.dygraph_sparkline === 'undefined') {
5403 var t = $(state.element).data('dygraph-theme');
5404 if(t === 'sparkline')
5405 state.dygraph_sparkline = true;
5407 state.dygraph_sparkline = false;
5409 return state.dygraph_sparkline;
5413 initialize: NETDATA.sparklineInitialize,
5414 create: NETDATA.sparklineChartCreate,
5415 update: NETDATA.sparklineChartUpdate,
5417 setSelection: undefined, // function(state, t) { return true; },
5418 clearSelection: undefined, // function(state) { return true; },
5419 toolboxPanAndZoom: null,
5422 format: function(state) { return 'array'; },
5423 options: function(state) { return 'flip|abs'; },
5424 legend: function(state) { return null; },
5425 autoresize: function(state) { return false; },
5426 max_updates_to_recreate: function(state) { return 5000; },
5427 track_colors: function(state) { return false; },
5428 pixels_per_point: function(state) { return 3; }
5431 initialize: NETDATA.peityInitialize,
5432 create: NETDATA.peityChartCreate,
5433 update: NETDATA.peityChartUpdate,
5435 setSelection: undefined, // function(state, t) { return true; },
5436 clearSelection: undefined, // function(state) { return true; },
5437 toolboxPanAndZoom: null,
5440 format: function(state) { return 'ssvcomma'; },
5441 options: function(state) { return 'null2zero|flip|abs'; },
5442 legend: function(state) { return null; },
5443 autoresize: function(state) { return false; },
5444 max_updates_to_recreate: function(state) { return 5000; },
5445 track_colors: function(state) { return false; },
5446 pixels_per_point: function(state) { return 3; }
5449 initialize: NETDATA.morrisInitialize,
5450 create: NETDATA.morrisChartCreate,
5451 update: NETDATA.morrisChartUpdate,
5453 setSelection: undefined, // function(state, t) { return true; },
5454 clearSelection: undefined, // function(state) { return true; },
5455 toolboxPanAndZoom: null,
5458 format: function(state) { return 'json'; },
5459 options: function(state) { return 'objectrows|ms'; },
5460 legend: function(state) { return null; },
5461 autoresize: function(state) { return false; },
5462 max_updates_to_recreate: function(state) { return 50; },
5463 track_colors: function(state) { return false; },
5464 pixels_per_point: function(state) { return 15; }
5467 initialize: NETDATA.googleInitialize,
5468 create: NETDATA.googleChartCreate,
5469 update: NETDATA.googleChartUpdate,
5471 setSelection: undefined, //function(state, t) { return true; },
5472 clearSelection: undefined, //function(state) { return true; },
5473 toolboxPanAndZoom: null,
5476 format: function(state) { return 'datatable'; },
5477 options: function(state) { return ''; },
5478 legend: function(state) { return null; },
5479 autoresize: function(state) { return false; },
5480 max_updates_to_recreate: function(state) { return 300; },
5481 track_colors: function(state) { return false; },
5482 pixels_per_point: function(state) { return 4; }
5485 initialize: NETDATA.raphaelInitialize,
5486 create: NETDATA.raphaelChartCreate,
5487 update: NETDATA.raphaelChartUpdate,
5489 setSelection: undefined, // function(state, t) { return true; },
5490 clearSelection: undefined, // function(state) { return true; },
5491 toolboxPanAndZoom: null,
5494 format: function(state) { return 'json'; },
5495 options: function(state) { return ''; },
5496 legend: function(state) { return null; },
5497 autoresize: function(state) { return false; },
5498 max_updates_to_recreate: function(state) { return 5000; },
5499 track_colors: function(state) { return false; },
5500 pixels_per_point: function(state) { return 3; }
5503 initialize: NETDATA.c3Initialize,
5504 create: NETDATA.c3ChartCreate,
5505 update: NETDATA.c3ChartUpdate,
5507 setSelection: undefined, // function(state, t) { return true; },
5508 clearSelection: undefined, // function(state) { return true; },
5509 toolboxPanAndZoom: null,
5512 format: function(state) { return 'csvjsonarray'; },
5513 options: function(state) { return 'milliseconds'; },
5514 legend: function(state) { return null; },
5515 autoresize: function(state) { return false; },
5516 max_updates_to_recreate: function(state) { return 5000; },
5517 track_colors: function(state) { return false; },
5518 pixels_per_point: function(state) { return 15; }
5521 initialize: NETDATA.d3Initialize,
5522 create: NETDATA.d3ChartCreate,
5523 update: NETDATA.d3ChartUpdate,
5525 setSelection: undefined, // function(state, t) { return true; },
5526 clearSelection: undefined, // function(state) { return true; },
5527 toolboxPanAndZoom: null,
5530 format: function(state) { return 'json'; },
5531 options: function(state) { return ''; },
5532 legend: function(state) { return null; },
5533 autoresize: function(state) { return false; },
5534 max_updates_to_recreate: function(state) { return 5000; },
5535 track_colors: function(state) { return false; },
5536 pixels_per_point: function(state) { return 3; }
5539 initialize: NETDATA.easypiechartInitialize,
5540 create: NETDATA.easypiechartChartCreate,
5541 update: NETDATA.easypiechartChartUpdate,
5543 setSelection: NETDATA.easypiechartSetSelection,
5544 clearSelection: NETDATA.easypiechartClearSelection,
5545 toolboxPanAndZoom: null,
5548 format: function(state) { return 'array'; },
5549 options: function(state) { return 'absolute'; },
5550 legend: function(state) { return null; },
5551 autoresize: function(state) { return false; },
5552 max_updates_to_recreate: function(state) { return 5000; },
5553 track_colors: function(state) { return true; },
5554 pixels_per_point: function(state) { return 3; },
5558 initialize: NETDATA.gaugeInitialize,
5559 create: NETDATA.gaugeChartCreate,
5560 update: NETDATA.gaugeChartUpdate,
5562 setSelection: NETDATA.gaugeSetSelection,
5563 clearSelection: NETDATA.gaugeClearSelection,
5564 toolboxPanAndZoom: null,
5567 format: function(state) { return 'array'; },
5568 options: function(state) { return 'absolute'; },
5569 legend: function(state) { return null; },
5570 autoresize: function(state) { return false; },
5571 max_updates_to_recreate: function(state) { return 5000; },
5572 track_colors: function(state) { return true; },
5573 pixels_per_point: function(state) { return 3; },
5578 NETDATA.registerChartLibrary = function(library, url) {
5579 if(NETDATA.options.debug.libraries === true)
5580 console.log("registering chart library: " + library);
5582 NETDATA.chartLibraries[library].url = url;
5583 NETDATA.chartLibraries[library].initialized = true;
5584 NETDATA.chartLibraries[library].enabled = true;
5587 // ----------------------------------------------------------------------------------------------------------------
5588 // Load required JS libraries and CSS
5590 NETDATA.requiredJs = [
5592 url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js',
5594 isAlreadyLoaded: function() {
5595 // check if bootstrap is loaded
5596 if(typeof $().emulateTransitionEnd == 'function')
5599 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5607 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js',
5608 isAlreadyLoaded: function() { return false; }
5612 NETDATA.requiredCSS = [
5614 url: NETDATA.themes.current.bootstrap_css,
5615 isAlreadyLoaded: function() {
5616 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5623 url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
5624 isAlreadyLoaded: function() { return false; }
5627 url: NETDATA.themes.current.dashboard_css,
5628 isAlreadyLoaded: function() { return false; }
5632 NETDATA.loadedRequiredJs = 0;
5633 NETDATA.loadRequiredJs = function(index, callback) {
5634 if(index >= NETDATA.requiredJs.length) {
5635 if(typeof callback === 'function')
5640 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5641 NETDATA.loadedRequiredJs++;
5642 NETDATA.loadRequiredJs(++index, callback);
5646 if(NETDATA.options.debug.main_loop === true)
5647 console.log('loading ' + NETDATA.requiredJs[index].url);
5650 if(typeof NETDATA.requiredJs[index].async !== 'undefined' && NETDATA.requiredJs[index].async === false)
5654 url: NETDATA.requiredJs[index].url,
5657 xhrFields: { withCredentials: true } // required for the cookie
5660 if(NETDATA.options.debug.main_loop === true)
5661 console.log('loaded ' + NETDATA.requiredJs[index].url);
5664 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5666 .always(function() {
5667 NETDATA.loadedRequiredJs++;
5670 NETDATA.loadRequiredJs(++index, callback);
5674 NETDATA.loadRequiredJs(++index, callback);
5677 NETDATA.loadRequiredCSS = function(index) {
5678 if(index >= NETDATA.requiredCSS.length)
5681 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5682 NETDATA.loadRequiredCSS(++index);
5686 if(NETDATA.options.debug.main_loop === true)
5687 console.log('loading ' + NETDATA.requiredCSS[index].url);
5689 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5690 NETDATA.loadRequiredCSS(++index);
5694 // ----------------------------------------------------------------------------------------------------------------
5695 // Registry of netdata hosts
5698 onclick: null, // the callback to handle the click - it will be called with the alarm log entry
5699 chart_div_offset: 100, // give that space above the chart when scrolling to it
5700 chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id))
5701 chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart
5703 ms_penalty: 0, // the time penalty of the next alarm
5704 ms_between_notifications: 500, // firefox moves the alarms off-screen (above, outside the top of the screen)
5705 // if alarms are shown faster than: one per 500ms
5707 notifications: false, // when true, the browser supports notifications (may not be granted though)
5708 last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5709 first_notification_id: 0, // the id of the first alarm_log entry for this session
5710 // this is used to prevent CLEAR notifications for past events
5711 // notifications_shown: new Array(),
5713 server: null, // the server to connect to for fetching alarms
5714 current: null, // the list of raised alarms - updated in the background
5715 callback: null, // a callback function to call every time the list of raised alarms is refreshed
5717 notify: function(entry) {
5718 // console.log('alarm ' + entry.unique_id);
5720 if(entry.updated === true) {
5721 // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5725 var value = entry.value;
5726 if(NETDATA.alarms.current !== null) {
5727 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
5728 if(typeof t !== 'undefined' && entry.status == t.status)
5732 var name = entry.name.replace(/_/g, ' ');
5733 var status = entry.status.toLowerCase();
5734 var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
5735 var tag = entry.alarm_id;
5736 var icon = 'images/seo-performance-128.png';
5737 var interaction = false;
5741 // console.log('alarm ' + entry.unique_id + ' ' + entry.chart + '.' + entry.name + ' is ' + entry.status);
5743 switch(entry.status) {
5751 case 'UNINITIALIZED':
5755 if(entry.unique_id < NETDATA.alarms.first_notification_id) {
5756 // console.log('alarm ' + entry.unique_id + ' is not current');
5759 if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5760 // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5763 title = name + ' back to normal';
5764 icon = 'images/check-mark-2-128-green.png'
5765 interaction = false;
5769 if(entry.old_status === 'CRITICAL')
5770 status = 'demoted to ' + entry.status.toLowerCase();
5772 icon = 'images/alert-128-orange.png';
5773 interaction = false;
5777 if(entry.old_status === 'WARNING')
5778 status = 'escalated to ' + entry.status.toLowerCase();
5780 icon = 'images/alert-128-red.png'
5785 console.log('invalid alarm status ' + entry.status);
5790 // cleanup old notifications with the same alarm_id as this one
5791 // FIXME: it does not seem to work on any web browser!
5792 var len = NETDATA.alarms.notifications_shown.length;
5794 var n = NETDATA.alarms.notifications_shown[len];
5795 if(n.data.alarm_id === entry.alarm_id) {
5796 console.log('removing old alarm ' + n.data.unique_id);
5798 // close the notification
5801 // remove it from the array
5802 NETDATA.alarms.notifications_shown.splice(len, 1);
5803 len = NETDATA.alarms.notifications_shown.length;
5810 setTimeout(function() {
5811 // show this notification
5812 // console.log('new notification: ' + title);
5813 var n = new Notification(title, {
5814 body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info,
5816 requireInteraction: interaction,
5817 icon: NETDATA.serverDefault + icon,
5821 n.onclick = function(event) {
5822 event.preventDefault();
5823 NETDATA.alarms.onclick(event.target.data);
5827 // NETDATA.alarms.notifications_shown.push(n);
5828 // console.log(entry);
5829 }, NETDATA.alarms.ms_penalty);
5831 NETDATA.alarms.ms_penalty += NETDATA.alarms.ms_between_notifications;
5835 scrollToChart: function(chart_id) {
5836 if(typeof chart_id === 'string') {
5837 var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset();
5838 if(typeof offset !== 'undefined') {
5839 $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration);
5846 scrollToAlarm: function(alarm) {
5847 if(typeof alarm === 'object') {
5848 var ret = NETDATA.alarms.scrollToChart(alarm.chart);
5850 if(ret === true && NETDATA.options.page_is_visible === false)
5852 // 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.');
5857 notifyAll: function() {
5858 // console.log('FETCHING ALARM LOG');
5859 NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5860 // console.log('ALARM LOG FETCHED');
5862 if(data === null || typeof data !== 'object') {
5863 console.log('invalid alarms log response');
5867 if(data.length === 0) {
5868 console.log('received empty alarm log');
5872 // console.log('received alarm log of ' + data.length + ' entries, from ' + data[data.length - 1].unique_id.toString() + ' to ' + data[0].unique_id.toString());
5874 data.sort(function(a, b) {
5875 if(a.unique_id > b.unique_id) return -1;
5876 if(a.unique_id < b.unique_id) return 1;
5880 NETDATA.alarms.ms_penalty = 0;
5882 var len = data.length;
5884 if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5885 NETDATA.alarms.notify(data[len]);
5888 // console.log('ignoring alarm (older) with id ' + data[len].unique_id.toString());
5891 NETDATA.alarms.last_notification_id = data[0].unique_id;
5892 NETDATA.localStorageSet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5893 // console.log('last notification id = ' + NETDATA.alarms.last_notification_id);
5897 check_notifications: function() {
5898 // returns true if we should fire 1+ notifications
5900 if(NETDATA.alarms.notifications !== true) {
5901 // console.log('notifications not available');
5905 if(Notification.permission !== 'granted') {
5906 // console.log('notifications not granted');
5910 if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5911 // console.log('can do alarms: old id = ' + NETDATA.alarms.last_notification_id + ' new id = ' + NETDATA.alarms.current.latest_alarm_log_unique_id);
5913 if(NETDATA.alarms.current.latest_alarm_log_unique_id > NETDATA.alarms.last_notification_id) {
5914 // console.log('new alarms detected');
5917 //else console.log('no new alarms');
5919 // else console.log('cannot process alarms');
5924 get: function(what, callback) {
5926 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5929 xhrFields: { withCredentials: true } // required for the cookie
5931 .done(function(data) {
5932 if(NETDATA.alarms.first_notification_id === 0 && typeof data.latest_alarm_log_unique_id === 'number')
5933 NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
5935 if(typeof callback === 'function')
5939 NETDATA.error(415, NETDATA.alarms.server);
5941 if(typeof callback === 'function')
5946 update_forever: function() {
5947 NETDATA.alarms.get('active', function(data) {
5949 NETDATA.alarms.current = data;
5951 if(NETDATA.alarms.check_notifications() === true) {
5952 NETDATA.alarms.notifyAll();
5955 if (typeof NETDATA.alarms.callback === 'function') {
5956 NETDATA.alarms.callback(data);
5959 // Health monitoring is disabled on this netdata
5960 if(data.status === false) return;
5963 setTimeout(NETDATA.alarms.update_forever, 10000);
5967 get_log: function(last_id, callback) {
5968 // console.log('fetching all log after ' + last_id.toString());
5970 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5973 xhrFields: { withCredentials: true } // required for the cookie
5975 .done(function(data) {
5976 if(typeof callback === 'function')
5980 NETDATA.error(416, NETDATA.alarms.server);
5982 if(typeof callback === 'function')
5988 var host = NETDATA.serverDefault;
5989 while(host.slice(-1) === '/')
5990 host = host.substring(0, host.length - 1);
5991 NETDATA.alarms.server = host;
5993 NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5995 if(NETDATA.alarms.onclick === null)
5996 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
5998 if(netdataShowAlarms === true) {
5999 NETDATA.alarms.update_forever();
6001 if('Notification' in window) {
6002 // console.log('notifications available');
6003 NETDATA.alarms.notifications = true;
6005 if(Notification.permission === 'default')
6006 Notification.requestPermission();
6012 // ----------------------------------------------------------------------------------------------------------------
6013 // Registry of netdata hosts
6015 NETDATA.registry = {
6016 server: null, // the netdata registry server
6017 person_guid: null, // the unique ID of this browser / user
6018 machine_guid: null, // the unique ID the netdata server that served dashboard.js
6019 hostname: null, // the hostname of the netdata server that served dashboard.js
6020 machines: null, // the user's other URLs
6021 machines_array: null, // the user's other URLs in an array
6024 parsePersonUrls: function(person_urls) {
6025 // console.log(person_urls);
6026 NETDATA.registry.person_urls = person_urls;
6029 NETDATA.registry.machines = {};
6030 NETDATA.registry.machines_array = new Array();
6032 var now = new Date().getTime();
6033 var apu = person_urls;
6036 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
6037 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6043 accesses: apu[i][3],
6045 alternate_urls: new Array()
6047 obj.alternate_urls.push(apu[i][1]);
6049 NETDATA.registry.machines[apu[i][0]] = obj;
6050 NETDATA.registry.machines_array.push(obj);
6053 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6055 var pu = NETDATA.registry.machines[apu[i][0]];
6056 if(pu.last_t < apu[i][2]) {
6058 pu.last_t = apu[i][2];
6059 pu.name = apu[i][4];
6061 pu.accesses += apu[i][3];
6062 pu.alternate_urls.push(apu[i][1]);
6067 if(typeof netdataRegistryCallback === 'function')
6068 netdataRegistryCallback(NETDATA.registry.machines_array);
6072 if(netdataRegistry !== true) return;
6074 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
6076 NETDATA.registry.server = data.registry;
6077 NETDATA.registry.machine_guid = data.machine_guid;
6078 NETDATA.registry.hostname = data.hostname;
6080 NETDATA.registry.access(2, function (person_urls) {
6081 NETDATA.registry.parsePersonUrls(person_urls);
6088 hello: function(host, callback) {
6089 while(host.slice(-1) === '/')
6090 host = host.substring(0, host.length - 1);
6092 // send HELLO to a netdata server:
6093 // 1. verifies the server is reachable
6094 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
6096 url: host + '/api/v1/registry?action=hello',
6099 xhrFields: { withCredentials: true } // required for the cookie
6101 .done(function(data) {
6102 if(typeof data.status !== 'string' || data.status !== 'ok') {
6103 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
6107 if(typeof callback === 'function')
6111 NETDATA.error(407, host);
6113 if(typeof callback === 'function')
6118 access: function(max_redirects, callback) {
6119 // send ACCESS to a netdata registry:
6120 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
6121 // 2. it responds with a list of netdata servers we know
6122 // the registry identifies us using a cookie it sets the first time we access it
6123 // the registry may respond with a redirect URL to send us to another registry
6125 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),
6128 xhrFields: { withCredentials: true } // required for the cookie
6130 .done(function(data) {
6131 var redirect = null;
6132 if(typeof data.registry === 'string')
6133 redirect = data.registry;
6135 if(typeof data.status !== 'string' || data.status !== 'ok') {
6136 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6141 if(redirect !== null && max_redirects > 0) {
6142 NETDATA.registry.server = redirect;
6143 NETDATA.registry.access(max_redirects - 1, callback);
6146 if(typeof callback === 'function')
6151 if(typeof data.person_guid === 'string')
6152 NETDATA.registry.person_guid = data.person_guid;
6154 if(typeof callback === 'function')
6155 callback(data.urls);
6159 NETDATA.error(410, NETDATA.registry.server);
6161 if(typeof callback === 'function')
6166 delete: function(delete_url, callback) {
6167 // send DELETE to a netdata registry:
6169 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),
6172 xhrFields: { withCredentials: true } // required for the cookie
6174 .done(function(data) {
6175 if(typeof data.status !== 'string' || data.status !== 'ok') {
6176 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6180 if(typeof callback === 'function')
6184 NETDATA.error(412, NETDATA.registry.server);
6186 if(typeof callback === 'function')
6191 search: function(machine_guid, callback) {
6192 // SEARCH for the URLs of a machine:
6194 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,
6197 xhrFields: { withCredentials: true } // required for the cookie
6199 .done(function(data) {
6200 if(typeof data.status !== 'string' || data.status !== 'ok') {
6201 NETDATA.error(417, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6205 if(typeof callback === 'function')
6209 NETDATA.error(418, NETDATA.registry.server);
6211 if(typeof callback === 'function')
6216 switch: function(new_person_guid, callback) {
6219 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,
6222 xhrFields: { withCredentials: true } // required for the cookie
6224 .done(function(data) {
6225 if(typeof data.status !== 'string' || data.status !== 'ok') {
6226 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6230 if(typeof callback === 'function')
6234 NETDATA.error(414, NETDATA.registry.server);
6236 if(typeof callback === 'function')
6242 // ----------------------------------------------------------------------------------------------------------------
6245 if(typeof netdataPrepCallback === 'function')
6246 netdataPrepCallback();
6248 NETDATA.errorReset();
6249 NETDATA.loadRequiredCSS(0);
6251 NETDATA._loadjQuery(function() {
6252 NETDATA.loadRequiredJs(0, function() {
6253 if(typeof $().emulateTransitionEnd !== 'function') {
6254 // bootstrap is not available
6255 NETDATA.options.current.show_help = false;
6258 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
6259 if(NETDATA.options.debug.main_loop === true)
6260 console.log('starting chart refresh thread');
6267 // window.NETDATA = NETDATA;
6268 // })(window, document);