1 // You can set the following variables before loading this script:
3 // var netdataNoDygraphs = true; // do not use dygraph
4 // var netdataNoSparklines = true; // do not use sparkline
5 // var netdataNoPeitys = true; // do not use peity
6 // var netdataNoGoogleCharts = true; // do not use google
7 // var netdataNoMorris = true; // do not use morris
8 // var netdataNoEasyPieChart = true; // do not use easy pie chart
9 // var netdataNoGauge = true; // do not use gauge.js
10 // var netdataNoD3 = true; // do not use D3
11 // var netdataNoC3 = true; // do not use C3
12 // var netdataNoBootstrap = true; // do not load bootstrap
13 // var netdataDontStart = true; // do not start the thread to process the charts
14 // var netdataErrorCallback = null; // Callback function that will be invoked upon error
15 // var netdataRegistry = true; // Update the registry (default disabled)
16 // var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
17 // the URLs from the registry
18 // var netdataShowHelp = false; // enable/disable help (default enabled)
19 // var netdataShowAlarms = true; // enable/disable alarms checks and notifications (default disabled)
21 // var netdataRegistryAfterMs = 1500 // the time to consult to registry on startup
23 // var netdataCallback = null; // a function to call when netdata is ready
24 // // netdata will be running while this is called (call NETDATA.pause to stop it)
25 // var netdataPrepCallback = null; // a callback to be called before netdata does anything else
27 // You can also set the default netdata server, using the following.
28 // When this variable is not set, we assume the page is hosted on your
29 // netdata server already.
30 // var netdataServer = "http://yourhost:19999"; // set your NetData server
32 //(function(window, document, undefined) {
34 // ------------------------------------------------------------------------
35 // compatibility fixes
37 // fix IE issue with console
38 if(!window.console) { window.console = { log: function(){} }; }
40 // if string.endsWith is not defined, define it
41 if(typeof String.prototype.endsWith !== 'function') {
42 String.prototype.endsWith = function(s) {
43 if(s.length > this.length) return false;
44 return this.slice(-s.length) === s;
48 // if string.startsWith is not defined, define it
49 if(typeof String.prototype.startsWith !== 'function') {
50 String.prototype.startsWith = function(s) {
51 if(s.length > this.length) return false;
52 return this.slice(s.length) === s;
57 var NETDATA = window.NETDATA || {};
59 NETDATA.name2id = function(s) {
68 // ----------------------------------------------------------------------------------------------------------------
69 // Detect the netdata server
71 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
72 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
73 NETDATA._scriptSource = function() {
76 if(typeof document.currentScript !== 'undefined') {
77 script = document.currentScript;
80 var all_scripts = document.getElementsByTagName('script');
81 script = all_scripts[all_scripts.length - 1];
84 if (typeof script.getAttribute.length !== 'undefined')
87 script = script.getAttribute('src', -1);
92 if(typeof netdataServer !== 'undefined')
93 NETDATA.serverDefault = netdataServer;
95 var s = NETDATA._scriptSource();
96 if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
98 console.log('WARNING: Cannot detect the URL of the netdata server.');
99 NETDATA.serverDefault = null;
103 if(NETDATA.serverDefault === null)
104 NETDATA.serverDefault = '';
105 else if(NETDATA.serverDefault.slice(-1) !== '/')
106 NETDATA.serverDefault += '/';
108 // default URLs for all the external files we need
109 // make them RELATIVE so that the whole thing can also be
110 // installed under a web server
111 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-2.2.4.min.js';
112 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js';
113 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js';
114 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js';
115 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-d5260c3.min.js';
116 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js';
117 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js';
118 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js';
119 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3-0.4.11.min.js';
120 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3-0.4.11.min.css';
121 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3-3.5.17.min.js';
122 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris-0.5.1.min.js';
123 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris-0.5.1.css';
124 NETDATA.google_js = 'https://www.google.com/jsapi';
128 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css',
129 dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161002-1',
130 background: '#FFFFFF',
131 foreground: '#000000',
134 colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477',
135 '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6',
136 '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707',
137 '#329262', '#3B3EAC' ],
138 easypiechart_track: '#f0f0f0',
139 easypiechart_scale: '#dfe0e0',
140 gauge_pointer: '#C0C0C0',
141 gauge_stroke: '#F0F0F0',
142 gauge_gradient: false
145 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css?v20161002-1',
146 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-1',
147 background: '#272b30',
148 foreground: '#C8C8C8',
151 /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00',
152 '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0',
153 '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a',
154 '#a6a479', '#a66da8' ],
156 colors: [ '#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00',
157 '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700',
158 '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737',
159 '#329262', '#3B3EFF' ],
160 easypiechart_track: '#373b40',
161 easypiechart_scale: '#373b40',
162 gauge_pointer: '#474b50',
163 gauge_stroke: '#373b40',
164 gauge_gradient: false
168 if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
169 NETDATA.themes.current = NETDATA.themes[netdataTheme];
171 NETDATA.themes.current = NETDATA.themes.white;
173 NETDATA.colors = NETDATA.themes.current.colors;
175 // these are the colors Google Charts are using
176 // we have them here to attempt emulate their look and feel on the other chart libraries
177 // http://there4.io/2012/05/02/google-chart-color-list/
178 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
179 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
180 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
182 // an alternative set
183 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
184 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
185 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
187 if(typeof netdataShowHelp === 'undefined')
188 netdataShowHelp = true;
190 if(typeof netdataShowAlarms === 'undefined')
191 netdataShowAlarms = false;
193 if(typeof netdataRegistryAfterMs !== 'number' || netdataRegistryAfterMs < 0)
194 netdataRegistryAfterMs = 1500;
196 if(typeof netdataRegistry === 'undefined') {
197 // backward compatibility
198 if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false)
199 netdataRegistry = true;
201 netdataRegistry = false;
203 if(netdataRegistry === false && typeof netdataRegistryCallback === 'function')
204 netdataRegistry = true;
206 // ----------------------------------------------------------------------------------------------------------------
207 // the defaults for all charts
209 // if the user does not specify any of these, the following will be used
211 NETDATA.chartDefaults = {
212 host: NETDATA.serverDefault, // the server to get data from
213 width: '100%', // the chart width - can be null
214 height: '100%', // the chart height - can be null
215 min_width: null, // the chart minimum width - can be null
216 library: 'dygraph', // the graphing library to use
217 method: 'average', // the grouping method
218 before: 0, // panning
219 after: -600, // panning
220 pixels_per_point: 1, // the detail of the chart
221 fill_luminance: 0.8 // luminance of colors in solit areas
224 // ----------------------------------------------------------------------------------------------------------------
228 pauseCallback: null, // a callback when we are really paused
230 pause: false, // when enabled we don't auto-refresh the charts
232 targets: null, // an array of all the state objects that are
233 // currently active (independently of their
234 // viewport visibility)
236 updated_dom: true, // when true, the DOM has been updated with
237 // new elements we have to check.
239 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
240 // rendering charts continiously.
241 // used with .current.fast_render_timeframe
243 page_is_visible: true, // when true, this page is visible
245 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
246 // auto-refresher for some time (when a chart is
247 // performing pan or zoom, we need to stop refreshing
248 // all other charts, to have the maximum speed for
249 // rendering the chart that is panned or zoomed).
250 // Used with .current.global_pan_sync_time
252 last_resized: new Date().getTime(), // the timestamp of the last resize request
254 last_page_scroll: 0, // the timestamp the last time the page was scrolled
256 // the current profile
257 // we may have many...
259 pixels_per_point: 1, // the minimum pixels per point for all charts
260 // increase this to speed javascript up
261 // each chart library has its own limit too
262 // the max of this and the chart library is used
263 // the final is calculated every time, so a change
264 // here will have immediate effect on the next chart
267 idle_between_charts: 100, // ms - how much time to wait between chart updates
269 fast_render_timeframe: 200, // ms - render continously until this time of continious
270 // rendering has been reached
271 // this setting is used to make it render e.g. 10
272 // charts at once, sleep idle_between_charts time
273 // and continue for another 10 charts.
275 idle_between_loops: 500, // ms - if all charts have been updated, wait this
276 // time before starting again.
278 idle_parallel_loops: 100, // ms - the time between parallel refresher updates
280 idle_lost_focus: 500, // ms - when the window does not have focus, check
281 // if focus has been regained, every this time
283 global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
284 // autorefreshing of charts is paused for this amount
287 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
288 // of time before setting up synchronized selections
291 sync_selection: true, // enable or disable selection sync
293 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
295 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
297 pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
299 update_only_visible: true, // enable or disable visibility management
301 parallel_refresher: true, // enable parallel refresh of charts
303 concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
305 destroy_on_hide: false, // destroy charts when they are not visible
307 show_help: netdataShowHelp, // when enabled the charts will show some help
308 show_help_delay_show_ms: 500,
309 show_help_delay_hide_ms: 0,
311 eliminate_zero_dimensions: true, // do not show dimensions with just zeros
313 stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
314 stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts
316 double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap
318 smooth_plot: true, // enable smooth plot, where possible
320 charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
322 color_fill_opacity_line: 1.0,
323 color_fill_opacity_area: 0.2,
324 color_fill_opacity_stacked: 0.8,
326 pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
327 pan_and_zoom_factor_multiplier_control: 2.0,
328 pan_and_zoom_factor_multiplier_shift: 3.0,
329 pan_and_zoom_factor_multiplier_alt: 4.0,
331 abort_ajax_on_scroll: false, // kill pending ajax page scroll
332 async_on_scroll: false, // sync/async onscroll handler
333 onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler
335 setOptionCallback: function() { ; }
343 chart_data_url: false,
344 chart_errors: false, // FIXME
352 NETDATA.statistics = {
355 refreshes_active_max: 0
359 // ----------------------------------------------------------------------------------------------------------------
360 // local storage options
362 NETDATA.localStorage = {
365 callback: {} // only used for resetting back to defaults
368 NETDATA.localStorageGet = function(key, def, callback) {
371 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
372 NETDATA.localStorage.default[key.toString()] = def;
373 NETDATA.localStorage.callback[key.toString()] = callback;
376 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
378 // console.log('localStorage: loading "' + key.toString() + '"');
379 ret = localStorage.getItem(key.toString());
380 // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
381 if(ret === null || ret === 'undefined') {
382 // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
383 localStorage.setItem(key.toString(), JSON.stringify(def));
387 // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
388 ret = JSON.parse(ret);
389 // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
393 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
398 if(typeof ret === 'undefined' || ret === 'undefined') {
399 console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
403 NETDATA.localStorage.current[key.toString()] = ret;
407 NETDATA.localStorageSet = function(key, value, callback) {
408 if(typeof value === 'undefined' || value === 'undefined') {
409 console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
412 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
413 NETDATA.localStorage.default[key.toString()] = value;
414 NETDATA.localStorage.current[key.toString()] = value;
415 NETDATA.localStorage.callback[key.toString()] = callback;
418 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
419 // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
421 localStorage.setItem(key.toString(), JSON.stringify(value));
424 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
428 NETDATA.localStorage.current[key.toString()] = value;
432 NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
434 if(typeof obj[i] === 'object') {
435 //console.log('object ' + prefix + '.' + i.toString());
436 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
440 obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
444 NETDATA.setOption = function(key, value) {
445 if(key.toString() === 'setOptionCallback') {
446 if(typeof NETDATA.options.current.setOptionCallback === 'function') {
447 NETDATA.options.current[key.toString()] = value;
448 NETDATA.options.current.setOptionCallback();
451 else if(NETDATA.options.current[key.toString()] !== value) {
452 var name = 'options.' + key.toString();
454 if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
455 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
457 //console.log(NETDATA.localStorage);
458 //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
459 //console.log(NETDATA.options);
460 NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
462 if(typeof NETDATA.options.current.setOptionCallback === 'function')
463 NETDATA.options.current.setOptionCallback();
469 NETDATA.getOption = function(key) {
470 return NETDATA.options.current[key.toString()];
473 // read settings from local storage
474 NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
476 // always start with this option enabled.
477 NETDATA.setOption('stop_updates_when_focus_is_lost', true);
479 NETDATA.resetOptions = function() {
480 for(var i in NETDATA.localStorage.default) {
481 var a = i.split('.');
483 if(a[0] === 'options') {
484 if(a[1] === 'setOptionCallback') continue;
485 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
486 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
488 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
490 else if(a[0] === 'chart_heights') {
491 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
492 NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
498 // ----------------------------------------------------------------------------------------------------------------
500 if(NETDATA.options.debug.main_loop === true)
501 console.log('welcome to NETDATA');
503 NETDATA.onresize = function() {
504 NETDATA.options.last_resized = new Date().getTime();
508 NETDATA.onscroll_updater_count = 0;
509 NETDATA.onscroll_updater_running = false;
510 NETDATA.onscroll_updater_last_run = 0;
511 NETDATA.onscroll_updater_watchdog = null;
512 NETDATA.onscroll_updater_max_duration = 0;
513 NETDATA.onscroll_updater_above_threshold_count = 0;
514 NETDATA.onscroll_updater = function() {
515 NETDATA.onscroll_updater_running = true;
516 NETDATA.onscroll_updater_count++;
517 var start = new Date().getTime();
519 var targets = NETDATA.options.targets;
520 var len = targets.length;
522 // when the user scrolls he sees that we have
523 // hidden all the not-visible charts
524 // using this little function we try to switch
525 // the charts back to visible quickly
528 if(NETDATA.options.abort_ajax_on_scroll === true) {
529 // we have to cancel pending requests too
532 if (targets[len]._updating === true) {
533 if (typeof targets[len].xhr !== 'undefined') {
534 targets[len].xhr.abort();
535 targets[len].running = false;
536 targets[len]._updating = false;
538 targets[len].isVisible();
543 // just find which chart is visible
546 targets[len].isVisible();
549 var end = new Date().getTime();
550 // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms');
552 if(NETDATA.options.current.async_on_scroll === false) {
553 var dt = end - start;
554 if(dt > NETDATA.onscroll_updater_max_duration) {
555 // console.log('max onscroll event handler duration increased to ' + dt);
556 NETDATA.onscroll_updater_max_duration = dt;
559 if(dt > NETDATA.options.current.onscroll_worker_duration_threshold) {
560 // console.log('slow: ' + dt);
561 NETDATA.onscroll_updater_above_threshold_count++;
563 if(NETDATA.onscroll_updater_above_threshold_count > 2 && NETDATA.onscroll_updater_above_threshold_count * 100 / NETDATA.onscroll_updater_count > 2) {
564 NETDATA.setOption('async_on_scroll', true);
565 console.log('NETDATA: your browser is slow - enabling asynchronous onscroll event handler.');
570 NETDATA.onscroll_updater_last_run = start;
571 NETDATA.onscroll_updater_running = false;
574 NETDATA.onscroll = function() {
575 // console.log('onscroll');
577 NETDATA.options.last_page_scroll = new Date().getTime();
578 NETDATA.options.auto_refresher_stop_until = 0;
580 if(NETDATA.options.targets === null) return;
582 if(NETDATA.options.current.async_on_scroll === true) {
584 if(NETDATA.onscroll_updater_running === false) {
585 NETDATA.onscroll_updater_running = true;
586 setTimeout(NETDATA.onscroll_updater, 0);
589 if(NETDATA.onscroll_updater_watchdog !== null)
590 clearTimeout(NETDATA.onscroll_updater_watchdog);
592 NETDATA.onscroll_updater_watchdog = setTimeout(function() {
593 if(NETDATA.onscroll_updater_running === false && NETDATA.options.last_page_scroll > NETDATA.onscroll_updater_last_run) {
594 // console.log('watchdog');
595 NETDATA.onscroll_updater();
598 NETDATA.onscroll_updater_watchdog = null;
604 NETDATA.onscroll_updater();
608 window.onresize = NETDATA.onresize;
609 window.onscroll = NETDATA.onscroll;
611 // ----------------------------------------------------------------------------------------------------------------
614 NETDATA.errorCodes = {
615 100: { message: "Cannot load chart library", alert: true },
616 101: { message: "Cannot load jQuery", alert: true },
617 402: { message: "Chart library not found", alert: false },
618 403: { message: "Chart library not enabled/is failed", alert: false },
619 404: { message: "Chart not found", alert: false },
620 405: { message: "Cannot download charts index from server", alert: true },
621 406: { message: "Invalid charts index downloaded from server", alert: true },
622 407: { message: "Cannot HELLO netdata server", alert: false },
623 408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
624 409: { message: "Cannot ACCESS netdata registry", alert: false },
625 410: { message: "Netdata registry ACCESS failed", alert: false },
626 411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
627 412: { message: "Netdata registry DELETE failed", alert: false },
628 413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
629 414: { message: "Netdata registry SWITCH failed", alert: false },
630 415: { message: "Netdata alarms download failed", alert: false },
631 416: { message: "Netdata alarms log download failed", alert: false },
632 417: { message: "Netdata registry server send invalid response to SEARCH ", alert: false },
633 418: { message: "Netdata registry SEARCH failed", alert: false }
635 NETDATA.errorLast = {
641 NETDATA.error = function(code, msg) {
642 NETDATA.errorLast.code = code;
643 NETDATA.errorLast.message = msg;
644 NETDATA.errorLast.datetime = new Date().getTime();
646 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
649 if(typeof netdataErrorCallback === 'function') {
650 ret = netdataErrorCallback('system', code, msg);
653 if(ret && NETDATA.errorCodes[code].alert)
654 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
657 NETDATA.errorReset = function() {
658 NETDATA.errorLast.code = 0;
659 NETDATA.errorLast.message = "You are doing fine!";
660 NETDATA.errorLast.datetime = 0;
663 // ----------------------------------------------------------------------------------------------------------------
666 // When multiple charts need the same chart, we avoid downloading it
667 // multiple times (and having it in browser memory multiple time)
668 // by using this registry.
670 // Every time we download a chart definition, we save it here with .add()
671 // Then we try to get it back with .get(). If that fails, we download it.
673 NETDATA.chartRegistry = {
676 fixid: function(id) {
677 return id.replace(/:/g, "_").replace(/\//g, "_");
680 add: function(host, id, data) {
681 host = this.fixid(host);
684 if(typeof this.charts[host] === 'undefined')
685 this.charts[host] = {};
687 //console.log('added ' + host + '/' + id);
688 this.charts[host][id] = data;
691 get: function(host, id) {
692 host = this.fixid(host);
695 if(typeof this.charts[host] === 'undefined')
698 if(typeof this.charts[host][id] === 'undefined')
701 //console.log('cached ' + host + '/' + id);
702 return this.charts[host][id];
705 downloadAll: function(host, callback) {
706 while(host.slice(-1) === '/')
707 host = host.substring(0, host.length - 1);
712 url: host + '/api/v1/charts',
715 xhrFields: { withCredentials: true } // required for the cookie
717 .done(function(data) {
719 var h = NETDATA.chartRegistry.fixid(host);
720 self.charts[h] = data.charts;
722 else NETDATA.error(406, host + '/api/v1/charts');
724 if(typeof callback === 'function')
728 NETDATA.error(405, host + '/api/v1/charts');
730 if(typeof callback === 'function')
736 // ----------------------------------------------------------------------------------------------------------------
737 // Global Pan and Zoom on charts
739 // Using this structure are synchronize all the charts, so that
740 // when you pan or zoom one, all others are automatically refreshed
741 // to the same timespan.
743 NETDATA.globalPanAndZoom = {
744 seq: 0, // timestamp ms
745 // every time a chart is panned or zoomed
746 // we set the timestamp here
747 // then we use it as a sequence number
748 // to find if other charts are syncronized
751 master: null, // the master chart (state), to which all others
754 force_before_ms: null, // the timespan to sync all other charts
755 force_after_ms: null,
760 setMaster: function(state, after, before) {
761 if(NETDATA.options.current.sync_pan_and_zoom === false)
764 if(this.master !== null && this.master !== state)
765 this.master.resetChart(true, true);
767 var now = new Date().getTime();
770 this.force_after_ms = after;
771 this.force_before_ms = before;
772 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
774 if(typeof this.callback === 'function')
775 this.callback(true, after, before);
779 clearMaster: function() {
780 if(this.master !== null) {
781 var st = this.master;
788 this.force_after_ms = null;
789 this.force_before_ms = null;
790 NETDATA.options.auto_refresher_stop_until = 0;
792 if(typeof this.callback === 'function')
793 this.callback(false, 0, 0);
796 // is the given state the master of the global
797 // pan and zoom sync?
798 isMaster: function(state) {
799 if(this.master === state) return true;
803 // are we currently have a global pan and zoom sync?
804 isActive: function() {
805 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
809 // check if a chart, other than the master
810 // needs to be refreshed, due to the global pan and zoom
811 shouldBeAutoRefreshed: function(state) {
812 if(this.master === null || this.seq === 0)
815 //if(state.needsRecreation())
818 if(state.tm.pan_and_zoom_seq === this.seq)
825 // ----------------------------------------------------------------------------------------------------------------
826 // dimensions selection
829 // move color assignment to dimensions, here
831 dimensionStatus = function(parent, label, name_div, value_div, color) {
832 this.enabled = false;
833 this.parent = parent;
835 this.name_div = null;
836 this.value_div = null;
837 this.color = NETDATA.themes.current.foreground;
839 if(parent.unselected_count === 0)
840 this.selected = true;
842 this.selected = false;
844 this.setOptions(name_div, value_div, color);
847 dimensionStatus.prototype.invalidate = function() {
848 this.name_div = null;
849 this.value_div = null;
850 this.enabled = false;
853 dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
856 if(this.name_div != name_div) {
857 this.name_div = name_div;
858 this.name_div.title = this.label;
859 this.name_div.style.color = this.color;
860 if(this.selected === false)
861 this.name_div.className = 'netdata-legend-name not-selected';
863 this.name_div.className = 'netdata-legend-name selected';
866 if(this.value_div != value_div) {
867 this.value_div = value_div;
868 this.value_div.title = this.label;
869 this.value_div.style.color = this.color;
870 if(this.selected === false)
871 this.value_div.className = 'netdata-legend-value not-selected';
873 this.value_div.className = 'netdata-legend-value selected';
880 dimensionStatus.prototype.setHandler = function() {
881 if(this.enabled === false) return;
885 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
886 this.name_div.onclick = this.value_div.onclick = function(e) {
888 if(ds.isSelected()) {
890 if(e.shiftKey === true || e.ctrlKey === true) {
891 // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
894 if(ds.parent.countSelected() === 0)
895 ds.parent.selectAll();
898 // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
899 if(ds.parent.countSelected() === 1) {
900 ds.parent.selectAll();
903 ds.parent.selectNone();
909 // this is not selected
910 if(e.shiftKey === true || e.ctrlKey === true) {
911 // control or shift key is pressed -> select this too
915 // no key is pressed -> select only this
916 ds.parent.selectNone();
921 ds.parent.state.redrawChart();
925 dimensionStatus.prototype.select = function() {
926 if(this.enabled === false) return;
928 this.name_div.className = 'netdata-legend-name selected';
929 this.value_div.className = 'netdata-legend-value selected';
930 this.selected = true;
933 dimensionStatus.prototype.unselect = function() {
934 if(this.enabled === false) return;
936 this.name_div.className = 'netdata-legend-name not-selected';
937 this.value_div.className = 'netdata-legend-value hidden';
938 this.selected = false;
941 dimensionStatus.prototype.isSelected = function() {
942 return(this.enabled === true && this.selected === true);
945 // ----------------------------------------------------------------------------------------------------------------
947 dimensionsVisibility = function(state) {
950 this.dimensions = {};
951 this.selected_count = 0;
952 this.unselected_count = 0;
955 dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
956 if(typeof this.dimensions[label] === 'undefined') {
958 this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
961 this.dimensions[label].setOptions(name_div, value_div, color);
963 return this.dimensions[label];
966 dimensionsVisibility.prototype.dimensionGet = function(label) {
967 return this.dimensions[label];
970 dimensionsVisibility.prototype.invalidateAll = function() {
971 for(var d in this.dimensions)
972 this.dimensions[d].invalidate();
975 dimensionsVisibility.prototype.selectAll = function() {
976 for(var d in this.dimensions)
977 this.dimensions[d].select();
980 dimensionsVisibility.prototype.countSelected = function() {
982 for(var d in this.dimensions)
983 if(this.dimensions[d].isSelected()) i++;
988 dimensionsVisibility.prototype.selectNone = function() {
989 for(var d in this.dimensions)
990 this.dimensions[d].unselect();
993 dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
994 var ret = new Array();
995 this.selected_count = 0;
996 this.unselected_count = 0;
998 var len = array.length;
1000 var ds = this.dimensions[array[len]];
1001 if(typeof ds === 'undefined') {
1002 // console.log(array[i] + ' is not found');
1005 else if(ds.isSelected()) {
1007 this.selected_count++;
1011 this.unselected_count++;
1015 if(this.selected_count === 0 && this.unselected_count !== 0) {
1017 return this.selected2BooleanArray(array);
1024 // ----------------------------------------------------------------------------------------------------------------
1025 // global selection sync
1027 NETDATA.globalSelectionSync = {
1029 dont_sync_before: 0,
1034 if(this.state !== null)
1035 this.state.globalSelectionSyncStop();
1039 if(this.state !== null) {
1040 this.state.globalSelectionSyncDelay();
1045 // ----------------------------------------------------------------------------------------------------------------
1046 // Our state object, where all per-chart values are stored
1048 chartState = function(element) {
1049 var self = $(element);
1050 this.element = element;
1053 // all private functions should use 'that', instead of 'this'
1056 /* error() - private
1057 * show an error instead of the chart
1059 var error = function(msg) {
1062 if(typeof netdataErrorCallback === 'function') {
1063 ret = netdataErrorCallback('chart', that.id, msg);
1067 that.element.innerHTML = that.id + ': ' + msg;
1068 that.enabled = false;
1069 that.current = that.pan;
1073 // GUID - a unique identifier for the chart
1074 this.uuid = NETDATA.guid();
1076 // string - the name of chart
1077 this.id = self.data('netdata');
1079 // string - the key for localStorage settings
1080 this.settings_id = self.data('id') || null;
1082 // the user given dimensions of the element
1083 this.width = self.data('width') || NETDATA.chartDefaults.width;
1084 this.height = self.data('height') || NETDATA.chartDefaults.height;
1086 if(this.settings_id !== null) {
1087 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
1088 // this is the callback that will be called
1089 // if and when the user resets all localStorage variables
1090 // to their defaults
1092 resizeChartToHeight(height);
1096 // string - the netdata server URL, without any path
1097 this.host = self.data('host') || NETDATA.chartDefaults.host;
1099 // make sure the host does not end with /
1100 // all netdata API requests use absolute paths
1101 while(this.host.slice(-1) === '/')
1102 this.host = this.host.substring(0, this.host.length - 1);
1104 // string - the grouping method requested by the user
1105 this.method = self.data('method') || NETDATA.chartDefaults.method;
1107 // the time-range requested by the user
1108 this.after = self.data('after') || NETDATA.chartDefaults.after;
1109 this.before = self.data('before') || NETDATA.chartDefaults.before;
1111 // the pixels per point requested by the user
1112 this.pixels_per_point = self.data('pixels-per-point') || 1;
1113 this.points = self.data('points') || null;
1115 // the dimensions requested by the user
1116 this.dimensions = self.data('dimensions') || null;
1118 // the chart library requested by the user
1119 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
1121 // object - the chart library used
1122 this.library = null;
1126 this.colors_assigned = {};
1127 this.colors_available = null;
1129 // the element already created by the user
1130 this.element_message = null;
1132 // the element with the chart
1133 this.element_chart = null;
1135 // the element with the legend of the chart (if created by us)
1136 this.element_legend = null;
1137 this.element_legend_childs = {
1147 this.chart_url = null; // string - the url to download chart info
1148 this.chart = null; // object - the chart as downloaded from the server
1150 this.title = self.data('title') || null; // the title of the chart
1151 this.units = self.data('units') || null; // the units of the chart dimensions
1152 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1154 this.running = false; // boolean - true when the chart is being refreshed now
1155 this.validated = false; // boolean - has the chart been validated?
1156 this.enabled = true; // boolean - is the chart enabled for refresh?
1157 this.paused = false; // boolean - is the chart paused for any reason?
1158 this.selected = false; // boolean - is the chart shown a selection?
1159 this.debug = false; // boolean - console.log() debug info about this chart
1161 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1162 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1163 this.requested_after = null; // milliseconds - the timestamp of the request after param
1164 this.requested_before = null; // milliseconds - the timestamp of the request before param
1165 this.requested_padding = null;
1166 this.view_after = 0;
1167 this.view_before = 0;
1172 force_update_at: 0, // the timestamp to force the update at
1173 force_before_ms: null,
1174 force_after_ms: null
1179 force_update_at: 0, // the timestamp to force the update at
1180 force_before_ms: null,
1181 force_after_ms: null
1186 force_update_at: 0, // the timestamp to force the update at
1187 force_before_ms: null,
1188 force_after_ms: null
1191 // this is a pointer to one of the sub-classes below
1193 this.current = this.auto;
1195 // check the requested library is available
1196 // we don't initialize it here - it will be initialized when
1197 // this chart will be first used
1198 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1199 NETDATA.error(402, that.library_name);
1200 error('chart library "' + that.library_name + '" is not found');
1203 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1204 NETDATA.error(403, that.library_name);
1205 error('chart library "' + that.library_name + '" is not enabled');
1209 that.library = NETDATA.chartLibraries[that.library_name];
1211 // milliseconds - the time the last refresh took
1212 this.refresh_dt_ms = 0;
1214 // if we need to report the rendering speed
1215 // find the element that needs to be updated
1216 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1218 if(refresh_dt_element_name !== null)
1219 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1221 this.refresh_dt_element = null;
1223 this.dimensions_visibility = new dimensionsVisibility(this);
1225 this._updating = false;
1227 // ============================================================================================================
1228 // PRIVATE FUNCTIONS
1230 var createDOM = function() {
1231 if(that.enabled === false) return;
1233 if(that.element_message !== null) that.element_message.innerHTML = '';
1234 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1235 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1237 that.element.innerHTML = '';
1239 that.element_message = document.createElement('div');
1240 that.element_message.className = ' netdata-message hidden';
1241 that.element.appendChild(that.element_message);
1243 that.element_chart = document.createElement('div');
1244 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1245 that.element.appendChild(that.element_chart);
1247 if(that.hasLegend() === true) {
1248 that.element.className = "netdata-container-with-legend";
1249 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1251 that.element_legend = document.createElement('div');
1252 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1253 that.element.appendChild(that.element_legend);
1256 that.element.className = "netdata-container";
1257 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1259 that.element_legend = null;
1261 that.element_legend_childs.series = null;
1263 if(typeof(that.width) === 'string')
1264 $(that.element).css('width', that.width);
1265 else if(typeof(that.width) === 'number')
1266 $(that.element).css('width', that.width + 'px');
1268 if(typeof(that.library.aspect_ratio) === 'undefined') {
1269 if(typeof(that.height) === 'string')
1270 $(that.element).css('height', that.height);
1271 else if(typeof(that.height) === 'number')
1272 $(that.element).css('height', that.height + 'px');
1275 var w = that.element.offsetWidth;
1276 if(w === null || w === 0) {
1277 // the div is hidden
1278 // this will resize the chart when next viewed
1279 that.tm.last_resized = 0;
1282 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1285 if(NETDATA.chartDefaults.min_width !== null)
1286 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1288 that.tm.last_dom_created = new Date().getTime();
1294 * initialize state variables
1295 * destroy all (possibly) created state elements
1296 * create the basic DOM for a chart
1298 var init = function() {
1299 if(that.enabled === false) return;
1301 that.paused = false;
1302 that.selected = false;
1304 that.chart_created = false; // boolean - is the library.create() been called?
1305 that.updates_counter = 0; // numeric - the number of refreshes made so far
1306 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1307 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1310 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1311 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1312 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1314 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1315 last_updated: 0, // the timestamp the chart last updated with data
1316 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1318 // Used with NETDATA.globalPanAndZoom.seq
1319 last_visible_check: 0, // the time we last checked if it is visible
1320 last_resized: 0, // the time the chart was resized
1321 last_hidden: 0, // the time the chart was hidden
1322 last_unhidden: 0, // the time the chart was unhidden
1323 last_autorefreshed: 0 // the time the chart was last refreshed
1326 that.data = null; // the last data as downloaded from the netdata server
1327 that.data_url = 'invalid://'; // string - the last url used to update the chart
1328 that.data_points = 0; // number - the number of points returned from netdata
1329 that.data_after = 0; // milliseconds - the first timestamp of the data
1330 that.data_before = 0; // milliseconds - the last timestamp of the data
1331 that.data_update_every = 0; // milliseconds - the frequency to update the data
1333 that.tm.last_initialized = new Date().getTime();
1336 that.setMode('auto');
1339 var maxMessageFontSize = function() {
1340 // normally we want a font size, as tall as the element
1341 var h = that.element_message.clientHeight;
1343 // but give it some air, 20% let's say, or 5 pixels min
1344 var lost = Math.max(h * 0.2, 5);
1347 // center the text, vertically
1348 var paddingTop = (lost - 5) / 2;
1350 // but check the width too
1351 // it should fit 10 characters in it
1352 var w = that.element_message.clientWidth / 10;
1354 paddingTop += (h - w) / 2;
1358 // and don't make it too huge
1359 // 5% of the screen size is good
1360 if(h > screen.height / 20) {
1361 paddingTop += (h - (screen.height / 20)) / 2;
1362 h = screen.height / 20;
1366 that.element_message.style.fontSize = h.toString() + 'px';
1367 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1370 var showMessage = function(msg) {
1371 that.element_message.className = 'netdata-message';
1372 that.element_message.innerHTML = msg;
1373 that.element_message.style.fontSize = 'x-small';
1374 that.element_message.style.paddingTop = '0px';
1375 that.___messageHidden___ = undefined;
1378 var showMessageIcon = function(icon) {
1379 that.element_message.innerHTML = icon;
1380 that.element_message.className = 'netdata-message icon';
1381 maxMessageFontSize();
1382 that.___messageHidden___ = undefined;
1385 var hideMessage = function() {
1386 if(typeof that.___messageHidden___ === 'undefined') {
1387 that.___messageHidden___ = true;
1388 that.element_message.className = 'netdata-message hidden';
1392 var showRendering = function() {
1394 if(that.chart !== null) {
1395 if(that.chart.chart_type === 'line')
1396 icon = '<i class="fa fa-line-chart"></i>';
1398 icon = '<i class="fa fa-area-chart"></i>';
1401 icon = '<i class="fa fa-area-chart"></i>';
1403 showMessageIcon(icon + ' netdata');
1406 var showLoading = function() {
1407 if(that.chart_created === false) {
1408 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1414 var isHidden = function() {
1415 if(typeof that.___chartIsHidden___ !== 'undefined')
1421 // hide the chart, when it is not visible - called from isVisible()
1422 var hideChart = function() {
1423 // hide it, if it is not already hidden
1424 if(isHidden() === true) return;
1426 if(that.chart_created === true) {
1427 if(NETDATA.options.current.destroy_on_hide === true) {
1428 // we should destroy it
1433 that.element_chart.style.display = 'none';
1434 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1435 that.tm.last_hidden = new Date().getTime();
1438 // This works, but I not sure there are no corner cases somewhere
1439 // so it is commented - if the user has memory issues he can
1440 // set Destroy on Hide for all charts
1441 // that.data = null;
1445 that.___chartIsHidden___ = true;
1448 // unhide the chart, when it is visible - called from isVisible()
1449 var unhideChart = function() {
1450 if(isHidden() === false) return;
1452 that.___chartIsHidden___ = undefined;
1453 that.updates_since_last_unhide = 0;
1455 if(that.chart_created === false) {
1456 // we need to re-initialize it, to show our background
1457 // logo in bootstrap tabs, until the chart loads
1461 that.tm.last_unhidden = new Date().getTime();
1462 that.element_chart.style.display = '';
1463 if(that.element_legend !== null) that.element_legend.style.display = '';
1469 var canBeRendered = function() {
1470 if(isHidden() === true || that.isVisible(true) === false)
1476 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1477 var callChartLibraryUpdateSafely = function(data) {
1480 if(canBeRendered() === false)
1483 if(NETDATA.options.debug.chart_errors === true)
1484 status = that.library.update(that, data);
1487 status = that.library.update(that, data);
1494 if(status === false) {
1495 error('chart failed to be updated as ' + that.library_name);
1502 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1503 var callChartLibraryCreateSafely = function(data) {
1506 if(canBeRendered() === false)
1509 if(NETDATA.options.debug.chart_errors === true)
1510 status = that.library.create(that, data);
1513 status = that.library.create(that, data);
1520 if(status === false) {
1521 error('chart failed to be created as ' + that.library_name);
1525 that.chart_created = true;
1526 that.updates_since_last_creation = 0;
1530 // ----------------------------------------------------------------------------------------------------------------
1533 // resizeChart() - private
1534 // to be called just before the chart library to make sure that
1535 // a properly sized dom is available
1536 var resizeChart = function() {
1537 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1538 if(that.chart_created === false) return;
1540 if(that.needsRecreation()) {
1543 else if(typeof that.library.resize === 'function') {
1544 that.library.resize(that);
1546 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1547 $(that.element_legend_childs.nano).nanoScroller();
1549 maxMessageFontSize();
1552 that.tm.last_resized = new Date().getTime();
1556 // this is the actual chart resize algorithm
1558 // - resize the entire container
1559 // - update the internal states
1560 // - resize the chart as the div changes height
1561 // - update the scrollbar of the legend
1562 var resizeChartToHeight = function(h) {
1564 that.element.style.height = h;
1566 if(that.settings_id !== null)
1567 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1569 var now = new Date().getTime();
1570 NETDATA.options.last_page_scroll = now;
1571 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1574 that.tm.last_resized = 0;
1578 this.resizeHandler = function(e) {
1581 if(typeof this.event_resize === 'undefined'
1582 || this.event_resize.chart_original_w === 'undefined'
1583 || this.event_resize.chart_original_h === 'undefined')
1584 this.event_resize = {
1585 chart_original_w: this.element.clientWidth,
1586 chart_original_h: this.element.clientHeight,
1590 if(e.type === 'touchstart') {
1591 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1592 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1595 this.event_resize.mouse_start_x = e.clientX;
1596 this.event_resize.mouse_start_y = e.clientY;
1599 this.event_resize.chart_start_w = this.element.clientWidth;
1600 this.event_resize.chart_start_h = this.element.clientHeight;
1601 this.event_resize.chart_last_w = this.element.clientWidth;
1602 this.event_resize.chart_last_h = this.element.clientHeight;
1604 var now = new Date().getTime();
1605 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1606 // double click / double tap event
1608 // the optimal height of the chart
1609 // showing the entire legend
1610 var optimal = this.event_resize.chart_last_h
1611 + this.element_legend_childs.content.scrollHeight
1612 - this.element_legend_childs.content.clientHeight;
1614 // if we are not optimal, be optimal
1615 if(this.event_resize.chart_last_h != optimal)
1616 resizeChartToHeight(optimal.toString() + 'px');
1618 // else if we do not have the original height
1619 // reset to the original height
1620 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1621 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1624 this.event_resize.last = now;
1626 // process movement event
1627 document.onmousemove =
1628 document.ontouchmove =
1629 this.element_legend_childs.resize_handler.onmousemove =
1630 this.element_legend_childs.resize_handler.ontouchmove =
1635 case 'mousemove': y = e.clientY; break;
1636 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1640 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1642 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1643 resizeChartToHeight(newH.toString() + 'px');
1644 that.event_resize.chart_last_h = newH;
1649 // process end event
1650 document.onmouseup =
1651 document.ontouchend =
1652 this.element_legend_childs.resize_handler.onmouseup =
1653 this.element_legend_childs.resize_handler.ontouchend =
1655 // remove all the hooks
1656 document.onmouseup =
1657 document.onmousemove =
1658 document.ontouchmove =
1659 document.ontouchend =
1660 that.element_legend_childs.resize_handler.onmousemove =
1661 that.element_legend_childs.resize_handler.ontouchmove =
1662 that.element_legend_childs.resize_handler.onmouseout =
1663 that.element_legend_childs.resize_handler.onmouseup =
1664 that.element_legend_childs.resize_handler.ontouchend =
1667 // allow auto-refreshes
1668 NETDATA.options.auto_refresher_stop_until = 0;
1674 var noDataToShow = function() {
1675 showMessageIcon('<i class="fa fa-warning"></i> empty');
1676 that.legendUpdateDOM();
1677 that.tm.last_autorefreshed = new Date().getTime();
1678 // that.data_update_every = 30 * 1000;
1679 //that.element_chart.style.display = 'none';
1680 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1681 //that.___chartIsHidden___ = true;
1684 // ============================================================================================================
1687 this.error = function(msg) {
1691 this.setMode = function(m) {
1692 if(this.current !== null && this.current.name === m) return;
1695 this.current = this.auto;
1696 else if(m === 'pan')
1697 this.current = this.pan;
1698 else if(m === 'zoom')
1699 this.current = this.zoom;
1701 this.current = this.auto;
1703 this.current.force_update_at = 0;
1704 this.current.force_before_ms = null;
1705 this.current.force_after_ms = null;
1707 this.tm.last_mode_switch = new Date().getTime();
1710 // ----------------------------------------------------------------------------------------------------------------
1711 // global selection sync
1713 // prevent to global selection sync for some time
1714 this.globalSelectionSyncDelay = function(ms) {
1715 if(NETDATA.options.current.sync_selection === false)
1718 if(typeof ms === 'number')
1719 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1721 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1724 // can we globally apply selection sync?
1725 this.globalSelectionSyncAbility = function() {
1726 if(NETDATA.options.current.sync_selection === false)
1729 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1735 this.globalSelectionSyncIsMaster = function() {
1736 if(NETDATA.globalSelectionSync.state === this)
1742 // this chart is the master of the global selection sync
1743 this.globalSelectionSyncBeMaster = function() {
1745 if(this.globalSelectionSyncIsMaster()) {
1746 if(this.debug === true)
1747 this.log('sync: I am the master already.');
1752 if(NETDATA.globalSelectionSync.state) {
1753 if(this.debug === true)
1754 this.log('sync: I am not the sync master. Resetting global sync.');
1756 this.globalSelectionSyncStop();
1759 // become the master
1760 if(this.debug === true)
1761 this.log('sync: becoming sync master.');
1763 this.selected = true;
1764 NETDATA.globalSelectionSync.state = this;
1766 // find the all slaves
1767 var targets = NETDATA.options.targets;
1768 var len = targets.length;
1773 if(this.debug === true)
1774 st.log('sync: not adding me to sync');
1776 else if(st.globalSelectionSyncIsEligible()) {
1777 if(this.debug === true)
1778 st.log('sync: adding to sync as slave');
1780 st.globalSelectionSyncBeSlave();
1784 // this.globalSelectionSyncDelay(100);
1787 // can the chart participate to the global selection sync as a slave?
1788 this.globalSelectionSyncIsEligible = function() {
1789 if(this.enabled === true
1790 && this.library !== null
1791 && typeof this.library.setSelection === 'function'
1792 && this.isVisible() === true
1793 && this.chart_created === true)
1799 // this chart becomes a slave of the global selection sync
1800 this.globalSelectionSyncBeSlave = function() {
1801 if(NETDATA.globalSelectionSync.state !== this)
1802 NETDATA.globalSelectionSync.slaves.push(this);
1805 // sync all the visible charts to the given time
1806 // this is to be called from the chart libraries
1807 this.globalSelectionSync = function(t) {
1808 if(this.globalSelectionSyncAbility() === false) {
1809 if(this.debug === true)
1810 this.log('sync: cannot sync (yet?).');
1815 if(this.globalSelectionSyncIsMaster() === false) {
1816 if(this.debug === true)
1817 this.log('sync: trying to be sync master.');
1819 this.globalSelectionSyncBeMaster();
1821 if(this.globalSelectionSyncAbility() === false) {
1822 if(this.debug === true)
1823 this.log('sync: cannot sync (yet?).');
1829 NETDATA.globalSelectionSync.last_t = t;
1830 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1835 // stop syncing all charts to the given time
1836 this.globalSelectionSyncStop = function() {
1837 if(NETDATA.globalSelectionSync.slaves.length) {
1838 if(this.debug === true)
1839 this.log('sync: cleaning up...');
1841 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1843 if(that.debug === true)
1844 st.log('sync: not adding me to sync stop');
1847 if(that.debug === true)
1848 st.log('sync: removed slave from sync');
1850 st.clearSelection();
1854 NETDATA.globalSelectionSync.last_t = 0;
1855 NETDATA.globalSelectionSync.slaves = [];
1856 NETDATA.globalSelectionSync.state = null;
1859 this.clearSelection();
1862 this.setSelection = function(t) {
1863 if(typeof this.library.setSelection === 'function') {
1864 if(this.library.setSelection(this, t) === true)
1865 this.selected = true;
1867 this.selected = false;
1869 else this.selected = true;
1871 if(this.selected === true && this.debug === true)
1872 this.log('selection set to ' + t.toString());
1874 return this.selected;
1877 this.clearSelection = function() {
1878 if(this.selected === true) {
1879 if(typeof this.library.clearSelection === 'function') {
1880 if(this.library.clearSelection(this) === true)
1881 this.selected = false;
1883 this.selected = true;
1885 else this.selected = false;
1887 if(this.selected === false && this.debug === true)
1888 this.log('selection cleared');
1893 return this.selected;
1896 // find if a timestamp (ms) is shown in the current chart
1897 this.timeIsVisible = function(t) {
1898 if(t >= this.data_after && t <= this.data_before)
1903 this.calculateRowForTime = function(t) {
1904 if(this.timeIsVisible(t) === false) return -1;
1905 return Math.floor((t - this.data_after) / this.data_update_every);
1908 // ----------------------------------------------------------------------------------------------------------------
1911 this.log = function(msg) {
1912 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1915 this.pauseChart = function() {
1916 if(this.paused === false) {
1917 if(this.debug === true)
1918 this.log('pauseChart()');
1924 this.unpauseChart = function() {
1925 if(this.paused === true) {
1926 if(this.debug === true)
1927 this.log('unpauseChart()');
1929 this.paused = false;
1933 this.resetChart = function(dont_clear_master, dont_update) {
1934 if(this.debug === true)
1935 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1937 if(typeof dont_clear_master === 'undefined')
1938 dont_clear_master = false;
1940 if(typeof dont_update === 'undefined')
1941 dont_update = false;
1943 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1944 if(this.debug === true)
1945 this.log('resetChart() diverting to clearMaster().');
1946 // this will call us back with master === true
1947 NETDATA.globalPanAndZoom.clearMaster();
1951 this.clearSelection();
1953 this.tm.pan_and_zoom_seq = 0;
1955 this.setMode('auto');
1956 this.current.force_update_at = 0;
1957 this.current.force_before_ms = null;
1958 this.current.force_after_ms = null;
1959 this.tm.last_autorefreshed = 0;
1960 this.paused = false;
1961 this.selected = false;
1962 this.enabled = true;
1963 // this.debug = false;
1965 // do not update the chart here
1966 // or the chart will flip-flop when it is the master
1967 // of a selection sync and another chart becomes
1970 if(dont_update !== true && this.isVisible() === true) {
1975 this.updateChartPanOrZoom = function(after, before) {
1976 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1979 if(this.debug === true)
1982 if(before < after) {
1983 if(this.debug === true)
1984 this.log(logme + 'flipped parameters, rejecting it.');
1989 if(typeof this.fixed_min_duration === 'undefined')
1990 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1992 var min_duration = this.fixed_min_duration;
1993 var current_duration = Math.round(this.view_before - this.view_after);
1995 // round the numbers
1996 after = Math.round(after);
1997 before = Math.round(before);
1999 // align them to update_every
2000 // stretching them further away
2001 after -= after % this.data_update_every;
2002 before += this.data_update_every - (before % this.data_update_every);
2004 // the final wanted duration
2005 var wanted_duration = before - after;
2007 // to allow panning, accept just a point below our minimum
2008 if((current_duration - this.data_update_every) < min_duration)
2009 min_duration = current_duration - this.data_update_every;
2011 // we do it, but we adjust to minimum size and return false
2012 // when the wanted size is below the current and the minimum
2014 if(wanted_duration < current_duration && wanted_duration < min_duration) {
2015 if(this.debug === true)
2016 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
2018 min_duration = this.fixed_min_duration;
2020 var dt = (min_duration - wanted_duration) / 2;
2023 wanted_duration = before - after;
2027 var tolerance = this.data_update_every * 2;
2028 var movement = Math.abs(before - this.view_before);
2030 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
2031 if(this.debug === true)
2032 this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
2036 if(this.current.name === 'auto') {
2037 this.log(logme + 'caller called me with mode: ' + this.current.name);
2038 this.setMode('pan');
2041 if(this.debug === true)
2042 this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
2044 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
2045 this.current.force_after_ms = after;
2046 this.current.force_before_ms = before;
2047 NETDATA.globalPanAndZoom.setMaster(this, after, before);
2051 this.legendFormatValue = function(value) {
2052 if(value === null || value === 'undefined') return '-';
2053 if(typeof value !== 'number') return value;
2055 var abs = Math.abs(value);
2056 if(abs >= 1000) return (Math.round(value)).toLocaleString();
2057 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
2058 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
2059 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
2060 return (Math.round(value * 10000) / 10000).toLocaleString();
2063 this.legendSetLabelValue = function(label, value) {
2064 var series = this.element_legend_childs.series[label];
2065 if(typeof series === 'undefined') return;
2066 if(series.value === null && series.user === null) return;
2068 // if the value has not changed, skip DOM update
2069 //if(series.last === value) return;
2072 if(typeof value === 'number') {
2073 var v = Math.abs(value);
2074 s = r = this.legendFormatValue(value);
2076 if(typeof series.last === 'number') {
2077 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2078 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2079 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2081 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
2086 series.last = value;
2089 if(series.value !== null) series.value.innerHTML = s;
2090 if(series.user !== null) series.user.innerHTML = r;
2093 this.legendSetDate = function(ms) {
2094 if(typeof ms !== 'number') {
2095 this.legendShowUndefined();
2099 var d = new Date(ms);
2101 if(this.element_legend_childs.title_date)
2102 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
2104 if(this.element_legend_childs.title_time)
2105 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
2107 if(this.element_legend_childs.title_units)
2108 this.element_legend_childs.title_units.innerHTML = this.units;
2111 this.legendShowUndefined = function() {
2112 if(this.element_legend_childs.title_date)
2113 this.element_legend_childs.title_date.innerHTML = ' ';
2115 if(this.element_legend_childs.title_time)
2116 this.element_legend_childs.title_time.innerHTML = this.chart.name;
2118 if(this.element_legend_childs.title_units)
2119 this.element_legend_childs.title_units.innerHTML = ' ';
2121 if(this.data && this.element_legend_childs.series !== null) {
2122 var labels = this.data.dimension_names;
2123 var i = labels.length;
2125 var label = labels[i];
2127 if(typeof label === 'undefined') continue;
2128 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2129 this.legendSetLabelValue(label, null);
2134 this.legendShowLatestValues = function() {
2135 if(this.chart === null) return;
2136 if(this.selected) return;
2138 if(this.data === null || this.element_legend_childs.series === null) {
2139 this.legendShowUndefined();
2143 var show_undefined = true;
2144 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
2145 show_undefined = false;
2147 if(show_undefined) {
2148 this.legendShowUndefined();
2152 this.legendSetDate(this.view_before);
2154 var labels = this.data.dimension_names;
2155 var i = labels.length;
2157 var label = labels[i];
2159 if(typeof label === 'undefined') continue;
2160 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2163 this.legendSetLabelValue(label, null);
2165 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2169 this.legendReset = function() {
2170 this.legendShowLatestValues();
2173 // this should be called just ONCE per dimension per chart
2174 this._chartDimensionColor = function(label) {
2175 if(this.colors === null) this.chartColors();
2177 if(typeof this.colors_assigned[label] === 'undefined') {
2178 if(this.colors_available.length === 0) {
2179 var len = NETDATA.themes.current.colors.length;
2181 this.colors_available.unshift(NETDATA.themes.current.colors[len]);
2184 this.colors_assigned[label] = this.colors_available.shift();
2186 if(this.debug === true)
2187 this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
2190 if(this.debug === true)
2191 this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
2194 this.colors.push(this.colors_assigned[label]);
2195 return this.colors_assigned[label];
2198 this.chartColors = function() {
2199 if(this.colors !== null) return this.colors;
2201 this.colors = new Array();
2202 this.colors_available = new Array();
2204 // add the standard colors
2205 var len = NETDATA.themes.current.colors.length;
2207 this.colors_available.unshift(NETDATA.themes.current.colors[len]);
2209 // add the user supplied colors
2210 var c = $(this.element).data('colors');
2211 // this.log('read colors: ' + c);
2212 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2213 if(typeof c !== 'string') {
2214 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2224 this.colors_available.unshift(c[len]);
2225 // this.log('adding color: ' + c[len]);
2234 this.legendUpdateDOM = function() {
2237 // check that the legend DOM is up to date for the downloaded dimensions
2238 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2239 // this.log('the legend does not have any series - requesting legend update');
2242 else if(this.data === null) {
2243 // this.log('the chart does not have any data - requesting legend update');
2246 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2250 var labels = this.data.dimension_names.toString();
2251 if(labels !== this.element_legend_childs.series.labels_key) {
2254 if(this.debug === true)
2255 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2259 if(needed === false) {
2260 // make sure colors available
2263 // do we have to update the current values?
2264 // we do this, only when the visible chart is current
2265 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2266 if(this.debug === true)
2267 this.log('chart is in latest position... updating values on legend...');
2269 //var labels = this.data.dimension_names;
2270 //var i = labels.length;
2272 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2276 if(this.colors === null) {
2277 // this is the first time we update the chart
2278 // let's assign colors to all dimensions
2279 if(this.library.track_colors() === true)
2280 for(var dim in this.chart.dimensions)
2281 this._chartDimensionColor(this.chart.dimensions[dim].name);
2283 // we will re-generate the colors for the chart
2284 // based on the selected dimensions
2287 if(this.debug === true)
2288 this.log('updating Legend DOM');
2290 // mark all dimensions as invalid
2291 this.dimensions_visibility.invalidateAll();
2293 var genLabel = function(state, parent, dim, name, count) {
2294 var color = state._chartDimensionColor(name);
2296 var user_element = null;
2297 var user_id = self.data('show-value-of-' + dim + '-at') || null;
2298 if(user_id !== null) {
2299 user_element = document.getElementById(user_id) || null;
2300 if(user_element === null)
2301 state.log('Cannot find element with id: ' + user_id);
2304 state.element_legend_childs.series[name] = {
2305 name: document.createElement('span'),
2306 value: document.createElement('span'),
2311 var label = state.element_legend_childs.series[name];
2313 // create the dimension visibility tracking for this label
2314 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2316 var rgb = NETDATA.colorHex2Rgb(color);
2317 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2318 + state.chart.chart_type
2319 + '" style="background-color: '
2320 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2321 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2323 var text = document.createTextNode(' ' + name);
2324 label.name.appendChild(text);
2327 parent.appendChild(document.createElement('br'));
2329 parent.appendChild(label.name);
2330 parent.appendChild(label.value);
2333 var content = document.createElement('div');
2335 if(this.hasLegend()) {
2336 this.element_legend_childs = {
2338 resize_handler: document.createElement('div'),
2339 toolbox: document.createElement('div'),
2340 toolbox_left: document.createElement('div'),
2341 toolbox_right: document.createElement('div'),
2342 toolbox_reset: document.createElement('div'),
2343 toolbox_zoomin: document.createElement('div'),
2344 toolbox_zoomout: document.createElement('div'),
2345 toolbox_volume: document.createElement('div'),
2346 title_date: document.createElement('span'),
2347 title_time: document.createElement('span'),
2348 title_units: document.createElement('span'),
2349 nano: document.createElement('div'),
2351 paneClass: 'netdata-legend-series-pane',
2352 sliderClass: 'netdata-legend-series-slider',
2353 contentClass: 'netdata-legend-series-content',
2354 enabledClass: '__enabled',
2355 flashedClass: '__flashed',
2356 activeClass: '__active',
2358 alwaysVisible: true,
2364 this.element_legend.innerHTML = '';
2366 if(this.library.toolboxPanAndZoom !== null) {
2368 function get_pan_and_zoom_step(event) {
2370 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2372 else if (event.shiftKey)
2373 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2375 else if (event.altKey)
2376 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2379 return NETDATA.options.current.pan_and_zoom_factor;
2382 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2383 this.element.appendChild(this.element_legend_childs.toolbox);
2385 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2386 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2387 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2388 this.element_legend_childs.toolbox_left.onclick = function(e) {
2391 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2392 var before = that.view_before - step;
2393 var after = that.view_after - step;
2394 if(after >= that.netdata_first)
2395 that.library.toolboxPanAndZoom(that, after, before);
2397 if(NETDATA.options.current.show_help === true)
2398 $(this.element_legend_childs.toolbox_left).popover({
2403 placement: 'bottom',
2404 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2406 content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2410 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2411 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2412 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2413 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2415 NETDATA.resetAllCharts(that);
2417 if(NETDATA.options.current.show_help === true)
2418 $(this.element_legend_childs.toolbox_reset).popover({
2423 placement: 'bottom',
2424 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2425 title: 'Chart Reset',
2426 content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2429 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2430 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2431 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2432 this.element_legend_childs.toolbox_right.onclick = function(e) {
2434 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2435 var before = that.view_before + step;
2436 var after = that.view_after + step;
2437 if(before <= that.netdata_last)
2438 that.library.toolboxPanAndZoom(that, after, before);
2440 if(NETDATA.options.current.show_help === true)
2441 $(this.element_legend_childs.toolbox_right).popover({
2446 placement: 'bottom',
2447 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2449 content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2453 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2454 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2455 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2456 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2458 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2459 var before = that.view_before - dt;
2460 var after = that.view_after + dt;
2461 that.library.toolboxPanAndZoom(that, after, before);
2463 if(NETDATA.options.current.show_help === true)
2464 $(this.element_legend_childs.toolbox_zoomin).popover({
2469 placement: 'bottom',
2470 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2471 title: 'Chart Zoom In',
2472 content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2475 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2476 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2477 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2478 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2480 var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
2481 var before = that.view_before + dt;
2482 var after = that.view_after - dt;
2484 that.library.toolboxPanAndZoom(that, after, before);
2486 if(NETDATA.options.current.show_help === true)
2487 $(this.element_legend_childs.toolbox_zoomout).popover({
2492 placement: 'bottom',
2493 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2494 title: 'Chart Zoom Out',
2495 content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2498 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2499 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2500 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2501 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2502 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2503 //e.preventDefault();
2504 //alert('clicked toolbox_volume on ' + that.id);
2508 this.element_legend_childs.toolbox = null;
2509 this.element_legend_childs.toolbox_left = null;
2510 this.element_legend_childs.toolbox_reset = null;
2511 this.element_legend_childs.toolbox_right = null;
2512 this.element_legend_childs.toolbox_zoomin = null;
2513 this.element_legend_childs.toolbox_zoomout = null;
2514 this.element_legend_childs.toolbox_volume = null;
2517 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2518 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2519 this.element.appendChild(this.element_legend_childs.resize_handler);
2520 if(NETDATA.options.current.show_help === true)
2521 $(this.element_legend_childs.resize_handler).popover({
2526 placement: 'bottom',
2527 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2528 title: 'Chart Resize',
2529 content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
2533 this.element_legend_childs.resize_handler.onmousedown =
2535 that.resizeHandler(e);
2539 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2540 that.resizeHandler(e);
2543 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2544 this.element_legend.appendChild(this.element_legend_childs.title_date);
2546 this.element_legend.appendChild(document.createElement('br'));
2548 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2549 this.element_legend.appendChild(this.element_legend_childs.title_time);
2551 this.element_legend.appendChild(document.createElement('br'));
2553 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2554 this.element_legend.appendChild(this.element_legend_childs.title_units);
2556 this.element_legend.appendChild(document.createElement('br'));
2558 this.element_legend_childs.nano.className = 'netdata-legend-series';
2559 this.element_legend.appendChild(this.element_legend_childs.nano);
2561 content.className = 'netdata-legend-series-content';
2562 this.element_legend_childs.nano.appendChild(content);
2564 if(NETDATA.options.current.show_help === true)
2565 $(content).popover({
2570 placement: 'bottom',
2571 title: 'Chart Legend',
2572 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2573 content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
2577 this.element_legend_childs = {
2579 resize_handler: null,
2582 toolbox_right: null,
2583 toolbox_reset: null,
2584 toolbox_zoomin: null,
2585 toolbox_zoomout: null,
2586 toolbox_volume: null,
2597 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2598 if(this.debug === true)
2599 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2601 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2602 genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
2606 var tmp = new Array();
2607 for(var dim in this.chart.dimensions) {
2608 tmp.push(this.chart.dimensions[dim].name);
2609 genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
2611 this.element_legend_childs.series.labels_key = tmp.toString();
2612 if(this.debug === true)
2613 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2616 // create a hidden div to be used for hidding
2617 // the original legend of the chart library
2618 var el = document.createElement('div');
2619 if(this.element_legend !== null)
2620 this.element_legend.appendChild(el);
2621 el.style.display = 'none';
2623 this.element_legend_childs.hidden = document.createElement('div');
2624 el.appendChild(this.element_legend_childs.hidden);
2626 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2627 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2629 this.legendShowLatestValues();
2632 this.hasLegend = function() {
2633 if(typeof this.___hasLegendCache___ !== 'undefined')
2634 return this.___hasLegendCache___;
2637 if(this.library && this.library.legend(this) === 'right-side') {
2638 var legend = $(this.element).data('legend') || 'yes';
2639 if(legend === 'yes') leg = true;
2642 this.___hasLegendCache___ = leg;
2646 this.legendWidth = function() {
2647 return (this.hasLegend())?140:0;
2650 this.legendHeight = function() {
2651 return $(this.element).height();
2654 this.chartWidth = function() {
2655 return $(this.element).width() - this.legendWidth();
2658 this.chartHeight = function() {
2659 return $(this.element).height();
2662 this.chartPixelsPerPoint = function() {
2663 // force an options provided detail
2664 var px = this.pixels_per_point;
2666 if(this.library && px < this.library.pixels_per_point(this))
2667 px = this.library.pixels_per_point(this);
2669 if(px < NETDATA.options.current.pixels_per_point)
2670 px = NETDATA.options.current.pixels_per_point;
2675 this.needsRecreation = function() {
2677 this.chart_created === true
2679 && this.library.autoresize() === false
2680 && this.tm.last_resized < NETDATA.options.last_resized
2684 this.chartURL = function() {
2685 var after, before, points_multiplier = 1;
2686 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2687 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2689 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2690 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2691 this.view_after = after * 1000;
2692 this.view_before = before * 1000;
2694 this.requested_padding = null;
2695 points_multiplier = 1;
2697 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2698 this.tm.pan_and_zoom_seq = 0;
2700 before = Math.round(this.current.force_before_ms / 1000);
2701 after = Math.round(this.current.force_after_ms / 1000);
2702 this.view_after = after * 1000;
2703 this.view_before = before * 1000;
2705 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2706 this.requested_padding = Math.round((before - after) / 2);
2707 after -= this.requested_padding;
2708 before += this.requested_padding;
2709 this.requested_padding *= 1000;
2710 points_multiplier = 2;
2713 this.current.force_before_ms = null;
2714 this.current.force_after_ms = null;
2717 this.tm.pan_and_zoom_seq = 0;
2719 before = this.before;
2721 this.view_after = after * 1000;
2722 this.view_before = before * 1000;
2724 this.requested_padding = null;
2725 points_multiplier = 1;
2728 this.requested_after = after * 1000;
2729 this.requested_before = before * 1000;
2731 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2733 // build the data URL
2734 this.data_url = this.host + this.chart.data_url;
2735 this.data_url += "&format=" + this.library.format();
2736 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2737 this.data_url += "&group=" + this.method;
2738 this.data_url += "&options=" + this.library.options(this);
2739 this.data_url += '|jsonwrap';
2741 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2742 this.data_url += '|nonzero';
2744 if(this.append_options !== null)
2745 this.data_url += '|' + this.append_options.toString();
2748 this.data_url += "&after=" + after.toString();
2751 this.data_url += "&before=" + before.toString();
2754 this.data_url += "&dimensions=" + this.dimensions;
2756 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2757 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2760 this.redrawChart = function() {
2761 if(this.data !== null)
2762 this.updateChartWithData(this.data);
2765 this.updateChartWithData = function(data) {
2766 if(this.debug === true)
2767 this.log('updateChartWithData() called.');
2769 // this may force the chart to be re-created
2773 this.updates_counter++;
2774 this.updates_since_last_unhide++;
2775 this.updates_since_last_creation++;
2777 var started = new Date().getTime();
2779 // if the result is JSON, find the latest update-every
2780 this.data_update_every = data.view_update_every * 1000;
2781 this.data_after = data.after * 1000;
2782 this.data_before = data.before * 1000;
2783 this.netdata_first = data.first_entry * 1000;
2784 this.netdata_last = data.last_entry * 1000;
2785 this.data_points = data.points;
2788 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2789 if(this.view_after < this.data_after) {
2790 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2791 this.view_after = this.data_after;
2794 if(this.view_before > this.data_before) {
2795 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2796 this.view_before = this.data_before;
2800 this.view_after = this.data_after;
2801 this.view_before = this.data_before;
2804 if(this.debug === true) {
2805 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2807 if(this.current.force_after_ms)
2808 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2810 this.log('STATUS: forced : unset');
2812 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2813 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2814 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2815 this.log('STATUS: points : ' + (this.data_points).toString());
2818 if(this.data_points === 0) {
2823 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2824 if(this.debug === true)
2825 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2827 this.chart_created = false;
2830 // check and update the legend
2831 this.legendUpdateDOM();
2833 if(this.chart_created === true
2834 && typeof this.library.update === 'function') {
2836 if(this.debug === true)
2837 this.log('updating chart...');
2839 if(callChartLibraryUpdateSafely(data) === false)
2843 if(this.debug === true)
2844 this.log('creating chart...');
2846 if(callChartLibraryCreateSafely(data) === false)
2850 this.legendShowLatestValues();
2851 if(this.selected === true)
2852 NETDATA.globalSelectionSync.stop();
2854 // update the performance counters
2855 var now = new Date().getTime();
2856 this.tm.last_updated = now;
2858 // don't update last_autorefreshed if this chart is
2859 // forced to be updated with global PanAndZoom
2860 if(NETDATA.globalPanAndZoom.isActive())
2861 this.tm.last_autorefreshed = 0;
2863 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2864 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2866 this.tm.last_autorefreshed = now;
2869 this.refresh_dt_ms = now - started;
2870 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2872 if(this.refresh_dt_element !== null)
2873 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2876 this.updateChart = function(callback) {
2877 if(this.debug === true)
2878 this.log('updateChart() called.');
2880 if(this._updating === true) {
2881 if(this.debug === true)
2882 this.log('I am already updating...');
2884 if(typeof callback === 'function') callback();
2888 // due to late initialization of charts and libraries
2889 // we need to check this too
2890 if(this.enabled === false) {
2891 if(this.debug === true)
2892 this.log('I am not enabled');
2894 if(typeof callback === 'function') callback();
2898 if(canBeRendered() === false) {
2899 if(typeof callback === 'function') callback();
2903 if(this.chart === null) {
2904 this.getChart(function() { that.updateChart(callback); });
2908 if(this.library.initialized === false) {
2909 if(this.library.enabled === true) {
2910 this.library.initialize(function() { that.updateChart(callback); });
2914 error('chart library "' + this.library_name + '" is not available.');
2915 if(typeof callback === 'function') callback();
2920 this.clearSelection();
2923 if(this.debug === true)
2924 this.log('updating from ' + this.data_url);
2926 NETDATA.statistics.refreshes_total++;
2927 NETDATA.statistics.refreshes_active++;
2929 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2930 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2932 this._updating = true;
2934 this.xhr = $.ajax( {
2939 'Cache-Control': 'no-cache, no-store',
2940 'Pragma': 'no-cache'
2942 xhrFields: { withCredentials: true } // required for the cookie
2944 .done(function(data) {
2945 that.xhr = undefined;
2947 if(that.debug === true)
2948 that.log('data received. updating chart.');
2950 that.updateChartWithData(data);
2952 .fail(function(msg) {
2953 that.xhr = undefined;
2955 if(msg.statusText !== 'abort')
2956 error('data download failed for url: ' + that.data_url);
2958 .always(function() {
2959 that.xhr = undefined;
2961 NETDATA.statistics.refreshes_active--;
2962 that._updating = false;
2963 if(typeof callback === 'function') callback();
2969 this.isVisible = function(nocache) {
2970 if(typeof nocache === 'undefined')
2973 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2975 // caching - we do not evaluate the charts visibility
2976 // if the page has not been scrolled since the last check
2977 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2978 return this.___isVisible___;
2980 this.tm.last_visible_check = new Date().getTime();
2982 var wh = window.innerHeight;
2983 var x = this.element.getBoundingClientRect();
2987 if(x.width === 0 || x.height === 0) {
2989 this.___isVisible___ = false;
2990 return this.___isVisible___;
2993 if(x.top < 0 && -x.top > x.height) {
2994 // the chart is entirely above
2995 ret = -x.top - x.height;
2997 else if(x.top > wh) {
2998 // the chart is entirely below
3002 if(ret > tolerance) {
3003 // the chart is too far
3006 this.___isVisible___ = false;
3007 return this.___isVisible___;
3010 // the chart is inside or very close
3013 this.___isVisible___ = true;
3014 return this.___isVisible___;
3018 this.isAutoRefreshable = function() {
3019 return (this.current.autorefresh);
3022 this.canBeAutoRefreshed = function() {
3023 var now = new Date().getTime();
3025 if(this.running === true) {
3026 if(this.debug === true)
3027 this.log('I am already running');
3032 if(this.enabled === false) {
3033 if(this.debug === true)
3034 this.log('I am not enabled');
3039 if(this.library === null || this.library.enabled === false) {
3040 error('charting library "' + this.library_name + '" is not available');
3041 if(this.debug === true)
3042 this.log('My chart library ' + this.library_name + ' is not available');
3047 if(this.isVisible() === false) {
3048 if(NETDATA.options.debug.visibility === true || this.debug === true)
3049 this.log('I am not visible');
3054 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
3055 if(this.debug === true)
3056 this.log('timed force update detected - allowing this update');
3058 this.current.force_update_at = 0;
3062 if(this.isAutoRefreshable() === true) {
3063 // allow the first update, even if the page is not visible
3064 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
3065 if(NETDATA.options.debug.focus === true || this.debug === true)
3066 this.log('canBeAutoRefreshed(): page does not have focus');
3071 if(this.needsRecreation() === true) {
3072 if(this.debug === true)
3073 this.log('canBeAutoRefreshed(): needs re-creation.');
3078 // options valid only for autoRefresh()
3079 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
3080 if(NETDATA.globalPanAndZoom.isActive()) {
3081 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
3082 if(this.debug === true)
3083 this.log('canBeAutoRefreshed(): global panning: I need an update.');
3088 if(this.debug === true)
3089 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
3095 if(this.selected === true) {
3096 if(this.debug === true)
3097 this.log('canBeAutoRefreshed(): I have a selection in place.');
3102 if(this.paused === true) {
3103 if(this.debug === true)
3104 this.log('canBeAutoRefreshed(): I am paused.');
3109 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
3110 if(this.debug === true)
3111 this.log('canBeAutoRefreshed(): It is time to update me.');
3121 this.autoRefresh = function(callback) {
3122 if(this.canBeAutoRefreshed() === true && this.running === false) {
3125 state.running = true;
3126 state.updateChart(function() {
3127 state.running = false;
3129 if(typeof callback !== 'undefined')
3134 if(typeof callback !== 'undefined')
3139 this._defaultsFromDownloadedChart = function(chart) {
3141 this.chart_url = chart.url;
3142 this.data_update_every = chart.update_every * 1000;
3143 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
3144 this.tm.last_info_downloaded = new Date().getTime();
3146 if(this.title === null)
3147 this.title = chart.title;
3149 if(this.units === null)
3150 this.units = chart.units;
3153 // fetch the chart description from the netdata server
3154 this.getChart = function(callback) {
3155 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
3157 this._defaultsFromDownloadedChart(this.chart);
3158 if(typeof callback === 'function') callback();
3161 this.chart_url = "/api/v1/chart?chart=" + this.id;
3163 if(this.debug === true)
3164 this.log('downloading ' + this.chart_url);
3167 url: this.host + this.chart_url,
3170 xhrFields: { withCredentials: true } // required for the cookie
3172 .done(function(chart) {
3173 chart.url = that.chart_url;
3174 that._defaultsFromDownloadedChart(chart);
3175 NETDATA.chartRegistry.add(that.host, that.id, chart);
3178 NETDATA.error(404, that.chart_url);
3179 error('chart not found on url "' + that.chart_url + '"');
3181 .always(function() {
3182 if(typeof callback === 'function') callback();
3187 // ============================================================================================================
3193 NETDATA.resetAllCharts = function(state) {
3194 // first clear the global selection sync
3195 // to make sure no chart is in selected state
3196 state.globalSelectionSyncStop();
3198 // there are 2 possibilities here
3199 // a. state is the global Pan and Zoom master
3200 // b. state is not the global Pan and Zoom master
3202 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3205 // clear the global Pan and Zoom
3206 // this will also refresh the master
3207 // and unblock any charts currently mirroring the master
3208 NETDATA.globalPanAndZoom.clearMaster();
3210 // if we were not the master, reset our status too
3211 // this is required because most probably the mouse
3212 // is over this chart, blocking it from auto-refreshing
3213 if(master === false && (state.paused === true || state.selected === true))
3217 // get or create a chart state, given a DOM element
3218 NETDATA.chartState = function(element) {
3219 var state = $(element).data('netdata-state-object') || null;
3220 if(state === null) {
3221 state = new chartState(element);
3222 $(element).data('netdata-state-object', state);
3227 // ----------------------------------------------------------------------------------------------------------------
3228 // Library functions
3230 // Load a script without jquery
3231 // This is used to load jquery - after it is loaded, we use jquery
3232 NETDATA._loadjQuery = function(callback) {
3233 if(typeof jQuery === 'undefined') {
3234 if(NETDATA.options.debug.main_loop === true)
3235 console.log('loading ' + NETDATA.jQuery);
3237 var script = document.createElement('script');
3238 script.type = 'text/javascript';
3239 script.async = true;
3240 script.src = NETDATA.jQuery;
3242 // script.onabort = onError;
3243 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3244 if(typeof callback === "function")
3245 script.onload = callback;
3247 var s = document.getElementsByTagName('script')[0];
3248 s.parentNode.insertBefore(script, s);
3250 else if(typeof callback === "function")
3254 NETDATA._loadCSS = function(filename) {
3255 // don't use jQuery here
3256 // styles are loaded before jQuery
3257 // to eliminate showing an unstyled page to the user
3259 var fileref = document.createElement("link");
3260 fileref.setAttribute("rel", "stylesheet");
3261 fileref.setAttribute("type", "text/css");
3262 fileref.setAttribute("href", filename);
3264 if (typeof fileref !== 'undefined')
3265 document.getElementsByTagName("head")[0].appendChild(fileref);
3268 NETDATA.colorHex2Rgb = function(hex) {
3269 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3270 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3271 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3272 return r + r + g + g + b + b;
3275 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3277 r: parseInt(result[1], 16),
3278 g: parseInt(result[2], 16),
3279 b: parseInt(result[3], 16)
3283 NETDATA.colorLuminance = function(hex, lum) {
3284 // validate hex string
3285 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3287 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3291 // convert to decimal and change luminosity
3292 var rgb = "#", c, i;
3293 for (i = 0; i < 3; i++) {
3294 c = parseInt(hex.substr(i*2,2), 16);
3295 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3296 rgb += ("00"+c).substr(c.length);
3302 NETDATA.guid = function() {
3304 return Math.floor((1 + Math.random()) * 0x10000)
3309 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3312 NETDATA.zeropad = function(x) {
3313 if(x > -10 && x < 10) return '0' + x.toString();
3314 else return x.toString();
3317 // user function to signal us the DOM has been
3319 NETDATA.updatedDom = function() {
3320 NETDATA.options.updated_dom = true;
3323 NETDATA.ready = function(callback) {
3324 NETDATA.options.pauseCallback = callback;
3327 NETDATA.pause = function(callback) {
3328 if(NETDATA.options.pause === true)
3331 NETDATA.options.pauseCallback = callback;
3334 NETDATA.unpause = function() {
3335 NETDATA.options.pauseCallback = null;
3336 NETDATA.options.updated_dom = true;
3337 NETDATA.options.pause = false;
3340 // ----------------------------------------------------------------------------------------------------------------
3342 // this is purely sequencial charts refresher
3343 // it is meant to be autonomous
3344 NETDATA.chartRefresherNoParallel = function(index) {
3345 if(NETDATA.options.debug.mail_loop === true)
3346 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3348 if(NETDATA.options.updated_dom === true) {
3349 // the dom has been updated
3350 // get the dom parts again
3351 NETDATA.parseDom(NETDATA.chartRefresher);
3354 if(index >= NETDATA.options.targets.length) {
3355 if(NETDATA.options.debug.main_loop === true)
3356 console.log('waiting to restart main loop...');
3358 NETDATA.options.auto_refresher_fast_weight = 0;
3360 setTimeout(function() {
3361 NETDATA.chartRefresher();
3362 }, NETDATA.options.current.idle_between_loops);
3365 var state = NETDATA.options.targets[index];
3367 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3368 if(NETDATA.options.debug.main_loop === true)
3369 console.log('fast rendering...');
3371 state.autoRefresh(function() {
3372 NETDATA.chartRefresherNoParallel(++index);
3376 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3377 NETDATA.options.auto_refresher_fast_weight = 0;
3379 setTimeout(function() {
3380 state.autoRefresh(function() {
3381 NETDATA.chartRefresherNoParallel(++index);
3383 }, NETDATA.options.current.idle_between_charts);
3388 // this is part of the parallel refresher
3389 // its cause is to refresh sequencially all the charts
3390 // that depend on chart library initialization
3391 // it will call the parallel refresher back
3392 // as soon as it sees a chart that its chart library
3394 NETDATA.chartRefresher_uninitialized = function() {
3395 if(NETDATA.options.updated_dom === true) {
3396 // the dom has been updated
3397 // get the dom parts again
3398 NETDATA.parseDom(NETDATA.chartRefresher);
3402 if(NETDATA.options.sequencial.length === 0)
3403 NETDATA.chartRefresher();
3405 var state = NETDATA.options.sequencial.pop();
3406 if(state.library.initialized === true)
3407 NETDATA.chartRefresher();
3409 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3413 NETDATA.chartRefresherWaitTime = function() {
3414 return NETDATA.options.current.idle_parallel_loops;
3417 // the default refresher
3418 // it will create 2 sets of charts:
3419 // - the ones that can be refreshed in parallel
3420 // - the ones that depend on something else
3421 // the first set will be executed in parallel
3422 // the second will be given to NETDATA.chartRefresher_uninitialized()
3423 NETDATA.chartRefresher = function() {
3424 // console.log('auto-refresher...');
3426 if(NETDATA.options.pause === true) {
3427 // console.log('auto-refresher is paused');
3428 setTimeout(NETDATA.chartRefresher,
3429 NETDATA.chartRefresherWaitTime());
3433 if(typeof NETDATA.options.pauseCallback === 'function') {
3434 // console.log('auto-refresher is calling pauseCallback');
3435 NETDATA.options.pause = true;
3436 NETDATA.options.pauseCallback();
3437 NETDATA.chartRefresher();
3441 if(NETDATA.options.current.parallel_refresher === false) {
3442 // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
3443 NETDATA.chartRefresherNoParallel(0);
3447 if(NETDATA.options.updated_dom === true) {
3448 // the dom has been updated
3449 // get the dom parts again
3450 // console.log('auto-refresher is calling parseDom()');
3451 NETDATA.parseDom(NETDATA.chartRefresher);
3455 var parallel = new Array();
3456 var targets = NETDATA.options.targets;
3457 var len = targets.length;
3460 state = targets[len];
3461 if(state.isVisible() === false || state.running === true)
3464 if(state.library.initialized === false) {
3465 if(state.library.enabled === true) {
3466 state.library.initialize(NETDATA.chartRefresher);
3470 state.error('chart library "' + state.library_name + '" is not enabled.');
3474 parallel.unshift(state);
3477 if(parallel.length > 0) {
3478 // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
3479 // this will execute the jobs in parallel
3480 $(parallel).each(function() {
3485 // console.log('auto-refresher nothing to do');
3488 // run the next refresh iteration
3489 setTimeout(NETDATA.chartRefresher,
3490 NETDATA.chartRefresherWaitTime());
3493 NETDATA.parseDom = function(callback) {
3494 NETDATA.options.last_page_scroll = new Date().getTime();
3495 NETDATA.options.updated_dom = false;
3497 var targets = $('div[data-netdata]'); //.filter(':visible');
3499 if(NETDATA.options.debug.main_loop === true)
3500 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3502 NETDATA.options.targets = new Array();
3503 var len = targets.length;
3505 // the initialization will take care of sizing
3506 // and the "loading..." message
3507 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3510 if(typeof callback === 'function') callback();
3513 // this is the main function - where everything starts
3514 NETDATA.start = function() {
3515 // this should be called only once
3517 NETDATA.options.page_is_visible = true;
3519 $(window).blur(function() {
3520 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3521 NETDATA.options.page_is_visible = false;
3522 if(NETDATA.options.debug.focus === true)
3523 console.log('Lost Focus!');
3527 $(window).focus(function() {
3528 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3529 NETDATA.options.page_is_visible = true;
3530 if(NETDATA.options.debug.focus === true)
3531 console.log('Focus restored!');
3535 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3536 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3537 NETDATA.options.page_is_visible = false;
3538 if(NETDATA.options.debug.focus === true)
3539 console.log('Document has no focus!');
3543 // bootstrap tab switching
3544 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3546 // bootstrap modal switching
3547 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3548 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3550 // bootstrap collapse switching
3551 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3552 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3554 NETDATA.parseDom(NETDATA.chartRefresher);
3556 // Alarms initialization
3557 setTimeout(NETDATA.alarms.init, 1000);
3559 // Registry initialization
3560 setTimeout(NETDATA.registry.init, netdataRegistryAfterMs);
3562 if(typeof netdataCallback === 'function')
3566 // ----------------------------------------------------------------------------------------------------------------
3569 NETDATA.peityInitialize = function(callback) {
3570 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3572 url: NETDATA.peity_js,
3575 xhrFields: { withCredentials: true } // required for the cookie
3578 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3581 NETDATA.chartLibraries.peity.enabled = false;
3582 NETDATA.error(100, NETDATA.peity_js);
3584 .always(function() {
3585 if(typeof callback === "function")
3590 NETDATA.chartLibraries.peity.enabled = false;
3591 if(typeof callback === "function")
3596 NETDATA.peityChartUpdate = function(state, data) {
3597 state.peity_instance.innerHTML = data.result;
3599 if(state.peity_options.stroke !== state.chartColors()[0]) {
3600 state.peity_options.stroke = state.chartColors()[0];
3601 if(state.chart.chart_type === 'line')
3602 state.peity_options.fill = NETDATA.themes.current.background;
3604 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3607 $(state.peity_instance).peity('line', state.peity_options);
3611 NETDATA.peityChartCreate = function(state, data) {
3612 state.peity_instance = document.createElement('div');
3613 state.element_chart.appendChild(state.peity_instance);
3615 var self = $(state.element);
3616 state.peity_options = {
3617 stroke: NETDATA.themes.current.foreground,
3618 strokeWidth: self.data('peity-strokewidth') || 1,
3619 width: state.chartWidth(),
3620 height: state.chartHeight(),
3621 fill: NETDATA.themes.current.foreground
3624 NETDATA.peityChartUpdate(state, data);
3628 // ----------------------------------------------------------------------------------------------------------------
3631 NETDATA.sparklineInitialize = function(callback) {
3632 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3634 url: NETDATA.sparkline_js,
3637 xhrFields: { withCredentials: true } // required for the cookie
3640 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3643 NETDATA.chartLibraries.sparkline.enabled = false;
3644 NETDATA.error(100, NETDATA.sparkline_js);
3646 .always(function() {
3647 if(typeof callback === "function")
3652 NETDATA.chartLibraries.sparkline.enabled = false;
3653 if(typeof callback === "function")
3658 NETDATA.sparklineChartUpdate = function(state, data) {
3659 state.sparkline_options.width = state.chartWidth();
3660 state.sparkline_options.height = state.chartHeight();
3662 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3666 NETDATA.sparklineChartCreate = function(state, data) {
3667 var self = $(state.element);
3668 var type = self.data('sparkline-type') || 'line';
3669 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3670 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3671 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3672 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3673 var composite = self.data('sparkline-composite') || undefined;
3674 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3675 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3676 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3677 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3678 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3679 var spotColor = self.data('sparkline-spotcolor') || undefined;
3680 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3681 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3682 var spotRadius = self.data('sparkline-spotradius') || undefined;
3683 var valueSpots = self.data('sparkline-valuespots') || undefined;
3684 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3685 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3686 var lineWidth = self.data('sparkline-linewidth') || undefined;
3687 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3688 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3689 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3690 var xvalues = self.data('sparkline-xvalues') || undefined;
3691 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3692 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3693 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3694 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3695 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3696 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3697 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3698 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3699 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3700 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3701 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3702 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3703 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3704 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3705 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3706 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3707 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3708 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3709 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3710 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3711 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3712 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3714 if(spotColor === 'disable') spotColor='';
3715 if(minSpotColor === 'disable') minSpotColor='';
3716 if(maxSpotColor === 'disable') maxSpotColor='';
3718 state.sparkline_options = {
3720 lineColor: lineColor,
3721 fillColor: fillColor,
3722 chartRangeMin: chartRangeMin,
3723 chartRangeMax: chartRangeMax,
3724 composite: composite,
3725 enableTagOptions: enableTagOptions,
3726 tagOptionPrefix: tagOptionPrefix,
3727 tagValuesAttribute: tagValuesAttribute,
3728 disableHiddenCheck: disableHiddenCheck,
3729 defaultPixelsPerValue: defaultPixelsPerValue,
3730 spotColor: spotColor,
3731 minSpotColor: minSpotColor,
3732 maxSpotColor: maxSpotColor,
3733 spotRadius: spotRadius,
3734 valueSpots: valueSpots,
3735 highlightSpotColor: highlightSpotColor,
3736 highlightLineColor: highlightLineColor,
3737 lineWidth: lineWidth,
3738 normalRangeMin: normalRangeMin,
3739 normalRangeMax: normalRangeMax,
3740 drawNormalOnTop: drawNormalOnTop,
3742 chartRangeClip: chartRangeClip,
3743 chartRangeMinX: chartRangeMinX,
3744 chartRangeMaxX: chartRangeMaxX,
3745 disableInteraction: disableInteraction,
3746 disableTooltips: disableTooltips,
3747 disableHighlight: disableHighlight,
3748 highlightLighten: highlightLighten,
3749 highlightColor: highlightColor,
3750 tooltipContainer: tooltipContainer,
3751 tooltipClassname: tooltipClassname,
3752 tooltipChartTitle: state.title,
3753 tooltipFormat: tooltipFormat,
3754 tooltipPrefix: tooltipPrefix,
3755 tooltipSuffix: tooltipSuffix,
3756 tooltipSkipNull: tooltipSkipNull,
3757 tooltipValueLookups: tooltipValueLookups,
3758 tooltipFormatFieldlist: tooltipFormatFieldlist,
3759 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3760 numberFormatter: numberFormatter,
3761 numberDigitGroupSep: numberDigitGroupSep,
3762 numberDecimalMark: numberDecimalMark,
3763 numberDigitGroupCount: numberDigitGroupCount,
3764 animatedZooms: animatedZooms,
3765 width: state.chartWidth(),
3766 height: state.chartHeight()
3769 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3773 // ----------------------------------------------------------------------------------------------------------------
3780 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3781 if(after < state.netdata_first)
3782 after = state.netdata_first;
3784 if(before > state.netdata_last)
3785 before = state.netdata_last;
3787 state.setMode('zoom');
3788 state.globalSelectionSyncStop();
3789 state.globalSelectionSyncDelay();
3790 state.dygraph_user_action = true;
3791 state.dygraph_force_zoom = true;
3792 state.updateChartPanOrZoom(after, before);
3793 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3796 NETDATA.dygraphSetSelection = function(state, t) {
3797 if(typeof state.dygraph_instance !== 'undefined') {
3798 var r = state.calculateRowForTime(t);
3800 state.dygraph_instance.setSelection(r);
3802 state.dygraph_instance.clearSelection();
3803 state.legendShowUndefined();
3810 NETDATA.dygraphClearSelection = function(state, t) {
3811 if(typeof state.dygraph_instance !== 'undefined') {
3812 state.dygraph_instance.clearSelection();
3817 NETDATA.dygraphSmoothInitialize = function(callback) {
3819 url: NETDATA.dygraph_smooth_js,
3822 xhrFields: { withCredentials: true } // required for the cookie
3825 NETDATA.dygraph.smooth = true;
3826 smoothPlotter.smoothing = 0.3;
3829 NETDATA.dygraph.smooth = false;
3831 .always(function() {
3832 if(typeof callback === "function")
3837 NETDATA.dygraphInitialize = function(callback) {
3838 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3840 url: NETDATA.dygraph_js,
3843 xhrFields: { withCredentials: true } // required for the cookie
3846 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3849 NETDATA.chartLibraries.dygraph.enabled = false;
3850 NETDATA.error(100, NETDATA.dygraph_js);
3852 .always(function() {
3853 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3854 NETDATA.dygraphSmoothInitialize(callback);
3855 else if(typeof callback === "function")
3860 NETDATA.chartLibraries.dygraph.enabled = false;
3861 if(typeof callback === "function")
3866 NETDATA.dygraphChartUpdate = function(state, data) {
3867 var dygraph = state.dygraph_instance;
3869 if(typeof dygraph === 'undefined')
3870 return NETDATA.dygraphChartCreate(state, data);
3872 // when the chart is not visible, and hidden
3873 // if there is a window resize, dygraph detects
3874 // its element size as 0x0.
3875 // this will make it re-appear properly
3877 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3881 file: data.result.data,
3882 colors: state.chartColors(),
3883 labels: data.result.labels,
3884 labelsDivWidth: state.chartWidth() - 70,
3885 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3888 if(state.dygraph_force_zoom === true) {
3889 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3890 state.log('dygraphChartUpdate() forced zoom update');
3892 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3893 options.valueRange = state.dygraph_options.valueRange;
3894 options.isZoomedIgnoreProgrammaticZoom = true;
3895 state.dygraph_force_zoom = false;
3897 else if(state.current.name !== 'auto') {
3898 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3899 state.log('dygraphChartUpdate() loose update');
3901 options.valueRange = state.dygraph_options.valueRange;
3904 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3905 state.log('dygraphChartUpdate() strict update');
3907 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3908 options.valueRange = state.dygraph_options.valueRange;
3909 options.isZoomedIgnoreProgrammaticZoom = true;
3912 if(state.dygraph_smooth_eligible === true) {
3913 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3914 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3915 NETDATA.dygraphChartCreate(state, data);
3920 dygraph.updateOptions(options);
3922 state.dygraph_last_rendered = new Date().getTime();
3926 NETDATA.dygraphChartCreate = function(state, data) {
3927 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3928 state.log('dygraphChartCreate()');
3930 var self = $(state.element);
3932 var chart_type = state.chart.chart_type;
3933 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3934 chart_type = self.data('dygraph-type') || chart_type;
3936 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3937 smooth = self.data('dygraph-smooth') || smooth;
3939 if(NETDATA.dygraph.smooth === false)
3942 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3943 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3945 state.dygraph_options = {
3946 colors: self.data('dygraph-colors') || state.chartColors(),
3948 // leave a few pixels empty on the right of the chart
3949 rightGap: self.data('dygraph-rightgap') || 5,
3950 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3951 showRoller: self.data('dygraph-showroller') || false,
3953 title: self.data('dygraph-title') || state.title,
3954 titleHeight: self.data('dygraph-titleheight') || 19,
3956 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3957 labels: data.result.labels,
3958 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3959 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3960 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3961 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3962 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3965 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3966 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3968 includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
3969 xRangePad: self.data('dygraph-xrangepad') || 0,
3970 yRangePad: self.data('dygraph-yrangepad') || 1,
3972 valueRange: self.data('dygraph-valuerange') || null,
3974 ylabel: state.units,
3975 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3977 // the function to plot the chart
3980 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3981 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3982 strokePattern: self.data('dygraph-strokepattern') || undefined,
3984 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3985 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3986 drawPoints: self.data('dygraph-drawpoints') || false,
3988 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3989 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3991 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3992 pointSize: self.data('dygraph-pointsize') || 1,
3994 // enabling this makes the chart with little square lines
3995 stepPlot: self.data('dygraph-stepplot') || false,
3997 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3998 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3999 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
4001 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
4002 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
4003 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
4004 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
4006 drawAxis: self.data('dygraph-drawaxis') || true,
4007 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
4008 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
4009 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
4011 drawGrid: self.data('dygraph-drawgrid') || true,
4012 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
4013 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4,
4014 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
4016 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
4017 sigFigs: self.data('dygraph-sigfigs') || null,
4018 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
4019 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
4021 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
4022 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
4023 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
4025 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
4026 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
4030 ticker: Dygraph.dateTicker,
4031 axisLabelFormatter: function (d, gran) {
4032 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4034 valueFormatter: function (ms) {
4035 var d = new Date(ms);
4036 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
4037 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
4042 valueFormatter: function (x) {
4043 // we format legends with the state object
4044 // no need to do anything here
4045 // return (Math.round(x*100) / 100).toLocaleString();
4046 // return state.legendFormatValue(x);
4051 legendFormatter: function(data) {
4052 var elements = state.element_legend_childs;
4054 // if the hidden div is not there
4055 // we are not managing the legend
4056 if(elements.hidden === null) return;
4058 if (typeof data.x !== 'undefined') {
4059 state.legendSetDate(data.x);
4060 var i = data.series.length;
4062 var series = data.series[i];
4063 if(!series.isVisible) continue;
4064 state.legendSetLabelValue(series.label, series.y);
4070 drawCallback: function(dygraph, is_initial) {
4071 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
4072 state.dygraph_user_action = false;
4074 var x_range = dygraph.xAxisRange();
4075 var after = Math.round(x_range[0]);
4076 var before = Math.round(x_range[1]);
4078 if(NETDATA.options.debug.dygraph === true)
4079 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
4081 if(before <= state.netdata_last && after >= state.netdata_first)
4082 state.updateChartPanOrZoom(after, before);
4085 zoomCallback: function(minDate, maxDate, yRanges) {
4086 if(NETDATA.options.debug.dygraph === true)
4087 state.log('dygraphZoomCallback()');
4089 state.globalSelectionSyncStop();
4090 state.globalSelectionSyncDelay();
4091 state.setMode('zoom');
4093 // refresh it to the greatest possible zoom level
4094 state.dygraph_user_action = true;
4095 state.dygraph_force_zoom = true;
4096 state.updateChartPanOrZoom(minDate, maxDate);
4098 highlightCallback: function(event, x, points, row, seriesName) {
4099 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4100 state.log('dygraphHighlightCallback()');
4104 // there is a bug in dygraph when the chart is zoomed enough
4105 // the time it thinks is selected is wrong
4106 // here we calculate the time t based on the row number selected
4108 var t = state.data_after + row * state.data_update_every;
4109 // 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);
4111 state.globalSelectionSync(x);
4113 // fix legend zIndex using the internal structures of dygraph legend module
4114 // this works, but it is a hack!
4115 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
4117 unhighlightCallback: function(event) {
4118 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4119 state.log('dygraphUnhighlightCallback()');
4121 state.unpauseChart();
4122 state.globalSelectionSyncStop();
4124 interactionModel : {
4125 mousedown: function(event, dygraph, context) {
4126 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4127 state.log('interactionModel.mousedown()');
4129 state.dygraph_user_action = true;
4130 state.globalSelectionSyncStop();
4132 if(NETDATA.options.debug.dygraph === true)
4133 state.log('dygraphMouseDown()');
4135 // Right-click should not initiate a zoom.
4136 if(event.button && event.button === 2) return;
4138 context.initializeMouseDown(event, dygraph, context);
4140 if(event.button && event.button === 1) {
4141 if (event.altKey || event.shiftKey) {
4142 state.setMode('pan');
4143 state.globalSelectionSyncDelay();
4144 Dygraph.startPan(event, dygraph, context);
4147 state.setMode('zoom');
4148 state.globalSelectionSyncDelay();
4149 Dygraph.startZoom(event, dygraph, context);
4153 if (event.altKey || event.shiftKey) {
4154 state.setMode('zoom');
4155 state.globalSelectionSyncDelay();
4156 Dygraph.startZoom(event, dygraph, context);
4159 state.setMode('pan');
4160 state.globalSelectionSyncDelay();
4161 Dygraph.startPan(event, dygraph, context);
4165 mousemove: function(event, dygraph, context) {
4166 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4167 state.log('interactionModel.mousemove()');
4169 if(context.isPanning) {
4170 state.dygraph_user_action = true;
4171 state.globalSelectionSyncStop();
4172 state.globalSelectionSyncDelay();
4173 state.setMode('pan');
4174 Dygraph.movePan(event, dygraph, context);
4176 else if(context.isZooming) {
4177 state.dygraph_user_action = true;
4178 state.globalSelectionSyncStop();
4179 state.globalSelectionSyncDelay();
4180 state.setMode('zoom');
4181 Dygraph.moveZoom(event, dygraph, context);
4184 mouseup: function(event, dygraph, context) {
4185 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4186 state.log('interactionModel.mouseup()');
4188 if (context.isPanning) {
4189 state.dygraph_user_action = true;
4190 state.globalSelectionSyncDelay();
4191 Dygraph.endPan(event, dygraph, context);
4193 else if (context.isZooming) {
4194 state.dygraph_user_action = true;
4195 state.globalSelectionSyncDelay();
4196 Dygraph.endZoom(event, dygraph, context);
4199 click: function(event, dygraph, context) {
4200 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4201 state.log('interactionModel.click()');
4203 event.preventDefault();
4205 dblclick: function(event, dygraph, context) {
4206 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4207 state.log('interactionModel.dblclick()');
4208 NETDATA.resetAllCharts(state);
4210 mousewheel: function(event, dygraph, context) {
4211 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4212 state.log('interactionModel.mousewheel()');
4214 // Take the offset of a mouse event on the dygraph canvas and
4215 // convert it to a pair of percentages from the bottom left.
4216 // (Not top left, bottom is where the lower value is.)
4217 function offsetToPercentage(g, offsetX, offsetY) {
4218 // This is calculating the pixel offset of the leftmost date.
4219 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4220 var yar0 = g.yAxisRange(0);
4222 // This is calculating the pixel of the higest value. (Top pixel)
4223 var yOffset = g.toDomCoords(null, yar0[1])[1];
4225 // x y w and h are relative to the corner of the drawing area,
4226 // so that the upper corner of the drawing area is (0, 0).
4227 var x = offsetX - xOffset;
4228 var y = offsetY - yOffset;
4230 // This is computing the rightmost pixel, effectively defining the
4232 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4234 // This is computing the lowest pixel, effectively defining the height.
4235 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4237 // Percentage from the left.
4238 var xPct = w === 0 ? 0 : (x / w);
4239 // Percentage from the top.
4240 var yPct = h === 0 ? 0 : (y / h);
4242 // The (1-) part below changes it from "% distance down from the top"
4243 // to "% distance up from the bottom".
4244 return [xPct, (1-yPct)];
4247 // Adjusts [x, y] toward each other by zoomInPercentage%
4248 // Split it so the left/bottom axis gets xBias/yBias of that change and
4249 // tight/top gets (1-xBias)/(1-yBias) of that change.
4251 // If a bias is missing it splits it down the middle.
4252 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4253 xBias = xBias || 0.5;
4254 yBias = yBias || 0.5;
4256 function adjustAxis(axis, zoomInPercentage, bias) {
4257 var delta = axis[1] - axis[0];
4258 var increment = delta * zoomInPercentage;
4259 var foo = [increment * bias, increment * (1-bias)];
4261 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4264 var yAxes = g.yAxisRanges();
4266 for (var i = 0; i < yAxes.length; i++) {
4267 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4270 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4273 if(event.altKey || event.shiftKey) {
4274 state.dygraph_user_action = true;
4276 state.globalSelectionSyncStop();
4277 state.globalSelectionSyncDelay();
4279 // http://dygraphs.com/gallery/interaction-api.js
4280 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4281 var percentage = normal / 50;
4283 if (!(event.offsetX && event.offsetY)){
4284 event.offsetX = event.layerX - event.target.offsetLeft;
4285 event.offsetY = event.layerY - event.target.offsetTop;
4288 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4289 var xPct = percentages[0];
4290 var yPct = percentages[1];
4292 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4294 var after = new_x_range[0];
4295 var before = new_x_range[1];
4297 var first = state.netdata_first + state.data_update_every;
4298 var last = state.netdata_last + state.data_update_every;
4301 after -= (before - last);
4308 state.setMode('zoom');
4309 if(state.updateChartPanOrZoom(after, before) === true)
4310 dygraph.updateOptions({ dateWindow: [ after, before ] });
4312 event.preventDefault();
4315 touchstart: function(event, dygraph, context) {
4316 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4317 state.log('interactionModel.touchstart()');
4319 state.dygraph_user_action = true;
4320 state.setMode('zoom');
4323 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4325 // we overwrite the touch directions at the end, to overwrite
4326 // the internal default of dygraphs
4327 context.touchDirections = { x: true, y: false };
4329 state.dygraph_last_touch_start = new Date().getTime();
4330 state.dygraph_last_touch_move = 0;
4332 if(typeof event.touches[0].pageX === 'number')
4333 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4335 state.dygraph_last_touch_page_x = 0;
4337 touchmove: function(event, dygraph, context) {
4338 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4339 state.log('interactionModel.touchmove()');
4341 state.dygraph_user_action = true;
4342 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4344 state.dygraph_last_touch_move = new Date().getTime();
4346 touchend: function(event, dygraph, context) {
4347 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4348 state.log('interactionModel.touchend()');
4350 state.dygraph_user_action = true;
4351 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4353 // if it didn't move, it is a selection
4354 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4355 // internal api of dygraphs
4356 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4357 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4358 if(NETDATA.dygraphSetSelection(state, t) === true)
4359 state.globalSelectionSync(t);
4362 // if it was double tap within double click time, reset the charts
4363 var now = new Date().getTime();
4364 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4365 if(state.dygraph_last_touch_move === 0) {
4366 var dt = now - state.dygraph_last_touch_end;
4367 if(dt <= NETDATA.options.current.double_click_speed)
4368 NETDATA.resetAllCharts(state);
4372 // remember the timestamp of the last touch end
4373 state.dygraph_last_touch_end = now;
4378 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4379 state.dygraph_options.drawGrid = false;
4380 state.dygraph_options.drawAxis = false;
4381 state.dygraph_options.title = undefined;
4382 state.dygraph_options.ylabel = undefined;
4383 state.dygraph_options.yLabelWidth = 0;
4384 state.dygraph_options.labelsDivWidth = 120;
4385 state.dygraph_options.labelsDivStyles.width = '120px';
4386 state.dygraph_options.labelsSeparateLines = true;
4387 state.dygraph_options.rightGap = 0;
4388 state.dygraph_options.yRangePad = 1;
4391 if(smooth === true) {
4392 state.dygraph_smooth_eligible = true;
4394 if(NETDATA.options.current.smooth_plot === true)
4395 state.dygraph_options.plotter = smoothPlotter;
4397 else state.dygraph_smooth_eligible = false;
4399 state.dygraph_instance = new Dygraph(state.element_chart,
4400 data.result.data, state.dygraph_options);
4402 state.dygraph_force_zoom = false;
4403 state.dygraph_user_action = false;
4404 state.dygraph_last_rendered = new Date().getTime();
4408 // ----------------------------------------------------------------------------------------------------------------
4411 NETDATA.morrisInitialize = function(callback) {
4412 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4414 // morris requires raphael
4415 if(!NETDATA.chartLibraries.raphael.initialized) {
4416 if(NETDATA.chartLibraries.raphael.enabled) {
4417 NETDATA.raphaelInitialize(function() {
4418 NETDATA.morrisInitialize(callback);
4422 NETDATA.chartLibraries.morris.enabled = false;
4423 if(typeof callback === "function")
4428 NETDATA._loadCSS(NETDATA.morris_css);
4431 url: NETDATA.morris_js,
4434 xhrFields: { withCredentials: true } // required for the cookie
4437 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4440 NETDATA.chartLibraries.morris.enabled = false;
4441 NETDATA.error(100, NETDATA.morris_js);
4443 .always(function() {
4444 if(typeof callback === "function")
4450 NETDATA.chartLibraries.morris.enabled = false;
4451 if(typeof callback === "function")
4456 NETDATA.morrisChartUpdate = function(state, data) {
4457 state.morris_instance.setData(data.result.data);
4461 NETDATA.morrisChartCreate = function(state, data) {
4463 state.morris_options = {
4464 element: state.element_chart.id,
4465 data: data.result.data,
4467 ykeys: data.dimension_names,
4468 labels: data.dimension_names,
4474 continuousLine: false,
4475 behaveLikeLine: false
4478 if(state.chart.chart_type === 'line')
4479 state.morris_instance = new Morris.Line(state.morris_options);
4481 else if(state.chart.chart_type === 'area') {
4482 state.morris_options.behaveLikeLine = true;
4483 state.morris_instance = new Morris.Area(state.morris_options);
4486 state.morris_instance = new Morris.Area(state.morris_options);
4491 // ----------------------------------------------------------------------------------------------------------------
4494 NETDATA.raphaelInitialize = function(callback) {
4495 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4497 url: NETDATA.raphael_js,
4500 xhrFields: { withCredentials: true } // required for the cookie
4503 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4506 NETDATA.chartLibraries.raphael.enabled = false;
4507 NETDATA.error(100, NETDATA.raphael_js);
4509 .always(function() {
4510 if(typeof callback === "function")
4515 NETDATA.chartLibraries.raphael.enabled = false;
4516 if(typeof callback === "function")
4521 NETDATA.raphaelChartUpdate = function(state, data) {
4522 $(state.element_chart).raphael(data.result, {
4523 width: state.chartWidth(),
4524 height: state.chartHeight()
4530 NETDATA.raphaelChartCreate = function(state, data) {
4531 $(state.element_chart).raphael(data.result, {
4532 width: state.chartWidth(),
4533 height: state.chartHeight()
4539 // ----------------------------------------------------------------------------------------------------------------
4542 NETDATA.c3Initialize = function(callback) {
4543 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4546 if(!NETDATA.chartLibraries.d3.initialized) {
4547 if(NETDATA.chartLibraries.d3.enabled) {
4548 NETDATA.d3Initialize(function() {
4549 NETDATA.c3Initialize(callback);
4553 NETDATA.chartLibraries.c3.enabled = false;
4554 if(typeof callback === "function")
4559 NETDATA._loadCSS(NETDATA.c3_css);
4565 xhrFields: { withCredentials: true } // required for the cookie
4568 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4571 NETDATA.chartLibraries.c3.enabled = false;
4572 NETDATA.error(100, NETDATA.c3_js);
4574 .always(function() {
4575 if(typeof callback === "function")
4581 NETDATA.chartLibraries.c3.enabled = false;
4582 if(typeof callback === "function")
4587 NETDATA.c3ChartUpdate = function(state, data) {
4588 state.c3_instance.destroy();
4589 return NETDATA.c3ChartCreate(state, data);
4591 //state.c3_instance.load({
4592 // rows: data.result,
4599 NETDATA.c3ChartCreate = function(state, data) {
4601 state.element_chart.id = 'c3-' + state.uuid;
4602 // console.log('id = ' + state.element_chart.id);
4604 state.c3_instance = c3.generate({
4605 bindto: '#' + state.element_chart.id,
4607 width: state.chartWidth(),
4608 height: state.chartHeight()
4611 pattern: state.chartColors()
4616 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4622 format: function(x) {
4623 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4650 // console.log(state.c3_instance);
4655 // ----------------------------------------------------------------------------------------------------------------
4658 NETDATA.d3Initialize = function(callback) {
4659 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4664 xhrFields: { withCredentials: true } // required for the cookie
4667 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4670 NETDATA.chartLibraries.d3.enabled = false;
4671 NETDATA.error(100, NETDATA.d3_js);
4673 .always(function() {
4674 if(typeof callback === "function")
4679 NETDATA.chartLibraries.d3.enabled = false;
4680 if(typeof callback === "function")
4685 NETDATA.d3ChartUpdate = function(state, data) {
4689 NETDATA.d3ChartCreate = function(state, data) {
4693 // ----------------------------------------------------------------------------------------------------------------
4696 NETDATA.googleInitialize = function(callback) {
4697 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4699 url: NETDATA.google_js,
4702 xhrFields: { withCredentials: true } // required for the cookie
4705 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4706 google.load('visualization', '1.1', {
4707 'packages': ['corechart', 'controls'],
4708 'callback': callback
4712 NETDATA.chartLibraries.google.enabled = false;
4713 NETDATA.error(100, NETDATA.google_js);
4714 if(typeof callback === "function")
4719 NETDATA.chartLibraries.google.enabled = false;
4720 if(typeof callback === "function")
4725 NETDATA.googleChartUpdate = function(state, data) {
4726 var datatable = new google.visualization.DataTable(data.result);
4727 state.google_instance.draw(datatable, state.google_options);
4731 NETDATA.googleChartCreate = function(state, data) {
4732 var datatable = new google.visualization.DataTable(data.result);
4734 state.google_options = {
4735 colors: state.chartColors(),
4737 // do not set width, height - the chart resizes itself
4738 //width: state.chartWidth(),
4739 //height: state.chartHeight(),
4744 // title: "Time of Day",
4745 // format:'HH:mm:ss',
4746 viewWindowMode: 'maximized',
4758 viewWindowMode: 'pretty',
4773 focusTarget: 'category',
4780 titlePosition: 'out',
4791 curveType: 'function',
4796 switch(state.chart.chart_type) {
4798 state.google_options.vAxis.viewWindowMode = 'maximized';
4799 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4800 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4804 state.google_options.isStacked = true;
4805 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4806 state.google_options.vAxis.viewWindowMode = 'maximized';
4807 state.google_options.vAxis.minValue = null;
4808 state.google_options.vAxis.maxValue = null;
4809 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4814 state.google_options.lineWidth = 2;
4815 state.google_instance = new google.visualization.LineChart(state.element_chart);
4819 state.google_instance.draw(datatable, state.google_options);
4823 // ----------------------------------------------------------------------------------------------------------------
4825 NETDATA.percentFromValueMax = function(value, max) {
4826 if(value === null) value = 0;
4827 if(max < value) max = value;
4831 pcent = Math.round(value * 100 / max);
4832 if(pcent === 0 && value > 0) pcent = 1;
4838 // ----------------------------------------------------------------------------------------------------------------
4841 NETDATA.easypiechartInitialize = function(callback) {
4842 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4844 url: NETDATA.easypiechart_js,
4847 xhrFields: { withCredentials: true } // required for the cookie
4850 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4853 NETDATA.chartLibraries.easypiechart.enabled = false;
4854 NETDATA.error(100, NETDATA.easypiechart_js);
4856 .always(function() {
4857 if(typeof callback === "function")
4862 NETDATA.chartLibraries.easypiechart.enabled = false;
4863 if(typeof callback === "function")
4868 NETDATA.easypiechartClearSelection = function(state) {
4869 if(typeof state.easyPieChartEvent !== 'undefined') {
4870 if(state.easyPieChartEvent.timer !== null)
4871 clearTimeout(state.easyPieChartEvent.timer);
4873 state.easyPieChartEvent.timer = null;
4876 if(state.isAutoRefreshable() === true && state.data !== null) {
4877 NETDATA.easypiechartChartUpdate(state, state.data);
4880 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4881 state.easyPieChart_instance.update(0);
4883 state.easyPieChart_instance.enableAnimation();
4888 NETDATA.easypiechartSetSelection = function(state, t) {
4889 if(state.timeIsVisible(t) !== true)
4890 return NETDATA.easypiechartClearSelection(state);
4892 var slot = state.calculateRowForTime(t);
4893 if(slot < 0 || slot >= state.data.result.length)
4894 return NETDATA.easypiechartClearSelection(state);
4896 if(typeof state.easyPieChartEvent === 'undefined') {
4897 state.easyPieChartEvent = {
4904 var value = state.data.result[state.data.result.length - 1 - slot];
4905 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4906 var pcent = NETDATA.percentFromValueMax(value, max);
4908 state.easyPieChartEvent.value = value;
4909 state.easyPieChartEvent.pcent = pcent;
4910 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4912 if(state.easyPieChartEvent.timer === null) {
4913 state.easyPieChart_instance.disableAnimation();
4915 state.easyPieChartEvent.timer = setTimeout(function() {
4916 state.easyPieChartEvent.timer = null;
4917 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4918 }, NETDATA.options.current.charts_selection_animation_delay);
4924 NETDATA.easypiechartChartUpdate = function(state, data) {
4925 var value, max, pcent;
4927 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4933 value = data.result[0];
4934 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4935 pcent = NETDATA.percentFromValueMax(value, max);
4938 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4939 state.easyPieChart_instance.update(pcent);
4943 NETDATA.easypiechartChartCreate = function(state, data) {
4944 var self = $(state.element);
4945 var chart = $(state.element_chart);
4947 var value = data.result[0];
4948 var max = self.data('easypiechart-max-value') || null;
4949 var adjust = self.data('easypiechart-adjust') || null;
4953 state.easyPieChartMax = null;
4956 state.easyPieChartMax = max;
4958 var pcent = NETDATA.percentFromValueMax(value, max);
4960 chart.data('data-percent', pcent);
4964 case 'width': size = state.chartHeight(); break;
4965 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4966 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4968 default: size = state.chartWidth(); break;
4970 state.element.style.width = size + 'px';
4971 state.element.style.height = size + 'px';
4973 var stroke = Math.floor(size / 22);
4974 if(stroke < 3) stroke = 2;
4976 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4977 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4978 state.easyPieChartLabel = document.createElement('span');
4979 state.easyPieChartLabel.className = 'easyPieChartLabel';
4980 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4981 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4982 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4983 state.element_chart.appendChild(state.easyPieChartLabel);
4985 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4986 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4987 state.easyPieChartTitle = document.createElement('span');
4988 state.easyPieChartTitle.className = 'easyPieChartTitle';
4989 state.easyPieChartTitle.innerHTML = state.title;
4990 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4991 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4992 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4993 state.element_chart.appendChild(state.easyPieChartTitle);
4995 var unitfontsize = Math.round(titlefontsize * 0.9);
4996 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4997 state.easyPieChartUnits = document.createElement('span');
4998 state.easyPieChartUnits.className = 'easyPieChartUnits';
4999 state.easyPieChartUnits.innerHTML = state.units;
5000 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
5001 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
5002 state.element_chart.appendChild(state.easyPieChartUnits);
5004 var barColor = self.data('easypiechart-barcolor');
5005 if(typeof barColor === 'undefined' || barColor === null)
5006 barColor = state.chartColors()[0];
5008 // <div ... data-easypiechart-barcolor="(function(percent){return(percent < 50 ? '#5cb85c' : percent < 85 ? '#f0ad4e' : '#cb3935');})" ...></div>
5009 var tmp = eval(barColor);
5010 if(typeof tmp === 'function')
5014 chart.easyPieChart({
5016 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
5017 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
5018 scaleLength: self.data('easypiechart-scalelength') || 5,
5019 lineCap: self.data('easypiechart-linecap') || 'round',
5020 lineWidth: self.data('easypiechart-linewidth') || stroke,
5021 trackWidth: self.data('easypiechart-trackwidth') || undefined,
5022 size: self.data('easypiechart-size') || size,
5023 rotate: self.data('easypiechart-rotate') || 0,
5024 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
5025 easing: self.data('easypiechart-easing') || undefined
5028 // when we just re-create the chart
5029 // do not animate the first update
5031 if(typeof state.easyPieChart_instance !== 'undefined')
5034 state.easyPieChart_instance = chart.data('easyPieChart');
5035 if(animate === false) state.easyPieChart_instance.disableAnimation();
5036 state.easyPieChart_instance.update(pcent);
5037 if(animate === false) state.easyPieChart_instance.enableAnimation();
5041 // ----------------------------------------------------------------------------------------------------------------
5044 NETDATA.gaugeInitialize = function(callback) {
5045 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
5047 url: NETDATA.gauge_js,
5050 xhrFields: { withCredentials: true } // required for the cookie
5053 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
5056 NETDATA.chartLibraries.gauge.enabled = false;
5057 NETDATA.error(100, NETDATA.gauge_js);
5059 .always(function() {
5060 if(typeof callback === "function")
5065 NETDATA.chartLibraries.gauge.enabled = false;
5066 if(typeof callback === "function")
5071 NETDATA.gaugeAnimation = function(state, status) {
5074 if(typeof status === 'boolean' && status === false)
5076 else if(typeof status === 'number')
5079 // console.log('gauge speed ' + speed);
5080 state.gauge_instance.animationSpeed = speed;
5081 state.___gaugeOld__.speed = speed;
5084 NETDATA.gaugeSet = function(state, value, min, max) {
5085 if(typeof value !== 'number') value = 0;
5086 if(typeof min !== 'number') min = 0;
5087 if(typeof max !== 'number') max = 0;
5088 if(value > max) max = value;
5089 if(value < min) min = value;
5098 // gauge.js has an issue if the needle
5099 // is smaller than min or larger than max
5100 // when we set the new values
5101 // the needle will go crazy
5103 // to prevent it, we always feed it
5104 // with a percentage, so that the needle
5105 // is always between min and max
5106 var pcent = (value - min) * 100 / (max - min);
5108 // these should never happen
5109 if(pcent < 0) pcent = 0;
5110 if(pcent > 100) pcent = 100;
5112 state.gauge_instance.set(pcent);
5113 // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
5115 state.___gaugeOld__.value = value;
5116 state.___gaugeOld__.min = min;
5117 state.___gaugeOld__.max = max;
5120 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5121 if(state.___gaugeOld__.valueLabel !== value) {
5122 state.___gaugeOld__.valueLabel = value;
5123 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5125 if(state.___gaugeOld__.minLabel !== min) {
5126 state.___gaugeOld__.minLabel = min;
5127 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5129 if(state.___gaugeOld__.maxLabel !== max) {
5130 state.___gaugeOld__.maxLabel = max;
5131 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5135 NETDATA.gaugeClearSelection = function(state) {
5136 if(typeof state.gaugeEvent !== 'undefined') {
5137 if(state.gaugeEvent.timer !== null)
5138 clearTimeout(state.gaugeEvent.timer);
5140 state.gaugeEvent.timer = null;
5143 if(state.isAutoRefreshable() === true && state.data !== null) {
5144 NETDATA.gaugeChartUpdate(state, state.data);
5147 NETDATA.gaugeAnimation(state, false);
5148 NETDATA.gaugeSet(state, null, null, null);
5149 NETDATA.gaugeSetLabels(state, null, null, null);
5152 NETDATA.gaugeAnimation(state, true);
5156 NETDATA.gaugeSetSelection = function(state, t) {
5157 if(state.timeIsVisible(t) !== true)
5158 return NETDATA.gaugeClearSelection(state);
5160 var slot = state.calculateRowForTime(t);
5161 if(slot < 0 || slot >= state.data.result.length)
5162 return NETDATA.gaugeClearSelection(state);
5164 if(typeof state.gaugeEvent === 'undefined') {
5165 state.gaugeEvent = {
5173 var value = state.data.result[state.data.result.length - 1 - slot];
5174 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5177 state.gaugeEvent.value = value;
5178 state.gaugeEvent.max = max;
5179 state.gaugeEvent.min = min;
5180 NETDATA.gaugeSetLabels(state, value, min, max);
5182 if(state.gaugeEvent.timer === null) {
5183 NETDATA.gaugeAnimation(state, false);
5185 state.gaugeEvent.timer = setTimeout(function() {
5186 state.gaugeEvent.timer = null;
5187 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5188 }, NETDATA.options.current.charts_selection_animation_delay);
5194 NETDATA.gaugeChartUpdate = function(state, data) {
5195 var value, min, max;
5197 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5201 NETDATA.gaugeSetLabels(state, null, null, null);
5204 value = data.result[0];
5206 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5207 if(value > max) max = value;
5208 NETDATA.gaugeSetLabels(state, value, min, max);
5211 NETDATA.gaugeSet(state, value, min, max);
5215 NETDATA.gaugeChartCreate = function(state, data) {
5216 var self = $(state.element);
5217 // var chart = $(state.element_chart);
5219 var value = data.result[0];
5220 var max = self.data('gauge-max-value') || null;
5221 var adjust = self.data('gauge-adjust') || null;
5222 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5223 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5224 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5225 var stopColor = self.data('gauge-stop-color') || void 0;
5226 var generateGradient = self.data('gauge-generate-gradient') || false;
5230 state.gaugeMax = null;
5233 state.gaugeMax = max;
5235 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5237 // case 'width': width = height * ratio; break;
5239 // default: height = width / ratio; break;
5241 //state.element.style.width = width.toString() + 'px';
5242 //state.element.style.height = height.toString() + 'px';
5247 lines: 12, // The number of lines to draw
5248 angle: 0.15, // The length of each line
5249 lineWidth: 0.44, // 0.44 The line thickness
5251 length: 0.8, // 0.9 The radius of the inner circle
5252 strokeWidth: 0.035, // The rotation offset
5253 color: pointerColor // Fill color
5255 colorStart: startColor, // Colors
5256 colorStop: stopColor, // just experiment with them
5257 strokeColor: strokeColor, // to see which ones work best for you
5259 generateGradient: (generateGradient === true)?true:false,
5263 if (generateGradient.constructor === Array) {
5265 // data-gauge-generate-gradient="[0, 50, 100]"
5266 // data-gauge-gradient-percent-color-0="#FFFFFF"
5267 // data-gauge-gradient-percent-color-50="#999900"
5268 // data-gauge-gradient-percent-color-100="#000000"
5270 options.percentColors = new Array();
5271 var len = generateGradient.length;
5273 var pcent = generateGradient[len];
5274 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5275 if(color !== false) {
5276 var a = new Array();
5279 options.percentColors.unshift(a);
5282 if(options.percentColors.length === 0)
5283 delete options.percentColors;
5285 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5286 options.percentColors = [
5287 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5288 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5289 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5290 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5291 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5292 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5293 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5294 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5295 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5296 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5297 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5300 state.gauge_canvas = document.createElement('canvas');
5301 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5302 state.gauge_canvas.className = 'gaugeChart';
5303 state.gauge_canvas.width = width;
5304 state.gauge_canvas.height = height;
5305 state.element_chart.appendChild(state.gauge_canvas);
5307 var valuefontsize = Math.floor(height / 6);
5308 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5309 state.gaugeChartLabel = document.createElement('span');
5310 state.gaugeChartLabel.className = 'gaugeChartLabel';
5311 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5312 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5313 state.element_chart.appendChild(state.gaugeChartLabel);
5315 var titlefontsize = Math.round(valuefontsize / 2);
5317 state.gaugeChartTitle = document.createElement('span');
5318 state.gaugeChartTitle.className = 'gaugeChartTitle';
5319 state.gaugeChartTitle.innerHTML = state.title;
5320 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5321 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5322 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5323 state.element_chart.appendChild(state.gaugeChartTitle);
5325 var unitfontsize = Math.round(titlefontsize * 0.9);
5326 state.gaugeChartUnits = document.createElement('span');
5327 state.gaugeChartUnits.className = 'gaugeChartUnits';
5328 state.gaugeChartUnits.innerHTML = state.units;
5329 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5330 state.element_chart.appendChild(state.gaugeChartUnits);
5332 state.gaugeChartMin = document.createElement('span');
5333 state.gaugeChartMin.className = 'gaugeChartMin';
5334 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5335 state.element_chart.appendChild(state.gaugeChartMin);
5337 state.gaugeChartMax = document.createElement('span');
5338 state.gaugeChartMax.className = 'gaugeChartMax';
5339 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5340 state.element_chart.appendChild(state.gaugeChartMax);
5342 // when we just re-create the chart
5343 // do not animate the first update
5345 if(typeof state.gauge_instance !== 'undefined')
5348 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5350 state.___gaugeOld__ = {
5359 // we will always feed a percentage
5360 state.gauge_instance.minValue = 0;
5361 state.gauge_instance.maxValue = 100;
5363 NETDATA.gaugeAnimation(state, animate);
5364 NETDATA.gaugeSet(state, value, 0, max);
5365 NETDATA.gaugeSetLabels(state, value, 0, max);
5366 NETDATA.gaugeAnimation(state, true);
5370 // ----------------------------------------------------------------------------------------------------------------
5371 // Charts Libraries Registration
5373 NETDATA.chartLibraries = {
5375 initialize: NETDATA.dygraphInitialize,
5376 create: NETDATA.dygraphChartCreate,
5377 update: NETDATA.dygraphChartUpdate,
5378 resize: function(state) {
5379 if(typeof state.dygraph_instance.resize === 'function')
5380 state.dygraph_instance.resize();
5382 setSelection: NETDATA.dygraphSetSelection,
5383 clearSelection: NETDATA.dygraphClearSelection,
5384 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5387 format: function(state) { return 'json'; },
5388 options: function(state) { return 'ms|flip'; },
5389 legend: function(state) {
5390 if(this.isSparkline(state) === false)
5391 return 'right-side';
5395 autoresize: function(state) { return true; },
5396 max_updates_to_recreate: function(state) { return 5000; },
5397 track_colors: function(state) { return true; },
5398 pixels_per_point: function(state) {
5399 if(this.isSparkline(state) === false)
5405 isSparkline: function(state) {
5406 if(typeof state.dygraph_sparkline === 'undefined') {
5407 var t = $(state.element).data('dygraph-theme');
5408 if(t === 'sparkline')
5409 state.dygraph_sparkline = true;
5411 state.dygraph_sparkline = false;
5413 return state.dygraph_sparkline;
5417 initialize: NETDATA.sparklineInitialize,
5418 create: NETDATA.sparklineChartCreate,
5419 update: NETDATA.sparklineChartUpdate,
5421 setSelection: undefined, // function(state, t) { return true; },
5422 clearSelection: undefined, // function(state) { return true; },
5423 toolboxPanAndZoom: null,
5426 format: function(state) { return 'array'; },
5427 options: function(state) { return 'flip|abs'; },
5428 legend: function(state) { return null; },
5429 autoresize: function(state) { return false; },
5430 max_updates_to_recreate: function(state) { return 5000; },
5431 track_colors: function(state) { return false; },
5432 pixels_per_point: function(state) { return 3; }
5435 initialize: NETDATA.peityInitialize,
5436 create: NETDATA.peityChartCreate,
5437 update: NETDATA.peityChartUpdate,
5439 setSelection: undefined, // function(state, t) { return true; },
5440 clearSelection: undefined, // function(state) { return true; },
5441 toolboxPanAndZoom: null,
5444 format: function(state) { return 'ssvcomma'; },
5445 options: function(state) { return 'null2zero|flip|abs'; },
5446 legend: function(state) { return null; },
5447 autoresize: function(state) { return false; },
5448 max_updates_to_recreate: function(state) { return 5000; },
5449 track_colors: function(state) { return false; },
5450 pixels_per_point: function(state) { return 3; }
5453 initialize: NETDATA.morrisInitialize,
5454 create: NETDATA.morrisChartCreate,
5455 update: NETDATA.morrisChartUpdate,
5457 setSelection: undefined, // function(state, t) { return true; },
5458 clearSelection: undefined, // function(state) { return true; },
5459 toolboxPanAndZoom: null,
5462 format: function(state) { return 'json'; },
5463 options: function(state) { return 'objectrows|ms'; },
5464 legend: function(state) { return null; },
5465 autoresize: function(state) { return false; },
5466 max_updates_to_recreate: function(state) { return 50; },
5467 track_colors: function(state) { return false; },
5468 pixels_per_point: function(state) { return 15; }
5471 initialize: NETDATA.googleInitialize,
5472 create: NETDATA.googleChartCreate,
5473 update: NETDATA.googleChartUpdate,
5475 setSelection: undefined, //function(state, t) { return true; },
5476 clearSelection: undefined, //function(state) { return true; },
5477 toolboxPanAndZoom: null,
5480 format: function(state) { return 'datatable'; },
5481 options: function(state) { return ''; },
5482 legend: function(state) { return null; },
5483 autoresize: function(state) { return false; },
5484 max_updates_to_recreate: function(state) { return 300; },
5485 track_colors: function(state) { return false; },
5486 pixels_per_point: function(state) { return 4; }
5489 initialize: NETDATA.raphaelInitialize,
5490 create: NETDATA.raphaelChartCreate,
5491 update: NETDATA.raphaelChartUpdate,
5493 setSelection: undefined, // function(state, t) { return true; },
5494 clearSelection: undefined, // function(state) { return true; },
5495 toolboxPanAndZoom: null,
5498 format: function(state) { return 'json'; },
5499 options: function(state) { return ''; },
5500 legend: function(state) { return null; },
5501 autoresize: function(state) { return false; },
5502 max_updates_to_recreate: function(state) { return 5000; },
5503 track_colors: function(state) { return false; },
5504 pixels_per_point: function(state) { return 3; }
5507 initialize: NETDATA.c3Initialize,
5508 create: NETDATA.c3ChartCreate,
5509 update: NETDATA.c3ChartUpdate,
5511 setSelection: undefined, // function(state, t) { return true; },
5512 clearSelection: undefined, // function(state) { return true; },
5513 toolboxPanAndZoom: null,
5516 format: function(state) { return 'csvjsonarray'; },
5517 options: function(state) { return 'milliseconds'; },
5518 legend: function(state) { return null; },
5519 autoresize: function(state) { return false; },
5520 max_updates_to_recreate: function(state) { return 5000; },
5521 track_colors: function(state) { return false; },
5522 pixels_per_point: function(state) { return 15; }
5525 initialize: NETDATA.d3Initialize,
5526 create: NETDATA.d3ChartCreate,
5527 update: NETDATA.d3ChartUpdate,
5529 setSelection: undefined, // function(state, t) { return true; },
5530 clearSelection: undefined, // function(state) { return true; },
5531 toolboxPanAndZoom: null,
5534 format: function(state) { return 'json'; },
5535 options: function(state) { return ''; },
5536 legend: function(state) { return null; },
5537 autoresize: function(state) { return false; },
5538 max_updates_to_recreate: function(state) { return 5000; },
5539 track_colors: function(state) { return false; },
5540 pixels_per_point: function(state) { return 3; }
5543 initialize: NETDATA.easypiechartInitialize,
5544 create: NETDATA.easypiechartChartCreate,
5545 update: NETDATA.easypiechartChartUpdate,
5547 setSelection: NETDATA.easypiechartSetSelection,
5548 clearSelection: NETDATA.easypiechartClearSelection,
5549 toolboxPanAndZoom: null,
5552 format: function(state) { return 'array'; },
5553 options: function(state) { return 'absolute'; },
5554 legend: function(state) { return null; },
5555 autoresize: function(state) { return false; },
5556 max_updates_to_recreate: function(state) { return 5000; },
5557 track_colors: function(state) { return true; },
5558 pixels_per_point: function(state) { return 3; },
5562 initialize: NETDATA.gaugeInitialize,
5563 create: NETDATA.gaugeChartCreate,
5564 update: NETDATA.gaugeChartUpdate,
5566 setSelection: NETDATA.gaugeSetSelection,
5567 clearSelection: NETDATA.gaugeClearSelection,
5568 toolboxPanAndZoom: null,
5571 format: function(state) { return 'array'; },
5572 options: function(state) { return 'absolute'; },
5573 legend: function(state) { return null; },
5574 autoresize: function(state) { return false; },
5575 max_updates_to_recreate: function(state) { return 5000; },
5576 track_colors: function(state) { return true; },
5577 pixels_per_point: function(state) { return 3; },
5582 NETDATA.registerChartLibrary = function(library, url) {
5583 if(NETDATA.options.debug.libraries === true)
5584 console.log("registering chart library: " + library);
5586 NETDATA.chartLibraries[library].url = url;
5587 NETDATA.chartLibraries[library].initialized = true;
5588 NETDATA.chartLibraries[library].enabled = true;
5591 // ----------------------------------------------------------------------------------------------------------------
5592 // Load required JS libraries and CSS
5594 NETDATA.requiredJs = [
5596 url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js',
5598 isAlreadyLoaded: function() {
5599 // check if bootstrap is loaded
5600 if(typeof $().emulateTransitionEnd == 'function')
5603 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5611 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js',
5612 isAlreadyLoaded: function() { return false; }
5616 NETDATA.requiredCSS = [
5618 url: NETDATA.themes.current.bootstrap_css,
5619 isAlreadyLoaded: function() {
5620 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5627 url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
5628 isAlreadyLoaded: function() { return false; }
5631 url: NETDATA.themes.current.dashboard_css,
5632 isAlreadyLoaded: function() { return false; }
5636 NETDATA.loadedRequiredJs = 0;
5637 NETDATA.loadRequiredJs = function(index, callback) {
5638 if(index >= NETDATA.requiredJs.length) {
5639 if(typeof callback === 'function')
5644 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5645 NETDATA.loadedRequiredJs++;
5646 NETDATA.loadRequiredJs(++index, callback);
5650 if(NETDATA.options.debug.main_loop === true)
5651 console.log('loading ' + NETDATA.requiredJs[index].url);
5654 if(typeof NETDATA.requiredJs[index].async !== 'undefined' && NETDATA.requiredJs[index].async === false)
5658 url: NETDATA.requiredJs[index].url,
5661 xhrFields: { withCredentials: true } // required for the cookie
5664 if(NETDATA.options.debug.main_loop === true)
5665 console.log('loaded ' + NETDATA.requiredJs[index].url);
5668 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5670 .always(function() {
5671 NETDATA.loadedRequiredJs++;
5674 NETDATA.loadRequiredJs(++index, callback);
5678 NETDATA.loadRequiredJs(++index, callback);
5681 NETDATA.loadRequiredCSS = function(index) {
5682 if(index >= NETDATA.requiredCSS.length)
5685 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5686 NETDATA.loadRequiredCSS(++index);
5690 if(NETDATA.options.debug.main_loop === true)
5691 console.log('loading ' + NETDATA.requiredCSS[index].url);
5693 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5694 NETDATA.loadRequiredCSS(++index);
5698 // ----------------------------------------------------------------------------------------------------------------
5699 // Registry of netdata hosts
5702 onclick: null, // the callback to handle the click - it will be called with the alarm log entry
5703 chart_div_offset: 100, // give that space above the chart when scrolling to it
5704 chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id))
5705 chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart
5707 ms_penalty: 0, // the time penalty of the next alarm
5708 ms_between_notifications: 500, // firefox moves the alarms off-screen (above, outside the top of the screen)
5709 // if alarms are shown faster than: one per 500ms
5711 notifications: false, // when true, the browser supports notifications (may not be granted though)
5712 last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5713 first_notification_id: 0, // the id of the first alarm_log entry for this session
5714 // this is used to prevent CLEAR notifications for past events
5715 // notifications_shown: new Array(),
5717 server: null, // the server to connect to for fetching alarms
5718 current: null, // the list of raised alarms - updated in the background
5719 callback: null, // a callback function to call every time the list of raised alarms is refreshed
5721 notify: function(entry) {
5722 // console.log('alarm ' + entry.unique_id);
5724 if(entry.updated === true) {
5725 // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5729 var value = entry.value;
5730 if(NETDATA.alarms.current !== null) {
5731 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
5732 if(typeof t !== 'undefined' && entry.status == t.status)
5736 var name = entry.name.replace(/_/g, ' ');
5737 var status = entry.status.toLowerCase();
5738 var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
5739 var tag = entry.alarm_id;
5740 var icon = 'images/seo-performance-128.png';
5741 var interaction = false;
5745 // console.log('alarm ' + entry.unique_id + ' ' + entry.chart + '.' + entry.name + ' is ' + entry.status);
5747 switch(entry.status) {
5755 case 'UNINITIALIZED':
5759 if(entry.unique_id < NETDATA.alarms.first_notification_id) {
5760 // console.log('alarm ' + entry.unique_id + ' is not current');
5763 if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5764 // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5767 title = name + ' back to normal';
5768 icon = 'images/check-mark-2-128-green.png'
5769 interaction = false;
5773 if(entry.old_status === 'CRITICAL')
5774 status = 'demoted to ' + entry.status.toLowerCase();
5776 icon = 'images/alert-128-orange.png';
5777 interaction = false;
5781 if(entry.old_status === 'WARNING')
5782 status = 'escalated to ' + entry.status.toLowerCase();
5784 icon = 'images/alert-128-red.png'
5789 console.log('invalid alarm status ' + entry.status);
5794 // cleanup old notifications with the same alarm_id as this one
5795 // FIXME: it does not seem to work on any web browser!
5796 var len = NETDATA.alarms.notifications_shown.length;
5798 var n = NETDATA.alarms.notifications_shown[len];
5799 if(n.data.alarm_id === entry.alarm_id) {
5800 console.log('removing old alarm ' + n.data.unique_id);
5802 // close the notification
5805 // remove it from the array
5806 NETDATA.alarms.notifications_shown.splice(len, 1);
5807 len = NETDATA.alarms.notifications_shown.length;
5814 setTimeout(function() {
5815 // show this notification
5816 // console.log('new notification: ' + title);
5817 var n = new Notification(title, {
5818 body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info,
5820 requireInteraction: interaction,
5821 icon: NETDATA.serverDefault + icon,
5825 n.onclick = function(event) {
5826 event.preventDefault();
5827 NETDATA.alarms.onclick(event.target.data);
5831 // NETDATA.alarms.notifications_shown.push(n);
5832 // console.log(entry);
5833 }, NETDATA.alarms.ms_penalty);
5835 NETDATA.alarms.ms_penalty += NETDATA.alarms.ms_between_notifications;
5839 scrollToChart: function(chart_id) {
5840 if(typeof chart_id === 'string') {
5841 var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset();
5842 if(typeof offset !== 'undefined') {
5843 $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration);
5850 scrollToAlarm: function(alarm) {
5851 if(typeof alarm === 'object') {
5852 var ret = NETDATA.alarms.scrollToChart(alarm.chart);
5854 if(ret === true && NETDATA.options.page_is_visible === false)
5856 // 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.');
5861 notifyAll: function() {
5862 // console.log('FETCHING ALARM LOG');
5863 NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5864 // console.log('ALARM LOG FETCHED');
5866 if(data === null || typeof data !== 'object') {
5867 console.log('invalid alarms log response');
5871 if(data.length === 0) {
5872 console.log('received empty alarm log');
5876 // console.log('received alarm log of ' + data.length + ' entries, from ' + data[data.length - 1].unique_id.toString() + ' to ' + data[0].unique_id.toString());
5878 data.sort(function(a, b) {
5879 if(a.unique_id > b.unique_id) return -1;
5880 if(a.unique_id < b.unique_id) return 1;
5884 NETDATA.alarms.ms_penalty = 0;
5886 var len = data.length;
5888 if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5889 NETDATA.alarms.notify(data[len]);
5892 // console.log('ignoring alarm (older) with id ' + data[len].unique_id.toString());
5895 NETDATA.alarms.last_notification_id = data[0].unique_id;
5896 NETDATA.localStorageSet('last_notification_id', NETDATA.alarms.last_notification_id, null);
5897 // console.log('last notification id = ' + NETDATA.alarms.last_notification_id);
5901 check_notifications: function() {
5902 // returns true if we should fire 1+ notifications
5904 if(NETDATA.alarms.notifications !== true) {
5905 // console.log('notifications not available');
5909 if(Notification.permission !== 'granted') {
5910 // console.log('notifications not granted');
5914 if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5915 // console.log('can do alarms: old id = ' + NETDATA.alarms.last_notification_id + ' new id = ' + NETDATA.alarms.current.latest_alarm_log_unique_id);
5917 if(NETDATA.alarms.current.latest_alarm_log_unique_id > NETDATA.alarms.last_notification_id) {
5918 // console.log('new alarms detected');
5921 //else console.log('no new alarms');
5923 // else console.log('cannot process alarms');
5928 get: function(what, callback) {
5930 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5934 'Cache-Control': 'no-cache, no-store',
5935 'Pragma': 'no-cache'
5937 xhrFields: { withCredentials: true } // required for the cookie
5939 .done(function(data) {
5940 if(NETDATA.alarms.first_notification_id === 0 && typeof data.latest_alarm_log_unique_id === 'number')
5941 NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
5943 if(typeof callback === 'function')
5947 NETDATA.error(415, NETDATA.alarms.server);
5949 if(typeof callback === 'function')
5954 update_forever: function() {
5955 NETDATA.alarms.get('active', function(data) {
5957 NETDATA.alarms.current = data;
5959 if(NETDATA.alarms.check_notifications() === true) {
5960 NETDATA.alarms.notifyAll();
5963 if (typeof NETDATA.alarms.callback === 'function') {
5964 NETDATA.alarms.callback(data);
5967 // Health monitoring is disabled on this netdata
5968 if(data.status === false) return;
5971 setTimeout(NETDATA.alarms.update_forever, 10000);
5975 get_log: function(last_id, callback) {
5976 // console.log('fetching all log after ' + last_id.toString());
5978 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5982 'Cache-Control': 'no-cache, no-store',
5983 'Pragma': 'no-cache'
5985 xhrFields: { withCredentials: true } // required for the cookie
5987 .done(function(data) {
5988 if(typeof callback === 'function')
5992 NETDATA.error(416, NETDATA.alarms.server);
5994 if(typeof callback === 'function')
6000 var host = NETDATA.serverDefault;
6001 while(host.slice(-1) === '/')
6002 host = host.substring(0, host.length - 1);
6003 NETDATA.alarms.server = host;
6005 NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
6007 if(NETDATA.alarms.onclick === null)
6008 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
6010 if(netdataShowAlarms === true) {
6011 NETDATA.alarms.update_forever();
6013 if('Notification' in window) {
6014 // console.log('notifications available');
6015 NETDATA.alarms.notifications = true;
6017 if(Notification.permission === 'default')
6018 Notification.requestPermission();
6024 // ----------------------------------------------------------------------------------------------------------------
6025 // Registry of netdata hosts
6027 NETDATA.registry = {
6028 server: null, // the netdata registry server
6029 person_guid: null, // the unique ID of this browser / user
6030 machine_guid: null, // the unique ID the netdata server that served dashboard.js
6031 hostname: null, // the hostname of the netdata server that served dashboard.js
6032 machines: null, // the user's other URLs
6033 machines_array: null, // the user's other URLs in an array
6036 parsePersonUrls: function(person_urls) {
6037 // console.log(person_urls);
6038 NETDATA.registry.person_urls = person_urls;
6041 NETDATA.registry.machines = {};
6042 NETDATA.registry.machines_array = new Array();
6044 var now = new Date().getTime();
6045 var apu = person_urls;
6048 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
6049 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6055 accesses: apu[i][3],
6057 alternate_urls: new Array()
6059 obj.alternate_urls.push(apu[i][1]);
6061 NETDATA.registry.machines[apu[i][0]] = obj;
6062 NETDATA.registry.machines_array.push(obj);
6065 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
6067 var pu = NETDATA.registry.machines[apu[i][0]];
6068 if(pu.last_t < apu[i][2]) {
6070 pu.last_t = apu[i][2];
6071 pu.name = apu[i][4];
6073 pu.accesses += apu[i][3];
6074 pu.alternate_urls.push(apu[i][1]);
6079 if(typeof netdataRegistryCallback === 'function')
6080 netdataRegistryCallback(NETDATA.registry.machines_array);
6084 if(netdataRegistry !== true) return;
6086 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
6088 NETDATA.registry.server = data.registry;
6089 NETDATA.registry.machine_guid = data.machine_guid;
6090 NETDATA.registry.hostname = data.hostname;
6092 NETDATA.registry.access(2, function (person_urls) {
6093 NETDATA.registry.parsePersonUrls(person_urls);
6100 hello: function(host, callback) {
6101 while(host.slice(-1) === '/')
6102 host = host.substring(0, host.length - 1);
6104 // send HELLO to a netdata server:
6105 // 1. verifies the server is reachable
6106 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
6108 url: host + '/api/v1/registry?action=hello',
6112 'Cache-Control': 'no-cache, no-store',
6113 'Pragma': 'no-cache'
6115 xhrFields: { withCredentials: true } // required for the cookie
6117 .done(function(data) {
6118 if(typeof data.status !== 'string' || data.status !== 'ok') {
6119 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
6123 if(typeof callback === 'function')
6127 NETDATA.error(407, host);
6129 if(typeof callback === 'function')
6134 access: function(max_redirects, callback) {
6135 // send ACCESS to a netdata registry:
6136 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
6137 // 2. it responds with a list of netdata servers we know
6138 // the registry identifies us using a cookie it sets the first time we access it
6139 // the registry may respond with a redirect URL to send us to another registry
6141 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),
6145 'Cache-Control': 'no-cache, no-store',
6146 'Pragma': 'no-cache'
6148 xhrFields: { withCredentials: true } // required for the cookie
6150 .done(function(data) {
6151 var redirect = null;
6152 if(typeof data.registry === 'string')
6153 redirect = data.registry;
6155 if(typeof data.status !== 'string' || data.status !== 'ok') {
6156 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6161 if(redirect !== null && max_redirects > 0) {
6162 NETDATA.registry.server = redirect;
6163 NETDATA.registry.access(max_redirects - 1, callback);
6166 if(typeof callback === 'function')
6171 if(typeof data.person_guid === 'string')
6172 NETDATA.registry.person_guid = data.person_guid;
6174 if(typeof callback === 'function')
6175 callback(data.urls);
6179 NETDATA.error(410, NETDATA.registry.server);
6181 if(typeof callback === 'function')
6186 delete: function(delete_url, callback) {
6187 // send DELETE to a netdata registry:
6189 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),
6193 'Cache-Control': 'no-cache, no-store',
6194 'Pragma': 'no-cache'
6196 xhrFields: { withCredentials: true } // required for the cookie
6198 .done(function(data) {
6199 if(typeof data.status !== 'string' || data.status !== 'ok') {
6200 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6204 if(typeof callback === 'function')
6208 NETDATA.error(412, NETDATA.registry.server);
6210 if(typeof callback === 'function')
6215 search: function(machine_guid, callback) {
6216 // SEARCH for the URLs of a machine:
6218 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,
6222 'Cache-Control': 'no-cache, no-store',
6223 'Pragma': 'no-cache'
6225 xhrFields: { withCredentials: true } // required for the cookie
6227 .done(function(data) {
6228 if(typeof data.status !== 'string' || data.status !== 'ok') {
6229 NETDATA.error(417, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6233 if(typeof callback === 'function')
6237 NETDATA.error(418, NETDATA.registry.server);
6239 if(typeof callback === 'function')
6244 switch: function(new_person_guid, callback) {
6247 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,
6251 'Cache-Control': 'no-cache, no-store',
6252 'Pragma': 'no-cache'
6254 xhrFields: { withCredentials: true } // required for the cookie
6256 .done(function(data) {
6257 if(typeof data.status !== 'string' || data.status !== 'ok') {
6258 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
6262 if(typeof callback === 'function')
6266 NETDATA.error(414, NETDATA.registry.server);
6268 if(typeof callback === 'function')
6274 // ----------------------------------------------------------------------------------------------------------------
6277 if(typeof netdataPrepCallback === 'function')
6278 netdataPrepCallback();
6280 NETDATA.errorReset();
6281 NETDATA.loadRequiredCSS(0);
6283 NETDATA._loadjQuery(function() {
6284 NETDATA.loadRequiredJs(0, function() {
6285 if(typeof $().emulateTransitionEnd !== 'function') {
6286 // bootstrap is not available
6287 NETDATA.options.current.show_help = false;
6290 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
6291 if(NETDATA.options.debug.main_loop === true)
6292 console.log('starting chart refresh thread');
6299 // window.NETDATA = NETDATA;
6300 // })(window, document);