1 // You can set the following variables before loading this script:
3 // var netdataNoDygraphs = true; // do not use dygraph
4 // var netdataNoSparklines = true; // do not use sparkline
5 // var netdataNoPeitys = true; // do not use peity
6 // var netdataNoGoogleCharts = true; // do not use google
7 // var netdataNoMorris = true; // do not use morris
8 // var netdataNoEasyPieChart = true; // do not use easy pie chart
9 // var netdataNoGauge = true; // do not use gauge.js
10 // var netdataNoD3 = true; // do not use D3
11 // var netdataNoC3 = true; // do not use C3
12 // var netdataNoBootstrap = true; // do not load bootstrap
13 // var netdataDontStart = true; // do not start the thread to process the charts
14 // var netdataErrorCallback = null; // Callback function that will be invoked upon error
15 // var netdataRegistry = true; // Update the registry (default disabled)
16 // var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
17 // the URLs from the registry
18 // var netdataShowHelp = false; // enable/disable help (default enabled)
19 // var netdataShowAlarms = true; // enable/disable alarms checks and notifications (default disabled)
21 // var netdataRegistryAfterMs = 1500 // the time to consult to registry on startup
23 // var netdataCallback = null; // a function to call when netdata is ready
24 // // netdata will be running while this is called (call NETDATA.pause to stop it)
25 // var netdataPrepCallback = null; // a callback to be called before netdata does anything else
27 // You can also set the default netdata server, using the following.
28 // When this variable is not set, we assume the page is hosted on your
29 // netdata server already.
30 // var netdataServer = "http://yourhost:19999"; // set your NetData server
32 //(function(window, document, undefined) {
34 // ------------------------------------------------------------------------
35 // compatibility fixes
37 // fix IE issue with console
38 if(!window.console) { window.console = { log: function(){} }; }
40 // if string.endsWith is not defined, define it
41 if(typeof String.prototype.endsWith !== 'function') {
42 String.prototype.endsWith = function(s) {
43 if(s.length > this.length) return false;
44 return this.slice(-s.length) === s;
48 // if string.startsWith is not defined, define it
49 if(typeof String.prototype.startsWith !== 'function') {
50 String.prototype.startsWith = function(s) {
51 if(s.length > this.length) return false;
52 return this.slice(s.length) === s;
57 var NETDATA = window.NETDATA || {};
59 NETDATA.name2id = function(s) {
68 // ----------------------------------------------------------------------------------------------------------------
69 // Detect the netdata server
71 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
72 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
73 NETDATA._scriptSource = function() {
76 if(typeof document.currentScript !== 'undefined') {
77 script = document.currentScript;
80 var all_scripts = document.getElementsByTagName('script');
81 script = all_scripts[all_scripts.length - 1];
84 if (typeof script.getAttribute.length !== 'undefined')
87 script = script.getAttribute('src', -1);
92 if(typeof netdataServer !== 'undefined')
93 NETDATA.serverDefault = netdataServer;
95 var s = NETDATA._scriptSource();
96 if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
98 console.log('WARNING: Cannot detect the URL of the netdata server.');
99 NETDATA.serverDefault = null;
103 if(NETDATA.serverDefault === null)
104 NETDATA.serverDefault = '';
105 else if(NETDATA.serverDefault.slice(-1) !== '/')
106 NETDATA.serverDefault += '/';
108 // default URLs for all the external files we need
109 // make them RELATIVE so that the whole thing can also be
110 // installed under a web server
111 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-2.2.4.min.js';
112 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js';
113 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js';
114 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js';
115 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-d5260c3.min.js';
116 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js';
117 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js';
118 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js';
119 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3-0.4.11.min.js';
120 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3-0.4.11.min.css';
121 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3-3.5.17.min.js';
122 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris-0.5.1.min.js';
123 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris-0.5.1.css';
124 NETDATA.google_js = 'https://www.google.com/jsapi';
128 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css',
129 dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161002-1',
130 background: '#FFFFFF',
131 foreground: '#000000',
134 colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477',
135 '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6',
136 '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707',
137 '#329262', '#3B3EAC' ],
138 easypiechart_track: '#f0f0f0',
139 easypiechart_scale: '#dfe0e0',
140 gauge_pointer: '#C0C0C0',
141 gauge_stroke: '#F0F0F0',
142 gauge_gradient: false
145 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css?v20161002-1',
146 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-1',
147 background: '#272b30',
148 foreground: '#C8C8C8',
151 /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00',
152 '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0',
153 '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a',
154 '#a6a479', '#a66da8' ],
156 colors: [ '#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00',
157 '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700',
158 '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737',
159 '#329262', '#3B3EFF' ],
160 easypiechart_track: '#373b40',
161 easypiechart_scale: '#373b40',
162 gauge_pointer: '#474b50',
163 gauge_stroke: '#373b40',
164 gauge_gradient: false
168 if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
169 NETDATA.themes.current = NETDATA.themes[netdataTheme];
171 NETDATA.themes.current = NETDATA.themes.white;
173 NETDATA.colors = NETDATA.themes.current.colors;
175 // these are the colors Google Charts are using
176 // we have them here to attempt emulate their look and feel on the other chart libraries
177 // http://there4.io/2012/05/02/google-chart-color-list/
178 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
179 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
180 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
182 // an alternative set
183 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
184 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
185 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
187 if(typeof netdataShowHelp === 'undefined')
188 netdataShowHelp = true;
190 if(typeof netdataShowAlarms === 'undefined')
191 netdataShowAlarms = false;
193 if(typeof netdataRegistryAfterMs !== 'number' || netdataRegistryAfterMs < 0)
194 netdataRegistryAfterMs = 1500;
196 if(typeof netdataRegistry === 'undefined') {
197 // backward compatibility
198 if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false)
199 netdataRegistry = true;
201 netdataRegistry = false;
203 if(netdataRegistry === false && typeof netdataRegistryCallback === 'function')
204 netdataRegistry = true;
206 // ----------------------------------------------------------------------------------------------------------------
207 // the defaults for all charts
209 // if the user does not specify any of these, the following will be used
211 NETDATA.chartDefaults = {
212 host: NETDATA.serverDefault, // the server to get data from
213 width: '100%', // the chart width - can be null
214 height: '100%', // the chart height - can be null
215 min_width: null, // the chart minimum width - can be null
216 library: 'dygraph', // the graphing library to use
217 method: 'average', // the grouping method
218 before: 0, // panning
219 after: -600, // panning
220 pixels_per_point: 1, // the detail of the chart
221 fill_luminance: 0.8 // luminance of colors in solit areas
224 // ----------------------------------------------------------------------------------------------------------------
228 pauseCallback: null, // a callback when we are really paused
230 pause: false, // when enabled we don't auto-refresh the charts
232 targets: null, // an array of all the state objects that are
233 // currently active (independently of their
234 // viewport visibility)
236 updated_dom: true, // when true, the DOM has been updated with
237 // new elements we have to check.
239 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
240 // rendering charts continiously.
241 // used with .current.fast_render_timeframe
243 page_is_visible: true, // when true, this page is visible
245 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
246 // auto-refresher for some time (when a chart is
247 // performing pan or zoom, we need to stop refreshing
248 // all other charts, to have the maximum speed for
249 // rendering the chart that is panned or zoomed).
250 // Used with .current.global_pan_sync_time
252 last_resized: new Date().getTime(), // the timestamp of the last resize request
254 last_page_scroll: 0, // the timestamp the last time the page was scrolled
256 // the current profile
257 // we may have many...
259 pixels_per_point: 1, // the minimum pixels per point for all charts
260 // increase this to speed javascript up
261 // each chart library has its own limit too
262 // the max of this and the chart library is used
263 // the final is calculated every time, so a change
264 // here will have immediate effect on the next chart
267 idle_between_charts: 100, // ms - how much time to wait between chart updates
269 fast_render_timeframe: 200, // ms - render continously until this time of continious
270 // rendering has been reached
271 // this setting is used to make it render e.g. 10
272 // charts at once, sleep idle_between_charts time
273 // and continue for another 10 charts.
275 idle_between_loops: 500, // ms - if all charts have been updated, wait this
276 // time before starting again.
278 idle_parallel_loops: 100, // ms - the time between parallel refresher updates
280 idle_lost_focus: 500, // ms - when the window does not have focus, check
281 // if focus has been regained, every this time
283 global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
284 // autorefreshing of charts is paused for this amount
287 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
288 // of time before setting up synchronized selections
291 sync_selection: true, // enable or disable selection sync
293 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
295 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
297 pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
299 update_only_visible: true, // enable or disable visibility management
301 parallel_refresher: true, // enable parallel refresh of charts
303 concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
305 destroy_on_hide: false, // destroy charts when they are not visible
307 show_help: netdataShowHelp, // when enabled the charts will show some help
308 show_help_delay_show_ms: 500,
309 show_help_delay_hide_ms: 0,
311 eliminate_zero_dimensions: true, // do not show dimensions with just zeros
313 stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
314 stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts
316 double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap
318 smooth_plot: true, // enable smooth plot, where possible
320 charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
322 color_fill_opacity_line: 1.0,
323 color_fill_opacity_area: 0.2,
324 color_fill_opacity_stacked: 0.8,
326 pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
327 pan_and_zoom_factor_multiplier_control: 2.0,
328 pan_and_zoom_factor_multiplier_shift: 3.0,
329 pan_and_zoom_factor_multiplier_alt: 4.0,
331 abort_ajax_on_scroll: false, // kill pending ajax page scroll
332 async_on_scroll: false, // sync/async onscroll handler
333 onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler
335 setOptionCallback: function() { ; }
343 chart_data_url: false,
344 chart_errors: false, // FIXME
352 NETDATA.statistics = {
355 refreshes_active_max: 0
359 // ----------------------------------------------------------------------------------------------------------------
360 // local storage options
362 NETDATA.localStorage = {
365 callback: {} // only used for resetting back to defaults
368 NETDATA.localStorageGet = function(key, def, callback) {
371 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
372 NETDATA.localStorage.default[key.toString()] = def;
373 NETDATA.localStorage.callback[key.toString()] = callback;
376 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
378 // console.log('localStorage: loading "' + key.toString() + '"');
379 ret = localStorage.getItem(key.toString());
380 // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
381 if(ret === null || ret === 'undefined') {
382 // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
383 localStorage.setItem(key.toString(), JSON.stringify(def));
387 // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
388 ret = JSON.parse(ret);
389 // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
393 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
398 if(typeof ret === 'undefined' || ret === 'undefined') {
399 console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
403 NETDATA.localStorage.current[key.toString()] = ret;
407 NETDATA.localStorageSet = function(key, value, callback) {
408 if(typeof value === 'undefined' || value === 'undefined') {
409 console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
412 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
413 NETDATA.localStorage.default[key.toString()] = value;
414 NETDATA.localStorage.current[key.toString()] = value;
415 NETDATA.localStorage.callback[key.toString()] = callback;
418 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
419 // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
421 localStorage.setItem(key.toString(), JSON.stringify(value));
424 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
428 NETDATA.localStorage.current[key.toString()] = value;
432 NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
434 if(typeof obj[i] === 'object') {
435 //console.log('object ' + prefix + '.' + i.toString());
436 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
440 obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
444 NETDATA.setOption = function(key, value) {
445 if(key.toString() === 'setOptionCallback') {
446 if(typeof NETDATA.options.current.setOptionCallback === 'function') {
447 NETDATA.options.current[key.toString()] = value;
448 NETDATA.options.current.setOptionCallback();
451 else if(NETDATA.options.current[key.toString()] !== value) {
452 var name = 'options.' + key.toString();
454 if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
455 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
457 //console.log(NETDATA.localStorage);
458 //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
459 //console.log(NETDATA.options);
460 NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
462 if(typeof NETDATA.options.current.setOptionCallback === 'function')
463 NETDATA.options.current.setOptionCallback();
469 NETDATA.getOption = function(key) {
470 return NETDATA.options.current[key.toString()];
473 // read settings from local storage
474 NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
476 // always start with this option enabled.
477 NETDATA.setOption('stop_updates_when_focus_is_lost', true);
479 NETDATA.resetOptions = function() {
480 for(var i in NETDATA.localStorage.default) {
481 var a = i.split('.');
483 if(a[0] === 'options') {
484 if(a[1] === 'setOptionCallback') continue;
485 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
486 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
488 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
490 else if(a[0] === 'chart_heights') {
491 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
492 NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
498 // ----------------------------------------------------------------------------------------------------------------
500 if(NETDATA.options.debug.main_loop === true)
501 console.log('welcome to NETDATA');
503 NETDATA.onresize = function() {
504 NETDATA.options.last_resized = new Date().getTime();
508 NETDATA.onscroll_updater_count = 0;
509 NETDATA.onscroll_updater_running = false;
510 NETDATA.onscroll_updater_last_run = 0;
511 NETDATA.onscroll_updater_watchdog = null;
512 NETDATA.onscroll_updater_max_duration = 0;
513 NETDATA.onscroll_updater_above_threshold_count = 0;
514 NETDATA.onscroll_updater = function() {
515 NETDATA.onscroll_updater_running = true;
516 NETDATA.onscroll_updater_count++;
517 var start = new Date().getTime();
519 var targets = NETDATA.options.targets;
520 var len = targets.length;
522 // when the user scrolls he sees that we have
523 // hidden all the not-visible charts
524 // using this little function we try to switch
525 // the charts back to visible quickly
528 if(NETDATA.options.abort_ajax_on_scroll === true) {
529 // we have to cancel pending requests too
532 if (targets[len]._updating === true) {
533 if (typeof targets[len].xhr !== 'undefined') {
534 targets[len].xhr.abort();
535 targets[len].running = false;
536 targets[len]._updating = false;
538 targets[len].isVisible();
543 // just find which chart is visible
546 targets[len].isVisible();
549 var end = new Date().getTime();
550 // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms');
552 if(NETDATA.options.current.async_on_scroll === false) {
553 var dt = end - start;
554 if(dt > NETDATA.onscroll_updater_max_duration) {
555 // console.log('max onscroll event handler duration increased to ' + dt);
556 NETDATA.onscroll_updater_max_duration = dt;
559 if(dt > NETDATA.options.current.onscroll_worker_duration_threshold) {
560 // console.log('slow: ' + dt);
561 NETDATA.onscroll_updater_above_threshold_count++;
563 if(NETDATA.onscroll_updater_above_threshold_count > 2 && NETDATA.onscroll_updater_above_threshold_count * 100 / NETDATA.onscroll_updater_count > 2) {
564 NETDATA.setOption('async_on_scroll', true);
565 console.log('NETDATA: your browser is slow - enabling asynchronous onscroll event handler.');
570 NETDATA.onscroll_updater_last_run = start;
571 NETDATA.onscroll_updater_running = false;
574 NETDATA.onscroll = function() {
575 // console.log('onscroll');
577 NETDATA.options.last_page_scroll = new Date().getTime();
578 NETDATA.options.auto_refresher_stop_until = 0;
580 if(NETDATA.options.targets === null) return;
582 if(NETDATA.options.current.async_on_scroll === true) {
584 if(NETDATA.onscroll_updater_running === false) {
585 NETDATA.onscroll_updater_running = true;
586 setTimeout(NETDATA.onscroll_updater, 0);
589 if(NETDATA.onscroll_updater_watchdog !== null)
590 clearTimeout(NETDATA.onscroll_updater_watchdog);
592 NETDATA.onscroll_updater_watchdog = setTimeout(function() {
593 if(NETDATA.onscroll_updater_running === false && NETDATA.options.last_page_scroll > NETDATA.onscroll_updater_last_run) {
594 // console.log('watchdog');
595 NETDATA.onscroll_updater();
598 NETDATA.onscroll_updater_watchdog = null;
604 NETDATA.onscroll_updater();
608 window.onresize = NETDATA.onresize;
609 window.onscroll = NETDATA.onscroll;
611 // ----------------------------------------------------------------------------------------------------------------
614 NETDATA.errorCodes = {
615 100: { message: "Cannot load chart library", alert: true },
616 101: { message: "Cannot load jQuery", alert: true },
617 402: { message: "Chart library not found", alert: false },
618 403: { message: "Chart library not enabled/is failed", alert: false },
619 404: { message: "Chart not found", alert: false },
620 405: { message: "Cannot download charts index from server", alert: true },
621 406: { message: "Invalid charts index downloaded from server", alert: true },
622 407: { message: "Cannot HELLO netdata server", alert: false },
623 408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
624 409: { message: "Cannot ACCESS netdata registry", alert: false },
625 410: { message: "Netdata registry ACCESS failed", alert: false },
626 411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
627 412: { message: "Netdata registry DELETE failed", alert: false },
628 413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
629 414: { message: "Netdata registry SWITCH failed", alert: false },
630 415: { message: "Netdata alarms download failed", alert: false },
631 416: { message: "Netdata alarms log download failed", alert: false },
632 417: { message: "Netdata registry server send invalid response to SEARCH ", alert: false },
633 418: { message: "Netdata registry SEARCH failed", alert: false }
635 NETDATA.errorLast = {
641 NETDATA.error = function(code, msg) {
642 NETDATA.errorLast.code = code;
643 NETDATA.errorLast.message = msg;
644 NETDATA.errorLast.datetime = new Date().getTime();
646 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
649 if(typeof netdataErrorCallback === 'function') {
650 ret = netdataErrorCallback('system', code, msg);
653 if(ret && NETDATA.errorCodes[code].alert)
654 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
657 NETDATA.errorReset = function() {
658 NETDATA.errorLast.code = 0;
659 NETDATA.errorLast.message = "You are doing fine!";
660 NETDATA.errorLast.datetime = 0;
663 // ----------------------------------------------------------------------------------------------------------------
666 // When multiple charts need the same chart, we avoid downloading it
667 // multiple times (and having it in browser memory multiple time)
668 // by using this registry.
670 // Every time we download a chart definition, we save it here with .add()
671 // Then we try to get it back with .get(). If that fails, we download it.
673 NETDATA.chartRegistry = {
676 fixid: function(id) {
677 return id.replace(/:/g, "_").replace(/\//g, "_");
680 add: function(host, id, data) {
681 host = this.fixid(host);
684 if(typeof this.charts[host] === 'undefined')
685 this.charts[host] = {};
687 //console.log('added ' + host + '/' + id);
688 this.charts[host][id] = data;
691 get: function(host, id) {
692 host = this.fixid(host);
695 if(typeof this.charts[host] === 'undefined')
698 if(typeof this.charts[host][id] === 'undefined')
701 //console.log('cached ' + host + '/' + id);
702 return this.charts[host][id];
705 downloadAll: function(host, callback) {
706 while(host.slice(-1) === '/')
707 host = host.substring(0, host.length - 1);
712 url: host + '/api/v1/charts',
715 xhrFields: { withCredentials: true } // required for the cookie
717 .done(function(data) {
719 var h = NETDATA.chartRegistry.fixid(host);
720 self.charts[h] = data.charts;
722 else NETDATA.error(406, host + '/api/v1/charts');
724 if(typeof callback === 'function')
728 NETDATA.error(405, host + '/api/v1/charts');
730 if(typeof callback === 'function')
736 // ----------------------------------------------------------------------------------------------------------------
737 // Global Pan and Zoom on charts
739 // Using this structure are synchronize all the charts, so that
740 // when you pan or zoom one, all others are automatically refreshed
741 // to the same timespan.
743 NETDATA.globalPanAndZoom = {
744 seq: 0, // timestamp ms
745 // every time a chart is panned or zoomed
746 // we set the timestamp here
747 // then we use it as a sequence number
748 // to find if other charts are syncronized
751 master: null, // the master chart (state), to which all others
754 force_before_ms: null, // the timespan to sync all other charts
755 force_after_ms: null,
760 setMaster: function(state, after, before) {
761 if(NETDATA.options.current.sync_pan_and_zoom === false)
764 if(this.master !== null && this.master !== state)
765 this.master.resetChart(true, true);
767 var now = new Date().getTime();
770 this.force_after_ms = after;
771 this.force_before_ms = before;
772 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
774 if(typeof this.callback === 'function')
775 this.callback(true, after, before);
779 clearMaster: function() {
780 if(this.master !== null) {
781 var st = this.master;
788 this.force_after_ms = null;
789 this.force_before_ms = null;
790 NETDATA.options.auto_refresher_stop_until = 0;
792 if(typeof this.callback === 'function')
793 this.callback(false, 0, 0);
796 // is the given state the master of the global
797 // pan and zoom sync?
798 isMaster: function(state) {
799 if(this.master === state) return true;
803 // are we currently have a global pan and zoom sync?
804 isActive: function() {
805 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
809 // check if a chart, other than the master
810 // needs to be refreshed, due to the global pan and zoom
811 shouldBeAutoRefreshed: function(state) {
812 if(this.master === null || this.seq === 0)
815 //if(state.needsRecreation())
818 if(state.tm.pan_and_zoom_seq === this.seq)
825 // ----------------------------------------------------------------------------------------------------------------
826 // dimensions selection
829 // move color assignment to dimensions, here
831 dimensionStatus = function(parent, label, name_div, value_div, color) {
832 this.enabled = false;
833 this.parent = parent;
835 this.name_div = null;
836 this.value_div = null;
837 this.color = NETDATA.themes.current.foreground;
839 if(parent.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 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
4007 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4,
4008 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
4010 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
4011 sigFigs: self.data('dygraph-sigfigs') || null,
4012 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
4013 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
4015 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
4016 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
4017 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
4019 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
4020 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
4024 ticker: Dygraph.dateTicker,
4025 axisLabelFormatter: function (d, gran) {
4026 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4028 valueFormatter: function (ms) {
4029 var d = new Date(ms);
4030 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
4031 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4036 valueFormatter: function (x) {
4037 // we format legends with the state object
4038 // no need to do anything here
4039 // return (Math.round(x*100) / 100).toLocaleString();
4040 // return state.legendFormatValue(x);
4045 legendFormatter: function(data) {
4046 var elements = state.element_legend_childs;
4048 // if the hidden div is not there
4049 // we are not managing the legend
4050 if(elements.hidden === null) return;
4052 if (typeof data.x !== 'undefined') {
4053 state.legendSetDate(data.x);
4054 var i = data.series.length;
4056 var series = data.series[i];
4057 if(!series.isVisible) continue;
4058 state.legendSetLabelValue(series.label, series.y);
4064 drawCallback: function(dygraph, is_initial) {
4065 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
4066 state.dygraph_user_action = false;
4068 var x_range = dygraph.xAxisRange();
4069 var after = Math.round(x_range[0]);
4070 var before = Math.round(x_range[1]);
4072 if(NETDATA.options.debug.dygraph === true)
4073 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
4075 if(before <= state.netdata_last && after >= state.netdata_first)
4076 state.updateChartPanOrZoom(after, before);
4079 zoomCallback: function(minDate, maxDate, yRanges) {
4080 if(NETDATA.options.debug.dygraph === true)
4081 state.log('dygraphZoomCallback()');
4083 state.globalSelectionSyncStop();
4084 state.globalSelectionSyncDelay();
4085 state.setMode('zoom');
4087 // refresh it to the greatest possible zoom level
4088 state.dygraph_user_action = true;
4089 state.dygraph_force_zoom = true;
4090 state.updateChartPanOrZoom(minDate, maxDate);
4092 highlightCallback: function(event, x, points, row, seriesName) {
4093 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4094 state.log('dygraphHighlightCallback()');
4098 // there is a bug in dygraph when the chart is zoomed enough
4099 // the time it thinks is selected is wrong
4100 // here we calculate the time t based on the row number selected
4102 var t = state.data_after + row * state.data_update_every;
4103 // 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);
4105 state.globalSelectionSync(x);
4107 // fix legend zIndex using the internal structures of dygraph legend module
4108 // this works, but it is a hack!
4109 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
4111 unhighlightCallback: function(event) {
4112 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4113 state.log('dygraphUnhighlightCallback()');
4115 state.unpauseChart();
4116 state.globalSelectionSyncStop();
4118 interactionModel : {
4119 mousedown: function(event, dygraph, context) {
4120 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4121 state.log('interactionModel.mousedown()');
4123 state.dygraph_user_action = true;
4124 state.globalSelectionSyncStop();
4126 if(NETDATA.options.debug.dygraph === true)
4127 state.log('dygraphMouseDown()');
4129 // Right-click should not initiate a zoom.
4130 if(event.button && event.button === 2) return;
4132 context.initializeMouseDown(event, dygraph, context);
4134 if(event.button && event.button === 1) {
4135 if (event.altKey || event.shiftKey) {
4136 state.setMode('pan');
4137 state.globalSelectionSyncDelay();
4138 Dygraph.startPan(event, dygraph, context);
4141 state.setMode('zoom');
4142 state.globalSelectionSyncDelay();
4143 Dygraph.startZoom(event, dygraph, context);
4147 if (event.altKey || event.shiftKey) {
4148 state.setMode('zoom');
4149 state.globalSelectionSyncDelay();
4150 Dygraph.startZoom(event, dygraph, context);
4153 state.setMode('pan');
4154 state.globalSelectionSyncDelay();
4155 Dygraph.startPan(event, dygraph, context);
4159 mousemove: function(event, dygraph, context) {
4160 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4161 state.log('interactionModel.mousemove()');
4163 if(context.isPanning) {
4164 state.dygraph_user_action = true;
4165 state.globalSelectionSyncStop();
4166 state.globalSelectionSyncDelay();
4167 state.setMode('pan');
4168 Dygraph.movePan(event, dygraph, context);
4170 else if(context.isZooming) {
4171 state.dygraph_user_action = true;
4172 state.globalSelectionSyncStop();
4173 state.globalSelectionSyncDelay();
4174 state.setMode('zoom');
4175 Dygraph.moveZoom(event, dygraph, context);
4178 mouseup: function(event, dygraph, context) {
4179 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4180 state.log('interactionModel.mouseup()');
4182 if (context.isPanning) {
4183 state.dygraph_user_action = true;
4184 state.globalSelectionSyncDelay();
4185 Dygraph.endPan(event, dygraph, context);
4187 else if (context.isZooming) {
4188 state.dygraph_user_action = true;
4189 state.globalSelectionSyncDelay();
4190 Dygraph.endZoom(event, dygraph, context);
4193 click: function(event, dygraph, context) {
4194 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4195 state.log('interactionModel.click()');
4197 event.preventDefault();
4199 dblclick: function(event, dygraph, context) {
4200 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4201 state.log('interactionModel.dblclick()');
4202 NETDATA.resetAllCharts(state);
4204 mousewheel: function(event, dygraph, context) {
4205 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4206 state.log('interactionModel.mousewheel()');
4208 // Take the offset of a mouse event on the dygraph canvas and
4209 // convert it to a pair of percentages from the bottom left.
4210 // (Not top left, bottom is where the lower value is.)
4211 function offsetToPercentage(g, offsetX, offsetY) {
4212 // This is calculating the pixel offset of the leftmost date.
4213 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4214 var yar0 = g.yAxisRange(0);
4216 // This is calculating the pixel of the higest value. (Top pixel)
4217 var yOffset = g.toDomCoords(null, yar0[1])[1];
4219 // x y w and h are relative to the corner of the drawing area,
4220 // so that the upper corner of the drawing area is (0, 0).
4221 var x = offsetX - xOffset;
4222 var y = offsetY - yOffset;
4224 // This is computing the rightmost pixel, effectively defining the
4226 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4228 // This is computing the lowest pixel, effectively defining the height.
4229 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4231 // Percentage from the left.
4232 var xPct = w === 0 ? 0 : (x / w);
4233 // Percentage from the top.
4234 var yPct = h === 0 ? 0 : (y / h);
4236 // The (1-) part below changes it from "% distance down from the top"
4237 // to "% distance up from the bottom".
4238 return [xPct, (1-yPct)];
4241 // Adjusts [x, y] toward each other by zoomInPercentage%
4242 // Split it so the left/bottom axis gets xBias/yBias of that change and
4243 // tight/top gets (1-xBias)/(1-yBias) of that change.
4245 // If a bias is missing it splits it down the middle.
4246 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4247 xBias = xBias || 0.5;
4248 yBias = yBias || 0.5;
4250 function adjustAxis(axis, zoomInPercentage, bias) {
4251 var delta = axis[1] - axis[0];
4252 var increment = delta * zoomInPercentage;
4253 var foo = [increment * bias, increment * (1-bias)];
4255 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4258 var yAxes = g.yAxisRanges();
4260 for (var i = 0; i < yAxes.length; i++) {
4261 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4264 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4267 if(event.altKey || event.shiftKey) {
4268 state.dygraph_user_action = true;
4270 state.globalSelectionSyncStop();
4271 state.globalSelectionSyncDelay();
4273 // http://dygraphs.com/gallery/interaction-api.js
4274 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4275 var percentage = normal / 50;
4277 if (!(event.offsetX && event.offsetY)){
4278 event.offsetX = event.layerX - event.target.offsetLeft;
4279 event.offsetY = event.layerY - event.target.offsetTop;
4282 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4283 var xPct = percentages[0];
4284 var yPct = percentages[1];
4286 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4288 var after = new_x_range[0];
4289 var before = new_x_range[1];
4291 var first = state.netdata_first + state.data_update_every;
4292 var last = state.netdata_last + state.data_update_every;
4295 after -= (before - last);
4302 state.setMode('zoom');
4303 if(state.updateChartPanOrZoom(after, before) === true)
4304 dygraph.updateOptions({ dateWindow: [ after, before ] });
4306 event.preventDefault();
4309 touchstart: function(event, dygraph, context) {
4310 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4311 state.log('interactionModel.touchstart()');
4313 state.dygraph_user_action = true;
4314 state.setMode('zoom');
4317 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4319 // we overwrite the touch directions at the end, to overwrite
4320 // the internal default of dygraphs
4321 context.touchDirections = { x: true, y: false };
4323 state.dygraph_last_touch_start = new Date().getTime();
4324 state.dygraph_last_touch_move = 0;
4326 if(typeof event.touches[0].pageX === 'number')
4327 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4329 state.dygraph_last_touch_page_x = 0;
4331 touchmove: function(event, dygraph, context) {
4332 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4333 state.log('interactionModel.touchmove()');
4335 state.dygraph_user_action = true;
4336 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4338 state.dygraph_last_touch_move = new Date().getTime();
4340 touchend: function(event, dygraph, context) {
4341 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4342 state.log('interactionModel.touchend()');
4344 state.dygraph_user_action = true;
4345 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4347 // if it didn't move, it is a selection
4348 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4349 // internal api of dygraphs
4350 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4351 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4352 if(NETDATA.dygraphSetSelection(state, t) === true)
4353 state.globalSelectionSync(t);
4356 // if it was double tap within double click time, reset the charts
4357 var now = new Date().getTime();
4358 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4359 if(state.dygraph_last_touch_move === 0) {
4360 var dt = now - state.dygraph_last_touch_end;
4361 if(dt <= NETDATA.options.current.double_click_speed)
4362 NETDATA.resetAllCharts(state);
4366 // remember the timestamp of the last touch end
4367 state.dygraph_last_touch_end = now;
4372 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4373 state.dygraph_options.drawGrid = false;
4374 state.dygraph_options.drawAxis = false;
4375 state.dygraph_options.title = undefined;
4376 state.dygraph_options.ylabel = undefined;
4377 state.dygraph_options.yLabelWidth = 0;
4378 state.dygraph_options.labelsDivWidth = 120;
4379 state.dygraph_options.labelsDivStyles.width = '120px';
4380 state.dygraph_options.labelsSeparateLines = true;
4381 state.dygraph_options.rightGap = 0;
4382 state.dygraph_options.yRangePad = 1;
4385 if(smooth === true) {
4386 state.dygraph_smooth_eligible = true;
4388 if(NETDATA.options.current.smooth_plot === true)
4389 state.dygraph_options.plotter = smoothPlotter;
4391 else state.dygraph_smooth_eligible = false;
4393 state.dygraph_instance = new Dygraph(state.element_chart,
4394 data.result.data, state.dygraph_options);
4396 state.dygraph_force_zoom = false;
4397 state.dygraph_user_action = false;
4398 state.dygraph_last_rendered = new Date().getTime();
4402 // ----------------------------------------------------------------------------------------------------------------
4405 NETDATA.morrisInitialize = function(callback) {
4406 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4408 // morris requires raphael
4409 if(!NETDATA.chartLibraries.raphael.initialized) {
4410 if(NETDATA.chartLibraries.raphael.enabled) {
4411 NETDATA.raphaelInitialize(function() {
4412 NETDATA.morrisInitialize(callback);
4416 NETDATA.chartLibraries.morris.enabled = false;
4417 if(typeof callback === "function")
4422 NETDATA._loadCSS(NETDATA.morris_css);
4425 url: NETDATA.morris_js,
4428 xhrFields: { withCredentials: true } // required for the cookie
4431 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4434 NETDATA.chartLibraries.morris.enabled = false;
4435 NETDATA.error(100, NETDATA.morris_js);
4437 .always(function() {
4438 if(typeof callback === "function")
4444 NETDATA.chartLibraries.morris.enabled = false;
4445 if(typeof callback === "function")
4450 NETDATA.morrisChartUpdate = function(state, data) {
4451 state.morris_instance.setData(data.result.data);
4455 NETDATA.morrisChartCreate = function(state, data) {
4457 state.morris_options = {
4458 element: state.element_chart.id,
4459 data: data.result.data,
4461 ykeys: data.dimension_names,
4462 labels: data.dimension_names,
4468 continuousLine: false,
4469 behaveLikeLine: false
4472 if(state.chart.chart_type === 'line')
4473 state.morris_instance = new Morris.Line(state.morris_options);
4475 else if(state.chart.chart_type === 'area') {
4476 state.morris_options.behaveLikeLine = true;
4477 state.morris_instance = new Morris.Area(state.morris_options);
4480 state.morris_instance = new Morris.Area(state.morris_options);
4485 // ----------------------------------------------------------------------------------------------------------------
4488 NETDATA.raphaelInitialize = function(callback) {
4489 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4491 url: NETDATA.raphael_js,
4494 xhrFields: { withCredentials: true } // required for the cookie
4497 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4500 NETDATA.chartLibraries.raphael.enabled = false;
4501 NETDATA.error(100, NETDATA.raphael_js);
4503 .always(function() {
4504 if(typeof callback === "function")
4509 NETDATA.chartLibraries.raphael.enabled = false;
4510 if(typeof callback === "function")
4515 NETDATA.raphaelChartUpdate = function(state, data) {
4516 $(state.element_chart).raphael(data.result, {
4517 width: state.chartWidth(),
4518 height: state.chartHeight()
4524 NETDATA.raphaelChartCreate = function(state, data) {
4525 $(state.element_chart).raphael(data.result, {
4526 width: state.chartWidth(),
4527 height: state.chartHeight()
4533 // ----------------------------------------------------------------------------------------------------------------
4536 NETDATA.c3Initialize = function(callback) {
4537 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4540 if(!NETDATA.chartLibraries.d3.initialized) {
4541 if(NETDATA.chartLibraries.d3.enabled) {
4542 NETDATA.d3Initialize(function() {
4543 NETDATA.c3Initialize(callback);
4547 NETDATA.chartLibraries.c3.enabled = false;
4548 if(typeof callback === "function")
4553 NETDATA._loadCSS(NETDATA.c3_css);
4559 xhrFields: { withCredentials: true } // required for the cookie
4562 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4565 NETDATA.chartLibraries.c3.enabled = false;
4566 NETDATA.error(100, NETDATA.c3_js);
4568 .always(function() {
4569 if(typeof callback === "function")
4575 NETDATA.chartLibraries.c3.enabled = false;
4576 if(typeof callback === "function")
4581 NETDATA.c3ChartUpdate = function(state, data) {
4582 state.c3_instance.destroy();
4583 return NETDATA.c3ChartCreate(state, data);
4585 //state.c3_instance.load({
4586 // rows: data.result,
4593 NETDATA.c3ChartCreate = function(state, data) {
4595 state.element_chart.id = 'c3-' + state.uuid;
4596 // console.log('id = ' + state.element_chart.id);
4598 state.c3_instance = c3.generate({
4599 bindto: '#' + state.element_chart.id,
4601 width: state.chartWidth(),
4602 height: state.chartHeight()
4605 pattern: state.chartColors()
4610 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4616 format: function(x) {
4617 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4644 // console.log(state.c3_instance);
4649 // ----------------------------------------------------------------------------------------------------------------
4652 NETDATA.d3Initialize = function(callback) {
4653 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4658 xhrFields: { withCredentials: true } // required for the cookie
4661 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4664 NETDATA.chartLibraries.d3.enabled = false;
4665 NETDATA.error(100, NETDATA.d3_js);
4667 .always(function() {
4668 if(typeof callback === "function")
4673 NETDATA.chartLibraries.d3.enabled = false;
4674 if(typeof callback === "function")
4679 NETDATA.d3ChartUpdate = function(state, data) {
4683 NETDATA.d3ChartCreate = function(state, data) {
4687 // ----------------------------------------------------------------------------------------------------------------
4690 NETDATA.googleInitialize = function(callback) {
4691 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4693 url: NETDATA.google_js,
4696 xhrFields: { withCredentials: true } // required for the cookie
4699 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4700 google.load('visualization', '1.1', {
4701 'packages': ['corechart', 'controls'],
4702 'callback': callback
4706 NETDATA.chartLibraries.google.enabled = false;
4707 NETDATA.error(100, NETDATA.google_js);
4708 if(typeof callback === "function")
4713 NETDATA.chartLibraries.google.enabled = false;
4714 if(typeof callback === "function")
4719 NETDATA.googleChartUpdate = function(state, data) {
4720 var datatable = new google.visualization.DataTable(data.result);
4721 state.google_instance.draw(datatable, state.google_options);
4725 NETDATA.googleChartCreate = function(state, data) {
4726 var datatable = new google.visualization.DataTable(data.result);
4728 state.google_options = {
4729 colors: state.chartColors(),
4731 // do not set width, height - the chart resizes itself
4732 //width: state.chartWidth(),
4733 //height: state.chartHeight(),
4738 // title: "Time of Day",
4739 // format:'HH:mm:ss',
4740 viewWindowMode: 'maximized',
4752 viewWindowMode: 'pretty',
4767 focusTarget: 'category',
4774 titlePosition: 'out',
4785 curveType: 'function',
4790 switch(state.chart.chart_type) {
4792 state.google_options.vAxis.viewWindowMode = 'maximized';
4793 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4794 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4798 state.google_options.isStacked = true;
4799 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4800 state.google_options.vAxis.viewWindowMode = 'maximized';
4801 state.google_options.vAxis.minValue = null;
4802 state.google_options.vAxis.maxValue = null;
4803 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4808 state.google_options.lineWidth = 2;
4809 state.google_instance = new google.visualization.LineChart(state.element_chart);
4813 state.google_instance.draw(datatable, state.google_options);
4817 // ----------------------------------------------------------------------------------------------------------------
4819 NETDATA.percentFromValueMax = function(value, max) {
4820 if(value === null) value = 0;
4821 if(max < value) max = value;
4825 pcent = Math.round(value * 100 / max);
4826 if(pcent === 0 && value > 0) pcent = 1;
4832 // ----------------------------------------------------------------------------------------------------------------
4835 NETDATA.easypiechartInitialize = function(callback) {
4836 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4838 url: NETDATA.easypiechart_js,
4841 xhrFields: { withCredentials: true } // required for the cookie
4844 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4847 NETDATA.chartLibraries.easypiechart.enabled = false;
4848 NETDATA.error(100, NETDATA.easypiechart_js);
4850 .always(function() {
4851 if(typeof callback === "function")
4856 NETDATA.chartLibraries.easypiechart.enabled = false;
4857 if(typeof callback === "function")
4862 NETDATA.easypiechartClearSelection = function(state) {
4863 if(typeof state.easyPieChartEvent !== 'undefined') {
4864 if(state.easyPieChartEvent.timer !== null)
4865 clearTimeout(state.easyPieChartEvent.timer);
4867 state.easyPieChartEvent.timer = null;
4870 if(state.isAutoRefreshable() === true && state.data !== null) {
4871 NETDATA.easypiechartChartUpdate(state, state.data);
4874 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4875 state.easyPieChart_instance.update(0);
4877 state.easyPieChart_instance.enableAnimation();
4882 NETDATA.easypiechartSetSelection = function(state, t) {
4883 if(state.timeIsVisible(t) !== true)
4884 return NETDATA.easypiechartClearSelection(state);
4886 var slot = state.calculateRowForTime(t);
4887 if(slot < 0 || slot >= state.data.result.length)
4888 return NETDATA.easypiechartClearSelection(state);
4890 if(typeof state.easyPieChartEvent === 'undefined') {
4891 state.easyPieChartEvent = {
4898 var value = state.data.result[state.data.result.length - 1 - slot];
4899 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4900 var pcent = NETDATA.percentFromValueMax(value, max);
4902 state.easyPieChartEvent.value = value;
4903 state.easyPieChartEvent.pcent = pcent;
4904 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4906 if(state.easyPieChartEvent.timer === null) {
4907 state.easyPieChart_instance.disableAnimation();
4909 state.easyPieChartEvent.timer = setTimeout(function() {
4910 state.easyPieChartEvent.timer = null;
4911 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4912 }, NETDATA.options.current.charts_selection_animation_delay);
4918 NETDATA.easypiechartChartUpdate = function(state, data) {
4919 var value, max, pcent;
4921 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4927 value = data.result[0];
4928 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4929 pcent = NETDATA.percentFromValueMax(value, max);
4932 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4933 state.easyPieChart_instance.update(pcent);
4937 NETDATA.easypiechartChartCreate = function(state, data) {
4938 var self = $(state.element);
4939 var chart = $(state.element_chart);
4941 var value = data.result[0];
4942 var max = self.data('easypiechart-max-value') || null;
4943 var adjust = self.data('easypiechart-adjust') || null;
4947 state.easyPieChartMax = null;
4950 state.easyPieChartMax = max;
4952 var pcent = NETDATA.percentFromValueMax(value, max);
4954 chart.data('data-percent', pcent);
4958 case 'width': size = state.chartHeight(); break;
4959 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4960 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4962 default: size = state.chartWidth(); break;
4964 state.element.style.width = size + 'px';
4965 state.element.style.height = size + 'px';
4967 var stroke = Math.floor(size / 22);
4968 if(stroke < 3) stroke = 2;
4970 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4971 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4972 state.easyPieChartLabel = document.createElement('span');
4973 state.easyPieChartLabel.className = 'easyPieChartLabel';
4974 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4975 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4976 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4977 state.element_chart.appendChild(state.easyPieChartLabel);
4979 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4980 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4981 state.easyPieChartTitle = document.createElement('span');
4982 state.easyPieChartTitle.className = 'easyPieChartTitle';
4983 state.easyPieChartTitle.innerHTML = state.title;
4984 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4985 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4986 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4987 state.element_chart.appendChild(state.easyPieChartTitle);
4989 var unitfontsize = Math.round(titlefontsize * 0.9);
4990 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4991 state.easyPieChartUnits = document.createElement('span');
4992 state.easyPieChartUnits.className = 'easyPieChartUnits';
4993 state.easyPieChartUnits.innerHTML = state.units;
4994 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4995 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4996 state.element_chart.appendChild(state.easyPieChartUnits);
4998 chart.easyPieChart({
4999 barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
5000 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
5001 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
5002 scaleLength: self.data('easypiechart-scalelength') || 5,
5003 lineCap: self.data('easypiechart-linecap') || 'round',
5004 lineWidth: self.data('easypiechart-linewidth') || stroke,
5005 trackWidth: self.data('easypiechart-trackwidth') || undefined,
5006 size: self.data('easypiechart-size') || size,
5007 rotate: self.data('easypiechart-rotate') || 0,
5008 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
5009 easing: self.data('easypiechart-easing') || undefined
5012 // when we just re-create the chart
5013 // do not animate the first update
5015 if(typeof state.easyPieChart_instance !== 'undefined')
5018 state.easyPieChart_instance = chart.data('easyPieChart');
5019 if(animate === false) state.easyPieChart_instance.disableAnimation();
5020 state.easyPieChart_instance.update(pcent);
5021 if(animate === false) state.easyPieChart_instance.enableAnimation();
5025 // ----------------------------------------------------------------------------------------------------------------
5028 NETDATA.gaugeInitialize = function(callback) {
5029 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
5031 url: NETDATA.gauge_js,
5034 xhrFields: { withCredentials: true } // required for the cookie
5037 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
5040 NETDATA.chartLibraries.gauge.enabled = false;
5041 NETDATA.error(100, NETDATA.gauge_js);
5043 .always(function() {
5044 if(typeof callback === "function")
5049 NETDATA.chartLibraries.gauge.enabled = false;
5050 if(typeof callback === "function")
5055 NETDATA.gaugeAnimation = function(state, status) {
5058 if(typeof status === 'boolean' && status === false)
5060 else if(typeof status === 'number')
5063 // console.log('gauge speed ' + speed);
5064 state.gauge_instance.animationSpeed = speed;
5065 state.___gaugeOld__.speed = speed;
5068 NETDATA.gaugeSet = function(state, value, min, max) {
5069 if(typeof value !== 'number') value = 0;
5070 if(typeof min !== 'number') min = 0;
5071 if(typeof max !== 'number') max = 0;
5072 if(value > max) max = value;
5073 if(value < min) min = value;
5082 // gauge.js has an issue if the needle
5083 // is smaller than min or larger than max
5084 // when we set the new values
5085 // the needle will go crazy
5087 // to prevent it, we always feed it
5088 // with a percentage, so that the needle
5089 // is always between min and max
5090 var pcent = (value - min) * 100 / (max - min);
5092 // these should never happen
5093 if(pcent < 0) pcent = 0;
5094 if(pcent > 100) pcent = 100;
5096 state.gauge_instance.set(pcent);
5097 // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
5099 state.___gaugeOld__.value = value;
5100 state.___gaugeOld__.min = min;
5101 state.___gaugeOld__.max = max;
5104 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5105 if(state.___gaugeOld__.valueLabel !== value) {
5106 state.___gaugeOld__.valueLabel = value;
5107 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5109 if(state.___gaugeOld__.minLabel !== min) {
5110 state.___gaugeOld__.minLabel = min;
5111 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5113 if(state.___gaugeOld__.maxLabel !== max) {
5114 state.___gaugeOld__.maxLabel = max;
5115 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5119 NETDATA.gaugeClearSelection = function(state) {
5120 if(typeof state.gaugeEvent !== 'undefined') {
5121 if(state.gaugeEvent.timer !== null)
5122 clearTimeout(state.gaugeEvent.timer);
5124 state.gaugeEvent.timer = null;
5127 if(state.isAutoRefreshable() === true && state.data !== null) {
5128 NETDATA.gaugeChartUpdate(state, state.data);
5131 NETDATA.gaugeAnimation(state, false);
5132 NETDATA.gaugeSet(state, null, null, null);
5133 NETDATA.gaugeSetLabels(state, null, null, null);
5136 NETDATA.gaugeAnimation(state, true);
5140 NETDATA.gaugeSetSelection = function(state, t) {
5141 if(state.timeIsVisible(t) !== true)
5142 return NETDATA.gaugeClearSelection(state);
5144 var slot = state.calculateRowForTime(t);
5145 if(slot < 0 || slot >= state.data.result.length)
5146 return NETDATA.gaugeClearSelection(state);
5148 if(typeof state.gaugeEvent === 'undefined') {
5149 state.gaugeEvent = {
5157 var value = state.data.result[state.data.result.length - 1 - slot];
5158 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5161 state.gaugeEvent.value = value;
5162 state.gaugeEvent.max = max;
5163 state.gaugeEvent.min = min;
5164 NETDATA.gaugeSetLabels(state, value, min, max);
5166 if(state.gaugeEvent.timer === null) {
5167 NETDATA.gaugeAnimation(state, false);
5169 state.gaugeEvent.timer = setTimeout(function() {
5170 state.gaugeEvent.timer = null;
5171 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5172 }, NETDATA.options.current.charts_selection_animation_delay);
5178 NETDATA.gaugeChartUpdate = function(state, data) {
5179 var value, min, max;
5181 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5185 NETDATA.gaugeSetLabels(state, null, null, null);
5188 value = data.result[0];
5190 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5191 if(value > max) max = value;
5192 NETDATA.gaugeSetLabels(state, value, min, max);
5195 NETDATA.gaugeSet(state, value, min, max);
5199 NETDATA.gaugeChartCreate = function(state, data) {
5200 var self = $(state.element);
5201 // var chart = $(state.element_chart);
5203 var value = data.result[0];
5204 var max = self.data('gauge-max-value') || null;
5205 var adjust = self.data('gauge-adjust') || null;
5206 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5207 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5208 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5209 var stopColor = self.data('gauge-stop-color') || void 0;
5210 var generateGradient = self.data('gauge-generate-gradient') || false;
5214 state.gaugeMax = null;
5217 state.gaugeMax = max;
5219 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5221 // case 'width': width = height * ratio; break;
5223 // default: height = width / ratio; break;
5225 //state.element.style.width = width.toString() + 'px';
5226 //state.element.style.height = height.toString() + 'px';
5231 lines: 12, // The number of lines to draw
5232 angle: 0.15, // The length of each line
5233 lineWidth: 0.44, // 0.44 The line thickness
5235 length: 0.8, // 0.9 The radius of the inner circle
5236 strokeWidth: 0.035, // The rotation offset
5237 color: pointerColor // Fill color
5239 colorStart: startColor, // Colors
5240 colorStop: stopColor, // just experiment with them
5241 strokeColor: strokeColor, // to see which ones work best for you
5243 generateGradient: (generateGradient === true)?true:false,
5247 if (generateGradient.constructor === Array) {
5249 // data-gauge-generate-gradient="[0, 50, 100]"
5250 // data-gauge-gradient-percent-color-0="#FFFFFF"
5251 // data-gauge-gradient-percent-color-50="#999900"
5252 // data-gauge-gradient-percent-color-100="#000000"
5254 options.percentColors = new Array();
5255 var len = generateGradient.length;
5257 var pcent = generateGradient[len];
5258 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5259 if(color !== false) {
5260 var a = new Array();
5263 options.percentColors.unshift(a);
5266 if(options.percentColors.length === 0)
5267 delete options.percentColors;
5269 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5270 options.percentColors = [
5271 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5272 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5273 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5274 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5275 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5276 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5277 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5278 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5279 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5280 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5281 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5284 state.gauge_canvas = document.createElement('canvas');
5285 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5286 state.gauge_canvas.className = 'gaugeChart';
5287 state.gauge_canvas.width = width;
5288 state.gauge_canvas.height = height;
5289 state.element_chart.appendChild(state.gauge_canvas);
5291 var valuefontsize = Math.floor(height / 6);
5292 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5293 state.gaugeChartLabel = document.createElement('span');
5294 state.gaugeChartLabel.className = 'gaugeChartLabel';
5295 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5296 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5297 state.element_chart.appendChild(state.gaugeChartLabel);
5299 var titlefontsize = Math.round(valuefontsize / 2);
5301 state.gaugeChartTitle = document.createElement('span');
5302 state.gaugeChartTitle.className = 'gaugeChartTitle';
5303 state.gaugeChartTitle.innerHTML = state.title;
5304 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5305 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5306 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5307 state.element_chart.appendChild(state.gaugeChartTitle);
5309 var unitfontsize = Math.round(titlefontsize * 0.9);
5310 state.gaugeChartUnits = document.createElement('span');
5311 state.gaugeChartUnits.className = 'gaugeChartUnits';
5312 state.gaugeChartUnits.innerHTML = state.units;
5313 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5314 state.element_chart.appendChild(state.gaugeChartUnits);
5316 state.gaugeChartMin = document.createElement('span');
5317 state.gaugeChartMin.className = 'gaugeChartMin';
5318 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5319 state.element_chart.appendChild(state.gaugeChartMin);
5321 state.gaugeChartMax = document.createElement('span');
5322 state.gaugeChartMax.className = 'gaugeChartMax';
5323 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5324 state.element_chart.appendChild(state.gaugeChartMax);
5326 // when we just re-create the chart
5327 // do not animate the first update
5329 if(typeof state.gauge_instance !== 'undefined')
5332 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5334 state.___gaugeOld__ = {
5343 // we will always feed a percentage
5344 state.gauge_instance.minValue = 0;
5345 state.gauge_instance.maxValue = 100;
5347 NETDATA.gaugeAnimation(state, animate);
5348 NETDATA.gaugeSet(state, value, 0, max);
5349 NETDATA.gaugeSetLabels(state, value, 0, max);
5350 NETDATA.gaugeAnimation(state, true);
5354 // ----------------------------------------------------------------------------------------------------------------
5355 // Charts Libraries Registration
5357 NETDATA.chartLibraries = {
5359 initialize: NETDATA.dygraphInitialize,
5360 create: NETDATA.dygraphChartCreate,
5361 update: NETDATA.dygraphChartUpdate,
5362 resize: function(state) {
5363 if(typeof state.dygraph_instance.resize === 'function')
5364 state.dygraph_instance.resize();
5366 setSelection: NETDATA.dygraphSetSelection,
5367 clearSelection: NETDATA.dygraphClearSelection,
5368 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5371 format: function(state) { return 'json'; },
5372 options: function(state) { return 'ms|flip'; },
5373 legend: function(state) {
5374 if(this.isSparkline(state) === false)
5375 return 'right-side';
5379 autoresize: function(state) { return true; },
5380 max_updates_to_recreate: function(state) { return 5000; },
5381 track_colors: function(state) { return true; },
5382 pixels_per_point: function(state) {
5383 if(this.isSparkline(state) === false)
5389 isSparkline: function(state) {
5390 if(typeof state.dygraph_sparkline === 'undefined') {
5391 var t = $(state.element).data('dygraph-theme');
5392 if(t === 'sparkline')
5393 state.dygraph_sparkline = true;
5395 state.dygraph_sparkline = false;
5397 return state.dygraph_sparkline;
5401 initialize: NETDATA.sparklineInitialize,
5402 create: NETDATA.sparklineChartCreate,
5403 update: NETDATA.sparklineChartUpdate,
5405 setSelection: undefined, // function(state, t) { return true; },
5406 clearSelection: undefined, // function(state) { return true; },
5407 toolboxPanAndZoom: null,
5410 format: function(state) { return 'array'; },
5411 options: function(state) { return 'flip|abs'; },
5412 legend: function(state) { return null; },
5413 autoresize: function(state) { return false; },
5414 max_updates_to_recreate: function(state) { return 5000; },
5415 track_colors: function(state) { return false; },
5416 pixels_per_point: function(state) { return 3; }
5419 initialize: NETDATA.peityInitialize,
5420 create: NETDATA.peityChartCreate,
5421 update: NETDATA.peityChartUpdate,
5423 setSelection: undefined, // function(state, t) { return true; },
5424 clearSelection: undefined, // function(state) { return true; },
5425 toolboxPanAndZoom: null,
5428 format: function(state) { return 'ssvcomma'; },
5429 options: function(state) { return 'null2zero|flip|abs'; },
5430 legend: function(state) { return null; },
5431 autoresize: function(state) { return false; },
5432 max_updates_to_recreate: function(state) { return 5000; },
5433 track_colors: function(state) { return false; },
5434 pixels_per_point: function(state) { return 3; }
5437 initialize: NETDATA.morrisInitialize,
5438 create: NETDATA.morrisChartCreate,
5439 update: NETDATA.morrisChartUpdate,
5441 setSelection: undefined, // function(state, t) { return true; },
5442 clearSelection: undefined, // function(state) { return true; },
5443 toolboxPanAndZoom: null,
5446 format: function(state) { return 'json'; },
5447 options: function(state) { return 'objectrows|ms'; },
5448 legend: function(state) { return null; },
5449 autoresize: function(state) { return false; },
5450 max_updates_to_recreate: function(state) { return 50; },
5451 track_colors: function(state) { return false; },
5452 pixels_per_point: function(state) { return 15; }
5455 initialize: NETDATA.googleInitialize,
5456 create: NETDATA.googleChartCreate,
5457 update: NETDATA.googleChartUpdate,
5459 setSelection: undefined, //function(state, t) { return true; },
5460 clearSelection: undefined, //function(state) { return true; },
5461 toolboxPanAndZoom: null,
5464 format: function(state) { return 'datatable'; },
5465 options: function(state) { return ''; },
5466 legend: function(state) { return null; },
5467 autoresize: function(state) { return false; },
5468 max_updates_to_recreate: function(state) { return 300; },
5469 track_colors: function(state) { return false; },
5470 pixels_per_point: function(state) { return 4; }
5473 initialize: NETDATA.raphaelInitialize,
5474 create: NETDATA.raphaelChartCreate,
5475 update: NETDATA.raphaelChartUpdate,
5477 setSelection: undefined, // function(state, t) { return true; },
5478 clearSelection: undefined, // function(state) { return true; },
5479 toolboxPanAndZoom: null,
5482 format: function(state) { return 'json'; },
5483 options: function(state) { return ''; },
5484 legend: function(state) { return null; },
5485 autoresize: function(state) { return false; },
5486 max_updates_to_recreate: function(state) { return 5000; },
5487 track_colors: function(state) { return false; },
5488 pixels_per_point: function(state) { return 3; }
5491 initialize: NETDATA.c3Initialize,
5492 create: NETDATA.c3ChartCreate,
5493 update: NETDATA.c3ChartUpdate,
5495 setSelection: undefined, // function(state, t) { return true; },
5496 clearSelection: undefined, // function(state) { return true; },
5497 toolboxPanAndZoom: null,
5500 format: function(state) { return 'csvjsonarray'; },
5501 options: function(state) { return 'milliseconds'; },
5502 legend: function(state) { return null; },
5503 autoresize: function(state) { return false; },
5504 max_updates_to_recreate: function(state) { return 5000; },
5505 track_colors: function(state) { return false; },
5506 pixels_per_point: function(state) { return 15; }
5509 initialize: NETDATA.d3Initialize,
5510 create: NETDATA.d3ChartCreate,
5511 update: NETDATA.d3ChartUpdate,
5513 setSelection: undefined, // function(state, t) { return true; },
5514 clearSelection: undefined, // function(state) { return true; },
5515 toolboxPanAndZoom: null,
5518 format: function(state) { return 'json'; },
5519 options: function(state) { return ''; },
5520 legend: function(state) { return null; },
5521 autoresize: function(state) { return false; },
5522 max_updates_to_recreate: function(state) { return 5000; },
5523 track_colors: function(state) { return false; },
5524 pixels_per_point: function(state) { return 3; }
5527 initialize: NETDATA.easypiechartInitialize,
5528 create: NETDATA.easypiechartChartCreate,
5529 update: NETDATA.easypiechartChartUpdate,
5531 setSelection: NETDATA.easypiechartSetSelection,
5532 clearSelection: NETDATA.easypiechartClearSelection,
5533 toolboxPanAndZoom: null,
5536 format: function(state) { return 'array'; },
5537 options: function(state) { return 'absolute'; },
5538 legend: function(state) { return null; },
5539 autoresize: function(state) { return false; },
5540 max_updates_to_recreate: function(state) { return 5000; },
5541 track_colors: function(state) { return true; },
5542 pixels_per_point: function(state) { return 3; },
5546 initialize: NETDATA.gaugeInitialize,
5547 create: NETDATA.gaugeChartCreate,
5548 update: NETDATA.gaugeChartUpdate,
5550 setSelection: NETDATA.gaugeSetSelection,
5551 clearSelection: NETDATA.gaugeClearSelection,
5552 toolboxPanAndZoom: null,
5555 format: function(state) { return 'array'; },
5556 options: function(state) { return 'absolute'; },
5557 legend: function(state) { return null; },
5558 autoresize: function(state) { return false; },
5559 max_updates_to_recreate: function(state) { return 5000; },
5560 track_colors: function(state) { return true; },
5561 pixels_per_point: function(state) { return 3; },
5566 NETDATA.registerChartLibrary = function(library, url) {
5567 if(NETDATA.options.debug.libraries === true)
5568 console.log("registering chart library: " + library);
5570 NETDATA.chartLibraries[library].url = url;
5571 NETDATA.chartLibraries[library].initialized = true;
5572 NETDATA.chartLibraries[library].enabled = true;
5575 // ----------------------------------------------------------------------------------------------------------------
5576 // Load required JS libraries and CSS
5578 NETDATA.requiredJs = [
5580 url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js',
5582 isAlreadyLoaded: function() {
5583 // check if bootstrap is loaded
5584 if(typeof $().emulateTransitionEnd == 'function')
5587 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5595 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js',
5596 isAlreadyLoaded: function() { return false; }
5600 NETDATA.requiredCSS = [
5602 url: NETDATA.themes.current.bootstrap_css,
5603 isAlreadyLoaded: function() {
5604 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5611 url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
5612 isAlreadyLoaded: function() { return false; }
5615 url: NETDATA.themes.current.dashboard_css,
5616 isAlreadyLoaded: function() { return false; }
5620 NETDATA.loadedRequiredJs = 0;
5621 NETDATA.loadRequiredJs = function(index, callback) {
5622 if(index >= NETDATA.requiredJs.length) {
5623 if(typeof callback === 'function')
5628 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5629 NETDATA.loadedRequiredJs++;
5630 NETDATA.loadRequiredJs(++index, callback);
5634 if(NETDATA.options.debug.main_loop === true)
5635 console.log('loading ' + NETDATA.requiredJs[index].url);
5638 if(typeof NETDATA.requiredJs[index].async !== 'undefined' && NETDATA.requiredJs[index].async === false)
5642 url: NETDATA.requiredJs[index].url,
5645 xhrFields: { withCredentials: true } // required for the cookie
5648 if(NETDATA.options.debug.main_loop === true)
5649 console.log('loaded ' + NETDATA.requiredJs[index].url);
5652 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5654 .always(function() {
5655 NETDATA.loadedRequiredJs++;
5658 NETDATA.loadRequiredJs(++index, callback);
5662 NETDATA.loadRequiredJs(++index, callback);
5665 NETDATA.loadRequiredCSS = function(index) {
5666 if(index >= NETDATA.requiredCSS.length)
5669 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5670 NETDATA.loadRequiredCSS(++index);
5674 if(NETDATA.options.debug.main_loop === true)
5675 console.log('loading ' + NETDATA.requiredCSS[index].url);
5677 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5678 NETDATA.loadRequiredCSS(++index);
5682 // ----------------------------------------------------------------------------------------------------------------
5683 // Registry of netdata hosts
5686 onclick: null, // the callback to handle the click - it will be called with the alarm log entry
5687 chart_div_offset: 100, // give that space above the chart when scrolling to it
5688 chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id))
5689 chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart
5691 ms_penalty: 0, // the time penalty of the next alarm
5692 ms_between_notifications: 500, // firefox moves the alarms off-screen (above, outside the top of the screen)
5693 // if alarms are shown faster than: one per 500ms
5695 notifications: false, // when true, the browser supports notifications (may not be granted though)
5696 last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5697 first_notification_id: 0, // the id of the first alarm_log entry for this session
5698 // this is used to prevent CLEAR notifications for past events
5699 // notifications_shown: new Array(),
5701 server: null, // the server to connect to for fetching alarms
5702 current: null, // the list of raised alarms - updated in the background
5703 callback: null, // a callback function to call every time the list of raised alarms is refreshed
5705 notify: function(entry) {
5706 // console.log('alarm ' + entry.unique_id);
5708 if(entry.updated === true) {
5709 // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5713 var value = entry.value;
5714 if(NETDATA.alarms.current !== null) {
5715 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
5716 if(typeof t !== 'undefined' && entry.status == t.status)
5720 var name = entry.name.replace(/_/g, ' ');
5721 var status = entry.status.toLowerCase();
5722 var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
5723 var tag = entry.alarm_id;
5724 var icon = 'images/seo-performance-128.png';
5725 var interaction = false;
5729 // console.log('alarm ' + entry.unique_id + ' ' + entry.chart + '.' + entry.name + ' is ' + entry.status);
5731 switch(entry.status) {
5739 case 'UNINITIALIZED':
5743 if(entry.unique_id < NETDATA.alarms.first_notification_id) {
5744 // console.log('alarm ' + entry.unique_id + ' is not current');
5747 if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5748 // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5751 title = name + ' back to normal';
5752 icon = 'images/check-mark-2-128-green.png'
5753 interaction = false;
5757 if(entry.old_status === 'CRITICAL')
5758 status = 'demoted to ' + entry.status.toLowerCase();
5760 icon = 'images/alert-128-orange.png';
5761 interaction = false;
5765 if(entry.old_status === 'WARNING')
5766 status = 'escalated to ' + entry.status.toLowerCase();
5768 icon = 'images/alert-128-red.png'
5773 console.log('invalid alarm status ' + entry.status);
5778 // cleanup old notifications with the same alarm_id as this one
5779 // FIXME: it does not seem to work on any web browser!
5780 var len = NETDATA.alarms.notifications_shown.length;
5782 var n = NETDATA.alarms.notifications_shown[len];
5783 if(n.data.alarm_id === entry.alarm_id) {
5784 console.log('removing old alarm ' + n.data.unique_id);
5786 // close the notification
5789 // remove it from the array
5790 NETDATA.alarms.notifications_shown.splice(len, 1);
5791 len = NETDATA.alarms.notifications_shown.length;
5798 setTimeout(function() {
5799 // show this notification
5800 // console.log('new notification: ' + title);
5801 var n = new Notification(title, {
5802 body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info,
5804 requireInteraction: interaction,
5805 icon: NETDATA.serverDefault + icon,
5809 n.onclick = function(event) {
5810 event.preventDefault();
5811 NETDATA.alarms.onclick(event.target.data);
5815 // NETDATA.alarms.notifications_shown.push(n);
5816 // console.log(entry);
5817 }, NETDATA.alarms.ms_penalty);
5819 NETDATA.alarms.ms_penalty += NETDATA.alarms.ms_between_notifications;
5823 scrollToChart: function(chart_id) {
5824 if(typeof chart_id === 'string') {
5825 var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset();
5826 if(typeof offset !== 'undefined') {
5827 $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration);
5834 scrollToAlarm: function(alarm) {
5835 if(typeof alarm === 'object') {
5836 var ret = NETDATA.alarms.scrollToChart(alarm.chart);
5838 if(ret === true && NETDATA.options.page_is_visible === false)
5840 // 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.');
5845 notifyAll: function() {
5846 // console.log('FETCHING ALARM LOG');
5847 NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5848 // console.log('ALARM LOG FETCHED');
5850 if(data === null || typeof data !== 'object') {
5851 console.log('invalid alarms log response');
5855 if(data.length === 0) {
5856 console.log('received empty alarm log');
5860 // console.log('received alarm log of ' + data.length + ' entries, from ' + data[data.length - 1].unique_id.toString() + ' to ' + data[0].unique_id.toString());
5862 data.sort(function(a, b) {
5863 if(a.unique_id > b.unique_id) return -1;
5864 if(a.unique_id < b.unique_id) return 1;
5868 NETDATA.alarms.ms_penalty = 0;
5870 var len = data.length;
5872 if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5873 NETDATA.alarms.notify(data[len]);
5876 // console.log('ignoring alarm (older) with id ' + data[len].unique_id.toString());
5879 NETDATA.alarms.last_notification_id = data[0].unique_id;
5880 NETDATA.localStorageSet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5881 // console.log('last notification id = ' + NETDATA.alarms.last_notification_id);
5885 check_notifications: function() {
5886 // returns true if we should fire 1+ notifications
5888 if(NETDATA.alarms.notifications !== true) {
5889 // console.log('notifications not available');
5893 if(Notification.permission !== 'granted') {
5894 // console.log('notifications not granted');
5898 if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5899 // console.log('can do alarms: old id = ' + NETDATA.alarms.last_notification_id + ' new id = ' + NETDATA.alarms.current.latest_alarm_log_unique_id);
5901 if(NETDATA.alarms.current.latest_alarm_log_unique_id > NETDATA.alarms.last_notification_id) {
5902 // console.log('new alarms detected');
5905 //else console.log('no new alarms');
5907 // else console.log('cannot process alarms');
5912 get: function(what, callback) {
5914 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5917 xhrFields: { withCredentials: true } // required for the cookie
5919 .done(function(data) {
5920 if(NETDATA.alarms.first_notification_id === 0 && typeof data.latest_alarm_log_unique_id === 'number')
5921 NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
5923 if(typeof callback === 'function')
5927 NETDATA.error(415, NETDATA.alarms.server);
5929 if(typeof callback === 'function')
5934 update_forever: function() {
5935 NETDATA.alarms.get('active', function(data) {
5937 NETDATA.alarms.current = data;
5939 if(NETDATA.alarms.check_notifications() === true) {
5940 NETDATA.alarms.notifyAll();
5943 if (typeof NETDATA.alarms.callback === 'function') {
5944 NETDATA.alarms.callback(data);
5947 // Health monitoring is disabled on this netdata
5948 if(data.status === false) return;
5951 setTimeout(NETDATA.alarms.update_forever, 10000);
5955 get_log: function(last_id, callback) {
5956 // console.log('fetching all log after ' + last_id.toString());
5958 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5961 xhrFields: { withCredentials: true } // required for the cookie
5963 .done(function(data) {
5964 if(typeof callback === 'function')
5968 NETDATA.error(416, NETDATA.alarms.server);
5970 if(typeof callback === 'function')
5976 var host = NETDATA.serverDefault;
5977 while(host.slice(-1) === '/')
5978 host = host.substring(0, host.length - 1);
5979 NETDATA.alarms.server = host;
5981 NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5983 if(NETDATA.alarms.onclick === null)
5984 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
5986 if(netdataShowAlarms === true) {
5987 NETDATA.alarms.update_forever();
5989 if('Notification' in window) {
5990 // console.log('notifications available');
5991 NETDATA.alarms.notifications = true;
5993 if(Notification.permission === 'default')
5994 Notification.requestPermission();
6000 // ----------------------------------------------------------------------------------------------------------------
6001 // Registry of netdata hosts
6003 NETDATA.registry = {
6004 server: null, // the netdata registry server
6005 person_guid: null, // the unique ID of this browser / user
6006 machine_guid: null, // the unique ID the netdata server that served dashboard.js
6007 hostname: null, // the hostname of the netdata server that served dashboard.js
6008 machines: null, // the user's other URLs
6009 machines_array: null, // the user's other URLs in an array
6012 parsePersonUrls: function(person_urls) {
6013 // console.log(person_urls);
6014 NETDATA.registry.person_urls = person_urls;
6017 NETDATA.registry.machines = {};
6018 NETDATA.registry.machines_array = new Array();
6020 var now = new Date().getTime();
6021 var apu = person_urls;
6024 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
6025 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6031 accesses: apu[i][3],
6033 alternate_urls: new Array()
6035 obj.alternate_urls.push(apu[i][1]);
6037 NETDATA.registry.machines[apu[i][0]] = obj;
6038 NETDATA.registry.machines_array.push(obj);
6041 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6043 var pu = NETDATA.registry.machines[apu[i][0]];
6044 if(pu.last_t < apu[i][2]) {
6046 pu.last_t = apu[i][2];
6047 pu.name = apu[i][4];
6049 pu.accesses += apu[i][3];
6050 pu.alternate_urls.push(apu[i][1]);
6055 if(typeof netdataRegistryCallback === 'function')
6056 netdataRegistryCallback(NETDATA.registry.machines_array);
6060 if(netdataRegistry !== true) return;
6062 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
6064 NETDATA.registry.server = data.registry;
6065 NETDATA.registry.machine_guid = data.machine_guid;
6066 NETDATA.registry.hostname = data.hostname;
6068 NETDATA.registry.access(2, function (person_urls) {
6069 NETDATA.registry.parsePersonUrls(person_urls);
6076 hello: function(host, callback) {
6077 while(host.slice(-1) === '/')
6078 host = host.substring(0, host.length - 1);
6080 // send HELLO to a netdata server:
6081 // 1. verifies the server is reachable
6082 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
6084 url: host + '/api/v1/registry?action=hello',
6087 xhrFields: { withCredentials: true } // required for the cookie
6089 .done(function(data) {
6090 if(typeof data.status !== 'string' || data.status !== 'ok') {
6091 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
6095 if(typeof callback === 'function')
6099 NETDATA.error(407, host);
6101 if(typeof callback === 'function')
6106 access: function(max_redirects, callback) {
6107 // send ACCESS to a netdata registry:
6108 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
6109 // 2. it responds with a list of netdata servers we know
6110 // the registry identifies us using a cookie it sets the first time we access it
6111 // the registry may respond with a redirect URL to send us to another registry
6113 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),
6116 xhrFields: { withCredentials: true } // required for the cookie
6118 .done(function(data) {
6119 var redirect = null;
6120 if(typeof data.registry === 'string')
6121 redirect = data.registry;
6123 if(typeof data.status !== 'string' || data.status !== 'ok') {
6124 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6129 if(redirect !== null && max_redirects > 0) {
6130 NETDATA.registry.server = redirect;
6131 NETDATA.registry.access(max_redirects - 1, callback);
6134 if(typeof callback === 'function')
6139 if(typeof data.person_guid === 'string')
6140 NETDATA.registry.person_guid = data.person_guid;
6142 if(typeof callback === 'function')
6143 callback(data.urls);
6147 NETDATA.error(410, NETDATA.registry.server);
6149 if(typeof callback === 'function')
6154 delete: function(delete_url, callback) {
6155 // send DELETE to a netdata registry:
6157 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),
6160 xhrFields: { withCredentials: true } // required for the cookie
6162 .done(function(data) {
6163 if(typeof data.status !== 'string' || data.status !== 'ok') {
6164 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6168 if(typeof callback === 'function')
6172 NETDATA.error(412, NETDATA.registry.server);
6174 if(typeof callback === 'function')
6179 search: function(machine_guid, callback) {
6180 // SEARCH for the URLs of a machine:
6182 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,
6185 xhrFields: { withCredentials: true } // required for the cookie
6187 .done(function(data) {
6188 if(typeof data.status !== 'string' || data.status !== 'ok') {
6189 NETDATA.error(417, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6193 if(typeof callback === 'function')
6197 NETDATA.error(418, NETDATA.registry.server);
6199 if(typeof callback === 'function')
6204 switch: function(new_person_guid, callback) {
6207 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,
6210 xhrFields: { withCredentials: true } // required for the cookie
6212 .done(function(data) {
6213 if(typeof data.status !== 'string' || data.status !== 'ok') {
6214 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6218 if(typeof callback === 'function')
6222 NETDATA.error(414, NETDATA.registry.server);
6224 if(typeof callback === 'function')
6230 // ----------------------------------------------------------------------------------------------------------------
6233 if(typeof netdataPrepCallback === 'function')
6234 netdataPrepCallback();
6236 NETDATA.errorReset();
6237 NETDATA.loadRequiredCSS(0);
6239 NETDATA._loadjQuery(function() {
6240 NETDATA.loadRequiredJs(0, function() {
6241 if(typeof $().emulateTransitionEnd !== 'function') {
6242 // bootstrap is not available
6243 NETDATA.options.current.show_help = false;
6246 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
6247 if(NETDATA.options.debug.main_loop === true)
6248 console.log('starting chart refresh thread');
6255 // window.NETDATA = NETDATA;
6256 // })(window, document);