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-f6ec7be.js';
117 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-f6ec7be.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.selected_count > parent.unselected_count)
840 this.selected = true;
842 this.selected = false;
844 this.setOptions(name_div, value_div, color);
847 dimensionStatus.prototype.invalidate = function() {
848 this.name_div = null;
849 this.value_div = null;
850 this.enabled = false;
853 dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
856 if(this.name_div != name_div) {
857 this.name_div = name_div;
858 this.name_div.title = this.label;
859 this.name_div.style.color = this.color;
860 if(this.selected === false)
861 this.name_div.className = 'netdata-legend-name not-selected';
863 this.name_div.className = 'netdata-legend-name selected';
866 if(this.value_div != value_div) {
867 this.value_div = value_div;
868 this.value_div.title = this.label;
869 this.value_div.style.color = this.color;
870 if(this.selected === false)
871 this.value_div.className = 'netdata-legend-value not-selected';
873 this.value_div.className = 'netdata-legend-value selected';
880 dimensionStatus.prototype.setHandler = function() {
881 if(this.enabled === false) return;
885 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
886 this.name_div.onclick = this.value_div.onclick = function(e) {
888 if(ds.isSelected()) {
890 if(e.shiftKey === true || e.ctrlKey === true) {
891 // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
894 if(ds.parent.countSelected() === 0)
895 ds.parent.selectAll();
898 // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
899 if(ds.parent.countSelected() === 1) {
900 ds.parent.selectAll();
903 ds.parent.selectNone();
909 // this is not selected
910 if(e.shiftKey === true || e.ctrlKey === true) {
911 // control or shift key is pressed -> select this too
915 // no key is pressed -> select only this
916 ds.parent.selectNone();
921 ds.parent.state.redrawChart();
925 dimensionStatus.prototype.select = function() {
926 if(this.enabled === false) return;
928 this.name_div.className = 'netdata-legend-name selected';
929 this.value_div.className = 'netdata-legend-value selected';
930 this.selected = true;
933 dimensionStatus.prototype.unselect = function() {
934 if(this.enabled === false) return;
936 this.name_div.className = 'netdata-legend-name not-selected';
937 this.value_div.className = 'netdata-legend-value hidden';
938 this.selected = false;
941 dimensionStatus.prototype.isSelected = function() {
942 return(this.enabled === true && this.selected === true);
945 // ----------------------------------------------------------------------------------------------------------------
947 dimensionsVisibility = function(state) {
950 this.dimensions = {};
951 this.selected_count = 0;
952 this.unselected_count = 0;
955 dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
956 if(typeof this.dimensions[label] === 'undefined') {
958 this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
961 this.dimensions[label].setOptions(name_div, value_div, color);
963 return this.dimensions[label];
966 dimensionsVisibility.prototype.dimensionGet = function(label) {
967 return this.dimensions[label];
970 dimensionsVisibility.prototype.invalidateAll = function() {
971 for(var d in this.dimensions)
972 this.dimensions[d].invalidate();
975 dimensionsVisibility.prototype.selectAll = function() {
976 for(var d in this.dimensions)
977 this.dimensions[d].select();
980 dimensionsVisibility.prototype.countSelected = function() {
982 for(var d in this.dimensions)
983 if(this.dimensions[d].isSelected()) i++;
988 dimensionsVisibility.prototype.selectNone = function() {
989 for(var d in this.dimensions)
990 this.dimensions[d].unselect();
993 dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
994 var ret = new Array();
995 this.selected_count = 0;
996 this.unselected_count = 0;
998 for(var i = 0, len = array.length; i < len ; i++) {
999 var ds = this.dimensions[array[i]];
1000 if(typeof ds === 'undefined') {
1001 // console.log(array[i] + ' is not found');
1006 if(ds.isSelected()) {
1008 this.selected_count++;
1012 this.unselected_count++;
1016 if(this.selected_count === 0 && this.unselected_count !== 0) {
1018 return this.selected2BooleanArray(array);
1025 // ----------------------------------------------------------------------------------------------------------------
1026 // global selection sync
1028 NETDATA.globalSelectionSync = {
1030 dont_sync_before: 0,
1035 if(this.state !== null)
1036 this.state.globalSelectionSyncStop();
1040 if(this.state !== null) {
1041 this.state.globalSelectionSyncDelay();
1046 // ----------------------------------------------------------------------------------------------------------------
1047 // Our state object, where all per-chart values are stored
1049 chartState = function(element) {
1050 var self = $(element);
1051 this.element = element;
1054 // all private functions should use 'that', instead of 'this'
1057 /* error() - private
1058 * show an error instead of the chart
1060 var error = function(msg) {
1063 if(typeof netdataErrorCallback === 'function') {
1064 ret = netdataErrorCallback('chart', that.id, msg);
1068 that.element.innerHTML = that.id + ': ' + msg;
1069 that.enabled = false;
1070 that.current = that.pan;
1074 // GUID - a unique identifier for the chart
1075 this.uuid = NETDATA.guid();
1077 // string - the name of chart
1078 this.id = self.data('netdata');
1080 // string - the key for localStorage settings
1081 this.settings_id = self.data('id') || null;
1083 // the user given dimensions of the element
1084 this.width = self.data('width') || NETDATA.chartDefaults.width;
1085 this.height = self.data('height') || NETDATA.chartDefaults.height;
1087 if(this.settings_id !== null) {
1088 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
1089 // this is the callback that will be called
1090 // if and when the user resets all localStorage variables
1091 // to their defaults
1093 resizeChartToHeight(height);
1097 // string - the netdata server URL, without any path
1098 this.host = self.data('host') || NETDATA.chartDefaults.host;
1100 // make sure the host does not end with /
1101 // all netdata API requests use absolute paths
1102 while(this.host.slice(-1) === '/')
1103 this.host = this.host.substring(0, this.host.length - 1);
1105 // string - the grouping method requested by the user
1106 this.method = self.data('method') || NETDATA.chartDefaults.method;
1108 // the time-range requested by the user
1109 this.after = self.data('after') || NETDATA.chartDefaults.after;
1110 this.before = self.data('before') || NETDATA.chartDefaults.before;
1112 // the pixels per point requested by the user
1113 this.pixels_per_point = self.data('pixels-per-point') || 1;
1114 this.points = self.data('points') || null;
1116 // the dimensions requested by the user
1117 this.dimensions = self.data('dimensions') || null;
1119 // the chart library requested by the user
1120 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
1122 // object - the chart library used
1123 this.library = null;
1127 this.colors_assigned = {};
1128 this.colors_available = null;
1130 // the element already created by the user
1131 this.element_message = null;
1133 // the element with the chart
1134 this.element_chart = null;
1136 // the element with the legend of the chart (if created by us)
1137 this.element_legend = null;
1138 this.element_legend_childs = {
1148 this.chart_url = null; // string - the url to download chart info
1149 this.chart = null; // object - the chart as downloaded from the server
1151 this.title = self.data('title') || null; // the title of the chart
1152 this.units = self.data('units') || null; // the units of the chart dimensions
1153 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1155 this.running = false; // boolean - true when the chart is being refreshed now
1156 this.validated = false; // boolean - has the chart been validated?
1157 this.enabled = true; // boolean - is the chart enabled for refresh?
1158 this.paused = false; // boolean - is the chart paused for any reason?
1159 this.selected = false; // boolean - is the chart shown a selection?
1160 this.debug = false; // boolean - console.log() debug info about this chart
1162 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1163 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1164 this.requested_after = null; // milliseconds - the timestamp of the request after param
1165 this.requested_before = null; // milliseconds - the timestamp of the request before param
1166 this.requested_padding = null;
1167 this.view_after = 0;
1168 this.view_before = 0;
1173 force_update_at: 0, // the timestamp to force the update at
1174 force_before_ms: null,
1175 force_after_ms: null
1180 force_update_at: 0, // the timestamp to force the update at
1181 force_before_ms: null,
1182 force_after_ms: null
1187 force_update_at: 0, // the timestamp to force the update at
1188 force_before_ms: null,
1189 force_after_ms: null
1192 // this is a pointer to one of the sub-classes below
1194 this.current = this.auto;
1196 // check the requested library is available
1197 // we don't initialize it here - it will be initialized when
1198 // this chart will be first used
1199 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1200 NETDATA.error(402, that.library_name);
1201 error('chart library "' + that.library_name + '" is not found');
1204 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1205 NETDATA.error(403, that.library_name);
1206 error('chart library "' + that.library_name + '" is not enabled');
1210 that.library = NETDATA.chartLibraries[that.library_name];
1212 // milliseconds - the time the last refresh took
1213 this.refresh_dt_ms = 0;
1215 // if we need to report the rendering speed
1216 // find the element that needs to be updated
1217 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1219 if(refresh_dt_element_name !== null)
1220 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1222 this.refresh_dt_element = null;
1224 this.dimensions_visibility = new dimensionsVisibility(this);
1226 this._updating = false;
1228 // ============================================================================================================
1229 // PRIVATE FUNCTIONS
1231 var createDOM = function() {
1232 if(that.enabled === false) return;
1234 if(that.element_message !== null) that.element_message.innerHTML = '';
1235 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1236 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1238 that.element.innerHTML = '';
1240 that.element_message = document.createElement('div');
1241 that.element_message.className = ' netdata-message hidden';
1242 that.element.appendChild(that.element_message);
1244 that.element_chart = document.createElement('div');
1245 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1246 that.element.appendChild(that.element_chart);
1248 if(that.hasLegend() === true) {
1249 that.element.className = "netdata-container-with-legend";
1250 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1252 that.element_legend = document.createElement('div');
1253 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1254 that.element.appendChild(that.element_legend);
1257 that.element.className = "netdata-container";
1258 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1260 that.element_legend = null;
1262 that.element_legend_childs.series = null;
1264 if(typeof(that.width) === 'string')
1265 $(that.element).css('width', that.width);
1266 else if(typeof(that.width) === 'number')
1267 $(that.element).css('width', that.width + 'px');
1269 if(typeof(that.library.aspect_ratio) === 'undefined') {
1270 if(typeof(that.height) === 'string')
1271 $(that.element).css('height', that.height);
1272 else if(typeof(that.height) === 'number')
1273 $(that.element).css('height', that.height + 'px');
1276 var w = that.element.offsetWidth;
1277 if(w === null || w === 0) {
1278 // the div is hidden
1279 // this will resize the chart when next viewed
1280 that.tm.last_resized = 0;
1283 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1286 if(NETDATA.chartDefaults.min_width !== null)
1287 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1289 that.tm.last_dom_created = new Date().getTime();
1295 * initialize state variables
1296 * destroy all (possibly) created state elements
1297 * create the basic DOM for a chart
1299 var init = function() {
1300 if(that.enabled === false) return;
1302 that.paused = false;
1303 that.selected = false;
1305 that.chart_created = false; // boolean - is the library.create() been called?
1306 that.updates_counter = 0; // numeric - the number of refreshes made so far
1307 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1308 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1311 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1312 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1313 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1315 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1316 last_updated: 0, // the timestamp the chart last updated with data
1317 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1319 // Used with NETDATA.globalPanAndZoom.seq
1320 last_visible_check: 0, // the time we last checked if it is visible
1321 last_resized: 0, // the time the chart was resized
1322 last_hidden: 0, // the time the chart was hidden
1323 last_unhidden: 0, // the time the chart was unhidden
1324 last_autorefreshed: 0 // the time the chart was last refreshed
1327 that.data = null; // the last data as downloaded from the netdata server
1328 that.data_url = 'invalid://'; // string - the last url used to update the chart
1329 that.data_points = 0; // number - the number of points returned from netdata
1330 that.data_after = 0; // milliseconds - the first timestamp of the data
1331 that.data_before = 0; // milliseconds - the last timestamp of the data
1332 that.data_update_every = 0; // milliseconds - the frequency to update the data
1334 that.tm.last_initialized = new Date().getTime();
1337 that.setMode('auto');
1340 var maxMessageFontSize = function() {
1341 // normally we want a font size, as tall as the element
1342 var h = that.element_message.clientHeight;
1344 // but give it some air, 20% let's say, or 5 pixels min
1345 var lost = Math.max(h * 0.2, 5);
1348 // center the text, vertically
1349 var paddingTop = (lost - 5) / 2;
1351 // but check the width too
1352 // it should fit 10 characters in it
1353 var w = that.element_message.clientWidth / 10;
1355 paddingTop += (h - w) / 2;
1359 // and don't make it too huge
1360 // 5% of the screen size is good
1361 if(h > screen.height / 20) {
1362 paddingTop += (h - (screen.height / 20)) / 2;
1363 h = screen.height / 20;
1367 that.element_message.style.fontSize = h.toString() + 'px';
1368 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1371 var showMessage = function(msg) {
1372 that.element_message.className = 'netdata-message';
1373 that.element_message.innerHTML = msg;
1374 that.element_message.style.fontSize = 'x-small';
1375 that.element_message.style.paddingTop = '0px';
1376 that.___messageHidden___ = undefined;
1379 var showMessageIcon = function(icon) {
1380 that.element_message.innerHTML = icon;
1381 that.element_message.className = 'netdata-message icon';
1382 maxMessageFontSize();
1383 that.___messageHidden___ = undefined;
1386 var hideMessage = function() {
1387 if(typeof that.___messageHidden___ === 'undefined') {
1388 that.___messageHidden___ = true;
1389 that.element_message.className = 'netdata-message hidden';
1393 var showRendering = function() {
1395 if(that.chart !== null) {
1396 if(that.chart.chart_type === 'line')
1397 icon = '<i class="fa fa-line-chart"></i>';
1399 icon = '<i class="fa fa-area-chart"></i>';
1402 icon = '<i class="fa fa-area-chart"></i>';
1404 showMessageIcon(icon + ' netdata');
1407 var showLoading = function() {
1408 if(that.chart_created === false) {
1409 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1415 var isHidden = function() {
1416 if(typeof that.___chartIsHidden___ !== 'undefined')
1422 // hide the chart, when it is not visible - called from isVisible()
1423 var hideChart = function() {
1424 // hide it, if it is not already hidden
1425 if(isHidden() === true) return;
1427 if(that.chart_created === true) {
1428 if(NETDATA.options.current.destroy_on_hide === true) {
1429 // we should destroy it
1434 that.element_chart.style.display = 'none';
1435 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1436 that.tm.last_hidden = new Date().getTime();
1439 // This works, but I not sure there are no corner cases somewhere
1440 // so it is commented - if the user has memory issues he can
1441 // set Destroy on Hide for all charts
1442 // that.data = null;
1446 that.___chartIsHidden___ = true;
1449 // unhide the chart, when it is visible - called from isVisible()
1450 var unhideChart = function() {
1451 if(isHidden() === false) return;
1453 that.___chartIsHidden___ = undefined;
1454 that.updates_since_last_unhide = 0;
1456 if(that.chart_created === false) {
1457 // we need to re-initialize it, to show our background
1458 // logo in bootstrap tabs, until the chart loads
1462 that.tm.last_unhidden = new Date().getTime();
1463 that.element_chart.style.display = '';
1464 if(that.element_legend !== null) that.element_legend.style.display = '';
1470 var canBeRendered = function() {
1471 if(isHidden() === true || that.isVisible(true) === false)
1477 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1478 var callChartLibraryUpdateSafely = function(data) {
1481 if(canBeRendered() === false)
1484 if(NETDATA.options.debug.chart_errors === true)
1485 status = that.library.update(that, data);
1488 status = that.library.update(that, data);
1495 if(status === false) {
1496 error('chart failed to be updated as ' + that.library_name);
1503 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1504 var callChartLibraryCreateSafely = function(data) {
1507 if(canBeRendered() === false)
1510 if(NETDATA.options.debug.chart_errors === true)
1511 status = that.library.create(that, data);
1514 status = that.library.create(that, data);
1521 if(status === false) {
1522 error('chart failed to be created as ' + that.library_name);
1526 that.chart_created = true;
1527 that.updates_since_last_creation = 0;
1531 // ----------------------------------------------------------------------------------------------------------------
1534 // resizeChart() - private
1535 // to be called just before the chart library to make sure that
1536 // a properly sized dom is available
1537 var resizeChart = function() {
1538 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1539 if(that.chart_created === false) return;
1541 if(that.needsRecreation()) {
1544 else if(typeof that.library.resize === 'function') {
1545 that.library.resize(that);
1547 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1548 $(that.element_legend_childs.nano).nanoScroller();
1550 maxMessageFontSize();
1553 that.tm.last_resized = new Date().getTime();
1557 // this is the actual chart resize algorithm
1559 // - resize the entire container
1560 // - update the internal states
1561 // - resize the chart as the div changes height
1562 // - update the scrollbar of the legend
1563 var resizeChartToHeight = function(h) {
1565 that.element.style.height = h;
1567 if(that.settings_id !== null)
1568 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1570 var now = new Date().getTime();
1571 NETDATA.options.last_page_scroll = now;
1572 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1575 that.tm.last_resized = 0;
1579 this.resizeHandler = function(e) {
1582 if(typeof this.event_resize === 'undefined'
1583 || this.event_resize.chart_original_w === 'undefined'
1584 || this.event_resize.chart_original_h === 'undefined')
1585 this.event_resize = {
1586 chart_original_w: this.element.clientWidth,
1587 chart_original_h: this.element.clientHeight,
1591 if(e.type === 'touchstart') {
1592 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1593 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1596 this.event_resize.mouse_start_x = e.clientX;
1597 this.event_resize.mouse_start_y = e.clientY;
1600 this.event_resize.chart_start_w = this.element.clientWidth;
1601 this.event_resize.chart_start_h = this.element.clientHeight;
1602 this.event_resize.chart_last_w = this.element.clientWidth;
1603 this.event_resize.chart_last_h = this.element.clientHeight;
1605 var now = new Date().getTime();
1606 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1607 // double click / double tap event
1609 // the optimal height of the chart
1610 // showing the entire legend
1611 var optimal = this.event_resize.chart_last_h
1612 + this.element_legend_childs.content.scrollHeight
1613 - this.element_legend_childs.content.clientHeight;
1615 // if we are not optimal, be optimal
1616 if(this.event_resize.chart_last_h != optimal)
1617 resizeChartToHeight(optimal.toString() + 'px');
1619 // else if we do not have the original height
1620 // reset to the original height
1621 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1622 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1625 this.event_resize.last = now;
1627 // process movement event
1628 document.onmousemove =
1629 document.ontouchmove =
1630 this.element_legend_childs.resize_handler.onmousemove =
1631 this.element_legend_childs.resize_handler.ontouchmove =
1636 case 'mousemove': y = e.clientY; break;
1637 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1641 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1643 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1644 resizeChartToHeight(newH.toString() + 'px');
1645 that.event_resize.chart_last_h = newH;
1650 // process end event
1651 document.onmouseup =
1652 document.ontouchend =
1653 this.element_legend_childs.resize_handler.onmouseup =
1654 this.element_legend_childs.resize_handler.ontouchend =
1656 // remove all the hooks
1657 document.onmouseup =
1658 document.onmousemove =
1659 document.ontouchmove =
1660 document.ontouchend =
1661 that.element_legend_childs.resize_handler.onmousemove =
1662 that.element_legend_childs.resize_handler.ontouchmove =
1663 that.element_legend_childs.resize_handler.onmouseout =
1664 that.element_legend_childs.resize_handler.onmouseup =
1665 that.element_legend_childs.resize_handler.ontouchend =
1668 // allow auto-refreshes
1669 NETDATA.options.auto_refresher_stop_until = 0;
1675 var noDataToShow = function() {
1676 showMessageIcon('<i class="fa fa-warning"></i> empty');
1677 that.legendUpdateDOM();
1678 that.tm.last_autorefreshed = new Date().getTime();
1679 // that.data_update_every = 30 * 1000;
1680 //that.element_chart.style.display = 'none';
1681 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1682 //that.___chartIsHidden___ = true;
1685 // ============================================================================================================
1688 this.error = function(msg) {
1692 this.setMode = function(m) {
1693 if(this.current !== null && this.current.name === m) return;
1696 this.current = this.auto;
1697 else if(m === 'pan')
1698 this.current = this.pan;
1699 else if(m === 'zoom')
1700 this.current = this.zoom;
1702 this.current = this.auto;
1704 this.current.force_update_at = 0;
1705 this.current.force_before_ms = null;
1706 this.current.force_after_ms = null;
1708 this.tm.last_mode_switch = new Date().getTime();
1711 // ----------------------------------------------------------------------------------------------------------------
1712 // global selection sync
1714 // prevent to global selection sync for some time
1715 this.globalSelectionSyncDelay = function(ms) {
1716 if(NETDATA.options.current.sync_selection === false)
1719 if(typeof ms === 'number')
1720 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1722 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1725 // can we globally apply selection sync?
1726 this.globalSelectionSyncAbility = function() {
1727 if(NETDATA.options.current.sync_selection === false)
1730 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1736 this.globalSelectionSyncIsMaster = function() {
1737 if(NETDATA.globalSelectionSync.state === this)
1743 // this chart is the master of the global selection sync
1744 this.globalSelectionSyncBeMaster = function() {
1746 if(this.globalSelectionSyncIsMaster()) {
1747 if(this.debug === true)
1748 this.log('sync: I am the master already.');
1753 if(NETDATA.globalSelectionSync.state) {
1754 if(this.debug === true)
1755 this.log('sync: I am not the sync master. Resetting global sync.');
1757 this.globalSelectionSyncStop();
1760 // become the master
1761 if(this.debug === true)
1762 this.log('sync: becoming sync master.');
1764 this.selected = true;
1765 NETDATA.globalSelectionSync.state = this;
1767 // find the all slaves
1768 var targets = NETDATA.options.targets;
1769 var len = targets.length;
1774 if(this.debug === true)
1775 st.log('sync: not adding me to sync');
1777 else if(st.globalSelectionSyncIsEligible()) {
1778 if(this.debug === true)
1779 st.log('sync: adding to sync as slave');
1781 st.globalSelectionSyncBeSlave();
1785 // this.globalSelectionSyncDelay(100);
1788 // can the chart participate to the global selection sync as a slave?
1789 this.globalSelectionSyncIsEligible = function() {
1790 if(this.enabled === true
1791 && this.library !== null
1792 && typeof this.library.setSelection === 'function'
1793 && this.isVisible() === true
1794 && this.chart_created === true)
1800 // this chart becomes a slave of the global selection sync
1801 this.globalSelectionSyncBeSlave = function() {
1802 if(NETDATA.globalSelectionSync.state !== this)
1803 NETDATA.globalSelectionSync.slaves.push(this);
1806 // sync all the visible charts to the given time
1807 // this is to be called from the chart libraries
1808 this.globalSelectionSync = function(t) {
1809 if(this.globalSelectionSyncAbility() === false) {
1810 if(this.debug === true)
1811 this.log('sync: cannot sync (yet?).');
1816 if(this.globalSelectionSyncIsMaster() === false) {
1817 if(this.debug === true)
1818 this.log('sync: trying to be sync master.');
1820 this.globalSelectionSyncBeMaster();
1822 if(this.globalSelectionSyncAbility() === false) {
1823 if(this.debug === true)
1824 this.log('sync: cannot sync (yet?).');
1830 NETDATA.globalSelectionSync.last_t = t;
1831 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1836 // stop syncing all charts to the given time
1837 this.globalSelectionSyncStop = function() {
1838 if(NETDATA.globalSelectionSync.slaves.length) {
1839 if(this.debug === true)
1840 this.log('sync: cleaning up...');
1842 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1844 if(that.debug === true)
1845 st.log('sync: not adding me to sync stop');
1848 if(that.debug === true)
1849 st.log('sync: removed slave from sync');
1851 st.clearSelection();
1855 NETDATA.globalSelectionSync.last_t = 0;
1856 NETDATA.globalSelectionSync.slaves = [];
1857 NETDATA.globalSelectionSync.state = null;
1860 this.clearSelection();
1863 this.setSelection = function(t) {
1864 if(typeof this.library.setSelection === 'function') {
1865 if(this.library.setSelection(this, t) === true)
1866 this.selected = true;
1868 this.selected = false;
1870 else this.selected = true;
1872 if(this.selected === true && this.debug === true)
1873 this.log('selection set to ' + t.toString());
1875 return this.selected;
1878 this.clearSelection = function() {
1879 if(this.selected === true) {
1880 if(typeof this.library.clearSelection === 'function') {
1881 if(this.library.clearSelection(this) === true)
1882 this.selected = false;
1884 this.selected = true;
1886 else this.selected = false;
1888 if(this.selected === false && this.debug === true)
1889 this.log('selection cleared');
1894 return this.selected;
1897 // find if a timestamp (ms) is shown in the current chart
1898 this.timeIsVisible = function(t) {
1899 if(t >= this.data_after && t <= this.data_before)
1904 this.calculateRowForTime = function(t) {
1905 if(this.timeIsVisible(t) === false) return -1;
1906 return Math.floor((t - this.data_after) / this.data_update_every);
1909 // ----------------------------------------------------------------------------------------------------------------
1912 this.log = function(msg) {
1913 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1916 this.pauseChart = function() {
1917 if(this.paused === false) {
1918 if(this.debug === true)
1919 this.log('pauseChart()');
1925 this.unpauseChart = function() {
1926 if(this.paused === true) {
1927 if(this.debug === true)
1928 this.log('unpauseChart()');
1930 this.paused = false;
1934 this.resetChart = function(dont_clear_master, dont_update) {
1935 if(this.debug === true)
1936 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1938 if(typeof dont_clear_master === 'undefined')
1939 dont_clear_master = false;
1941 if(typeof dont_update === 'undefined')
1942 dont_update = false;
1944 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1945 if(this.debug === true)
1946 this.log('resetChart() diverting to clearMaster().');
1947 // this will call us back with master === true
1948 NETDATA.globalPanAndZoom.clearMaster();
1952 this.clearSelection();
1954 this.tm.pan_and_zoom_seq = 0;
1956 this.setMode('auto');
1957 this.current.force_update_at = 0;
1958 this.current.force_before_ms = null;
1959 this.current.force_after_ms = null;
1960 this.tm.last_autorefreshed = 0;
1961 this.paused = false;
1962 this.selected = false;
1963 this.enabled = true;
1964 // this.debug = false;
1966 // do not update the chart here
1967 // or the chart will flip-flop when it is the master
1968 // of a selection sync and another chart becomes
1971 if(dont_update !== true && this.isVisible() === true) {
1976 this.updateChartPanOrZoom = function(after, before) {
1977 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1980 if(this.debug === true)
1983 if(before < after) {
1984 if(this.debug === true)
1985 this.log(logme + 'flipped parameters, rejecting it.');
1990 if(typeof this.fixed_min_duration === 'undefined')
1991 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1993 var min_duration = this.fixed_min_duration;
1994 var current_duration = Math.round(this.view_before - this.view_after);
1996 // round the numbers
1997 after = Math.round(after);
1998 before = Math.round(before);
2000 // align them to update_every
2001 // stretching them further away
2002 after -= after % this.data_update_every;
2003 before += this.data_update_every - (before % this.data_update_every);
2005 // the final wanted duration
2006 var wanted_duration = before - after;
2008 // to allow panning, accept just a point below our minimum
2009 if((current_duration - this.data_update_every) < min_duration)
2010 min_duration = current_duration - this.data_update_every;
2012 // we do it, but we adjust to minimum size and return false
2013 // when the wanted size is below the current and the minimum
2015 if(wanted_duration < current_duration && wanted_duration < min_duration) {
2016 if(this.debug === true)
2017 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
2019 min_duration = this.fixed_min_duration;
2021 var dt = (min_duration - wanted_duration) / 2;
2024 wanted_duration = before - after;
2028 var tolerance = this.data_update_every * 2;
2029 var movement = Math.abs(before - this.view_before);
2031 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
2032 if(this.debug === true)
2033 this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
2037 if(this.current.name === 'auto') {
2038 this.log(logme + 'caller called me with mode: ' + this.current.name);
2039 this.setMode('pan');
2042 if(this.debug === true)
2043 this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
2045 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
2046 this.current.force_after_ms = after;
2047 this.current.force_before_ms = before;
2048 NETDATA.globalPanAndZoom.setMaster(this, after, before);
2052 this.legendFormatValue = function(value) {
2053 if(value === null || value === 'undefined') return '-';
2054 if(typeof value !== 'number') return value;
2056 var abs = Math.abs(value);
2057 if(abs >= 1000) return (Math.round(value)).toLocaleString();
2058 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
2059 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
2060 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
2061 return (Math.round(value * 10000) / 10000).toLocaleString();
2064 this.legendSetLabelValue = function(label, value) {
2065 var series = this.element_legend_childs.series[label];
2066 if(typeof series === 'undefined') return;
2067 if(series.value === null && series.user === null) return;
2069 // if the value has not changed, skip DOM update
2070 //if(series.last === value) return;
2073 if(typeof value === 'number') {
2074 var v = Math.abs(value);
2075 s = r = this.legendFormatValue(value);
2077 if(typeof series.last === 'number') {
2078 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2079 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2080 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2082 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2087 series.last = value;
2090 if(series.value !== null) series.value.innerHTML = s;
2091 if(series.user !== null) series.user.innerHTML = r;
2094 this.legendSetDate = function(ms) {
2095 if(typeof ms !== 'number') {
2096 this.legendShowUndefined();
2100 var d = new Date(ms);
2102 if(this.element_legend_childs.title_date)
2103 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
2105 if(this.element_legend_childs.title_time)
2106 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
2108 if(this.element_legend_childs.title_units)
2109 this.element_legend_childs.title_units.innerHTML = this.units;
2112 this.legendShowUndefined = function() {
2113 if(this.element_legend_childs.title_date)
2114 this.element_legend_childs.title_date.innerHTML = ' ';
2116 if(this.element_legend_childs.title_time)
2117 this.element_legend_childs.title_time.innerHTML = this.chart.name;
2119 if(this.element_legend_childs.title_units)
2120 this.element_legend_childs.title_units.innerHTML = ' ';
2122 if(this.data && this.element_legend_childs.series !== null) {
2123 var labels = this.data.dimension_names;
2124 var i = labels.length;
2126 var label = labels[i];
2128 if(typeof label === 'undefined') continue;
2129 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2130 this.legendSetLabelValue(label, null);
2135 this.legendShowLatestValues = function() {
2136 if(this.chart === null) return;
2137 if(this.selected) return;
2139 if(this.data === null || this.element_legend_childs.series === null) {
2140 this.legendShowUndefined();
2144 var show_undefined = true;
2145 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
2146 show_undefined = false;
2148 if(show_undefined) {
2149 this.legendShowUndefined();
2153 this.legendSetDate(this.view_before);
2155 var labels = this.data.dimension_names;
2156 var i = labels.length;
2158 var label = labels[i];
2160 if(typeof label === 'undefined') continue;
2161 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2164 this.legendSetLabelValue(label, null);
2166 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2170 this.legendReset = function() {
2171 this.legendShowLatestValues();
2174 // this should be called just ONCE per dimension per chart
2175 this._chartDimensionColor = function(label) {
2176 if(this.colors === null) this.chartColors();
2178 if(typeof this.colors_assigned[label] === 'undefined') {
2179 if(this.colors_available.length === 0) {
2180 for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2181 this.colors_available.push(NETDATA.themes.current.colors[i]);
2184 this.colors_assigned[label] = this.colors_available.shift();
2186 if(this.debug === true)
2187 this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
2190 if(this.debug === true)
2191 this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
2194 this.colors.push(this.colors_assigned[label]);
2195 return this.colors_assigned[label];
2198 this.chartColors = function() {
2199 if(this.colors !== null) return this.colors;
2201 this.colors = new Array();
2202 this.colors_available = new Array();
2205 var c = $(this.element).data('colors');
2206 // this.log('read colors: ' + c);
2207 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2208 if(typeof c !== 'string') {
2209 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2216 for(i = 0, len = c.length; i < len ; i++) {
2218 this.colors_available.push(c[i]);
2219 // this.log('adding color: ' + c[i]);
2225 // push all the standard colors too
2226 for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2227 this.colors_available.push(NETDATA.themes.current.colors[i]);
2232 this.legendUpdateDOM = function() {
2235 // check that the legend DOM is up to date for the downloaded dimensions
2236 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2237 // this.log('the legend does not have any series - requesting legend update');
2240 else if(this.data === null) {
2241 // this.log('the chart does not have any data - requesting legend update');
2244 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2248 var labels = this.data.dimension_names.toString();
2249 if(labels !== this.element_legend_childs.series.labels_key) {
2252 if(this.debug === true)
2253 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2257 if(needed === false) {
2258 // make sure colors available
2261 // do we have to update the current values?
2262 // we do this, only when the visible chart is current
2263 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2264 if(this.debug === true)
2265 this.log('chart is in latest position... updating values on legend...');
2267 //var labels = this.data.dimension_names;
2268 //var i = labels.length;
2270 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2274 if(this.colors === null) {
2275 // this is the first time we update the chart
2276 // let's assign colors to all dimensions
2277 if(this.library.track_colors() === true)
2278 for(var dim in this.chart.dimensions)
2279 this._chartDimensionColor(this.chart.dimensions[dim].name);
2281 // we will re-generate the colors for the chart
2282 // based on the selected dimensions
2285 if(this.debug === true)
2286 this.log('updating Legend DOM');
2288 // mark all dimensions as invalid
2289 this.dimensions_visibility.invalidateAll();
2291 var genLabel = function(state, parent, dim, name, count) {
2292 var color = state._chartDimensionColor(name);
2294 var user_element = null;
2295 var user_id = self.data('show-value-of-' + dim + '-at') || null;
2296 if(user_id !== null) {
2297 user_element = document.getElementById(user_id) || null;
2298 if(user_element === null)
2299 state.log('Cannot find element with id: ' + user_id);
2302 state.element_legend_childs.series[name] = {
2303 name: document.createElement('span'),
2304 value: document.createElement('span'),
2309 var label = state.element_legend_childs.series[name];
2311 // create the dimension visibility tracking for this label
2312 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2314 var rgb = NETDATA.colorHex2Rgb(color);
2315 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2316 + state.chart.chart_type
2317 + '" style="background-color: '
2318 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2319 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2321 var text = document.createTextNode(' ' + name);
2322 label.name.appendChild(text);
2325 parent.appendChild(document.createElement('br'));
2327 parent.appendChild(label.name);
2328 parent.appendChild(label.value);
2331 var content = document.createElement('div');
2333 if(this.hasLegend()) {
2334 this.element_legend_childs = {
2336 resize_handler: document.createElement('div'),
2337 toolbox: document.createElement('div'),
2338 toolbox_left: document.createElement('div'),
2339 toolbox_right: document.createElement('div'),
2340 toolbox_reset: document.createElement('div'),
2341 toolbox_zoomin: document.createElement('div'),
2342 toolbox_zoomout: document.createElement('div'),
2343 toolbox_volume: document.createElement('div'),
2344 title_date: document.createElement('span'),
2345 title_time: document.createElement('span'),
2346 title_units: document.createElement('span'),
2347 nano: document.createElement('div'),
2349 paneClass: 'netdata-legend-series-pane',
2350 sliderClass: 'netdata-legend-series-slider',
2351 contentClass: 'netdata-legend-series-content',
2352 enabledClass: '__enabled',
2353 flashedClass: '__flashed',
2354 activeClass: '__active',
2356 alwaysVisible: true,
2362 this.element_legend.innerHTML = '';
2364 if(this.library.toolboxPanAndZoom !== null) {
2366 function get_pan_and_zoom_step(event) {
2368 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2370 else if (event.shiftKey)
2371 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2373 else if (event.altKey)
2374 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2377 return NETDATA.options.current.pan_and_zoom_factor;
2380 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2381 this.element.appendChild(this.element_legend_childs.toolbox);
2383 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2384 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2385 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2386 this.element_legend_childs.toolbox_left.onclick = function(e) {
2389 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2390 var before = that.view_before - step;
2391 var after = that.view_after - step;
2392 if(after >= that.netdata_first)
2393 that.library.toolboxPanAndZoom(that, after, before);
2395 if(NETDATA.options.current.show_help === true)
2396 $(this.element_legend_childs.toolbox_left).popover({
2401 placement: 'bottom',
2402 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2404 content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2408 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2409 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2410 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2411 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2413 NETDATA.resetAllCharts(that);
2415 if(NETDATA.options.current.show_help === true)
2416 $(this.element_legend_childs.toolbox_reset).popover({
2421 placement: 'bottom',
2422 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2423 title: 'Chart Reset',
2424 content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2427 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2428 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2429 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2430 this.element_legend_childs.toolbox_right.onclick = function(e) {
2432 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2433 var before = that.view_before + step;
2434 var after = that.view_after + step;
2435 if(before <= that.netdata_last)
2436 that.library.toolboxPanAndZoom(that, after, before);
2438 if(NETDATA.options.current.show_help === true)
2439 $(this.element_legend_childs.toolbox_right).popover({
2444 placement: 'bottom',
2445 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2447 content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2451 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2452 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2453 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2454 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2456 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2457 var before = that.view_before - dt;
2458 var after = that.view_after + dt;
2459 that.library.toolboxPanAndZoom(that, after, before);
2461 if(NETDATA.options.current.show_help === true)
2462 $(this.element_legend_childs.toolbox_zoomin).popover({
2467 placement: 'bottom',
2468 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2469 title: 'Chart Zoom In',
2470 content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2473 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2474 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2475 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2476 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2478 var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
2479 var before = that.view_before + dt;
2480 var after = that.view_after - dt;
2482 that.library.toolboxPanAndZoom(that, after, before);
2484 if(NETDATA.options.current.show_help === true)
2485 $(this.element_legend_childs.toolbox_zoomout).popover({
2490 placement: 'bottom',
2491 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2492 title: 'Chart Zoom Out',
2493 content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2496 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2497 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2498 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2499 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2500 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2501 //e.preventDefault();
2502 //alert('clicked toolbox_volume on ' + that.id);
2506 this.element_legend_childs.toolbox = null;
2507 this.element_legend_childs.toolbox_left = null;
2508 this.element_legend_childs.toolbox_reset = null;
2509 this.element_legend_childs.toolbox_right = null;
2510 this.element_legend_childs.toolbox_zoomin = null;
2511 this.element_legend_childs.toolbox_zoomout = null;
2512 this.element_legend_childs.toolbox_volume = null;
2515 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2516 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2517 this.element.appendChild(this.element_legend_childs.resize_handler);
2518 if(NETDATA.options.current.show_help === true)
2519 $(this.element_legend_childs.resize_handler).popover({
2524 placement: 'bottom',
2525 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2526 title: 'Chart Resize',
2527 content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
2531 this.element_legend_childs.resize_handler.onmousedown =
2533 that.resizeHandler(e);
2537 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2538 that.resizeHandler(e);
2541 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2542 this.element_legend.appendChild(this.element_legend_childs.title_date);
2544 this.element_legend.appendChild(document.createElement('br'));
2546 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2547 this.element_legend.appendChild(this.element_legend_childs.title_time);
2549 this.element_legend.appendChild(document.createElement('br'));
2551 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2552 this.element_legend.appendChild(this.element_legend_childs.title_units);
2554 this.element_legend.appendChild(document.createElement('br'));
2556 this.element_legend_childs.nano.className = 'netdata-legend-series';
2557 this.element_legend.appendChild(this.element_legend_childs.nano);
2559 content.className = 'netdata-legend-series-content';
2560 this.element_legend_childs.nano.appendChild(content);
2562 if(NETDATA.options.current.show_help === true)
2563 $(content).popover({
2568 placement: 'bottom',
2569 title: 'Chart Legend',
2570 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2571 content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
2575 this.element_legend_childs = {
2577 resize_handler: null,
2580 toolbox_right: null,
2581 toolbox_reset: null,
2582 toolbox_zoomin: null,
2583 toolbox_zoomout: null,
2584 toolbox_volume: null,
2595 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2596 if(this.debug === true)
2597 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2599 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2600 genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
2604 var tmp = new Array();
2605 for(var dim in this.chart.dimensions) {
2606 tmp.push(this.chart.dimensions[dim].name);
2607 genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
2609 this.element_legend_childs.series.labels_key = tmp.toString();
2610 if(this.debug === true)
2611 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2614 // create a hidden div to be used for hidding
2615 // the original legend of the chart library
2616 var el = document.createElement('div');
2617 if(this.element_legend !== null)
2618 this.element_legend.appendChild(el);
2619 el.style.display = 'none';
2621 this.element_legend_childs.hidden = document.createElement('div');
2622 el.appendChild(this.element_legend_childs.hidden);
2624 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2625 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2627 this.legendShowLatestValues();
2630 this.hasLegend = function() {
2631 if(typeof this.___hasLegendCache___ !== 'undefined')
2632 return this.___hasLegendCache___;
2635 if(this.library && this.library.legend(this) === 'right-side') {
2636 var legend = $(this.element).data('legend') || 'yes';
2637 if(legend === 'yes') leg = true;
2640 this.___hasLegendCache___ = leg;
2644 this.legendWidth = function() {
2645 return (this.hasLegend())?140:0;
2648 this.legendHeight = function() {
2649 return $(this.element).height();
2652 this.chartWidth = function() {
2653 return $(this.element).width() - this.legendWidth();
2656 this.chartHeight = function() {
2657 return $(this.element).height();
2660 this.chartPixelsPerPoint = function() {
2661 // force an options provided detail
2662 var px = this.pixels_per_point;
2664 if(this.library && px < this.library.pixels_per_point(this))
2665 px = this.library.pixels_per_point(this);
2667 if(px < NETDATA.options.current.pixels_per_point)
2668 px = NETDATA.options.current.pixels_per_point;
2673 this.needsRecreation = function() {
2675 this.chart_created === true
2677 && this.library.autoresize() === false
2678 && this.tm.last_resized < NETDATA.options.last_resized
2682 this.chartURL = function() {
2683 var after, before, points_multiplier = 1;
2684 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2685 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2687 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2688 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2689 this.view_after = after * 1000;
2690 this.view_before = before * 1000;
2692 this.requested_padding = null;
2693 points_multiplier = 1;
2695 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2696 this.tm.pan_and_zoom_seq = 0;
2698 before = Math.round(this.current.force_before_ms / 1000);
2699 after = Math.round(this.current.force_after_ms / 1000);
2700 this.view_after = after * 1000;
2701 this.view_before = before * 1000;
2703 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2704 this.requested_padding = Math.round((before - after) / 2);
2705 after -= this.requested_padding;
2706 before += this.requested_padding;
2707 this.requested_padding *= 1000;
2708 points_multiplier = 2;
2711 this.current.force_before_ms = null;
2712 this.current.force_after_ms = null;
2715 this.tm.pan_and_zoom_seq = 0;
2717 before = this.before;
2719 this.view_after = after * 1000;
2720 this.view_before = before * 1000;
2722 this.requested_padding = null;
2723 points_multiplier = 1;
2726 this.requested_after = after * 1000;
2727 this.requested_before = before * 1000;
2729 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2731 // build the data URL
2732 this.data_url = this.host + this.chart.data_url;
2733 this.data_url += "&format=" + this.library.format();
2734 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2735 this.data_url += "&group=" + this.method;
2736 this.data_url += "&options=" + this.library.options(this);
2737 this.data_url += '|jsonwrap';
2739 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2740 this.data_url += '|nonzero';
2742 if(this.append_options !== null)
2743 this.data_url += '|' + this.append_options.toString();
2746 this.data_url += "&after=" + after.toString();
2749 this.data_url += "&before=" + before.toString();
2752 this.data_url += "&dimensions=" + this.dimensions;
2754 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2755 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2758 this.redrawChart = function() {
2759 if(this.data !== null)
2760 this.updateChartWithData(this.data);
2763 this.updateChartWithData = function(data) {
2764 if(this.debug === true)
2765 this.log('updateChartWithData() called.');
2767 // this may force the chart to be re-created
2771 this.updates_counter++;
2772 this.updates_since_last_unhide++;
2773 this.updates_since_last_creation++;
2775 var started = new Date().getTime();
2777 // if the result is JSON, find the latest update-every
2778 this.data_update_every = data.view_update_every * 1000;
2779 this.data_after = data.after * 1000;
2780 this.data_before = data.before * 1000;
2781 this.netdata_first = data.first_entry * 1000;
2782 this.netdata_last = data.last_entry * 1000;
2783 this.data_points = data.points;
2786 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2787 if(this.view_after < this.data_after) {
2788 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2789 this.view_after = this.data_after;
2792 if(this.view_before > this.data_before) {
2793 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2794 this.view_before = this.data_before;
2798 this.view_after = this.data_after;
2799 this.view_before = this.data_before;
2802 if(this.debug === true) {
2803 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2805 if(this.current.force_after_ms)
2806 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2808 this.log('STATUS: forced : unset');
2810 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2811 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2812 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2813 this.log('STATUS: points : ' + (this.data_points).toString());
2816 if(this.data_points === 0) {
2821 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2822 if(this.debug === true)
2823 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2825 this.chart_created = false;
2828 // check and update the legend
2829 this.legendUpdateDOM();
2831 if(this.chart_created === true
2832 && typeof this.library.update === 'function') {
2834 if(this.debug === true)
2835 this.log('updating chart...');
2837 if(callChartLibraryUpdateSafely(data) === false)
2841 if(this.debug === true)
2842 this.log('creating chart...');
2844 if(callChartLibraryCreateSafely(data) === false)
2848 this.legendShowLatestValues();
2849 if(this.selected === true)
2850 NETDATA.globalSelectionSync.stop();
2852 // update the performance counters
2853 var now = new Date().getTime();
2854 this.tm.last_updated = now;
2856 // don't update last_autorefreshed if this chart is
2857 // forced to be updated with global PanAndZoom
2858 if(NETDATA.globalPanAndZoom.isActive())
2859 this.tm.last_autorefreshed = 0;
2861 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2862 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2864 this.tm.last_autorefreshed = now;
2867 this.refresh_dt_ms = now - started;
2868 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2870 if(this.refresh_dt_element !== null)
2871 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2874 this.updateChart = function(callback) {
2875 if(this.debug === true)
2876 this.log('updateChart() called.');
2878 if(this._updating === true) {
2879 if(this.debug === true)
2880 this.log('I am already updating...');
2882 if(typeof callback === 'function') callback();
2886 // due to late initialization of charts and libraries
2887 // we need to check this too
2888 if(this.enabled === false) {
2889 if(this.debug === true)
2890 this.log('I am not enabled');
2892 if(typeof callback === 'function') callback();
2896 if(canBeRendered() === false) {
2897 if(typeof callback === 'function') callback();
2901 if(this.chart === null) {
2902 this.getChart(function() { that.updateChart(callback); });
2906 if(this.library.initialized === false) {
2907 if(this.library.enabled === true) {
2908 this.library.initialize(function() { that.updateChart(callback); });
2912 error('chart library "' + this.library_name + '" is not available.');
2913 if(typeof callback === 'function') callback();
2918 this.clearSelection();
2921 if(this.debug === true)
2922 this.log('updating from ' + this.data_url);
2924 NETDATA.statistics.refreshes_total++;
2925 NETDATA.statistics.refreshes_active++;
2927 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2928 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2930 this._updating = true;
2932 this.xhr = $.ajax( {
2936 xhrFields: { withCredentials: true } // required for the cookie
2938 .done(function(data) {
2939 that.xhr = undefined;
2941 if(that.debug === true)
2942 that.log('data received. updating chart.');
2944 that.updateChartWithData(data);
2946 .fail(function(msg) {
2947 that.xhr = undefined;
2949 if(msg.statusText !== 'abort')
2950 error('data download failed for url: ' + that.data_url);
2952 .always(function() {
2953 that.xhr = undefined;
2955 NETDATA.statistics.refreshes_active--;
2956 that._updating = false;
2957 if(typeof callback === 'function') callback();
2963 this.isVisible = function(nocache) {
2964 if(typeof nocache === 'undefined')
2967 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2969 // caching - we do not evaluate the charts visibility
2970 // if the page has not been scrolled since the last check
2971 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2972 return this.___isVisible___;
2974 this.tm.last_visible_check = new Date().getTime();
2976 var wh = window.innerHeight;
2977 var x = this.element.getBoundingClientRect();
2981 if(x.width === 0 || x.height === 0) {
2983 this.___isVisible___ = false;
2984 return this.___isVisible___;
2987 if(x.top < 0 && -x.top > x.height) {
2988 // the chart is entirely above
2989 ret = -x.top - x.height;
2991 else if(x.top > wh) {
2992 // the chart is entirely below
2996 if(ret > tolerance) {
2997 // the chart is too far
3000 this.___isVisible___ = false;
3001 return this.___isVisible___;
3004 // the chart is inside or very close
3007 this.___isVisible___ = true;
3008 return this.___isVisible___;
3012 this.isAutoRefreshable = function() {
3013 return (this.current.autorefresh);
3016 this.canBeAutoRefreshed = function() {
3017 var now = new Date().getTime();
3019 if(this.running === true) {
3020 if(this.debug === true)
3021 this.log('I am already running');
3026 if(this.enabled === false) {
3027 if(this.debug === true)
3028 this.log('I am not enabled');
3033 if(this.library === null || this.library.enabled === false) {
3034 error('charting library "' + this.library_name + '" is not available');
3035 if(this.debug === true)
3036 this.log('My chart library ' + this.library_name + ' is not available');
3041 if(this.isVisible() === false) {
3042 if(NETDATA.options.debug.visibility === true || this.debug === true)
3043 this.log('I am not visible');
3048 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
3049 if(this.debug === true)
3050 this.log('timed force update detected - allowing this update');
3052 this.current.force_update_at = 0;
3056 if(this.isAutoRefreshable() === true) {
3057 // allow the first update, even if the page is not visible
3058 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
3059 if(NETDATA.options.debug.focus === true || this.debug === true)
3060 this.log('canBeAutoRefreshed(): page does not have focus');
3065 if(this.needsRecreation() === true) {
3066 if(this.debug === true)
3067 this.log('canBeAutoRefreshed(): needs re-creation.');
3072 // options valid only for autoRefresh()
3073 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
3074 if(NETDATA.globalPanAndZoom.isActive()) {
3075 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
3076 if(this.debug === true)
3077 this.log('canBeAutoRefreshed(): global panning: I need an update.');
3082 if(this.debug === true)
3083 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
3089 if(this.selected === true) {
3090 if(this.debug === true)
3091 this.log('canBeAutoRefreshed(): I have a selection in place.');
3096 if(this.paused === true) {
3097 if(this.debug === true)
3098 this.log('canBeAutoRefreshed(): I am paused.');
3103 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
3104 if(this.debug === true)
3105 this.log('canBeAutoRefreshed(): It is time to update me.');
3115 this.autoRefresh = function(callback) {
3116 if(this.canBeAutoRefreshed() === true && this.running === false) {
3119 state.running = true;
3120 state.updateChart(function() {
3121 state.running = false;
3123 if(typeof callback !== 'undefined')
3128 if(typeof callback !== 'undefined')
3133 this._defaultsFromDownloadedChart = function(chart) {
3135 this.chart_url = chart.url;
3136 this.data_update_every = chart.update_every * 1000;
3137 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
3138 this.tm.last_info_downloaded = new Date().getTime();
3140 if(this.title === null)
3141 this.title = chart.title;
3143 if(this.units === null)
3144 this.units = chart.units;
3147 // fetch the chart description from the netdata server
3148 this.getChart = function(callback) {
3149 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
3151 this._defaultsFromDownloadedChart(this.chart);
3152 if(typeof callback === 'function') callback();
3155 this.chart_url = "/api/v1/chart?chart=" + this.id;
3157 if(this.debug === true)
3158 this.log('downloading ' + this.chart_url);
3161 url: this.host + this.chart_url,
3164 xhrFields: { withCredentials: true } // required for the cookie
3166 .done(function(chart) {
3167 chart.url = that.chart_url;
3168 that._defaultsFromDownloadedChart(chart);
3169 NETDATA.chartRegistry.add(that.host, that.id, chart);
3172 NETDATA.error(404, that.chart_url);
3173 error('chart not found on url "' + that.chart_url + '"');
3175 .always(function() {
3176 if(typeof callback === 'function') callback();
3181 // ============================================================================================================
3187 NETDATA.resetAllCharts = function(state) {
3188 // first clear the global selection sync
3189 // to make sure no chart is in selected state
3190 state.globalSelectionSyncStop();
3192 // there are 2 possibilities here
3193 // a. state is the global Pan and Zoom master
3194 // b. state is not the global Pan and Zoom master
3196 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3199 // clear the global Pan and Zoom
3200 // this will also refresh the master
3201 // and unblock any charts currently mirroring the master
3202 NETDATA.globalPanAndZoom.clearMaster();
3204 // if we were not the master, reset our status too
3205 // this is required because most probably the mouse
3206 // is over this chart, blocking it from auto-refreshing
3207 if(master === false && (state.paused === true || state.selected === true))
3211 // get or create a chart state, given a DOM element
3212 NETDATA.chartState = function(element) {
3213 var state = $(element).data('netdata-state-object') || null;
3214 if(state === null) {
3215 state = new chartState(element);
3216 $(element).data('netdata-state-object', state);
3221 // ----------------------------------------------------------------------------------------------------------------
3222 // Library functions
3224 // Load a script without jquery
3225 // This is used to load jquery - after it is loaded, we use jquery
3226 NETDATA._loadjQuery = function(callback) {
3227 if(typeof jQuery === 'undefined') {
3228 if(NETDATA.options.debug.main_loop === true)
3229 console.log('loading ' + NETDATA.jQuery);
3231 var script = document.createElement('script');
3232 script.type = 'text/javascript';
3233 script.async = true;
3234 script.src = NETDATA.jQuery;
3236 // script.onabort = onError;
3237 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3238 if(typeof callback === "function")
3239 script.onload = callback;
3241 var s = document.getElementsByTagName('script')[0];
3242 s.parentNode.insertBefore(script, s);
3244 else if(typeof callback === "function")
3248 NETDATA._loadCSS = function(filename) {
3249 // don't use jQuery here
3250 // styles are loaded before jQuery
3251 // to eliminate showing an unstyled page to the user
3253 var fileref = document.createElement("link");
3254 fileref.setAttribute("rel", "stylesheet");
3255 fileref.setAttribute("type", "text/css");
3256 fileref.setAttribute("href", filename);
3258 if (typeof fileref !== 'undefined')
3259 document.getElementsByTagName("head")[0].appendChild(fileref);
3262 NETDATA.colorHex2Rgb = function(hex) {
3263 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3264 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3265 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3266 return r + r + g + g + b + b;
3269 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3271 r: parseInt(result[1], 16),
3272 g: parseInt(result[2], 16),
3273 b: parseInt(result[3], 16)
3277 NETDATA.colorLuminance = function(hex, lum) {
3278 // validate hex string
3279 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3281 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3285 // convert to decimal and change luminosity
3286 var rgb = "#", c, i;
3287 for (i = 0; i < 3; i++) {
3288 c = parseInt(hex.substr(i*2,2), 16);
3289 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3290 rgb += ("00"+c).substr(c.length);
3296 NETDATA.guid = function() {
3298 return Math.floor((1 + Math.random()) * 0x10000)
3303 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3306 NETDATA.zeropad = function(x) {
3307 if(x > -10 && x < 10) return '0' + x.toString();
3308 else return x.toString();
3311 // user function to signal us the DOM has been
3313 NETDATA.updatedDom = function() {
3314 NETDATA.options.updated_dom = true;
3317 NETDATA.ready = function(callback) {
3318 NETDATA.options.pauseCallback = callback;
3321 NETDATA.pause = function(callback) {
3322 if(NETDATA.options.pause === true)
3325 NETDATA.options.pauseCallback = callback;
3328 NETDATA.unpause = function() {
3329 NETDATA.options.pauseCallback = null;
3330 NETDATA.options.updated_dom = true;
3331 NETDATA.options.pause = false;
3334 // ----------------------------------------------------------------------------------------------------------------
3336 // this is purely sequencial charts refresher
3337 // it is meant to be autonomous
3338 NETDATA.chartRefresherNoParallel = function(index) {
3339 if(NETDATA.options.debug.mail_loop === true)
3340 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3342 if(NETDATA.options.updated_dom === true) {
3343 // the dom has been updated
3344 // get the dom parts again
3345 NETDATA.parseDom(NETDATA.chartRefresher);
3348 if(index >= NETDATA.options.targets.length) {
3349 if(NETDATA.options.debug.main_loop === true)
3350 console.log('waiting to restart main loop...');
3352 NETDATA.options.auto_refresher_fast_weight = 0;
3354 setTimeout(function() {
3355 NETDATA.chartRefresher();
3356 }, NETDATA.options.current.idle_between_loops);
3359 var state = NETDATA.options.targets[index];
3361 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3362 if(NETDATA.options.debug.main_loop === true)
3363 console.log('fast rendering...');
3365 state.autoRefresh(function() {
3366 NETDATA.chartRefresherNoParallel(++index);
3370 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3371 NETDATA.options.auto_refresher_fast_weight = 0;
3373 setTimeout(function() {
3374 state.autoRefresh(function() {
3375 NETDATA.chartRefresherNoParallel(++index);
3377 }, NETDATA.options.current.idle_between_charts);
3382 // this is part of the parallel refresher
3383 // its cause is to refresh sequencially all the charts
3384 // that depend on chart library initialization
3385 // it will call the parallel refresher back
3386 // as soon as it sees a chart that its chart library
3388 NETDATA.chartRefresher_uninitialized = function() {
3389 if(NETDATA.options.updated_dom === true) {
3390 // the dom has been updated
3391 // get the dom parts again
3392 NETDATA.parseDom(NETDATA.chartRefresher);
3396 if(NETDATA.options.sequencial.length === 0)
3397 NETDATA.chartRefresher();
3399 var state = NETDATA.options.sequencial.pop();
3400 if(state.library.initialized === true)
3401 NETDATA.chartRefresher();
3403 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3407 NETDATA.chartRefresherWaitTime = function() {
3408 return NETDATA.options.current.idle_parallel_loops;
3411 // the default refresher
3412 // it will create 2 sets of charts:
3413 // - the ones that can be refreshed in parallel
3414 // - the ones that depend on something else
3415 // the first set will be executed in parallel
3416 // the second will be given to NETDATA.chartRefresher_uninitialized()
3417 NETDATA.chartRefresher = function() {
3418 // console.log('auto-refresher...');
3420 if(NETDATA.options.pause === true) {
3421 // console.log('auto-refresher is paused');
3422 setTimeout(NETDATA.chartRefresher,
3423 NETDATA.chartRefresherWaitTime());
3427 if(typeof NETDATA.options.pauseCallback === 'function') {
3428 // console.log('auto-refresher is calling pauseCallback');
3429 NETDATA.options.pause = true;
3430 NETDATA.options.pauseCallback();
3431 NETDATA.chartRefresher();
3435 if(NETDATA.options.current.parallel_refresher === false) {
3436 // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
3437 NETDATA.chartRefresherNoParallel(0);
3441 if(NETDATA.options.updated_dom === true) {
3442 // the dom has been updated
3443 // get the dom parts again
3444 // console.log('auto-refresher is calling parseDom()');
3445 NETDATA.parseDom(NETDATA.chartRefresher);
3449 var parallel = new Array();
3450 var targets = NETDATA.options.targets;
3451 var len = targets.length;
3454 state = targets[len];
3455 if(state.isVisible() === false || state.running === true)
3458 if(state.library.initialized === false) {
3459 if(state.library.enabled === true) {
3460 state.library.initialize(NETDATA.chartRefresher);
3464 state.error('chart library "' + state.library_name + '" is not enabled.');
3468 parallel.unshift(state);
3471 if(parallel.length > 0) {
3472 // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
3473 // this will execute the jobs in parallel
3474 $(parallel).each(function() {
3479 // console.log('auto-refresher nothing to do');
3482 // run the next refresh iteration
3483 setTimeout(NETDATA.chartRefresher,
3484 NETDATA.chartRefresherWaitTime());
3487 NETDATA.parseDom = function(callback) {
3488 NETDATA.options.last_page_scroll = new Date().getTime();
3489 NETDATA.options.updated_dom = false;
3491 var targets = $('div[data-netdata]'); //.filter(':visible');
3493 if(NETDATA.options.debug.main_loop === true)
3494 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3496 NETDATA.options.targets = new Array();
3497 var len = targets.length;
3499 // the initialization will take care of sizing
3500 // and the "loading..." message
3501 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3504 if(typeof callback === 'function') callback();
3507 // this is the main function - where everything starts
3508 NETDATA.start = function() {
3509 // this should be called only once
3511 NETDATA.options.page_is_visible = true;
3513 $(window).blur(function() {
3514 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3515 NETDATA.options.page_is_visible = false;
3516 if(NETDATA.options.debug.focus === true)
3517 console.log('Lost Focus!');
3521 $(window).focus(function() {
3522 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3523 NETDATA.options.page_is_visible = true;
3524 if(NETDATA.options.debug.focus === true)
3525 console.log('Focus restored!');
3529 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3530 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3531 NETDATA.options.page_is_visible = false;
3532 if(NETDATA.options.debug.focus === true)
3533 console.log('Document has no focus!');
3537 // bootstrap tab switching
3538 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3540 // bootstrap modal switching
3541 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3542 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3544 // bootstrap collapse switching
3545 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3546 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3548 NETDATA.parseDom(NETDATA.chartRefresher);
3550 // Alarms initialization
3551 setTimeout(NETDATA.alarms.init, 1000);
3553 // Registry initialization
3554 setTimeout(NETDATA.registry.init, netdataRegistryAfterMs);
3556 if(typeof netdataCallback === 'function')
3560 // ----------------------------------------------------------------------------------------------------------------
3563 NETDATA.peityInitialize = function(callback) {
3564 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3566 url: NETDATA.peity_js,
3569 xhrFields: { withCredentials: true } // required for the cookie
3572 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3575 NETDATA.chartLibraries.peity.enabled = false;
3576 NETDATA.error(100, NETDATA.peity_js);
3578 .always(function() {
3579 if(typeof callback === "function")
3584 NETDATA.chartLibraries.peity.enabled = false;
3585 if(typeof callback === "function")
3590 NETDATA.peityChartUpdate = function(state, data) {
3591 state.peity_instance.innerHTML = data.result;
3593 if(state.peity_options.stroke !== state.chartColors()[0]) {
3594 state.peity_options.stroke = state.chartColors()[0];
3595 if(state.chart.chart_type === 'line')
3596 state.peity_options.fill = NETDATA.themes.current.background;
3598 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3601 $(state.peity_instance).peity('line', state.peity_options);
3605 NETDATA.peityChartCreate = function(state, data) {
3606 state.peity_instance = document.createElement('div');
3607 state.element_chart.appendChild(state.peity_instance);
3609 var self = $(state.element);
3610 state.peity_options = {
3611 stroke: NETDATA.themes.current.foreground,
3612 strokeWidth: self.data('peity-strokewidth') || 1,
3613 width: state.chartWidth(),
3614 height: state.chartHeight(),
3615 fill: NETDATA.themes.current.foreground
3618 NETDATA.peityChartUpdate(state, data);
3622 // ----------------------------------------------------------------------------------------------------------------
3625 NETDATA.sparklineInitialize = function(callback) {
3626 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3628 url: NETDATA.sparkline_js,
3631 xhrFields: { withCredentials: true } // required for the cookie
3634 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3637 NETDATA.chartLibraries.sparkline.enabled = false;
3638 NETDATA.error(100, NETDATA.sparkline_js);
3640 .always(function() {
3641 if(typeof callback === "function")
3646 NETDATA.chartLibraries.sparkline.enabled = false;
3647 if(typeof callback === "function")
3652 NETDATA.sparklineChartUpdate = function(state, data) {
3653 state.sparkline_options.width = state.chartWidth();
3654 state.sparkline_options.height = state.chartHeight();
3656 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3660 NETDATA.sparklineChartCreate = function(state, data) {
3661 var self = $(state.element);
3662 var type = self.data('sparkline-type') || 'line';
3663 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3664 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3665 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3666 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3667 var composite = self.data('sparkline-composite') || undefined;
3668 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3669 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3670 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3671 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3672 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3673 var spotColor = self.data('sparkline-spotcolor') || undefined;
3674 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3675 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3676 var spotRadius = self.data('sparkline-spotradius') || undefined;
3677 var valueSpots = self.data('sparkline-valuespots') || undefined;
3678 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3679 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3680 var lineWidth = self.data('sparkline-linewidth') || undefined;
3681 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3682 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3683 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3684 var xvalues = self.data('sparkline-xvalues') || undefined;
3685 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3686 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3687 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3688 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3689 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3690 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3691 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3692 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3693 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3694 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3695 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3696 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3697 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3698 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3699 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3700 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3701 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3702 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3703 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3704 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3705 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3706 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3708 if(spotColor === 'disable') spotColor='';
3709 if(minSpotColor === 'disable') minSpotColor='';
3710 if(maxSpotColor === 'disable') maxSpotColor='';
3712 state.sparkline_options = {
3714 lineColor: lineColor,
3715 fillColor: fillColor,
3716 chartRangeMin: chartRangeMin,
3717 chartRangeMax: chartRangeMax,
3718 composite: composite,
3719 enableTagOptions: enableTagOptions,
3720 tagOptionPrefix: tagOptionPrefix,
3721 tagValuesAttribute: tagValuesAttribute,
3722 disableHiddenCheck: disableHiddenCheck,
3723 defaultPixelsPerValue: defaultPixelsPerValue,
3724 spotColor: spotColor,
3725 minSpotColor: minSpotColor,
3726 maxSpotColor: maxSpotColor,
3727 spotRadius: spotRadius,
3728 valueSpots: valueSpots,
3729 highlightSpotColor: highlightSpotColor,
3730 highlightLineColor: highlightLineColor,
3731 lineWidth: lineWidth,
3732 normalRangeMin: normalRangeMin,
3733 normalRangeMax: normalRangeMax,
3734 drawNormalOnTop: drawNormalOnTop,
3736 chartRangeClip: chartRangeClip,
3737 chartRangeMinX: chartRangeMinX,
3738 chartRangeMaxX: chartRangeMaxX,
3739 disableInteraction: disableInteraction,
3740 disableTooltips: disableTooltips,
3741 disableHighlight: disableHighlight,
3742 highlightLighten: highlightLighten,
3743 highlightColor: highlightColor,
3744 tooltipContainer: tooltipContainer,
3745 tooltipClassname: tooltipClassname,
3746 tooltipChartTitle: state.title,
3747 tooltipFormat: tooltipFormat,
3748 tooltipPrefix: tooltipPrefix,
3749 tooltipSuffix: tooltipSuffix,
3750 tooltipSkipNull: tooltipSkipNull,
3751 tooltipValueLookups: tooltipValueLookups,
3752 tooltipFormatFieldlist: tooltipFormatFieldlist,
3753 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3754 numberFormatter: numberFormatter,
3755 numberDigitGroupSep: numberDigitGroupSep,
3756 numberDecimalMark: numberDecimalMark,
3757 numberDigitGroupCount: numberDigitGroupCount,
3758 animatedZooms: animatedZooms,
3759 width: state.chartWidth(),
3760 height: state.chartHeight()
3763 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3767 // ----------------------------------------------------------------------------------------------------------------
3774 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3775 if(after < state.netdata_first)
3776 after = state.netdata_first;
3778 if(before > state.netdata_last)
3779 before = state.netdata_last;
3781 state.setMode('zoom');
3782 state.globalSelectionSyncStop();
3783 state.globalSelectionSyncDelay();
3784 state.dygraph_user_action = true;
3785 state.dygraph_force_zoom = true;
3786 state.updateChartPanOrZoom(after, before);
3787 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3790 NETDATA.dygraphSetSelection = function(state, t) {
3791 if(typeof state.dygraph_instance !== 'undefined') {
3792 var r = state.calculateRowForTime(t);
3794 state.dygraph_instance.setSelection(r);
3796 state.dygraph_instance.clearSelection();
3797 state.legendShowUndefined();
3804 NETDATA.dygraphClearSelection = function(state, t) {
3805 if(typeof state.dygraph_instance !== 'undefined') {
3806 state.dygraph_instance.clearSelection();
3811 NETDATA.dygraphSmoothInitialize = function(callback) {
3813 url: NETDATA.dygraph_smooth_js,
3816 xhrFields: { withCredentials: true } // required for the cookie
3819 NETDATA.dygraph.smooth = true;
3820 smoothPlotter.smoothing = 0.3;
3823 NETDATA.dygraph.smooth = false;
3825 .always(function() {
3826 if(typeof callback === "function")
3831 NETDATA.dygraphInitialize = function(callback) {
3832 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3834 url: NETDATA.dygraph_js,
3837 xhrFields: { withCredentials: true } // required for the cookie
3840 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3843 NETDATA.chartLibraries.dygraph.enabled = false;
3844 NETDATA.error(100, NETDATA.dygraph_js);
3846 .always(function() {
3847 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3848 NETDATA.dygraphSmoothInitialize(callback);
3849 else if(typeof callback === "function")
3854 NETDATA.chartLibraries.dygraph.enabled = false;
3855 if(typeof callback === "function")
3860 NETDATA.dygraphChartUpdate = function(state, data) {
3861 var dygraph = state.dygraph_instance;
3863 if(typeof dygraph === 'undefined')
3864 return NETDATA.dygraphChartCreate(state, data);
3866 // when the chart is not visible, and hidden
3867 // if there is a window resize, dygraph detects
3868 // its element size as 0x0.
3869 // this will make it re-appear properly
3871 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3875 file: data.result.data,
3876 colors: state.chartColors(),
3877 labels: data.result.labels,
3878 labelsDivWidth: state.chartWidth() - 70,
3879 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3882 if(state.dygraph_force_zoom === true) {
3883 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3884 state.log('dygraphChartUpdate() forced zoom update');
3886 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3887 options.valueRange = state.dygraph_options.valueRange;
3888 options.isZoomedIgnoreProgrammaticZoom = true;
3889 state.dygraph_force_zoom = false;
3891 else if(state.current.name !== 'auto') {
3892 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3893 state.log('dygraphChartUpdate() loose update');
3895 options.valueRange = state.dygraph_options.valueRange;
3898 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3899 state.log('dygraphChartUpdate() strict update');
3901 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3902 options.valueRange = state.dygraph_options.valueRange;
3903 options.isZoomedIgnoreProgrammaticZoom = true;
3906 if(state.dygraph_smooth_eligible === true) {
3907 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3908 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3909 NETDATA.dygraphChartCreate(state, data);
3914 dygraph.updateOptions(options);
3916 state.dygraph_last_rendered = new Date().getTime();
3920 NETDATA.dygraphChartCreate = function(state, data) {
3921 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3922 state.log('dygraphChartCreate()');
3924 var self = $(state.element);
3926 var chart_type = state.chart.chart_type;
3927 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3928 chart_type = self.data('dygraph-type') || chart_type;
3930 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3931 smooth = self.data('dygraph-smooth') || smooth;
3933 if(NETDATA.dygraph.smooth === false)
3936 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3937 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3939 state.dygraph_options = {
3940 colors: self.data('dygraph-colors') || state.chartColors(),
3942 // leave a few pixels empty on the right of the chart
3943 rightGap: self.data('dygraph-rightgap') || 5,
3944 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3945 showRoller: self.data('dygraph-showroller') || false,
3947 title: self.data('dygraph-title') || state.title,
3948 titleHeight: self.data('dygraph-titleheight') || 19,
3950 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3951 labels: data.result.labels,
3952 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3953 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3954 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3955 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3956 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3959 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3960 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3962 includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
3963 xRangePad: self.data('dygraph-xrangepad') || 0,
3964 // yRangePad: self.data('dygraph-yrangepad') || 1,
3966 valueRange: self.data('dygraph-valuerange') || null,
3968 ylabel: state.units,
3969 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3971 // the function to plot the chart
3974 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3975 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3976 strokePattern: self.data('dygraph-strokepattern') || undefined,
3978 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3979 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3980 drawPoints: self.data('dygraph-drawpoints') || false,
3982 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3983 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3985 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3986 pointSize: self.data('dygraph-pointsize') || 1,
3988 // enabling this makes the chart with little square lines
3989 stepPlot: self.data('dygraph-stepplot') || false,
3991 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3992 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3993 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3995 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3996 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3997 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
3998 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
4000 drawAxis: self.data('dygraph-drawaxis') || true,
4001 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
4002 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
4003 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
4005 drawGrid: self.data('dygraph-drawgrid') || true,
4006 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
4007 drawYGrid: self.data('dygraph-drawygrid') || undefined,
4008 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
4009 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4,
4010 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
4012 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
4013 sigFigs: self.data('dygraph-sigfigs') || null,
4014 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
4015 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
4017 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
4018 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
4019 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
4021 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
4022 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
4026 ticker: Dygraph.dateTicker,
4027 axisLabelFormatter: function (d, gran) {
4028 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4030 valueFormatter: function (ms) {
4031 var d = new Date(ms);
4032 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
4033 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4038 valueFormatter: function (x) {
4039 // we format legends with the state object
4040 // no need to do anything here
4041 // return (Math.round(x*100) / 100).toLocaleString();
4042 // return state.legendFormatValue(x);
4047 legendFormatter: function(data) {
4048 var elements = state.element_legend_childs;
4050 // if the hidden div is not there
4051 // we are not managing the legend
4052 if(elements.hidden === null) return;
4054 if (typeof data.x !== 'undefined') {
4055 state.legendSetDate(data.x);
4056 var i = data.series.length;
4058 var series = data.series[i];
4059 if(!series.isVisible) continue;
4060 state.legendSetLabelValue(series.label, series.y);
4066 drawCallback: function(dygraph, is_initial) {
4067 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
4068 state.dygraph_user_action = false;
4070 var x_range = dygraph.xAxisRange();
4071 var after = Math.round(x_range[0]);
4072 var before = Math.round(x_range[1]);
4074 if(NETDATA.options.debug.dygraph === true)
4075 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
4077 if(before <= state.netdata_last && after >= state.netdata_first)
4078 state.updateChartPanOrZoom(after, before);
4081 zoomCallback: function(minDate, maxDate, yRanges) {
4082 if(NETDATA.options.debug.dygraph === true)
4083 state.log('dygraphZoomCallback()');
4085 state.globalSelectionSyncStop();
4086 state.globalSelectionSyncDelay();
4087 state.setMode('zoom');
4089 // refresh it to the greatest possible zoom level
4090 state.dygraph_user_action = true;
4091 state.dygraph_force_zoom = true;
4092 state.updateChartPanOrZoom(minDate, maxDate);
4094 highlightCallback: function(event, x, points, row, seriesName) {
4095 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4096 state.log('dygraphHighlightCallback()');
4100 // there is a bug in dygraph when the chart is zoomed enough
4101 // the time it thinks is selected is wrong
4102 // here we calculate the time t based on the row number selected
4104 var t = state.data_after + row * state.data_update_every;
4105 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
4107 state.globalSelectionSync(x);
4109 // fix legend zIndex using the internal structures of dygraph legend module
4110 // this works, but it is a hack!
4111 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
4113 unhighlightCallback: function(event) {
4114 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4115 state.log('dygraphUnhighlightCallback()');
4117 state.unpauseChart();
4118 state.globalSelectionSyncStop();
4120 interactionModel : {
4121 mousedown: function(event, dygraph, context) {
4122 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4123 state.log('interactionModel.mousedown()');
4125 state.dygraph_user_action = true;
4126 state.globalSelectionSyncStop();
4128 if(NETDATA.options.debug.dygraph === true)
4129 state.log('dygraphMouseDown()');
4131 // Right-click should not initiate a zoom.
4132 if(event.button && event.button === 2) return;
4134 context.initializeMouseDown(event, dygraph, context);
4136 if(event.button && event.button === 1) {
4137 if (event.altKey || event.shiftKey) {
4138 state.setMode('pan');
4139 state.globalSelectionSyncDelay();
4140 Dygraph.startPan(event, dygraph, context);
4143 state.setMode('zoom');
4144 state.globalSelectionSyncDelay();
4145 Dygraph.startZoom(event, dygraph, context);
4149 if (event.altKey || event.shiftKey) {
4150 state.setMode('zoom');
4151 state.globalSelectionSyncDelay();
4152 Dygraph.startZoom(event, dygraph, context);
4155 state.setMode('pan');
4156 state.globalSelectionSyncDelay();
4157 Dygraph.startPan(event, dygraph, context);
4161 mousemove: function(event, dygraph, context) {
4162 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4163 state.log('interactionModel.mousemove()');
4165 if(context.isPanning) {
4166 state.dygraph_user_action = true;
4167 state.globalSelectionSyncStop();
4168 state.globalSelectionSyncDelay();
4169 state.setMode('pan');
4170 Dygraph.movePan(event, dygraph, context);
4172 else if(context.isZooming) {
4173 state.dygraph_user_action = true;
4174 state.globalSelectionSyncStop();
4175 state.globalSelectionSyncDelay();
4176 state.setMode('zoom');
4177 Dygraph.moveZoom(event, dygraph, context);
4180 mouseup: function(event, dygraph, context) {
4181 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4182 state.log('interactionModel.mouseup()');
4184 if (context.isPanning) {
4185 state.dygraph_user_action = true;
4186 state.globalSelectionSyncDelay();
4187 Dygraph.endPan(event, dygraph, context);
4189 else if (context.isZooming) {
4190 state.dygraph_user_action = true;
4191 state.globalSelectionSyncDelay();
4192 Dygraph.endZoom(event, dygraph, context);
4195 click: function(event, dygraph, context) {
4196 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4197 state.log('interactionModel.click()');
4199 event.preventDefault();
4201 dblclick: function(event, dygraph, context) {
4202 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4203 state.log('interactionModel.dblclick()');
4204 NETDATA.resetAllCharts(state);
4206 mousewheel: function(event, dygraph, context) {
4207 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4208 state.log('interactionModel.mousewheel()');
4210 // Take the offset of a mouse event on the dygraph canvas and
4211 // convert it to a pair of percentages from the bottom left.
4212 // (Not top left, bottom is where the lower value is.)
4213 function offsetToPercentage(g, offsetX, offsetY) {
4214 // This is calculating the pixel offset of the leftmost date.
4215 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4216 var yar0 = g.yAxisRange(0);
4218 // This is calculating the pixel of the higest value. (Top pixel)
4219 var yOffset = g.toDomCoords(null, yar0[1])[1];
4221 // x y w and h are relative to the corner of the drawing area,
4222 // so that the upper corner of the drawing area is (0, 0).
4223 var x = offsetX - xOffset;
4224 var y = offsetY - yOffset;
4226 // This is computing the rightmost pixel, effectively defining the
4228 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4230 // This is computing the lowest pixel, effectively defining the height.
4231 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4233 // Percentage from the left.
4234 var xPct = w === 0 ? 0 : (x / w);
4235 // Percentage from the top.
4236 var yPct = h === 0 ? 0 : (y / h);
4238 // The (1-) part below changes it from "% distance down from the top"
4239 // to "% distance up from the bottom".
4240 return [xPct, (1-yPct)];
4243 // Adjusts [x, y] toward each other by zoomInPercentage%
4244 // Split it so the left/bottom axis gets xBias/yBias of that change and
4245 // tight/top gets (1-xBias)/(1-yBias) of that change.
4247 // If a bias is missing it splits it down the middle.
4248 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4249 xBias = xBias || 0.5;
4250 yBias = yBias || 0.5;
4252 function adjustAxis(axis, zoomInPercentage, bias) {
4253 var delta = axis[1] - axis[0];
4254 var increment = delta * zoomInPercentage;
4255 var foo = [increment * bias, increment * (1-bias)];
4257 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4260 var yAxes = g.yAxisRanges();
4262 for (var i = 0; i < yAxes.length; i++) {
4263 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4266 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4269 if(event.altKey || event.shiftKey) {
4270 state.dygraph_user_action = true;
4272 state.globalSelectionSyncStop();
4273 state.globalSelectionSyncDelay();
4275 // http://dygraphs.com/gallery/interaction-api.js
4276 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4277 var percentage = normal / 50;
4279 if (!(event.offsetX && event.offsetY)){
4280 event.offsetX = event.layerX - event.target.offsetLeft;
4281 event.offsetY = event.layerY - event.target.offsetTop;
4284 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4285 var xPct = percentages[0];
4286 var yPct = percentages[1];
4288 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4290 var after = new_x_range[0];
4291 var before = new_x_range[1];
4293 var first = state.netdata_first + state.data_update_every;
4294 var last = state.netdata_last + state.data_update_every;
4297 after -= (before - last);
4304 state.setMode('zoom');
4305 if(state.updateChartPanOrZoom(after, before) === true)
4306 dygraph.updateOptions({ dateWindow: [ after, before ] });
4308 event.preventDefault();
4311 touchstart: function(event, dygraph, context) {
4312 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4313 state.log('interactionModel.touchstart()');
4315 state.dygraph_user_action = true;
4316 state.setMode('zoom');
4319 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4321 // we overwrite the touch directions at the end, to overwrite
4322 // the internal default of dygraphs
4323 context.touchDirections = { x: true, y: false };
4325 state.dygraph_last_touch_start = new Date().getTime();
4326 state.dygraph_last_touch_move = 0;
4328 if(typeof event.touches[0].pageX === 'number')
4329 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4331 state.dygraph_last_touch_page_x = 0;
4333 touchmove: function(event, dygraph, context) {
4334 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4335 state.log('interactionModel.touchmove()');
4337 state.dygraph_user_action = true;
4338 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4340 state.dygraph_last_touch_move = new Date().getTime();
4342 touchend: function(event, dygraph, context) {
4343 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4344 state.log('interactionModel.touchend()');
4346 state.dygraph_user_action = true;
4347 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4349 // if it didn't move, it is a selection
4350 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4351 // internal api of dygraphs
4352 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4353 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4354 if(NETDATA.dygraphSetSelection(state, t) === true)
4355 state.globalSelectionSync(t);
4358 // if it was double tap within double click time, reset the charts
4359 var now = new Date().getTime();
4360 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4361 if(state.dygraph_last_touch_move === 0) {
4362 var dt = now - state.dygraph_last_touch_end;
4363 if(dt <= NETDATA.options.current.double_click_speed)
4364 NETDATA.resetAllCharts(state);
4368 // remember the timestamp of the last touch end
4369 state.dygraph_last_touch_end = now;
4374 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4375 state.dygraph_options.drawGrid = false;
4376 state.dygraph_options.drawAxis = false;
4377 state.dygraph_options.title = undefined;
4378 state.dygraph_options.units = undefined;
4379 state.dygraph_options.ylabel = undefined;
4380 state.dygraph_options.yLabelWidth = 0;
4381 state.dygraph_options.labelsDivWidth = 120;
4382 state.dygraph_options.labelsDivStyles.width = '120px';
4383 state.dygraph_options.labelsSeparateLines = true;
4384 state.dygraph_options.rightGap = 0;
4385 state.dygraph_options.yRangePad = 1;
4388 if(smooth === true) {
4389 state.dygraph_smooth_eligible = true;
4391 if(NETDATA.options.current.smooth_plot === true)
4392 state.dygraph_options.plotter = smoothPlotter;
4394 else state.dygraph_smooth_eligible = false;
4396 state.dygraph_instance = new Dygraph(state.element_chart,
4397 data.result.data, state.dygraph_options);
4399 state.dygraph_force_zoom = false;
4400 state.dygraph_user_action = false;
4401 state.dygraph_last_rendered = new Date().getTime();
4405 // ----------------------------------------------------------------------------------------------------------------
4408 NETDATA.morrisInitialize = function(callback) {
4409 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4411 // morris requires raphael
4412 if(!NETDATA.chartLibraries.raphael.initialized) {
4413 if(NETDATA.chartLibraries.raphael.enabled) {
4414 NETDATA.raphaelInitialize(function() {
4415 NETDATA.morrisInitialize(callback);
4419 NETDATA.chartLibraries.morris.enabled = false;
4420 if(typeof callback === "function")
4425 NETDATA._loadCSS(NETDATA.morris_css);
4428 url: NETDATA.morris_js,
4431 xhrFields: { withCredentials: true } // required for the cookie
4434 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4437 NETDATA.chartLibraries.morris.enabled = false;
4438 NETDATA.error(100, NETDATA.morris_js);
4440 .always(function() {
4441 if(typeof callback === "function")
4447 NETDATA.chartLibraries.morris.enabled = false;
4448 if(typeof callback === "function")
4453 NETDATA.morrisChartUpdate = function(state, data) {
4454 state.morris_instance.setData(data.result.data);
4458 NETDATA.morrisChartCreate = function(state, data) {
4460 state.morris_options = {
4461 element: state.element_chart.id,
4462 data: data.result.data,
4464 ykeys: data.dimension_names,
4465 labels: data.dimension_names,
4471 continuousLine: false,
4472 behaveLikeLine: false
4475 if(state.chart.chart_type === 'line')
4476 state.morris_instance = new Morris.Line(state.morris_options);
4478 else if(state.chart.chart_type === 'area') {
4479 state.morris_options.behaveLikeLine = true;
4480 state.morris_instance = new Morris.Area(state.morris_options);
4483 state.morris_instance = new Morris.Area(state.morris_options);
4488 // ----------------------------------------------------------------------------------------------------------------
4491 NETDATA.raphaelInitialize = function(callback) {
4492 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4494 url: NETDATA.raphael_js,
4497 xhrFields: { withCredentials: true } // required for the cookie
4500 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4503 NETDATA.chartLibraries.raphael.enabled = false;
4504 NETDATA.error(100, NETDATA.raphael_js);
4506 .always(function() {
4507 if(typeof callback === "function")
4512 NETDATA.chartLibraries.raphael.enabled = false;
4513 if(typeof callback === "function")
4518 NETDATA.raphaelChartUpdate = function(state, data) {
4519 $(state.element_chart).raphael(data.result, {
4520 width: state.chartWidth(),
4521 height: state.chartHeight()
4527 NETDATA.raphaelChartCreate = function(state, data) {
4528 $(state.element_chart).raphael(data.result, {
4529 width: state.chartWidth(),
4530 height: state.chartHeight()
4536 // ----------------------------------------------------------------------------------------------------------------
4539 NETDATA.c3Initialize = function(callback) {
4540 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4543 if(!NETDATA.chartLibraries.d3.initialized) {
4544 if(NETDATA.chartLibraries.d3.enabled) {
4545 NETDATA.d3Initialize(function() {
4546 NETDATA.c3Initialize(callback);
4550 NETDATA.chartLibraries.c3.enabled = false;
4551 if(typeof callback === "function")
4556 NETDATA._loadCSS(NETDATA.c3_css);
4562 xhrFields: { withCredentials: true } // required for the cookie
4565 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4568 NETDATA.chartLibraries.c3.enabled = false;
4569 NETDATA.error(100, NETDATA.c3_js);
4571 .always(function() {
4572 if(typeof callback === "function")
4578 NETDATA.chartLibraries.c3.enabled = false;
4579 if(typeof callback === "function")
4584 NETDATA.c3ChartUpdate = function(state, data) {
4585 state.c3_instance.destroy();
4586 return NETDATA.c3ChartCreate(state, data);
4588 //state.c3_instance.load({
4589 // rows: data.result,
4596 NETDATA.c3ChartCreate = function(state, data) {
4598 state.element_chart.id = 'c3-' + state.uuid;
4599 // console.log('id = ' + state.element_chart.id);
4601 state.c3_instance = c3.generate({
4602 bindto: '#' + state.element_chart.id,
4604 width: state.chartWidth(),
4605 height: state.chartHeight()
4608 pattern: state.chartColors()
4613 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4619 format: function(x) {
4620 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4647 // console.log(state.c3_instance);
4652 // ----------------------------------------------------------------------------------------------------------------
4655 NETDATA.d3Initialize = function(callback) {
4656 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4661 xhrFields: { withCredentials: true } // required for the cookie
4664 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4667 NETDATA.chartLibraries.d3.enabled = false;
4668 NETDATA.error(100, NETDATA.d3_js);
4670 .always(function() {
4671 if(typeof callback === "function")
4676 NETDATA.chartLibraries.d3.enabled = false;
4677 if(typeof callback === "function")
4682 NETDATA.d3ChartUpdate = function(state, data) {
4686 NETDATA.d3ChartCreate = function(state, data) {
4690 // ----------------------------------------------------------------------------------------------------------------
4693 NETDATA.googleInitialize = function(callback) {
4694 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4696 url: NETDATA.google_js,
4699 xhrFields: { withCredentials: true } // required for the cookie
4702 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4703 google.load('visualization', '1.1', {
4704 'packages': ['corechart', 'controls'],
4705 'callback': callback
4709 NETDATA.chartLibraries.google.enabled = false;
4710 NETDATA.error(100, NETDATA.google_js);
4711 if(typeof callback === "function")
4716 NETDATA.chartLibraries.google.enabled = false;
4717 if(typeof callback === "function")
4722 NETDATA.googleChartUpdate = function(state, data) {
4723 var datatable = new google.visualization.DataTable(data.result);
4724 state.google_instance.draw(datatable, state.google_options);
4728 NETDATA.googleChartCreate = function(state, data) {
4729 var datatable = new google.visualization.DataTable(data.result);
4731 state.google_options = {
4732 colors: state.chartColors(),
4734 // do not set width, height - the chart resizes itself
4735 //width: state.chartWidth(),
4736 //height: state.chartHeight(),
4741 // title: "Time of Day",
4742 // format:'HH:mm:ss',
4743 viewWindowMode: 'maximized',
4755 viewWindowMode: 'pretty',
4770 focusTarget: 'category',
4777 titlePosition: 'out',
4788 curveType: 'function',
4793 switch(state.chart.chart_type) {
4795 state.google_options.vAxis.viewWindowMode = 'maximized';
4796 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4797 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4801 state.google_options.isStacked = true;
4802 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4803 state.google_options.vAxis.viewWindowMode = 'maximized';
4804 state.google_options.vAxis.minValue = null;
4805 state.google_options.vAxis.maxValue = null;
4806 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4811 state.google_options.lineWidth = 2;
4812 state.google_instance = new google.visualization.LineChart(state.element_chart);
4816 state.google_instance.draw(datatable, state.google_options);
4820 // ----------------------------------------------------------------------------------------------------------------
4822 NETDATA.percentFromValueMax = function(value, max) {
4823 if(value === null) value = 0;
4824 if(max < value) max = value;
4828 pcent = Math.round(value * 100 / max);
4829 if(pcent === 0 && value > 0) pcent = 1;
4835 // ----------------------------------------------------------------------------------------------------------------
4838 NETDATA.easypiechartInitialize = function(callback) {
4839 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4841 url: NETDATA.easypiechart_js,
4844 xhrFields: { withCredentials: true } // required for the cookie
4847 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4850 NETDATA.chartLibraries.easypiechart.enabled = false;
4851 NETDATA.error(100, NETDATA.easypiechart_js);
4853 .always(function() {
4854 if(typeof callback === "function")
4859 NETDATA.chartLibraries.easypiechart.enabled = false;
4860 if(typeof callback === "function")
4865 NETDATA.easypiechartClearSelection = function(state) {
4866 if(typeof state.easyPieChartEvent !== 'undefined') {
4867 if(state.easyPieChartEvent.timer !== null)
4868 clearTimeout(state.easyPieChartEvent.timer);
4870 state.easyPieChartEvent.timer = null;
4873 if(state.isAutoRefreshable() === true && state.data !== null) {
4874 NETDATA.easypiechartChartUpdate(state, state.data);
4877 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4878 state.easyPieChart_instance.update(0);
4880 state.easyPieChart_instance.enableAnimation();
4885 NETDATA.easypiechartSetSelection = function(state, t) {
4886 if(state.timeIsVisible(t) !== true)
4887 return NETDATA.easypiechartClearSelection(state);
4889 var slot = state.calculateRowForTime(t);
4890 if(slot < 0 || slot >= state.data.result.length)
4891 return NETDATA.easypiechartClearSelection(state);
4893 if(typeof state.easyPieChartEvent === 'undefined') {
4894 state.easyPieChartEvent = {
4901 var value = state.data.result[state.data.result.length - 1 - slot];
4902 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4903 var pcent = NETDATA.percentFromValueMax(value, max);
4905 state.easyPieChartEvent.value = value;
4906 state.easyPieChartEvent.pcent = pcent;
4907 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4909 if(state.easyPieChartEvent.timer === null) {
4910 state.easyPieChart_instance.disableAnimation();
4912 state.easyPieChartEvent.timer = setTimeout(function() {
4913 state.easyPieChartEvent.timer = null;
4914 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4915 }, NETDATA.options.current.charts_selection_animation_delay);
4921 NETDATA.easypiechartChartUpdate = function(state, data) {
4922 var value, max, pcent;
4924 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4930 value = data.result[0];
4931 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4932 pcent = NETDATA.percentFromValueMax(value, max);
4935 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4936 state.easyPieChart_instance.update(pcent);
4940 NETDATA.easypiechartChartCreate = function(state, data) {
4941 var self = $(state.element);
4942 var chart = $(state.element_chart);
4944 var value = data.result[0];
4945 var max = self.data('easypiechart-max-value') || null;
4946 var adjust = self.data('easypiechart-adjust') || null;
4950 state.easyPieChartMax = null;
4953 state.easyPieChartMax = max;
4955 var pcent = NETDATA.percentFromValueMax(value, max);
4957 chart.data('data-percent', pcent);
4961 case 'width': size = state.chartHeight(); break;
4962 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4963 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4965 default: size = state.chartWidth(); break;
4967 state.element.style.width = size + 'px';
4968 state.element.style.height = size + 'px';
4970 var stroke = Math.floor(size / 22);
4971 if(stroke < 3) stroke = 2;
4973 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4974 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4975 state.easyPieChartLabel = document.createElement('span');
4976 state.easyPieChartLabel.className = 'easyPieChartLabel';
4977 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4978 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4979 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4980 state.element_chart.appendChild(state.easyPieChartLabel);
4982 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4983 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4984 state.easyPieChartTitle = document.createElement('span');
4985 state.easyPieChartTitle.className = 'easyPieChartTitle';
4986 state.easyPieChartTitle.innerHTML = state.title;
4987 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4988 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4989 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4990 state.element_chart.appendChild(state.easyPieChartTitle);
4992 var unitfontsize = Math.round(titlefontsize * 0.9);
4993 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4994 state.easyPieChartUnits = document.createElement('span');
4995 state.easyPieChartUnits.className = 'easyPieChartUnits';
4996 state.easyPieChartUnits.innerHTML = state.units;
4997 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4998 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4999 state.element_chart.appendChild(state.easyPieChartUnits);
5001 chart.easyPieChart({
5002 barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
5003 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
5004 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
5005 scaleLength: self.data('easypiechart-scalelength') || 5,
5006 lineCap: self.data('easypiechart-linecap') || 'round',
5007 lineWidth: self.data('easypiechart-linewidth') || stroke,
5008 trackWidth: self.data('easypiechart-trackwidth') || undefined,
5009 size: self.data('easypiechart-size') || size,
5010 rotate: self.data('easypiechart-rotate') || 0,
5011 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
5012 easing: self.data('easypiechart-easing') || undefined
5015 // when we just re-create the chart
5016 // do not animate the first update
5018 if(typeof state.easyPieChart_instance !== 'undefined')
5021 state.easyPieChart_instance = chart.data('easyPieChart');
5022 if(animate === false) state.easyPieChart_instance.disableAnimation();
5023 state.easyPieChart_instance.update(pcent);
5024 if(animate === false) state.easyPieChart_instance.enableAnimation();
5028 // ----------------------------------------------------------------------------------------------------------------
5031 NETDATA.gaugeInitialize = function(callback) {
5032 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
5034 url: NETDATA.gauge_js,
5037 xhrFields: { withCredentials: true } // required for the cookie
5040 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
5043 NETDATA.chartLibraries.gauge.enabled = false;
5044 NETDATA.error(100, NETDATA.gauge_js);
5046 .always(function() {
5047 if(typeof callback === "function")
5052 NETDATA.chartLibraries.gauge.enabled = false;
5053 if(typeof callback === "function")
5058 NETDATA.gaugeAnimation = function(state, status) {
5061 if(typeof status === 'boolean' && status === false)
5063 else if(typeof status === 'number')
5066 // console.log('gauge speed ' + speed);
5067 state.gauge_instance.animationSpeed = speed;
5068 state.___gaugeOld__.speed = speed;
5071 NETDATA.gaugeSet = function(state, value, min, max) {
5072 if(typeof value !== 'number') value = 0;
5073 if(typeof min !== 'number') min = 0;
5074 if(typeof max !== 'number') max = 0;
5075 if(value > max) max = value;
5076 if(value < min) min = value;
5085 // gauge.js has an issue if the needle
5086 // is smaller than min or larger than max
5087 // when we set the new values
5088 // the needle will go crazy
5090 // to prevent it, we always feed it
5091 // with a percentage, so that the needle
5092 // is always between min and max
5093 var pcent = (value - min) * 100 / (max - min);
5095 // these should never happen
5096 if(pcent < 0) pcent = 0;
5097 if(pcent > 100) pcent = 100;
5099 state.gauge_instance.set(pcent);
5100 // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
5102 state.___gaugeOld__.value = value;
5103 state.___gaugeOld__.min = min;
5104 state.___gaugeOld__.max = max;
5107 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5108 if(state.___gaugeOld__.valueLabel !== value) {
5109 state.___gaugeOld__.valueLabel = value;
5110 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5112 if(state.___gaugeOld__.minLabel !== min) {
5113 state.___gaugeOld__.minLabel = min;
5114 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5116 if(state.___gaugeOld__.maxLabel !== max) {
5117 state.___gaugeOld__.maxLabel = max;
5118 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5122 NETDATA.gaugeClearSelection = function(state) {
5123 if(typeof state.gaugeEvent !== 'undefined') {
5124 if(state.gaugeEvent.timer !== null)
5125 clearTimeout(state.gaugeEvent.timer);
5127 state.gaugeEvent.timer = null;
5130 if(state.isAutoRefreshable() === true && state.data !== null) {
5131 NETDATA.gaugeChartUpdate(state, state.data);
5134 NETDATA.gaugeAnimation(state, false);
5135 NETDATA.gaugeSet(state, null, null, null);
5136 NETDATA.gaugeSetLabels(state, null, null, null);
5139 NETDATA.gaugeAnimation(state, true);
5143 NETDATA.gaugeSetSelection = function(state, t) {
5144 if(state.timeIsVisible(t) !== true)
5145 return NETDATA.gaugeClearSelection(state);
5147 var slot = state.calculateRowForTime(t);
5148 if(slot < 0 || slot >= state.data.result.length)
5149 return NETDATA.gaugeClearSelection(state);
5151 if(typeof state.gaugeEvent === 'undefined') {
5152 state.gaugeEvent = {
5160 var value = state.data.result[state.data.result.length - 1 - slot];
5161 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5164 state.gaugeEvent.value = value;
5165 state.gaugeEvent.max = max;
5166 state.gaugeEvent.min = min;
5167 NETDATA.gaugeSetLabels(state, value, min, max);
5169 if(state.gaugeEvent.timer === null) {
5170 NETDATA.gaugeAnimation(state, false);
5172 state.gaugeEvent.timer = setTimeout(function() {
5173 state.gaugeEvent.timer = null;
5174 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5175 }, NETDATA.options.current.charts_selection_animation_delay);
5181 NETDATA.gaugeChartUpdate = function(state, data) {
5182 var value, min, max;
5184 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5188 NETDATA.gaugeSetLabels(state, null, null, null);
5191 value = data.result[0];
5193 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5194 if(value > max) max = value;
5195 NETDATA.gaugeSetLabels(state, value, min, max);
5198 NETDATA.gaugeSet(state, value, min, max);
5202 NETDATA.gaugeChartCreate = function(state, data) {
5203 var self = $(state.element);
5204 // var chart = $(state.element_chart);
5206 var value = data.result[0];
5207 var max = self.data('gauge-max-value') || null;
5208 var adjust = self.data('gauge-adjust') || null;
5209 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5210 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5211 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5212 var stopColor = self.data('gauge-stop-color') || void 0;
5213 var generateGradient = self.data('gauge-generate-gradient') || false;
5217 state.gaugeMax = null;
5220 state.gaugeMax = max;
5222 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5224 // case 'width': width = height * ratio; break;
5226 // default: height = width / ratio; break;
5228 //state.element.style.width = width.toString() + 'px';
5229 //state.element.style.height = height.toString() + 'px';
5234 lines: 12, // The number of lines to draw
5235 angle: 0.15, // The length of each line
5236 lineWidth: 0.44, // 0.44 The line thickness
5238 length: 0.8, // 0.9 The radius of the inner circle
5239 strokeWidth: 0.035, // The rotation offset
5240 color: pointerColor // Fill color
5242 colorStart: startColor, // Colors
5243 colorStop: stopColor, // just experiment with them
5244 strokeColor: strokeColor, // to see which ones work best for you
5246 generateGradient: (generateGradient === true)?true:false,
5250 if (generateGradient.constructor === Array) {
5252 // data-gauge-generate-gradient="[0, 50, 100]"
5253 // data-gauge-gradient-percent-color-0="#FFFFFF"
5254 // data-gauge-gradient-percent-color-50="#999900"
5255 // data-gauge-gradient-percent-color-100="#000000"
5257 options.percentColors = new Array();
5258 var len = generateGradient.length;
5260 var pcent = generateGradient[len];
5261 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5262 if(color !== false) {
5263 var a = new Array();
5266 options.percentColors.unshift(a);
5269 if(options.percentColors.length === 0)
5270 delete options.percentColors;
5272 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5273 options.percentColors = [
5274 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5275 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5276 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5277 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5278 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5279 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5280 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5281 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5282 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5283 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5284 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5287 state.gauge_canvas = document.createElement('canvas');
5288 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5289 state.gauge_canvas.className = 'gaugeChart';
5290 state.gauge_canvas.width = width;
5291 state.gauge_canvas.height = height;
5292 state.element_chart.appendChild(state.gauge_canvas);
5294 var valuefontsize = Math.floor(height / 6);
5295 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5296 state.gaugeChartLabel = document.createElement('span');
5297 state.gaugeChartLabel.className = 'gaugeChartLabel';
5298 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5299 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5300 state.element_chart.appendChild(state.gaugeChartLabel);
5302 var titlefontsize = Math.round(valuefontsize / 2);
5304 state.gaugeChartTitle = document.createElement('span');
5305 state.gaugeChartTitle.className = 'gaugeChartTitle';
5306 state.gaugeChartTitle.innerHTML = state.title;
5307 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5308 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5309 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5310 state.element_chart.appendChild(state.gaugeChartTitle);
5312 var unitfontsize = Math.round(titlefontsize * 0.9);
5313 state.gaugeChartUnits = document.createElement('span');
5314 state.gaugeChartUnits.className = 'gaugeChartUnits';
5315 state.gaugeChartUnits.innerHTML = state.units;
5316 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5317 state.element_chart.appendChild(state.gaugeChartUnits);
5319 state.gaugeChartMin = document.createElement('span');
5320 state.gaugeChartMin.className = 'gaugeChartMin';
5321 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5322 state.element_chart.appendChild(state.gaugeChartMin);
5324 state.gaugeChartMax = document.createElement('span');
5325 state.gaugeChartMax.className = 'gaugeChartMax';
5326 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5327 state.element_chart.appendChild(state.gaugeChartMax);
5329 // when we just re-create the chart
5330 // do not animate the first update
5332 if(typeof state.gauge_instance !== 'undefined')
5335 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5337 state.___gaugeOld__ = {
5346 // we will always feed a percentage
5347 state.gauge_instance.minValue = 0;
5348 state.gauge_instance.maxValue = 100;
5350 NETDATA.gaugeAnimation(state, animate);
5351 NETDATA.gaugeSet(state, value, 0, max);
5352 NETDATA.gaugeSetLabels(state, value, 0, max);
5353 NETDATA.gaugeAnimation(state, true);
5357 // ----------------------------------------------------------------------------------------------------------------
5358 // Charts Libraries Registration
5360 NETDATA.chartLibraries = {
5362 initialize: NETDATA.dygraphInitialize,
5363 create: NETDATA.dygraphChartCreate,
5364 update: NETDATA.dygraphChartUpdate,
5365 resize: function(state) {
5366 if(typeof state.dygraph_instance.resize === 'function')
5367 state.dygraph_instance.resize();
5369 setSelection: NETDATA.dygraphSetSelection,
5370 clearSelection: NETDATA.dygraphClearSelection,
5371 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5374 format: function(state) { return 'json'; },
5375 options: function(state) { return 'ms|flip'; },
5376 legend: function(state) {
5377 if(this.isSparkline(state) === false)
5378 return 'right-side';
5382 autoresize: function(state) { return true; },
5383 max_updates_to_recreate: function(state) { return 5000; },
5384 track_colors: function(state) { return true; },
5385 pixels_per_point: function(state) {
5386 if(this.isSparkline(state) === false)
5392 isSparkline: function(state) {
5393 if(typeof state.dygraph_sparkline === 'undefined') {
5394 var t = $(state.element).data('dygraph-theme');
5395 if(t === 'sparkline')
5396 state.dygraph_sparkline = true;
5398 state.dygraph_sparkline = false;
5400 return state.dygraph_sparkline;
5404 initialize: NETDATA.sparklineInitialize,
5405 create: NETDATA.sparklineChartCreate,
5406 update: NETDATA.sparklineChartUpdate,
5408 setSelection: undefined, // function(state, t) { return true; },
5409 clearSelection: undefined, // function(state) { return true; },
5410 toolboxPanAndZoom: null,
5413 format: function(state) { return 'array'; },
5414 options: function(state) { return 'flip|abs'; },
5415 legend: function(state) { return null; },
5416 autoresize: function(state) { return false; },
5417 max_updates_to_recreate: function(state) { return 5000; },
5418 track_colors: function(state) { return false; },
5419 pixels_per_point: function(state) { return 3; }
5422 initialize: NETDATA.peityInitialize,
5423 create: NETDATA.peityChartCreate,
5424 update: NETDATA.peityChartUpdate,
5426 setSelection: undefined, // function(state, t) { return true; },
5427 clearSelection: undefined, // function(state) { return true; },
5428 toolboxPanAndZoom: null,
5431 format: function(state) { return 'ssvcomma'; },
5432 options: function(state) { return 'null2zero|flip|abs'; },
5433 legend: function(state) { return null; },
5434 autoresize: function(state) { return false; },
5435 max_updates_to_recreate: function(state) { return 5000; },
5436 track_colors: function(state) { return false; },
5437 pixels_per_point: function(state) { return 3; }
5440 initialize: NETDATA.morrisInitialize,
5441 create: NETDATA.morrisChartCreate,
5442 update: NETDATA.morrisChartUpdate,
5444 setSelection: undefined, // function(state, t) { return true; },
5445 clearSelection: undefined, // function(state) { return true; },
5446 toolboxPanAndZoom: null,
5449 format: function(state) { return 'json'; },
5450 options: function(state) { return 'objectrows|ms'; },
5451 legend: function(state) { return null; },
5452 autoresize: function(state) { return false; },
5453 max_updates_to_recreate: function(state) { return 50; },
5454 track_colors: function(state) { return false; },
5455 pixels_per_point: function(state) { return 15; }
5458 initialize: NETDATA.googleInitialize,
5459 create: NETDATA.googleChartCreate,
5460 update: NETDATA.googleChartUpdate,
5462 setSelection: undefined, //function(state, t) { return true; },
5463 clearSelection: undefined, //function(state) { return true; },
5464 toolboxPanAndZoom: null,
5467 format: function(state) { return 'datatable'; },
5468 options: function(state) { return ''; },
5469 legend: function(state) { return null; },
5470 autoresize: function(state) { return false; },
5471 max_updates_to_recreate: function(state) { return 300; },
5472 track_colors: function(state) { return false; },
5473 pixels_per_point: function(state) { return 4; }
5476 initialize: NETDATA.raphaelInitialize,
5477 create: NETDATA.raphaelChartCreate,
5478 update: NETDATA.raphaelChartUpdate,
5480 setSelection: undefined, // function(state, t) { return true; },
5481 clearSelection: undefined, // function(state) { return true; },
5482 toolboxPanAndZoom: null,
5485 format: function(state) { return 'json'; },
5486 options: function(state) { return ''; },
5487 legend: function(state) { return null; },
5488 autoresize: function(state) { return false; },
5489 max_updates_to_recreate: function(state) { return 5000; },
5490 track_colors: function(state) { return false; },
5491 pixels_per_point: function(state) { return 3; }
5494 initialize: NETDATA.c3Initialize,
5495 create: NETDATA.c3ChartCreate,
5496 update: NETDATA.c3ChartUpdate,
5498 setSelection: undefined, // function(state, t) { return true; },
5499 clearSelection: undefined, // function(state) { return true; },
5500 toolboxPanAndZoom: null,
5503 format: function(state) { return 'csvjsonarray'; },
5504 options: function(state) { return 'milliseconds'; },
5505 legend: function(state) { return null; },
5506 autoresize: function(state) { return false; },
5507 max_updates_to_recreate: function(state) { return 5000; },
5508 track_colors: function(state) { return false; },
5509 pixels_per_point: function(state) { return 15; }
5512 initialize: NETDATA.d3Initialize,
5513 create: NETDATA.d3ChartCreate,
5514 update: NETDATA.d3ChartUpdate,
5516 setSelection: undefined, // function(state, t) { return true; },
5517 clearSelection: undefined, // function(state) { return true; },
5518 toolboxPanAndZoom: null,
5521 format: function(state) { return 'json'; },
5522 options: function(state) { return ''; },
5523 legend: function(state) { return null; },
5524 autoresize: function(state) { return false; },
5525 max_updates_to_recreate: function(state) { return 5000; },
5526 track_colors: function(state) { return false; },
5527 pixels_per_point: function(state) { return 3; }
5530 initialize: NETDATA.easypiechartInitialize,
5531 create: NETDATA.easypiechartChartCreate,
5532 update: NETDATA.easypiechartChartUpdate,
5534 setSelection: NETDATA.easypiechartSetSelection,
5535 clearSelection: NETDATA.easypiechartClearSelection,
5536 toolboxPanAndZoom: null,
5539 format: function(state) { return 'array'; },
5540 options: function(state) { return 'absolute'; },
5541 legend: function(state) { return null; },
5542 autoresize: function(state) { return false; },
5543 max_updates_to_recreate: function(state) { return 5000; },
5544 track_colors: function(state) { return true; },
5545 pixels_per_point: function(state) { return 3; },
5549 initialize: NETDATA.gaugeInitialize,
5550 create: NETDATA.gaugeChartCreate,
5551 update: NETDATA.gaugeChartUpdate,
5553 setSelection: NETDATA.gaugeSetSelection,
5554 clearSelection: NETDATA.gaugeClearSelection,
5555 toolboxPanAndZoom: null,
5558 format: function(state) { return 'array'; },
5559 options: function(state) { return 'absolute'; },
5560 legend: function(state) { return null; },
5561 autoresize: function(state) { return false; },
5562 max_updates_to_recreate: function(state) { return 5000; },
5563 track_colors: function(state) { return true; },
5564 pixels_per_point: function(state) { return 3; },
5569 NETDATA.registerChartLibrary = function(library, url) {
5570 if(NETDATA.options.debug.libraries === true)
5571 console.log("registering chart library: " + library);
5573 NETDATA.chartLibraries[library].url = url;
5574 NETDATA.chartLibraries[library].initialized = true;
5575 NETDATA.chartLibraries[library].enabled = true;
5578 // ----------------------------------------------------------------------------------------------------------------
5579 // Load required JS libraries and CSS
5581 NETDATA.requiredJs = [
5583 url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js',
5585 isAlreadyLoaded: function() {
5586 // check if bootstrap is loaded
5587 if(typeof $().emulateTransitionEnd == 'function')
5590 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5598 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js',
5599 isAlreadyLoaded: function() { return false; }
5603 NETDATA.requiredCSS = [
5605 url: NETDATA.themes.current.bootstrap_css,
5606 isAlreadyLoaded: function() {
5607 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5614 url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
5615 isAlreadyLoaded: function() { return false; }
5618 url: NETDATA.themes.current.dashboard_css,
5619 isAlreadyLoaded: function() { return false; }
5623 NETDATA.loadedRequiredJs = 0;
5624 NETDATA.loadRequiredJs = function(index, callback) {
5625 if(index >= NETDATA.requiredJs.length) {
5626 if(typeof callback === 'function')
5631 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5632 NETDATA.loadedRequiredJs++;
5633 NETDATA.loadRequiredJs(++index, callback);
5637 if(NETDATA.options.debug.main_loop === true)
5638 console.log('loading ' + NETDATA.requiredJs[index].url);
5641 if(typeof NETDATA.requiredJs[index].async !== 'undefined' && NETDATA.requiredJs[index].async === false)
5645 url: NETDATA.requiredJs[index].url,
5648 xhrFields: { withCredentials: true } // required for the cookie
5651 if(NETDATA.options.debug.main_loop === true)
5652 console.log('loaded ' + NETDATA.requiredJs[index].url);
5655 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5657 .always(function() {
5658 NETDATA.loadedRequiredJs++;
5661 NETDATA.loadRequiredJs(++index, callback);
5665 NETDATA.loadRequiredJs(++index, callback);
5668 NETDATA.loadRequiredCSS = function(index) {
5669 if(index >= NETDATA.requiredCSS.length)
5672 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5673 NETDATA.loadRequiredCSS(++index);
5677 if(NETDATA.options.debug.main_loop === true)
5678 console.log('loading ' + NETDATA.requiredCSS[index].url);
5680 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5681 NETDATA.loadRequiredCSS(++index);
5685 // ----------------------------------------------------------------------------------------------------------------
5686 // Registry of netdata hosts
5689 onclick: null, // the callback to handle the click - it will be called with the alarm log entry
5690 chart_div_offset: 100, // give that space above the chart when scrolling to it
5691 chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id))
5692 chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart
5694 ms_penalty: 0, // the time penalty of the next alarm
5695 ms_between_notifications: 500, // firefox moves the alarms off-screen (above, outside the top of the screen)
5696 // if alarms are shown faster than: one per 500ms
5698 notifications: false, // when true, the browser supports notifications (may not be granted though)
5699 last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5700 first_notification_id: 0, // the id of the first alarm_log entry for this session
5701 // this is used to prevent CLEAR notifications for past events
5702 // notifications_shown: new Array(),
5704 server: null, // the server to connect to for fetching alarms
5705 current: null, // the list of raised alarms - updated in the background
5706 callback: null, // a callback function to call every time the list of raised alarms is refreshed
5708 notify: function(entry) {
5709 // console.log('alarm ' + entry.unique_id);
5711 if(entry.updated === true) {
5712 // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5716 var value = entry.value;
5717 if(NETDATA.alarms.current !== null) {
5718 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
5719 if(typeof t !== 'undefined' && entry.status == t.status)
5723 var name = entry.name.replace(/_/g, ' ');
5724 var status = entry.status.toLowerCase();
5725 var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
5726 var tag = entry.alarm_id;
5727 var icon = 'images/seo-performance-128.png';
5728 var interaction = false;
5732 // console.log('alarm ' + entry.unique_id + ' ' + entry.chart + '.' + entry.name + ' is ' + entry.status);
5734 switch(entry.status) {
5742 case 'UNINITIALIZED':
5746 if(entry.unique_id < NETDATA.alarms.first_notification_id) {
5747 // console.log('alarm ' + entry.unique_id + ' is not current');
5750 if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5751 // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5754 title = name + ' back to normal';
5755 icon = 'images/check-mark-2-128-green.png'
5756 interaction = false;
5760 if(entry.old_status === 'CRITICAL')
5761 status = 'demoted to ' + entry.status.toLowerCase();
5763 icon = 'images/alert-128-orange.png';
5764 interaction = false;
5768 if(entry.old_status === 'WARNING')
5769 status = 'escalated to ' + entry.status.toLowerCase();
5771 icon = 'images/alert-128-red.png'
5776 console.log('invalid alarm status ' + entry.status);
5781 // cleanup old notifications with the same alarm_id as this one
5782 // FIXME: it does not seem to work on any web browser!
5783 var len = NETDATA.alarms.notifications_shown.length;
5785 var n = NETDATA.alarms.notifications_shown[len];
5786 if(n.data.alarm_id === entry.alarm_id) {
5787 console.log('removing old alarm ' + n.data.unique_id);
5789 // close the notification
5792 // remove it from the array
5793 NETDATA.alarms.notifications_shown.splice(len, 1);
5794 len = NETDATA.alarms.notifications_shown.length;
5801 setTimeout(function() {
5802 // show this notification
5803 // console.log('new notification: ' + title);
5804 var n = new Notification(title, {
5805 body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info,
5807 requireInteraction: interaction,
5808 icon: NETDATA.serverDefault + icon,
5812 n.onclick = function(event) {
5813 event.preventDefault();
5814 NETDATA.alarms.onclick(event.target.data);
5818 // NETDATA.alarms.notifications_shown.push(n);
5819 // console.log(entry);
5820 }, NETDATA.alarms.ms_penalty);
5822 NETDATA.alarms.ms_penalty += NETDATA.alarms.ms_between_notifications;
5826 scrollToChart: function(chart_id) {
5827 if(typeof chart_id === 'string') {
5828 var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset();
5829 if(typeof offset !== 'undefined') {
5830 $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration);
5837 scrollToAlarm: function(alarm) {
5838 if(typeof alarm === 'object') {
5839 var ret = NETDATA.alarms.scrollToChart(alarm.chart);
5841 if(ret === true && NETDATA.options.page_is_visible === false)
5843 // 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.');
5848 notifyAll: function() {
5849 // console.log('FETCHING ALARM LOG');
5850 NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5851 // console.log('ALARM LOG FETCHED');
5853 if(data === null || typeof data !== 'object') {
5854 console.log('invalid alarms log response');
5858 if(data.length === 0) {
5859 console.log('received empty alarm log');
5863 // console.log('received alarm log of ' + data.length + ' entries, from ' + data[data.length - 1].unique_id.toString() + ' to ' + data[0].unique_id.toString());
5865 data.sort(function(a, b) {
5866 if(a.unique_id > b.unique_id) return -1;
5867 if(a.unique_id < b.unique_id) return 1;
5871 NETDATA.alarms.ms_penalty = 0;
5873 var len = data.length;
5875 if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5876 NETDATA.alarms.notify(data[len]);
5879 // console.log('ignoring alarm (older) with id ' + data[len].unique_id.toString());
5882 NETDATA.alarms.last_notification_id = data[0].unique_id;
5883 NETDATA.localStorageSet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5884 // console.log('last notification id = ' + NETDATA.alarms.last_notification_id);
5888 check_notifications: function() {
5889 // returns true if we should fire 1+ notifications
5891 if(NETDATA.alarms.notifications !== true) {
5892 // console.log('notifications not available');
5896 if(Notification.permission !== 'granted') {
5897 // console.log('notifications not granted');
5901 if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5902 // console.log('can do alarms: old id = ' + NETDATA.alarms.last_notification_id + ' new id = ' + NETDATA.alarms.current.latest_alarm_log_unique_id);
5904 if(NETDATA.alarms.current.latest_alarm_log_unique_id > NETDATA.alarms.last_notification_id) {
5905 // console.log('new alarms detected');
5908 //else console.log('no new alarms');
5910 // else console.log('cannot process alarms');
5915 get: function(what, callback) {
5917 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5920 xhrFields: { withCredentials: true } // required for the cookie
5922 .done(function(data) {
5923 if(NETDATA.alarms.first_notification_id === 0 && typeof data.latest_alarm_log_unique_id === 'number')
5924 NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
5926 if(typeof callback === 'function')
5930 NETDATA.error(415, NETDATA.alarms.server);
5932 if(typeof callback === 'function')
5937 update_forever: function() {
5938 NETDATA.alarms.get('active', function(data) {
5940 NETDATA.alarms.current = data;
5942 if(NETDATA.alarms.check_notifications() === true) {
5943 NETDATA.alarms.notifyAll();
5946 if (typeof NETDATA.alarms.callback === 'function') {
5947 NETDATA.alarms.callback(data);
5950 // Health monitoring is disabled on this netdata
5951 if(data.status === false) return;
5954 setTimeout(NETDATA.alarms.update_forever, 10000);
5958 get_log: function(last_id, callback) {
5959 // console.log('fetching all log after ' + last_id.toString());
5961 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5964 xhrFields: { withCredentials: true } // required for the cookie
5966 .done(function(data) {
5967 if(typeof callback === 'function')
5971 NETDATA.error(416, NETDATA.alarms.server);
5973 if(typeof callback === 'function')
5979 var host = NETDATA.serverDefault;
5980 while(host.slice(-1) === '/')
5981 host = host.substring(0, host.length - 1);
5982 NETDATA.alarms.server = host;
5984 NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5986 if(NETDATA.alarms.onclick === null)
5987 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
5989 if(netdataShowAlarms === true) {
5990 NETDATA.alarms.update_forever();
5992 if('Notification' in window) {
5993 // console.log('notifications available');
5994 NETDATA.alarms.notifications = true;
5996 if(Notification.permission === 'default')
5997 Notification.requestPermission();
6003 // ----------------------------------------------------------------------------------------------------------------
6004 // Registry of netdata hosts
6006 NETDATA.registry = {
6007 server: null, // the netdata registry server
6008 person_guid: null, // the unique ID of this browser / user
6009 machine_guid: null, // the unique ID the netdata server that served dashboard.js
6010 hostname: null, // the hostname of the netdata server that served dashboard.js
6011 machines: null, // the user's other URLs
6012 machines_array: null, // the user's other URLs in an array
6015 parsePersonUrls: function(person_urls) {
6016 // console.log(person_urls);
6017 NETDATA.registry.person_urls = person_urls;
6020 NETDATA.registry.machines = {};
6021 NETDATA.registry.machines_array = new Array();
6023 var now = new Date().getTime();
6024 var apu = person_urls;
6027 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
6028 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6034 accesses: apu[i][3],
6036 alternate_urls: new Array()
6038 obj.alternate_urls.push(apu[i][1]);
6040 NETDATA.registry.machines[apu[i][0]] = obj;
6041 NETDATA.registry.machines_array.push(obj);
6044 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6046 var pu = NETDATA.registry.machines[apu[i][0]];
6047 if(pu.last_t < apu[i][2]) {
6049 pu.last_t = apu[i][2];
6050 pu.name = apu[i][4];
6052 pu.accesses += apu[i][3];
6053 pu.alternate_urls.push(apu[i][1]);
6058 if(typeof netdataRegistryCallback === 'function')
6059 netdataRegistryCallback(NETDATA.registry.machines_array);
6063 if(netdataRegistry !== true) return;
6065 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
6067 NETDATA.registry.server = data.registry;
6068 NETDATA.registry.machine_guid = data.machine_guid;
6069 NETDATA.registry.hostname = data.hostname;
6071 NETDATA.registry.access(2, function (person_urls) {
6072 NETDATA.registry.parsePersonUrls(person_urls);
6079 hello: function(host, callback) {
6080 while(host.slice(-1) === '/')
6081 host = host.substring(0, host.length - 1);
6083 // send HELLO to a netdata server:
6084 // 1. verifies the server is reachable
6085 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
6087 url: host + '/api/v1/registry?action=hello',
6090 xhrFields: { withCredentials: true } // required for the cookie
6092 .done(function(data) {
6093 if(typeof data.status !== 'string' || data.status !== 'ok') {
6094 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
6098 if(typeof callback === 'function')
6102 NETDATA.error(407, host);
6104 if(typeof callback === 'function')
6109 access: function(max_redirects, callback) {
6110 // send ACCESS to a netdata registry:
6111 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
6112 // 2. it responds with a list of netdata servers we know
6113 // the registry identifies us using a cookie it sets the first time we access it
6114 // the registry may respond with a redirect URL to send us to another registry
6116 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),
6119 xhrFields: { withCredentials: true } // required for the cookie
6121 .done(function(data) {
6122 var redirect = null;
6123 if(typeof data.registry === 'string')
6124 redirect = data.registry;
6126 if(typeof data.status !== 'string' || data.status !== 'ok') {
6127 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6132 if(redirect !== null && max_redirects > 0) {
6133 NETDATA.registry.server = redirect;
6134 NETDATA.registry.access(max_redirects - 1, callback);
6137 if(typeof callback === 'function')
6142 if(typeof data.person_guid === 'string')
6143 NETDATA.registry.person_guid = data.person_guid;
6145 if(typeof callback === 'function')
6146 callback(data.urls);
6150 NETDATA.error(410, NETDATA.registry.server);
6152 if(typeof callback === 'function')
6157 delete: function(delete_url, callback) {
6158 // send DELETE to a netdata registry:
6160 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),
6163 xhrFields: { withCredentials: true } // required for the cookie
6165 .done(function(data) {
6166 if(typeof data.status !== 'string' || data.status !== 'ok') {
6167 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6171 if(typeof callback === 'function')
6175 NETDATA.error(412, NETDATA.registry.server);
6177 if(typeof callback === 'function')
6182 search: function(machine_guid, callback) {
6183 // SEARCH for the URLs of a machine:
6185 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,
6188 xhrFields: { withCredentials: true } // required for the cookie
6190 .done(function(data) {
6191 if(typeof data.status !== 'string' || data.status !== 'ok') {
6192 NETDATA.error(417, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6196 if(typeof callback === 'function')
6200 NETDATA.error(418, NETDATA.registry.server);
6202 if(typeof callback === 'function')
6207 switch: function(new_person_guid, callback) {
6210 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,
6213 xhrFields: { withCredentials: true } // required for the cookie
6215 .done(function(data) {
6216 if(typeof data.status !== 'string' || data.status !== 'ok') {
6217 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6221 if(typeof callback === 'function')
6225 NETDATA.error(414, NETDATA.registry.server);
6227 if(typeof callback === 'function')
6233 // ----------------------------------------------------------------------------------------------------------------
6236 if(typeof netdataPrepCallback === 'function')
6237 netdataPrepCallback();
6239 NETDATA.errorReset();
6240 NETDATA.loadRequiredCSS(0);
6242 NETDATA._loadjQuery(function() {
6243 NETDATA.loadRequiredJs(0, function() {
6244 if(typeof $().emulateTransitionEnd !== 'function') {
6245 // bootstrap is not available
6246 NETDATA.options.current.show_help = false;
6249 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
6250 if(NETDATA.options.debug.main_loop === true)
6251 console.log('starting chart refresh thread');
6258 // window.NETDATA = NETDATA;
6259 // })(window, document);