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 netdataNoRegistry = true; // Don't update the registry for this access
16 // var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
17 // the URLs from the registry
18 // var netdataShowHelp = true; // enable/disable help
19 // var netdataShowAlarms = true; // enable/disable help
21 // You can also set the default netdata server, using the following.
22 // When this variable is not set, we assume the page is hosted on your
23 // netdata server already.
24 // var netdataServer = "http://yourhost:19999"; // set your NetData server
26 //(function(window, document, undefined) {
28 // ------------------------------------------------------------------------
29 // compatibility fixes
31 // fix IE issue with console
32 if(!window.console) { window.console = { log: function(){} }; }
34 // if string.endsWith is not defined, define it
35 if(typeof String.prototype.endsWith !== 'function') {
36 String.prototype.endsWith = function(s) {
37 if(s.length > this.length) return false;
38 return this.slice(-s.length) === s;
42 // if string.startsWith is not defined, define it
43 if(typeof String.prototype.startsWith !== 'function') {
44 String.prototype.startsWith = function(s) {
45 if(s.length > this.length) return false;
46 return this.slice(s.length) === s;
51 var NETDATA = window.NETDATA || {};
53 // ----------------------------------------------------------------------------------------------------------------
54 // Detect the netdata server
56 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
57 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
58 NETDATA._scriptSource = function() {
61 if(typeof document.currentScript !== 'undefined') {
62 script = document.currentScript;
65 var all_scripts = document.getElementsByTagName('script');
66 script = all_scripts[all_scripts.length - 1];
69 if (typeof script.getAttribute.length !== 'undefined')
72 script = script.getAttribute('src', -1);
77 if(typeof netdataServer !== 'undefined')
78 NETDATA.serverDefault = netdataServer;
80 var s = NETDATA._scriptSource();
81 if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
83 console.log('WARNING: Cannot detect the URL of the netdata server.');
84 NETDATA.serverDefault = null;
88 if(NETDATA.serverDefault === null)
89 NETDATA.serverDefault = '';
90 else if(NETDATA.serverDefault.slice(-1) !== '/')
91 NETDATA.serverDefault += '/';
93 // default URLs for all the external files we need
94 // make them RELATIVE so that the whole thing can also be
95 // installed under a web server
96 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
97 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
98 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
99 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
100 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge.min.js';
101 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
102 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
103 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
104 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
105 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3.min.js';
106 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3.min.js';
107 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3.min.css';
108 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
109 NETDATA.google_js = 'https://www.google.com/jsapi';
113 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
114 dashboard_css: NETDATA.serverDefault + 'dashboard.css',
115 background: '#FFFFFF',
116 foreground: '#000000',
119 colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477',
120 '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6',
121 '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707',
122 '#329262', '#3B3EAC' ],
123 easypiechart_track: '#f0f0f0',
124 easypiechart_scale: '#dfe0e0',
125 gauge_pointer: '#C0C0C0',
126 gauge_stroke: '#F0F0F0',
127 gauge_gradient: false
130 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
131 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
132 background: '#272b30',
133 foreground: '#C8C8C8',
136 /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00',
137 '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0',
138 '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a',
139 '#a6a479', '#a66da8' ],
141 colors: [ '#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00',
142 '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700',
143 '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737',
144 '#329262', '#3B3EFF' ],
145 easypiechart_track: '#373b40',
146 easypiechart_scale: '#373b40',
147 gauge_pointer: '#474b50',
148 gauge_stroke: '#373b40',
149 gauge_gradient: false
153 if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
154 NETDATA.themes.current = NETDATA.themes[netdataTheme];
156 NETDATA.themes.current = NETDATA.themes.white;
158 if(typeof netdataShowHelp === 'undefined')
159 netdataShowHelp = true;
161 if(typeof netdataShowAlarms === 'undefined')
162 netdataShowAlarms = true;
164 NETDATA.colors = NETDATA.themes.current.colors;
166 // these are the colors Google Charts are using
167 // we have them here to attempt emulate their look and feel on the other chart libraries
168 // http://there4.io/2012/05/02/google-chart-color-list/
169 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
170 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
171 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
173 // an alternative set
174 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
175 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
176 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
178 // ----------------------------------------------------------------------------------------------------------------
179 // the defaults for all charts
181 // if the user does not specify any of these, the following will be used
183 NETDATA.chartDefaults = {
184 host: NETDATA.serverDefault, // the server to get data from
185 width: '100%', // the chart width - can be null
186 height: '100%', // the chart height - can be null
187 min_width: null, // the chart minimum width - can be null
188 library: 'dygraph', // the graphing library to use
189 method: 'average', // the grouping method
190 before: 0, // panning
191 after: -600, // panning
192 pixels_per_point: 1, // the detail of the chart
193 fill_luminance: 0.8 // luminance of colors in solit areas
196 // ----------------------------------------------------------------------------------------------------------------
200 pauseCallback: null, // a callback when we are really paused
202 pause: false, // when enabled we don't auto-refresh the charts
204 targets: null, // an array of all the state objects that are
205 // currently active (independently of their
206 // viewport visibility)
208 updated_dom: true, // when true, the DOM has been updated with
209 // new elements we have to check.
211 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
212 // rendering charts continiously.
213 // used with .current.fast_render_timeframe
215 page_is_visible: true, // when true, this page is visible
217 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
218 // auto-refresher for some time (when a chart is
219 // performing pan or zoom, we need to stop refreshing
220 // all other charts, to have the maximum speed for
221 // rendering the chart that is panned or zoomed).
222 // Used with .current.global_pan_sync_time
224 last_resized: new Date().getTime(), // the timestamp of the last resize request
226 last_page_scroll: 0, // the timestamp the last time the page was scrolled
228 // the current profile
229 // we may have many...
231 pixels_per_point: 1, // the minimum pixels per point for all charts
232 // increase this to speed javascript up
233 // each chart library has its own limit too
234 // the max of this and the chart library is used
235 // the final is calculated every time, so a change
236 // here will have immediate effect on the next chart
239 idle_between_charts: 100, // ms - how much time to wait between chart updates
241 fast_render_timeframe: 200, // ms - render continously until this time of continious
242 // rendering has been reached
243 // this setting is used to make it render e.g. 10
244 // charts at once, sleep idle_between_charts time
245 // and continue for another 10 charts.
247 idle_between_loops: 500, // ms - if all charts have been updated, wait this
248 // time before starting again.
250 idle_parallel_loops: 100, // ms - the time between parallel refresher updates
252 idle_lost_focus: 500, // ms - when the window does not have focus, check
253 // if focus has been regained, every this time
255 global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
256 // autorefreshing of charts is paused for this amount
259 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
260 // of time before setting up synchronized selections
263 sync_selection: true, // enable or disable selection sync
265 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
267 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
269 pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
271 update_only_visible: true, // enable or disable visibility management
273 parallel_refresher: true, // enable parallel refresh of charts
275 concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
277 destroy_on_hide: false, // destroy charts when they are not visible
279 show_help: netdataShowHelp, // when enabled the charts will show some help
280 show_help_delay_show_ms: 500,
281 show_help_delay_hide_ms: 0,
283 eliminate_zero_dimensions: true, // do not show dimensions with just zeros
285 stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
286 stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts
288 double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap
290 smooth_plot: true, // enable smooth plot, where possible
292 charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
294 color_fill_opacity_line: 1.0,
295 color_fill_opacity_area: 0.2,
296 color_fill_opacity_stacked: 0.8,
298 pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
299 pan_and_zoom_factor_multiplier_control: 2.0,
300 pan_and_zoom_factor_multiplier_shift: 3.0,
301 pan_and_zoom_factor_multiplier_alt: 4.0,
303 abort_ajax_on_scroll: false,
305 setOptionCallback: function() { ; }
313 chart_data_url: false,
314 chart_errors: false, // FIXME
322 NETDATA.statistics = {
325 refreshes_active_max: 0
329 // ----------------------------------------------------------------------------------------------------------------
330 // local storage options
332 NETDATA.localStorage = {
335 callback: {} // only used for resetting back to defaults
338 NETDATA.localStorageGet = function(key, def, callback) {
341 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
342 NETDATA.localStorage.default[key.toString()] = def;
343 NETDATA.localStorage.callback[key.toString()] = callback;
346 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
348 // console.log('localStorage: loading "' + key.toString() + '"');
349 ret = localStorage.getItem(key.toString());
350 // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
351 if(ret === null || ret === 'undefined') {
352 // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
353 localStorage.setItem(key.toString(), JSON.stringify(def));
357 // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
358 ret = JSON.parse(ret);
359 // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
363 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
368 if(typeof ret === 'undefined' || ret === 'undefined') {
369 console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
373 NETDATA.localStorage.current[key.toString()] = ret;
377 NETDATA.localStorageSet = function(key, value, callback) {
378 if(typeof value === 'undefined' || value === 'undefined') {
379 console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
382 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
383 NETDATA.localStorage.default[key.toString()] = value;
384 NETDATA.localStorage.current[key.toString()] = value;
385 NETDATA.localStorage.callback[key.toString()] = callback;
388 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
389 // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
391 localStorage.setItem(key.toString(), JSON.stringify(value));
394 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
398 NETDATA.localStorage.current[key.toString()] = value;
402 NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
404 if(typeof obj[i] === 'object') {
405 //console.log('object ' + prefix + '.' + i.toString());
406 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
410 obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
414 NETDATA.setOption = function(key, value) {
415 if(key.toString() === 'setOptionCallback') {
416 if(typeof NETDATA.options.current.setOptionCallback === 'function') {
417 NETDATA.options.current[key.toString()] = value;
418 NETDATA.options.current.setOptionCallback();
421 else if(NETDATA.options.current[key.toString()] !== value) {
422 var name = 'options.' + key.toString();
424 if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
425 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
427 //console.log(NETDATA.localStorage);
428 //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
429 //console.log(NETDATA.options);
430 NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
432 if(typeof NETDATA.options.current.setOptionCallback === 'function')
433 NETDATA.options.current.setOptionCallback();
439 NETDATA.getOption = function(key) {
440 return NETDATA.options.current[key.toString()];
443 // read settings from local storage
444 NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
446 // always start with this option enabled.
447 NETDATA.setOption('stop_updates_when_focus_is_lost', true);
449 NETDATA.resetOptions = function() {
450 for(var i in NETDATA.localStorage.default) {
451 var a = i.split('.');
453 if(a[0] === 'options') {
454 if(a[1] === 'setOptionCallback') continue;
455 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
456 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
458 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
460 else if(a[0] === 'chart_heights') {
461 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
462 NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
468 // ----------------------------------------------------------------------------------------------------------------
470 if(NETDATA.options.debug.main_loop === true)
471 console.log('welcome to NETDATA');
473 NETDATA.onresize = function() {
474 NETDATA.options.last_resized = new Date().getTime();
478 NETDATA.onscroll = function() {
479 // console.log('onscroll');
481 NETDATA.options.last_page_scroll = new Date().getTime();
482 NETDATA.options.auto_refresher_stop_until = 0;
484 if(NETDATA.options.targets === null) return;
486 // when the user scrolls he sees that we have
487 // hidden all the not-visible charts
488 // using this little function we try to switch
489 // the charts back to visible quickly
490 var targets = NETDATA.options.targets;
491 var len = targets.length;
492 if(NETDATA.options.abort_ajax_on_scroll === true) {
494 if (targets[len]._updating === true) {
495 if (typeof targets[len].xhr !== 'undefined') {
496 targets[len].xhr.abort();
497 targets[len].running = false;
498 targets[len]._updating = false;
500 targets[len].isVisible();
506 targets[len].isVisible();
510 window.onresize = NETDATA.onresize;
511 window.onscroll = NETDATA.onscroll;
513 // ----------------------------------------------------------------------------------------------------------------
516 NETDATA.errorCodes = {
517 100: { message: "Cannot load chart library", alert: true },
518 101: { message: "Cannot load jQuery", alert: true },
519 402: { message: "Chart library not found", alert: false },
520 403: { message: "Chart library not enabled/is failed", alert: false },
521 404: { message: "Chart not found", alert: false },
522 405: { message: "Cannot download charts index from server", alert: true },
523 406: { message: "Invalid charts index downloaded from server", alert: true },
524 407: { message: "Cannot HELLO netdata server", alert: false },
525 408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
526 409: { message: "Cannot ACCESS netdata registry", alert: false },
527 410: { message: "Netdata registry ACCESS failed", alert: false },
528 411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
529 412: { message: "Netdata registry DELETE failed", alert: false },
530 413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
531 414: { message: "Netdata registry SWITCH failed", alert: false },
532 415: { message: "Netdata alarms download failed", alert: false },
533 416: { message: "Netdata alarms log download failed", alert: false }
535 NETDATA.errorLast = {
541 NETDATA.error = function(code, msg) {
542 NETDATA.errorLast.code = code;
543 NETDATA.errorLast.message = msg;
544 NETDATA.errorLast.datetime = new Date().getTime();
546 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
549 if(typeof netdataErrorCallback === 'function') {
550 ret = netdataErrorCallback('system', code, msg);
553 if(ret && NETDATA.errorCodes[code].alert)
554 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
557 NETDATA.errorReset = function() {
558 NETDATA.errorLast.code = 0;
559 NETDATA.errorLast.message = "You are doing fine!";
560 NETDATA.errorLast.datetime = 0;
563 // ----------------------------------------------------------------------------------------------------------------
566 // When multiple charts need the same chart, we avoid downloading it
567 // multiple times (and having it in browser memory multiple time)
568 // by using this registry.
570 // Every time we download a chart definition, we save it here with .add()
571 // Then we try to get it back with .get(). If that fails, we download it.
573 NETDATA.chartRegistry = {
576 fixid: function(id) {
577 return id.replace(/:/g, "_").replace(/\//g, "_");
580 add: function(host, id, data) {
581 host = this.fixid(host);
584 if(typeof this.charts[host] === 'undefined')
585 this.charts[host] = {};
587 //console.log('added ' + host + '/' + id);
588 this.charts[host][id] = data;
591 get: function(host, id) {
592 host = this.fixid(host);
595 if(typeof this.charts[host] === 'undefined')
598 if(typeof this.charts[host][id] === 'undefined')
601 //console.log('cached ' + host + '/' + id);
602 return this.charts[host][id];
605 downloadAll: function(host, callback) {
606 while(host.slice(-1) === '/')
607 host = host.substring(0, host.length - 1);
612 url: host + '/api/v1/charts',
615 xhrFields: { withCredentials: true } // required for the cookie
617 .done(function(data) {
619 var h = NETDATA.chartRegistry.fixid(host);
620 self.charts[h] = data.charts;
622 else NETDATA.error(406, host + '/api/v1/charts');
624 if(typeof callback === 'function')
628 NETDATA.error(405, host + '/api/v1/charts');
630 if(typeof callback === 'function')
636 // ----------------------------------------------------------------------------------------------------------------
637 // Global Pan and Zoom on charts
639 // Using this structure are synchronize all the charts, so that
640 // when you pan or zoom one, all others are automatically refreshed
641 // to the same timespan.
643 NETDATA.globalPanAndZoom = {
644 seq: 0, // timestamp ms
645 // every time a chart is panned or zoomed
646 // we set the timestamp here
647 // then we use it as a sequence number
648 // to find if other charts are syncronized
651 master: null, // the master chart (state), to which all others
654 force_before_ms: null, // the timespan to sync all other charts
655 force_after_ms: null,
660 setMaster: function(state, after, before) {
661 if(NETDATA.options.current.sync_pan_and_zoom === false)
664 if(this.master !== null && this.master !== state)
665 this.master.resetChart(true, true);
667 var now = new Date().getTime();
670 this.force_after_ms = after;
671 this.force_before_ms = before;
672 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
674 if(typeof this.callback === 'function')
675 this.callback(true, after, before);
679 clearMaster: function() {
680 if(this.master !== null) {
681 var st = this.master;
688 this.force_after_ms = null;
689 this.force_before_ms = null;
690 NETDATA.options.auto_refresher_stop_until = 0;
692 if(typeof this.callback === 'function')
693 this.callback(false, 0, 0);
696 // is the given state the master of the global
697 // pan and zoom sync?
698 isMaster: function(state) {
699 if(this.master === state) return true;
703 // are we currently have a global pan and zoom sync?
704 isActive: function() {
705 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
709 // check if a chart, other than the master
710 // needs to be refreshed, due to the global pan and zoom
711 shouldBeAutoRefreshed: function(state) {
712 if(this.master === null || this.seq === 0)
715 //if(state.needsRecreation())
718 if(state.tm.pan_and_zoom_seq === this.seq)
725 // ----------------------------------------------------------------------------------------------------------------
726 // dimensions selection
729 // move color assignment to dimensions, here
731 dimensionStatus = function(parent, label, name_div, value_div, color) {
732 this.enabled = false;
733 this.parent = parent;
735 this.name_div = null;
736 this.value_div = null;
737 this.color = NETDATA.themes.current.foreground;
739 if(parent.selected_count > parent.unselected_count)
740 this.selected = true;
742 this.selected = false;
744 this.setOptions(name_div, value_div, color);
747 dimensionStatus.prototype.invalidate = function() {
748 this.name_div = null;
749 this.value_div = null;
750 this.enabled = false;
753 dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
756 if(this.name_div != name_div) {
757 this.name_div = name_div;
758 this.name_div.title = this.label;
759 this.name_div.style.color = this.color;
760 if(this.selected === false)
761 this.name_div.className = 'netdata-legend-name not-selected';
763 this.name_div.className = 'netdata-legend-name selected';
766 if(this.value_div != value_div) {
767 this.value_div = value_div;
768 this.value_div.title = this.label;
769 this.value_div.style.color = this.color;
770 if(this.selected === false)
771 this.value_div.className = 'netdata-legend-value not-selected';
773 this.value_div.className = 'netdata-legend-value selected';
780 dimensionStatus.prototype.setHandler = function() {
781 if(this.enabled === false) return;
785 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
786 this.name_div.onclick = this.value_div.onclick = function(e) {
788 if(ds.isSelected()) {
790 if(e.shiftKey === true || e.ctrlKey === true) {
791 // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
794 if(ds.parent.countSelected() === 0)
795 ds.parent.selectAll();
798 // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
799 if(ds.parent.countSelected() === 1) {
800 ds.parent.selectAll();
803 ds.parent.selectNone();
809 // this is not selected
810 if(e.shiftKey === true || e.ctrlKey === true) {
811 // control or shift key is pressed -> select this too
815 // no key is pressed -> select only this
816 ds.parent.selectNone();
821 ds.parent.state.redrawChart();
825 dimensionStatus.prototype.select = function() {
826 if(this.enabled === false) return;
828 this.name_div.className = 'netdata-legend-name selected';
829 this.value_div.className = 'netdata-legend-value selected';
830 this.selected = true;
833 dimensionStatus.prototype.unselect = function() {
834 if(this.enabled === false) return;
836 this.name_div.className = 'netdata-legend-name not-selected';
837 this.value_div.className = 'netdata-legend-value hidden';
838 this.selected = false;
841 dimensionStatus.prototype.isSelected = function() {
842 return(this.enabled === true && this.selected === true);
845 // ----------------------------------------------------------------------------------------------------------------
847 dimensionsVisibility = function(state) {
850 this.dimensions = {};
851 this.selected_count = 0;
852 this.unselected_count = 0;
855 dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
856 if(typeof this.dimensions[label] === 'undefined') {
858 this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
861 this.dimensions[label].setOptions(name_div, value_div, color);
863 return this.dimensions[label];
866 dimensionsVisibility.prototype.dimensionGet = function(label) {
867 return this.dimensions[label];
870 dimensionsVisibility.prototype.invalidateAll = function() {
871 for(var d in this.dimensions)
872 this.dimensions[d].invalidate();
875 dimensionsVisibility.prototype.selectAll = function() {
876 for(var d in this.dimensions)
877 this.dimensions[d].select();
880 dimensionsVisibility.prototype.countSelected = function() {
882 for(var d in this.dimensions)
883 if(this.dimensions[d].isSelected()) i++;
888 dimensionsVisibility.prototype.selectNone = function() {
889 for(var d in this.dimensions)
890 this.dimensions[d].unselect();
893 dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
894 var ret = new Array();
895 this.selected_count = 0;
896 this.unselected_count = 0;
898 for(var i = 0, len = array.length; i < len ; i++) {
899 var ds = this.dimensions[array[i]];
900 if(typeof ds === 'undefined') {
901 // console.log(array[i] + ' is not found');
906 if(ds.isSelected()) {
908 this.selected_count++;
912 this.unselected_count++;
916 if(this.selected_count === 0 && this.unselected_count !== 0) {
918 return this.selected2BooleanArray(array);
925 // ----------------------------------------------------------------------------------------------------------------
926 // global selection sync
928 NETDATA.globalSelectionSync = {
935 if(this.state !== null)
936 this.state.globalSelectionSyncStop();
940 if(this.state !== null) {
941 this.state.globalSelectionSyncDelay();
946 // ----------------------------------------------------------------------------------------------------------------
947 // Our state object, where all per-chart values are stored
949 chartState = function(element) {
950 var self = $(element);
951 this.element = element;
954 // all private functions should use 'that', instead of 'this'
958 * show an error instead of the chart
960 var error = function(msg) {
963 if(typeof netdataErrorCallback === 'function') {
964 ret = netdataErrorCallback('chart', that.id, msg);
968 that.element.innerHTML = that.id + ': ' + msg;
969 that.enabled = false;
970 that.current = that.pan;
974 // GUID - a unique identifier for the chart
975 this.uuid = NETDATA.guid();
977 // string - the name of chart
978 this.id = self.data('netdata');
980 // string - the key for localStorage settings
981 this.settings_id = self.data('id') || null;
983 // the user given dimensions of the element
984 this.width = self.data('width') || NETDATA.chartDefaults.width;
985 this.height = self.data('height') || NETDATA.chartDefaults.height;
987 if(this.settings_id !== null) {
988 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
989 // this is the callback that will be called
990 // if and when the user resets all localStorage variables
993 resizeChartToHeight(height);
997 // string - the netdata server URL, without any path
998 this.host = self.data('host') || NETDATA.chartDefaults.host;
1000 // make sure the host does not end with /
1001 // all netdata API requests use absolute paths
1002 while(this.host.slice(-1) === '/')
1003 this.host = this.host.substring(0, this.host.length - 1);
1005 // string - the grouping method requested by the user
1006 this.method = self.data('method') || NETDATA.chartDefaults.method;
1008 // the time-range requested by the user
1009 this.after = self.data('after') || NETDATA.chartDefaults.after;
1010 this.before = self.data('before') || NETDATA.chartDefaults.before;
1012 // the pixels per point requested by the user
1013 this.pixels_per_point = self.data('pixels-per-point') || 1;
1014 this.points = self.data('points') || null;
1016 // the dimensions requested by the user
1017 this.dimensions = self.data('dimensions') || null;
1019 // the chart library requested by the user
1020 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
1022 // object - the chart library used
1023 this.library = null;
1027 this.colors_assigned = {};
1028 this.colors_available = null;
1030 // the element already created by the user
1031 this.element_message = null;
1033 // the element with the chart
1034 this.element_chart = null;
1036 // the element with the legend of the chart (if created by us)
1037 this.element_legend = null;
1038 this.element_legend_childs = {
1048 this.chart_url = null; // string - the url to download chart info
1049 this.chart = null; // object - the chart as downloaded from the server
1051 this.title = self.data('title') || null; // the title of the chart
1052 this.units = self.data('units') || null; // the units of the chart dimensions
1053 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1055 this.running = false; // boolean - true when the chart is being refreshed now
1056 this.validated = false; // boolean - has the chart been validated?
1057 this.enabled = true; // boolean - is the chart enabled for refresh?
1058 this.paused = false; // boolean - is the chart paused for any reason?
1059 this.selected = false; // boolean - is the chart shown a selection?
1060 this.debug = false; // boolean - console.log() debug info about this chart
1062 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1063 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1064 this.requested_after = null; // milliseconds - the timestamp of the request after param
1065 this.requested_before = null; // milliseconds - the timestamp of the request before param
1066 this.requested_padding = null;
1067 this.view_after = 0;
1068 this.view_before = 0;
1073 force_update_at: 0, // the timestamp to force the update at
1074 force_before_ms: null,
1075 force_after_ms: null
1080 force_update_at: 0, // the timestamp to force the update at
1081 force_before_ms: null,
1082 force_after_ms: null
1087 force_update_at: 0, // the timestamp to force the update at
1088 force_before_ms: null,
1089 force_after_ms: null
1092 // this is a pointer to one of the sub-classes below
1094 this.current = this.auto;
1096 // check the requested library is available
1097 // we don't initialize it here - it will be initialized when
1098 // this chart will be first used
1099 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1100 NETDATA.error(402, that.library_name);
1101 error('chart library "' + that.library_name + '" is not found');
1104 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1105 NETDATA.error(403, that.library_name);
1106 error('chart library "' + that.library_name + '" is not enabled');
1110 that.library = NETDATA.chartLibraries[that.library_name];
1112 // milliseconds - the time the last refresh took
1113 this.refresh_dt_ms = 0;
1115 // if we need to report the rendering speed
1116 // find the element that needs to be updated
1117 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1119 if(refresh_dt_element_name !== null)
1120 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1122 this.refresh_dt_element = null;
1124 this.dimensions_visibility = new dimensionsVisibility(this);
1126 this._updating = false;
1128 // ============================================================================================================
1129 // PRIVATE FUNCTIONS
1131 var createDOM = function() {
1132 if(that.enabled === false) return;
1134 if(that.element_message !== null) that.element_message.innerHTML = '';
1135 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1136 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1138 that.element.innerHTML = '';
1140 that.element_message = document.createElement('div');
1141 that.element_message.className = ' netdata-message hidden';
1142 that.element.appendChild(that.element_message);
1144 that.element_chart = document.createElement('div');
1145 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1146 that.element.appendChild(that.element_chart);
1148 if(that.hasLegend() === true) {
1149 that.element.className = "netdata-container-with-legend";
1150 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1152 that.element_legend = document.createElement('div');
1153 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1154 that.element.appendChild(that.element_legend);
1157 that.element.className = "netdata-container";
1158 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1160 that.element_legend = null;
1162 that.element_legend_childs.series = null;
1164 if(typeof(that.width) === 'string')
1165 $(that.element).css('width', that.width);
1166 else if(typeof(that.width) === 'number')
1167 $(that.element).css('width', that.width + 'px');
1169 if(typeof(that.library.aspect_ratio) === 'undefined') {
1170 if(typeof(that.height) === 'string')
1171 $(that.element).css('height', that.height);
1172 else if(typeof(that.height) === 'number')
1173 $(that.element).css('height', that.height + 'px');
1176 var w = that.element.offsetWidth;
1177 if(w === null || w === 0) {
1178 // the div is hidden
1179 // this will resize the chart when next viewed
1180 that.tm.last_resized = 0;
1183 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1186 if(NETDATA.chartDefaults.min_width !== null)
1187 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1189 that.tm.last_dom_created = new Date().getTime();
1195 * initialize state variables
1196 * destroy all (possibly) created state elements
1197 * create the basic DOM for a chart
1199 var init = function() {
1200 if(that.enabled === false) return;
1202 that.paused = false;
1203 that.selected = false;
1205 that.chart_created = false; // boolean - is the library.create() been called?
1206 that.updates_counter = 0; // numeric - the number of refreshes made so far
1207 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1208 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1211 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1212 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1213 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1215 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1216 last_updated: 0, // the timestamp the chart last updated with data
1217 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1219 // Used with NETDATA.globalPanAndZoom.seq
1220 last_visible_check: 0, // the time we last checked if it is visible
1221 last_resized: 0, // the time the chart was resized
1222 last_hidden: 0, // the time the chart was hidden
1223 last_unhidden: 0, // the time the chart was unhidden
1224 last_autorefreshed: 0 // the time the chart was last refreshed
1227 that.data = null; // the last data as downloaded from the netdata server
1228 that.data_url = 'invalid://'; // string - the last url used to update the chart
1229 that.data_points = 0; // number - the number of points returned from netdata
1230 that.data_after = 0; // milliseconds - the first timestamp of the data
1231 that.data_before = 0; // milliseconds - the last timestamp of the data
1232 that.data_update_every = 0; // milliseconds - the frequency to update the data
1234 that.tm.last_initialized = new Date().getTime();
1237 that.setMode('auto');
1240 var maxMessageFontSize = function() {
1241 // normally we want a font size, as tall as the element
1242 var h = that.element_message.clientHeight;
1244 // but give it some air, 20% let's say, or 5 pixels min
1245 var lost = Math.max(h * 0.2, 5);
1248 // center the text, vertically
1249 var paddingTop = (lost - 5) / 2;
1251 // but check the width too
1252 // it should fit 10 characters in it
1253 var w = that.element_message.clientWidth / 10;
1255 paddingTop += (h - w) / 2;
1259 // and don't make it too huge
1260 // 5% of the screen size is good
1261 if(h > screen.height / 20) {
1262 paddingTop += (h - (screen.height / 20)) / 2;
1263 h = screen.height / 20;
1267 that.element_message.style.fontSize = h.toString() + 'px';
1268 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1271 var showMessage = function(msg) {
1272 that.element_message.className = 'netdata-message';
1273 that.element_message.innerHTML = msg;
1274 that.element_message.style.fontSize = 'x-small';
1275 that.element_message.style.paddingTop = '0px';
1276 that.___messageHidden___ = undefined;
1279 var showMessageIcon = function(icon) {
1280 that.element_message.innerHTML = icon;
1281 that.element_message.className = 'netdata-message icon';
1282 maxMessageFontSize();
1283 that.___messageHidden___ = undefined;
1286 var hideMessage = function() {
1287 if(typeof that.___messageHidden___ === 'undefined') {
1288 that.___messageHidden___ = true;
1289 that.element_message.className = 'netdata-message hidden';
1293 var showRendering = function() {
1295 if(that.chart !== null) {
1296 if(that.chart.chart_type === 'line')
1297 icon = '<i class="fa fa-line-chart"></i>';
1299 icon = '<i class="fa fa-area-chart"></i>';
1302 icon = '<i class="fa fa-area-chart"></i>';
1304 showMessageIcon(icon + ' netdata');
1307 var showLoading = function() {
1308 if(that.chart_created === false) {
1309 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1315 var isHidden = function() {
1316 if(typeof that.___chartIsHidden___ !== 'undefined')
1322 // hide the chart, when it is not visible - called from isVisible()
1323 var hideChart = function() {
1324 // hide it, if it is not already hidden
1325 if(isHidden() === true) return;
1327 if(that.chart_created === true) {
1328 if(NETDATA.options.current.destroy_on_hide === true) {
1329 // we should destroy it
1334 that.element_chart.style.display = 'none';
1335 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1336 that.tm.last_hidden = new Date().getTime();
1339 // This works, but I not sure there are no corner cases somewhere
1340 // so it is commented - if the user has memory issues he can
1341 // set Destroy on Hide for all charts
1342 // that.data = null;
1346 that.___chartIsHidden___ = true;
1349 // unhide the chart, when it is visible - called from isVisible()
1350 var unhideChart = function() {
1351 if(isHidden() === false) return;
1353 that.___chartIsHidden___ = undefined;
1354 that.updates_since_last_unhide = 0;
1356 if(that.chart_created === false) {
1357 // we need to re-initialize it, to show our background
1358 // logo in bootstrap tabs, until the chart loads
1362 that.tm.last_unhidden = new Date().getTime();
1363 that.element_chart.style.display = '';
1364 if(that.element_legend !== null) that.element_legend.style.display = '';
1370 var canBeRendered = function() {
1371 if(isHidden() === true || that.isVisible(true) === false)
1377 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1378 var callChartLibraryUpdateSafely = function(data) {
1381 if(canBeRendered() === false)
1384 if(NETDATA.options.debug.chart_errors === true)
1385 status = that.library.update(that, data);
1388 status = that.library.update(that, data);
1395 if(status === false) {
1396 error('chart failed to be updated as ' + that.library_name);
1403 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1404 var callChartLibraryCreateSafely = function(data) {
1407 if(canBeRendered() === false)
1410 if(NETDATA.options.debug.chart_errors === true)
1411 status = that.library.create(that, data);
1414 status = that.library.create(that, data);
1421 if(status === false) {
1422 error('chart failed to be created as ' + that.library_name);
1426 that.chart_created = true;
1427 that.updates_since_last_creation = 0;
1431 // ----------------------------------------------------------------------------------------------------------------
1434 // resizeChart() - private
1435 // to be called just before the chart library to make sure that
1436 // a properly sized dom is available
1437 var resizeChart = function() {
1438 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1439 if(that.chart_created === false) return;
1441 if(that.needsRecreation()) {
1444 else if(typeof that.library.resize === 'function') {
1445 that.library.resize(that);
1447 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1448 $(that.element_legend_childs.nano).nanoScroller();
1450 maxMessageFontSize();
1453 that.tm.last_resized = new Date().getTime();
1457 // this is the actual chart resize algorithm
1459 // - resize the entire container
1460 // - update the internal states
1461 // - resize the chart as the div changes height
1462 // - update the scrollbar of the legend
1463 var resizeChartToHeight = function(h) {
1465 that.element.style.height = h;
1467 if(that.settings_id !== null)
1468 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1470 var now = new Date().getTime();
1471 NETDATA.options.last_page_scroll = now;
1472 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1475 that.tm.last_resized = 0;
1479 this.resizeHandler = function(e) {
1482 if(typeof this.event_resize === 'undefined'
1483 || this.event_resize.chart_original_w === 'undefined'
1484 || this.event_resize.chart_original_h === 'undefined')
1485 this.event_resize = {
1486 chart_original_w: this.element.clientWidth,
1487 chart_original_h: this.element.clientHeight,
1491 if(e.type === 'touchstart') {
1492 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1493 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1496 this.event_resize.mouse_start_x = e.clientX;
1497 this.event_resize.mouse_start_y = e.clientY;
1500 this.event_resize.chart_start_w = this.element.clientWidth;
1501 this.event_resize.chart_start_h = this.element.clientHeight;
1502 this.event_resize.chart_last_w = this.element.clientWidth;
1503 this.event_resize.chart_last_h = this.element.clientHeight;
1505 var now = new Date().getTime();
1506 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1507 // double click / double tap event
1509 // the optimal height of the chart
1510 // showing the entire legend
1511 var optimal = this.event_resize.chart_last_h
1512 + this.element_legend_childs.content.scrollHeight
1513 - this.element_legend_childs.content.clientHeight;
1515 // if we are not optimal, be optimal
1516 if(this.event_resize.chart_last_h != optimal)
1517 resizeChartToHeight(optimal.toString() + 'px');
1519 // else if we do not have the original height
1520 // reset to the original height
1521 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1522 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1525 this.event_resize.last = now;
1527 // process movement event
1528 document.onmousemove =
1529 document.ontouchmove =
1530 this.element_legend_childs.resize_handler.onmousemove =
1531 this.element_legend_childs.resize_handler.ontouchmove =
1536 case 'mousemove': y = e.clientY; break;
1537 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1541 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1543 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1544 resizeChartToHeight(newH.toString() + 'px');
1545 that.event_resize.chart_last_h = newH;
1550 // process end event
1551 document.onmouseup =
1552 document.ontouchend =
1553 this.element_legend_childs.resize_handler.onmouseup =
1554 this.element_legend_childs.resize_handler.ontouchend =
1556 // remove all the hooks
1557 document.onmouseup =
1558 document.onmousemove =
1559 document.ontouchmove =
1560 document.ontouchend =
1561 that.element_legend_childs.resize_handler.onmousemove =
1562 that.element_legend_childs.resize_handler.ontouchmove =
1563 that.element_legend_childs.resize_handler.onmouseout =
1564 that.element_legend_childs.resize_handler.onmouseup =
1565 that.element_legend_childs.resize_handler.ontouchend =
1568 // allow auto-refreshes
1569 NETDATA.options.auto_refresher_stop_until = 0;
1575 var noDataToShow = function() {
1576 showMessageIcon('<i class="fa fa-warning"></i> empty');
1577 that.legendUpdateDOM();
1578 that.tm.last_autorefreshed = new Date().getTime();
1579 // that.data_update_every = 30 * 1000;
1580 //that.element_chart.style.display = 'none';
1581 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1582 //that.___chartIsHidden___ = true;
1585 // ============================================================================================================
1588 this.error = function(msg) {
1592 this.setMode = function(m) {
1593 if(this.current !== null && this.current.name === m) return;
1596 this.current = this.auto;
1597 else if(m === 'pan')
1598 this.current = this.pan;
1599 else if(m === 'zoom')
1600 this.current = this.zoom;
1602 this.current = this.auto;
1604 this.current.force_update_at = 0;
1605 this.current.force_before_ms = null;
1606 this.current.force_after_ms = null;
1608 this.tm.last_mode_switch = new Date().getTime();
1611 // ----------------------------------------------------------------------------------------------------------------
1612 // global selection sync
1614 // prevent to global selection sync for some time
1615 this.globalSelectionSyncDelay = function(ms) {
1616 if(NETDATA.options.current.sync_selection === false)
1619 if(typeof ms === 'number')
1620 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1622 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1625 // can we globally apply selection sync?
1626 this.globalSelectionSyncAbility = function() {
1627 if(NETDATA.options.current.sync_selection === false)
1630 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1636 this.globalSelectionSyncIsMaster = function() {
1637 if(NETDATA.globalSelectionSync.state === this)
1643 // this chart is the master of the global selection sync
1644 this.globalSelectionSyncBeMaster = function() {
1646 if(this.globalSelectionSyncIsMaster()) {
1647 if(this.debug === true)
1648 this.log('sync: I am the master already.');
1653 if(NETDATA.globalSelectionSync.state) {
1654 if(this.debug === true)
1655 this.log('sync: I am not the sync master. Resetting global sync.');
1657 this.globalSelectionSyncStop();
1660 // become the master
1661 if(this.debug === true)
1662 this.log('sync: becoming sync master.');
1664 this.selected = true;
1665 NETDATA.globalSelectionSync.state = this;
1667 // find the all slaves
1668 var targets = NETDATA.options.targets;
1669 var len = targets.length;
1674 if(this.debug === true)
1675 st.log('sync: not adding me to sync');
1677 else if(st.globalSelectionSyncIsEligible()) {
1678 if(this.debug === true)
1679 st.log('sync: adding to sync as slave');
1681 st.globalSelectionSyncBeSlave();
1685 // this.globalSelectionSyncDelay(100);
1688 // can the chart participate to the global selection sync as a slave?
1689 this.globalSelectionSyncIsEligible = function() {
1690 if(this.enabled === true
1691 && this.library !== null
1692 && typeof this.library.setSelection === 'function'
1693 && this.isVisible() === true
1694 && this.chart_created === true)
1700 // this chart becomes a slave of the global selection sync
1701 this.globalSelectionSyncBeSlave = function() {
1702 if(NETDATA.globalSelectionSync.state !== this)
1703 NETDATA.globalSelectionSync.slaves.push(this);
1706 // sync all the visible charts to the given time
1707 // this is to be called from the chart libraries
1708 this.globalSelectionSync = function(t) {
1709 if(this.globalSelectionSyncAbility() === false) {
1710 if(this.debug === true)
1711 this.log('sync: cannot sync (yet?).');
1716 if(this.globalSelectionSyncIsMaster() === false) {
1717 if(this.debug === true)
1718 this.log('sync: trying to be sync master.');
1720 this.globalSelectionSyncBeMaster();
1722 if(this.globalSelectionSyncAbility() === false) {
1723 if(this.debug === true)
1724 this.log('sync: cannot sync (yet?).');
1730 NETDATA.globalSelectionSync.last_t = t;
1731 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1736 // stop syncing all charts to the given time
1737 this.globalSelectionSyncStop = function() {
1738 if(NETDATA.globalSelectionSync.slaves.length) {
1739 if(this.debug === true)
1740 this.log('sync: cleaning up...');
1742 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1744 if(that.debug === true)
1745 st.log('sync: not adding me to sync stop');
1748 if(that.debug === true)
1749 st.log('sync: removed slave from sync');
1751 st.clearSelection();
1755 NETDATA.globalSelectionSync.last_t = 0;
1756 NETDATA.globalSelectionSync.slaves = [];
1757 NETDATA.globalSelectionSync.state = null;
1760 this.clearSelection();
1763 this.setSelection = function(t) {
1764 if(typeof this.library.setSelection === 'function') {
1765 if(this.library.setSelection(this, t) === true)
1766 this.selected = true;
1768 this.selected = false;
1770 else this.selected = true;
1772 if(this.selected === true && this.debug === true)
1773 this.log('selection set to ' + t.toString());
1775 return this.selected;
1778 this.clearSelection = function() {
1779 if(this.selected === true) {
1780 if(typeof this.library.clearSelection === 'function') {
1781 if(this.library.clearSelection(this) === true)
1782 this.selected = false;
1784 this.selected = true;
1786 else this.selected = false;
1788 if(this.selected === false && this.debug === true)
1789 this.log('selection cleared');
1794 return this.selected;
1797 // find if a timestamp (ms) is shown in the current chart
1798 this.timeIsVisible = function(t) {
1799 if(t >= this.data_after && t <= this.data_before)
1804 this.calculateRowForTime = function(t) {
1805 if(this.timeIsVisible(t) === false) return -1;
1806 return Math.floor((t - this.data_after) / this.data_update_every);
1809 // ----------------------------------------------------------------------------------------------------------------
1812 this.log = function(msg) {
1813 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1816 this.pauseChart = function() {
1817 if(this.paused === false) {
1818 if(this.debug === true)
1819 this.log('pauseChart()');
1825 this.unpauseChart = function() {
1826 if(this.paused === true) {
1827 if(this.debug === true)
1828 this.log('unpauseChart()');
1830 this.paused = false;
1834 this.resetChart = function(dont_clear_master, dont_update) {
1835 if(this.debug === true)
1836 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1838 if(typeof dont_clear_master === 'undefined')
1839 dont_clear_master = false;
1841 if(typeof dont_update === 'undefined')
1842 dont_update = false;
1844 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1845 if(this.debug === true)
1846 this.log('resetChart() diverting to clearMaster().');
1847 // this will call us back with master === true
1848 NETDATA.globalPanAndZoom.clearMaster();
1852 this.clearSelection();
1854 this.tm.pan_and_zoom_seq = 0;
1856 this.setMode('auto');
1857 this.current.force_update_at = 0;
1858 this.current.force_before_ms = null;
1859 this.current.force_after_ms = null;
1860 this.tm.last_autorefreshed = 0;
1861 this.paused = false;
1862 this.selected = false;
1863 this.enabled = true;
1864 // this.debug = false;
1866 // do not update the chart here
1867 // or the chart will flip-flop when it is the master
1868 // of a selection sync and another chart becomes
1871 if(dont_update !== true && this.isVisible() === true) {
1876 this.updateChartPanOrZoom = function(after, before) {
1877 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1880 if(this.debug === true)
1883 if(before < after) {
1884 if(this.debug === true)
1885 this.log(logme + 'flipped parameters, rejecting it.');
1890 if(typeof this.fixed_min_duration === 'undefined')
1891 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1893 var min_duration = this.fixed_min_duration;
1894 var current_duration = Math.round(this.view_before - this.view_after);
1896 // round the numbers
1897 after = Math.round(after);
1898 before = Math.round(before);
1900 // align them to update_every
1901 // stretching them further away
1902 after -= after % this.data_update_every;
1903 before += this.data_update_every - (before % this.data_update_every);
1905 // the final wanted duration
1906 var wanted_duration = before - after;
1908 // to allow panning, accept just a point below our minimum
1909 if((current_duration - this.data_update_every) < min_duration)
1910 min_duration = current_duration - this.data_update_every;
1912 // we do it, but we adjust to minimum size and return false
1913 // when the wanted size is below the current and the minimum
1915 if(wanted_duration < current_duration && wanted_duration < min_duration) {
1916 if(this.debug === true)
1917 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
1919 min_duration = this.fixed_min_duration;
1921 var dt = (min_duration - wanted_duration) / 2;
1924 wanted_duration = before - after;
1928 var tolerance = this.data_update_every * 2;
1929 var movement = Math.abs(before - this.view_before);
1931 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
1932 if(this.debug === true)
1933 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);
1937 if(this.current.name === 'auto') {
1938 this.log(logme + 'caller called me with mode: ' + this.current.name);
1939 this.setMode('pan');
1942 if(this.debug === true)
1943 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);
1945 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1946 this.current.force_after_ms = after;
1947 this.current.force_before_ms = before;
1948 NETDATA.globalPanAndZoom.setMaster(this, after, before);
1952 this.legendFormatValue = function(value) {
1953 if(value === null || value === 'undefined') return '-';
1954 if(typeof value !== 'number') return value;
1956 var abs = Math.abs(value);
1957 if(abs >= 1000) return (Math.round(value)).toLocaleString();
1958 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
1959 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
1960 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
1961 return (Math.round(value * 10000) / 10000).toLocaleString();
1964 this.legendSetLabelValue = function(label, value) {
1965 var series = this.element_legend_childs.series[label];
1966 if(typeof series === 'undefined') return;
1967 if(series.value === null && series.user === null) return;
1969 // if the value has not changed, skip DOM update
1970 //if(series.last === value) return;
1973 if(typeof value === 'number') {
1974 var v = Math.abs(value);
1975 s = r = this.legendFormatValue(value);
1977 if(typeof series.last === 'number') {
1978 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1979 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1980 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1982 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1987 series.last = value;
1990 if(series.value !== null) series.value.innerHTML = s;
1991 if(series.user !== null) series.user.innerHTML = r;
1994 this.legendSetDate = function(ms) {
1995 if(typeof ms !== 'number') {
1996 this.legendShowUndefined();
2000 var d = new Date(ms);
2002 if(this.element_legend_childs.title_date)
2003 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
2005 if(this.element_legend_childs.title_time)
2006 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
2008 if(this.element_legend_childs.title_units)
2009 this.element_legend_childs.title_units.innerHTML = this.units;
2012 this.legendShowUndefined = function() {
2013 if(this.element_legend_childs.title_date)
2014 this.element_legend_childs.title_date.innerHTML = ' ';
2016 if(this.element_legend_childs.title_time)
2017 this.element_legend_childs.title_time.innerHTML = this.chart.name;
2019 if(this.element_legend_childs.title_units)
2020 this.element_legend_childs.title_units.innerHTML = ' ';
2022 if(this.data && this.element_legend_childs.series !== null) {
2023 var labels = this.data.dimension_names;
2024 var i = labels.length;
2026 var label = labels[i];
2028 if(typeof label === 'undefined') continue;
2029 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2030 this.legendSetLabelValue(label, null);
2035 this.legendShowLatestValues = function() {
2036 if(this.chart === null) return;
2037 if(this.selected) return;
2039 if(this.data === null || this.element_legend_childs.series === null) {
2040 this.legendShowUndefined();
2044 var show_undefined = true;
2045 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
2046 show_undefined = false;
2048 if(show_undefined) {
2049 this.legendShowUndefined();
2053 this.legendSetDate(this.view_before);
2055 var labels = this.data.dimension_names;
2056 var i = labels.length;
2058 var label = labels[i];
2060 if(typeof label === 'undefined') continue;
2061 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2064 this.legendSetLabelValue(label, null);
2066 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2070 this.legendReset = function() {
2071 this.legendShowLatestValues();
2074 // this should be called just ONCE per dimension per chart
2075 this._chartDimensionColor = function(label) {
2076 if(this.colors === null) this.chartColors();
2078 if(typeof this.colors_assigned[label] === 'undefined') {
2079 if(this.colors_available.length === 0) {
2080 for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2081 this.colors_available.push(NETDATA.themes.current.colors[i]);
2084 this.colors_assigned[label] = this.colors_available.shift();
2086 if(this.debug === true)
2087 this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
2090 if(this.debug === true)
2091 this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
2094 this.colors.push(this.colors_assigned[label]);
2095 return this.colors_assigned[label];
2098 this.chartColors = function() {
2099 if(this.colors !== null) return this.colors;
2101 this.colors = new Array();
2102 this.colors_available = new Array();
2105 var c = $(this.element).data('colors');
2106 // this.log('read colors: ' + c);
2107 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2108 if(typeof c !== 'string') {
2109 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2116 for(i = 0, len = c.length; i < len ; i++) {
2118 this.colors_available.push(c[i]);
2119 // this.log('adding color: ' + c[i]);
2125 // push all the standard colors too
2126 for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2127 this.colors_available.push(NETDATA.themes.current.colors[i]);
2132 this.legendUpdateDOM = function() {
2135 // check that the legend DOM is up to date for the downloaded dimensions
2136 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2137 // this.log('the legend does not have any series - requesting legend update');
2140 else if(this.data === null) {
2141 // this.log('the chart does not have any data - requesting legend update');
2144 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2148 var labels = this.data.dimension_names.toString();
2149 if(labels !== this.element_legend_childs.series.labels_key) {
2152 if(this.debug === true)
2153 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2157 if(needed === false) {
2158 // make sure colors available
2161 // do we have to update the current values?
2162 // we do this, only when the visible chart is current
2163 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2164 if(this.debug === true)
2165 this.log('chart is in latest position... updating values on legend...');
2167 //var labels = this.data.dimension_names;
2168 //var i = labels.length;
2170 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2174 if(this.colors === null) {
2175 // this is the first time we update the chart
2176 // let's assign colors to all dimensions
2177 if(this.library.track_colors() === true)
2178 for(var dim in this.chart.dimensions)
2179 this._chartDimensionColor(this.chart.dimensions[dim].name);
2181 // we will re-generate the colors for the chart
2182 // based on the selected dimensions
2185 if(this.debug === true)
2186 this.log('updating Legend DOM');
2188 // mark all dimensions as invalid
2189 this.dimensions_visibility.invalidateAll();
2191 var genLabel = function(state, parent, dim, name, count) {
2192 var color = state._chartDimensionColor(name);
2194 var user_element = null;
2195 var user_id = self.data('show-value-of-' + dim + '-at') || null;
2196 if(user_id !== null) {
2197 user_element = document.getElementById(user_id) || null;
2198 if(user_element === null)
2199 state.log('Cannot find element with id: ' + user_id);
2202 state.element_legend_childs.series[name] = {
2203 name: document.createElement('span'),
2204 value: document.createElement('span'),
2209 var label = state.element_legend_childs.series[name];
2211 // create the dimension visibility tracking for this label
2212 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2214 var rgb = NETDATA.colorHex2Rgb(color);
2215 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2216 + state.chart.chart_type
2217 + '" style="background-color: '
2218 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2219 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2221 var text = document.createTextNode(' ' + name);
2222 label.name.appendChild(text);
2225 parent.appendChild(document.createElement('br'));
2227 parent.appendChild(label.name);
2228 parent.appendChild(label.value);
2231 var content = document.createElement('div');
2233 if(this.hasLegend()) {
2234 this.element_legend_childs = {
2236 resize_handler: document.createElement('div'),
2237 toolbox: document.createElement('div'),
2238 toolbox_left: document.createElement('div'),
2239 toolbox_right: document.createElement('div'),
2240 toolbox_reset: document.createElement('div'),
2241 toolbox_zoomin: document.createElement('div'),
2242 toolbox_zoomout: document.createElement('div'),
2243 toolbox_volume: document.createElement('div'),
2244 title_date: document.createElement('span'),
2245 title_time: document.createElement('span'),
2246 title_units: document.createElement('span'),
2247 nano: document.createElement('div'),
2249 paneClass: 'netdata-legend-series-pane',
2250 sliderClass: 'netdata-legend-series-slider',
2251 contentClass: 'netdata-legend-series-content',
2252 enabledClass: '__enabled',
2253 flashedClass: '__flashed',
2254 activeClass: '__active',
2256 alwaysVisible: true,
2262 this.element_legend.innerHTML = '';
2264 if(this.library.toolboxPanAndZoom !== null) {
2266 function get_pan_and_zoom_step(event) {
2268 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2270 else if (event.shiftKey)
2271 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2273 else if (event.altKey)
2274 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2277 return NETDATA.options.current.pan_and_zoom_factor;
2280 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2281 this.element.appendChild(this.element_legend_childs.toolbox);
2283 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2284 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2285 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2286 this.element_legend_childs.toolbox_left.onclick = function(e) {
2289 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2290 var before = that.view_before - step;
2291 var after = that.view_after - step;
2292 if(after >= that.netdata_first)
2293 that.library.toolboxPanAndZoom(that, after, before);
2295 if(NETDATA.options.current.show_help === true)
2296 $(this.element_legend_childs.toolbox_left).popover({
2301 placement: 'bottom',
2302 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2304 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>'
2308 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2309 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2310 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2311 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2313 NETDATA.resetAllCharts(that);
2315 if(NETDATA.options.current.show_help === true)
2316 $(this.element_legend_childs.toolbox_reset).popover({
2321 placement: 'bottom',
2322 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2323 title: 'Chart Reset',
2324 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>'
2327 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2328 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2329 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2330 this.element_legend_childs.toolbox_right.onclick = function(e) {
2332 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2333 var before = that.view_before + step;
2334 var after = that.view_after + step;
2335 if(before <= that.netdata_last)
2336 that.library.toolboxPanAndZoom(that, after, before);
2338 if(NETDATA.options.current.show_help === true)
2339 $(this.element_legend_childs.toolbox_right).popover({
2344 placement: 'bottom',
2345 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2347 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>'
2351 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2352 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2353 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2354 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2356 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2357 var before = that.view_before - dt;
2358 var after = that.view_after + dt;
2359 that.library.toolboxPanAndZoom(that, after, before);
2361 if(NETDATA.options.current.show_help === true)
2362 $(this.element_legend_childs.toolbox_zoomin).popover({
2367 placement: 'bottom',
2368 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2369 title: 'Chart Zoom In',
2370 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>'
2373 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2374 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2375 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2376 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2378 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);
2379 var before = that.view_before + dt;
2380 var after = that.view_after - dt;
2382 that.library.toolboxPanAndZoom(that, after, before);
2384 if(NETDATA.options.current.show_help === true)
2385 $(this.element_legend_childs.toolbox_zoomout).popover({
2390 placement: 'bottom',
2391 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2392 title: 'Chart Zoom Out',
2393 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>'
2396 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2397 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2398 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2399 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2400 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2401 //e.preventDefault();
2402 //alert('clicked toolbox_volume on ' + that.id);
2406 this.element_legend_childs.toolbox = null;
2407 this.element_legend_childs.toolbox_left = null;
2408 this.element_legend_childs.toolbox_reset = null;
2409 this.element_legend_childs.toolbox_right = null;
2410 this.element_legend_childs.toolbox_zoomin = null;
2411 this.element_legend_childs.toolbox_zoomout = null;
2412 this.element_legend_childs.toolbox_volume = null;
2415 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2416 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2417 this.element.appendChild(this.element_legend_childs.resize_handler);
2418 if(NETDATA.options.current.show_help === true)
2419 $(this.element_legend_childs.resize_handler).popover({
2424 placement: 'bottom',
2425 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2426 title: 'Chart Resize',
2427 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>'
2431 this.element_legend_childs.resize_handler.onmousedown =
2433 that.resizeHandler(e);
2437 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2438 that.resizeHandler(e);
2441 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2442 this.element_legend.appendChild(this.element_legend_childs.title_date);
2444 this.element_legend.appendChild(document.createElement('br'));
2446 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2447 this.element_legend.appendChild(this.element_legend_childs.title_time);
2449 this.element_legend.appendChild(document.createElement('br'));
2451 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2452 this.element_legend.appendChild(this.element_legend_childs.title_units);
2454 this.element_legend.appendChild(document.createElement('br'));
2456 this.element_legend_childs.nano.className = 'netdata-legend-series';
2457 this.element_legend.appendChild(this.element_legend_childs.nano);
2459 content.className = 'netdata-legend-series-content';
2460 this.element_legend_childs.nano.appendChild(content);
2462 if(NETDATA.options.current.show_help === true)
2463 $(content).popover({
2468 placement: 'bottom',
2469 title: 'Chart Legend',
2470 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2471 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>'
2475 this.element_legend_childs = {
2477 resize_handler: null,
2480 toolbox_right: null,
2481 toolbox_reset: null,
2482 toolbox_zoomin: null,
2483 toolbox_zoomout: null,
2484 toolbox_volume: null,
2495 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2496 if(this.debug === true)
2497 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2499 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2500 genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
2504 var tmp = new Array();
2505 for(var dim in this.chart.dimensions) {
2506 tmp.push(this.chart.dimensions[dim].name);
2507 genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
2509 this.element_legend_childs.series.labels_key = tmp.toString();
2510 if(this.debug === true)
2511 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2514 // create a hidden div to be used for hidding
2515 // the original legend of the chart library
2516 var el = document.createElement('div');
2517 if(this.element_legend !== null)
2518 this.element_legend.appendChild(el);
2519 el.style.display = 'none';
2521 this.element_legend_childs.hidden = document.createElement('div');
2522 el.appendChild(this.element_legend_childs.hidden);
2524 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2525 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2527 this.legendShowLatestValues();
2530 this.hasLegend = function() {
2531 if(typeof this.___hasLegendCache___ !== 'undefined')
2532 return this.___hasLegendCache___;
2535 if(this.library && this.library.legend(this) === 'right-side') {
2536 var legend = $(this.element).data('legend') || 'yes';
2537 if(legend === 'yes') leg = true;
2540 this.___hasLegendCache___ = leg;
2544 this.legendWidth = function() {
2545 return (this.hasLegend())?140:0;
2548 this.legendHeight = function() {
2549 return $(this.element).height();
2552 this.chartWidth = function() {
2553 return $(this.element).width() - this.legendWidth();
2556 this.chartHeight = function() {
2557 return $(this.element).height();
2560 this.chartPixelsPerPoint = function() {
2561 // force an options provided detail
2562 var px = this.pixels_per_point;
2564 if(this.library && px < this.library.pixels_per_point(this))
2565 px = this.library.pixels_per_point(this);
2567 if(px < NETDATA.options.current.pixels_per_point)
2568 px = NETDATA.options.current.pixels_per_point;
2573 this.needsRecreation = function() {
2575 this.chart_created === true
2577 && this.library.autoresize() === false
2578 && this.tm.last_resized < NETDATA.options.last_resized
2582 this.chartURL = function() {
2583 var after, before, points_multiplier = 1;
2584 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2585 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2587 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2588 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2589 this.view_after = after * 1000;
2590 this.view_before = before * 1000;
2592 this.requested_padding = null;
2593 points_multiplier = 1;
2595 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2596 this.tm.pan_and_zoom_seq = 0;
2598 before = Math.round(this.current.force_before_ms / 1000);
2599 after = Math.round(this.current.force_after_ms / 1000);
2600 this.view_after = after * 1000;
2601 this.view_before = before * 1000;
2603 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2604 this.requested_padding = Math.round((before - after) / 2);
2605 after -= this.requested_padding;
2606 before += this.requested_padding;
2607 this.requested_padding *= 1000;
2608 points_multiplier = 2;
2611 this.current.force_before_ms = null;
2612 this.current.force_after_ms = null;
2615 this.tm.pan_and_zoom_seq = 0;
2617 before = this.before;
2619 this.view_after = after * 1000;
2620 this.view_before = before * 1000;
2622 this.requested_padding = null;
2623 points_multiplier = 1;
2626 this.requested_after = after * 1000;
2627 this.requested_before = before * 1000;
2629 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2631 // build the data URL
2632 this.data_url = this.host + this.chart.data_url;
2633 this.data_url += "&format=" + this.library.format();
2634 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2635 this.data_url += "&group=" + this.method;
2636 this.data_url += "&options=" + this.library.options(this);
2637 this.data_url += '|jsonwrap';
2639 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2640 this.data_url += '|nonzero';
2642 if(this.append_options !== null)
2643 this.data_url += '|' + this.append_options.toString();
2646 this.data_url += "&after=" + after.toString();
2649 this.data_url += "&before=" + before.toString();
2652 this.data_url += "&dimensions=" + this.dimensions;
2654 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2655 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2658 this.redrawChart = function() {
2659 if(this.data !== null)
2660 this.updateChartWithData(this.data);
2663 this.updateChartWithData = function(data) {
2664 if(this.debug === true)
2665 this.log('updateChartWithData() called.');
2667 // this may force the chart to be re-created
2671 this.updates_counter++;
2672 this.updates_since_last_unhide++;
2673 this.updates_since_last_creation++;
2675 var started = new Date().getTime();
2677 // if the result is JSON, find the latest update-every
2678 this.data_update_every = data.view_update_every * 1000;
2679 this.data_after = data.after * 1000;
2680 this.data_before = data.before * 1000;
2681 this.netdata_first = data.first_entry * 1000;
2682 this.netdata_last = data.last_entry * 1000;
2683 this.data_points = data.points;
2686 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2687 if(this.view_after < this.data_after) {
2688 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2689 this.view_after = this.data_after;
2692 if(this.view_before > this.data_before) {
2693 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2694 this.view_before = this.data_before;
2698 this.view_after = this.data_after;
2699 this.view_before = this.data_before;
2702 if(this.debug === true) {
2703 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2705 if(this.current.force_after_ms)
2706 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2708 this.log('STATUS: forced : unset');
2710 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2711 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2712 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2713 this.log('STATUS: points : ' + (this.data_points).toString());
2716 if(this.data_points === 0) {
2721 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2722 if(this.debug === true)
2723 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2725 this.chart_created = false;
2728 // check and update the legend
2729 this.legendUpdateDOM();
2731 if(this.chart_created === true
2732 && typeof this.library.update === 'function') {
2734 if(this.debug === true)
2735 this.log('updating chart...');
2737 if(callChartLibraryUpdateSafely(data) === false)
2741 if(this.debug === true)
2742 this.log('creating chart...');
2744 if(callChartLibraryCreateSafely(data) === false)
2748 this.legendShowLatestValues();
2749 if(this.selected === true)
2750 NETDATA.globalSelectionSync.stop();
2752 // update the performance counters
2753 var now = new Date().getTime();
2754 this.tm.last_updated = now;
2756 // don't update last_autorefreshed if this chart is
2757 // forced to be updated with global PanAndZoom
2758 if(NETDATA.globalPanAndZoom.isActive())
2759 this.tm.last_autorefreshed = 0;
2761 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2762 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2764 this.tm.last_autorefreshed = now;
2767 this.refresh_dt_ms = now - started;
2768 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2770 if(this.refresh_dt_element !== null)
2771 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2774 this.updateChart = function(callback) {
2775 if(this.debug === true)
2776 this.log('updateChart() called.');
2778 if(this._updating === true) {
2779 if(this.debug === true)
2780 this.log('I am already updating...');
2782 if(typeof callback === 'function') callback();
2786 // due to late initialization of charts and libraries
2787 // we need to check this too
2788 if(this.enabled === false) {
2789 if(this.debug === true)
2790 this.log('I am not enabled');
2792 if(typeof callback === 'function') callback();
2796 if(canBeRendered() === false) {
2797 if(typeof callback === 'function') callback();
2801 if(this.chart === null) {
2802 this.getChart(function() { that.updateChart(callback); });
2806 if(this.library.initialized === false) {
2807 if(this.library.enabled === true) {
2808 this.library.initialize(function() { that.updateChart(callback); });
2812 error('chart library "' + this.library_name + '" is not available.');
2813 if(typeof callback === 'function') callback();
2818 this.clearSelection();
2821 if(this.debug === true)
2822 this.log('updating from ' + this.data_url);
2824 NETDATA.statistics.refreshes_total++;
2825 NETDATA.statistics.refreshes_active++;
2827 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2828 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2830 this._updating = true;
2832 this.xhr = $.ajax( {
2836 xhrFields: { withCredentials: true } // required for the cookie
2838 .done(function(data) {
2839 that.xhr = undefined;
2841 if(that.debug === true)
2842 that.log('data received. updating chart.');
2844 that.updateChartWithData(data);
2846 .fail(function(msg) {
2847 that.xhr = undefined;
2849 if(msg.statusText !== 'abort')
2850 error('data download failed for url: ' + that.data_url);
2852 .always(function() {
2853 that.xhr = undefined;
2855 NETDATA.statistics.refreshes_active--;
2856 that._updating = false;
2857 if(typeof callback === 'function') callback();
2863 this.isVisible = function(nocache) {
2864 if(typeof nocache === 'undefined')
2867 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2869 // caching - we do not evaluate the charts visibility
2870 // if the page has not been scrolled since the last check
2871 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2872 return this.___isVisible___;
2874 this.tm.last_visible_check = new Date().getTime();
2876 var wh = window.innerHeight;
2877 var x = this.element.getBoundingClientRect();
2881 if(x.width === 0 || x.height === 0) {
2883 this.___isVisible___ = false;
2884 return this.___isVisible___;
2887 if(x.top < 0 && -x.top > x.height) {
2888 // the chart is entirely above
2889 ret = -x.top - x.height;
2891 else if(x.top > wh) {
2892 // the chart is entirely below
2896 if(ret > tolerance) {
2897 // the chart is too far
2900 this.___isVisible___ = false;
2901 return this.___isVisible___;
2904 // the chart is inside or very close
2907 this.___isVisible___ = true;
2908 return this.___isVisible___;
2912 this.isAutoRefreshable = function() {
2913 return (this.current.autorefresh);
2916 this.canBeAutoRefreshed = function() {
2917 var now = new Date().getTime();
2919 if(this.running === true) {
2920 if(this.debug === true)
2921 this.log('I am already running');
2926 if(this.enabled === false) {
2927 if(this.debug === true)
2928 this.log('I am not enabled');
2933 if(this.library === null || this.library.enabled === false) {
2934 error('charting library "' + this.library_name + '" is not available');
2935 if(this.debug === true)
2936 this.log('My chart library ' + this.library_name + ' is not available');
2941 if(this.isVisible() === false) {
2942 if(NETDATA.options.debug.visibility === true || this.debug === true)
2943 this.log('I am not visible');
2948 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
2949 if(this.debug === true)
2950 this.log('timed force update detected - allowing this update');
2952 this.current.force_update_at = 0;
2956 if(this.isAutoRefreshable() === true) {
2957 // allow the first update, even if the page is not visible
2958 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
2959 if(NETDATA.options.debug.focus === true || this.debug === true)
2960 this.log('canBeAutoRefreshed(): page does not have focus');
2965 if(this.needsRecreation() === true) {
2966 if(this.debug === true)
2967 this.log('canBeAutoRefreshed(): needs re-creation.');
2972 // options valid only for autoRefresh()
2973 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
2974 if(NETDATA.globalPanAndZoom.isActive()) {
2975 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
2976 if(this.debug === true)
2977 this.log('canBeAutoRefreshed(): global panning: I need an update.');
2982 if(this.debug === true)
2983 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
2989 if(this.selected === true) {
2990 if(this.debug === true)
2991 this.log('canBeAutoRefreshed(): I have a selection in place.');
2996 if(this.paused === true) {
2997 if(this.debug === true)
2998 this.log('canBeAutoRefreshed(): I am paused.');
3003 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
3004 if(this.debug === true)
3005 this.log('canBeAutoRefreshed(): It is time to update me.');
3015 this.autoRefresh = function(callback) {
3016 if(this.canBeAutoRefreshed() === true && this.running === false) {
3019 state.running = true;
3020 state.updateChart(function() {
3021 state.running = false;
3023 if(typeof callback !== 'undefined')
3028 if(typeof callback !== 'undefined')
3033 this._defaultsFromDownloadedChart = function(chart) {
3035 this.chart_url = chart.url;
3036 this.data_update_every = chart.update_every * 1000;
3037 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
3038 this.tm.last_info_downloaded = new Date().getTime();
3040 if(this.title === null)
3041 this.title = chart.title;
3043 if(this.units === null)
3044 this.units = chart.units;
3047 // fetch the chart description from the netdata server
3048 this.getChart = function(callback) {
3049 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
3051 this._defaultsFromDownloadedChart(this.chart);
3052 if(typeof callback === 'function') callback();
3055 this.chart_url = "/api/v1/chart?chart=" + this.id;
3057 if(this.debug === true)
3058 this.log('downloading ' + this.chart_url);
3061 url: this.host + this.chart_url,
3064 xhrFields: { withCredentials: true } // required for the cookie
3066 .done(function(chart) {
3067 chart.url = that.chart_url;
3068 that._defaultsFromDownloadedChart(chart);
3069 NETDATA.chartRegistry.add(that.host, that.id, chart);
3072 NETDATA.error(404, that.chart_url);
3073 error('chart not found on url "' + that.chart_url + '"');
3075 .always(function() {
3076 if(typeof callback === 'function') callback();
3081 // ============================================================================================================
3087 NETDATA.resetAllCharts = function(state) {
3088 // first clear the global selection sync
3089 // to make sure no chart is in selected state
3090 state.globalSelectionSyncStop();
3092 // there are 2 possibilities here
3093 // a. state is the global Pan and Zoom master
3094 // b. state is not the global Pan and Zoom master
3096 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3099 // clear the global Pan and Zoom
3100 // this will also refresh the master
3101 // and unblock any charts currently mirroring the master
3102 NETDATA.globalPanAndZoom.clearMaster();
3104 // if we were not the master, reset our status too
3105 // this is required because most probably the mouse
3106 // is over this chart, blocking it from auto-refreshing
3107 if(master === false && (state.paused === true || state.selected === true))
3111 // get or create a chart state, given a DOM element
3112 NETDATA.chartState = function(element) {
3113 var state = $(element).data('netdata-state-object') || null;
3114 if(state === null) {
3115 state = new chartState(element);
3116 $(element).data('netdata-state-object', state);
3121 // ----------------------------------------------------------------------------------------------------------------
3122 // Library functions
3124 // Load a script without jquery
3125 // This is used to load jquery - after it is loaded, we use jquery
3126 NETDATA._loadjQuery = function(callback) {
3127 if(typeof jQuery === 'undefined') {
3128 if(NETDATA.options.debug.main_loop === true)
3129 console.log('loading ' + NETDATA.jQuery);
3131 var script = document.createElement('script');
3132 script.type = 'text/javascript';
3133 script.async = true;
3134 script.src = NETDATA.jQuery;
3136 // script.onabort = onError;
3137 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3138 if(typeof callback === "function")
3139 script.onload = callback;
3141 var s = document.getElementsByTagName('script')[0];
3142 s.parentNode.insertBefore(script, s);
3144 else if(typeof callback === "function")
3148 NETDATA._loadCSS = function(filename) {
3149 // don't use jQuery here
3150 // styles are loaded before jQuery
3151 // to eliminate showing an unstyled page to the user
3153 var fileref = document.createElement("link");
3154 fileref.setAttribute("rel", "stylesheet");
3155 fileref.setAttribute("type", "text/css");
3156 fileref.setAttribute("href", filename);
3158 if (typeof fileref !== 'undefined')
3159 document.getElementsByTagName("head")[0].appendChild(fileref);
3162 NETDATA.colorHex2Rgb = function(hex) {
3163 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3164 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3165 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3166 return r + r + g + g + b + b;
3169 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3171 r: parseInt(result[1], 16),
3172 g: parseInt(result[2], 16),
3173 b: parseInt(result[3], 16)
3177 NETDATA.colorLuminance = function(hex, lum) {
3178 // validate hex string
3179 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3181 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3185 // convert to decimal and change luminosity
3186 var rgb = "#", c, i;
3187 for (i = 0; i < 3; i++) {
3188 c = parseInt(hex.substr(i*2,2), 16);
3189 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3190 rgb += ("00"+c).substr(c.length);
3196 NETDATA.guid = function() {
3198 return Math.floor((1 + Math.random()) * 0x10000)
3203 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3206 NETDATA.zeropad = function(x) {
3207 if(x > -10 && x < 10) return '0' + x.toString();
3208 else return x.toString();
3211 // user function to signal us the DOM has been
3213 NETDATA.updatedDom = function() {
3214 NETDATA.options.updated_dom = true;
3217 NETDATA.ready = function(callback) {
3218 NETDATA.options.pauseCallback = callback;
3221 NETDATA.pause = function(callback) {
3222 if(NETDATA.options.pause === true)
3225 NETDATA.options.pauseCallback = callback;
3228 NETDATA.unpause = function() {
3229 NETDATA.options.pauseCallback = null;
3230 NETDATA.options.updated_dom = true;
3231 NETDATA.options.pause = false;
3234 // ----------------------------------------------------------------------------------------------------------------
3236 // this is purely sequencial charts refresher
3237 // it is meant to be autonomous
3238 NETDATA.chartRefresherNoParallel = function(index) {
3239 if(NETDATA.options.debug.mail_loop === true)
3240 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3242 if(NETDATA.options.updated_dom === true) {
3243 // the dom has been updated
3244 // get the dom parts again
3245 NETDATA.parseDom(NETDATA.chartRefresher);
3248 if(index >= NETDATA.options.targets.length) {
3249 if(NETDATA.options.debug.main_loop === true)
3250 console.log('waiting to restart main loop...');
3252 NETDATA.options.auto_refresher_fast_weight = 0;
3254 setTimeout(function() {
3255 NETDATA.chartRefresher();
3256 }, NETDATA.options.current.idle_between_loops);
3259 var state = NETDATA.options.targets[index];
3261 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3262 if(NETDATA.options.debug.main_loop === true)
3263 console.log('fast rendering...');
3265 state.autoRefresh(function() {
3266 NETDATA.chartRefresherNoParallel(++index);
3270 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3271 NETDATA.options.auto_refresher_fast_weight = 0;
3273 setTimeout(function() {
3274 state.autoRefresh(function() {
3275 NETDATA.chartRefresherNoParallel(++index);
3277 }, NETDATA.options.current.idle_between_charts);
3282 // this is part of the parallel refresher
3283 // its cause is to refresh sequencially all the charts
3284 // that depend on chart library initialization
3285 // it will call the parallel refresher back
3286 // as soon as it sees a chart that its chart library
3288 NETDATA.chartRefresher_uninitialized = function() {
3289 if(NETDATA.options.updated_dom === true) {
3290 // the dom has been updated
3291 // get the dom parts again
3292 NETDATA.parseDom(NETDATA.chartRefresher);
3296 if(NETDATA.options.sequencial.length === 0)
3297 NETDATA.chartRefresher();
3299 var state = NETDATA.options.sequencial.pop();
3300 if(state.library.initialized === true)
3301 NETDATA.chartRefresher();
3303 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3307 NETDATA.chartRefresherWaitTime = function() {
3308 return NETDATA.options.current.idle_parallel_loops;
3311 // the default refresher
3312 // it will create 2 sets of charts:
3313 // - the ones that can be refreshed in parallel
3314 // - the ones that depend on something else
3315 // the first set will be executed in parallel
3316 // the second will be given to NETDATA.chartRefresher_uninitialized()
3317 NETDATA.chartRefresher = function() {
3318 // console.log('auto-refresher...');
3320 if(NETDATA.options.pause === true) {
3321 // console.log('auto-refresher is paused');
3322 setTimeout(NETDATA.chartRefresher,
3323 NETDATA.chartRefresherWaitTime());
3327 if(typeof NETDATA.options.pauseCallback === 'function') {
3328 // console.log('auto-refresher is calling pauseCallback');
3329 NETDATA.options.pause = true;
3330 NETDATA.options.pauseCallback();
3331 NETDATA.chartRefresher();
3335 if(NETDATA.options.current.parallel_refresher === false) {
3336 // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
3337 NETDATA.chartRefresherNoParallel(0);
3341 if(NETDATA.options.updated_dom === true) {
3342 // the dom has been updated
3343 // get the dom parts again
3344 // console.log('auto-refresher is calling parseDom()');
3345 NETDATA.parseDom(NETDATA.chartRefresher);
3349 var parallel = new Array();
3350 var targets = NETDATA.options.targets;
3351 var len = targets.length;
3354 state = targets[len];
3355 if(state.isVisible() === false || state.running === true)
3358 if(state.library.initialized === false) {
3359 if(state.library.enabled === true) {
3360 state.library.initialize(NETDATA.chartRefresher);
3364 state.error('chart library "' + state.library_name + '" is not enabled.');
3368 parallel.unshift(state);
3371 if(parallel.length > 0) {
3372 // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
3373 // this will execute the jobs in parallel
3374 $(parallel).each(function() {
3379 // console.log('auto-refresher nothing to do');
3382 // run the next refresh iteration
3383 setTimeout(NETDATA.chartRefresher,
3384 NETDATA.chartRefresherWaitTime());
3387 NETDATA.parseDom = function(callback) {
3388 NETDATA.options.last_page_scroll = new Date().getTime();
3389 NETDATA.options.updated_dom = false;
3391 var targets = $('div[data-netdata]'); //.filter(':visible');
3393 if(NETDATA.options.debug.main_loop === true)
3394 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3396 NETDATA.options.targets = new Array();
3397 var len = targets.length;
3399 // the initialization will take care of sizing
3400 // and the "loading..." message
3401 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3404 if(typeof callback === 'function') callback();
3407 // this is the main function - where everything starts
3408 NETDATA.start = function() {
3409 // this should be called only once
3411 NETDATA.options.page_is_visible = true;
3413 $(window).blur(function() {
3414 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3415 NETDATA.options.page_is_visible = false;
3416 if(NETDATA.options.debug.focus === true)
3417 console.log('Lost Focus!');
3421 $(window).focus(function() {
3422 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3423 NETDATA.options.page_is_visible = true;
3424 if(NETDATA.options.debug.focus === true)
3425 console.log('Focus restored!');
3429 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3430 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3431 NETDATA.options.page_is_visible = false;
3432 if(NETDATA.options.debug.focus === true)
3433 console.log('Document has no focus!');
3437 // bootstrap tab switching
3438 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3440 // bootstrap modal switching
3441 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3442 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3444 // bootstrap collapse switching
3445 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3446 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3448 NETDATA.parseDom(NETDATA.chartRefresher);
3450 // Alarms initialization
3451 if(netdataShowAlarms === true)
3452 setTimeout(NETDATA.alarms.init, 1000);
3454 // Registry initialization
3455 setTimeout(NETDATA.registry.init, 1500);
3458 // ----------------------------------------------------------------------------------------------------------------
3461 NETDATA.peityInitialize = function(callback) {
3462 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3464 url: NETDATA.peity_js,
3467 xhrFields: { withCredentials: true } // required for the cookie
3470 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3473 NETDATA.chartLibraries.peity.enabled = false;
3474 NETDATA.error(100, NETDATA.peity_js);
3476 .always(function() {
3477 if(typeof callback === "function")
3482 NETDATA.chartLibraries.peity.enabled = false;
3483 if(typeof callback === "function")
3488 NETDATA.peityChartUpdate = function(state, data) {
3489 state.peity_instance.innerHTML = data.result;
3491 if(state.peity_options.stroke !== state.chartColors()[0]) {
3492 state.peity_options.stroke = state.chartColors()[0];
3493 if(state.chart.chart_type === 'line')
3494 state.peity_options.fill = NETDATA.themes.current.background;
3496 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3499 $(state.peity_instance).peity('line', state.peity_options);
3503 NETDATA.peityChartCreate = function(state, data) {
3504 state.peity_instance = document.createElement('div');
3505 state.element_chart.appendChild(state.peity_instance);
3507 var self = $(state.element);
3508 state.peity_options = {
3509 stroke: NETDATA.themes.current.foreground,
3510 strokeWidth: self.data('peity-strokewidth') || 1,
3511 width: state.chartWidth(),
3512 height: state.chartHeight(),
3513 fill: NETDATA.themes.current.foreground
3516 NETDATA.peityChartUpdate(state, data);
3520 // ----------------------------------------------------------------------------------------------------------------
3523 NETDATA.sparklineInitialize = function(callback) {
3524 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3526 url: NETDATA.sparkline_js,
3529 xhrFields: { withCredentials: true } // required for the cookie
3532 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3535 NETDATA.chartLibraries.sparkline.enabled = false;
3536 NETDATA.error(100, NETDATA.sparkline_js);
3538 .always(function() {
3539 if(typeof callback === "function")
3544 NETDATA.chartLibraries.sparkline.enabled = false;
3545 if(typeof callback === "function")
3550 NETDATA.sparklineChartUpdate = function(state, data) {
3551 state.sparkline_options.width = state.chartWidth();
3552 state.sparkline_options.height = state.chartHeight();
3554 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3558 NETDATA.sparklineChartCreate = function(state, data) {
3559 var self = $(state.element);
3560 var type = self.data('sparkline-type') || 'line';
3561 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3562 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3563 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3564 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3565 var composite = self.data('sparkline-composite') || undefined;
3566 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3567 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3568 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3569 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3570 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3571 var spotColor = self.data('sparkline-spotcolor') || undefined;
3572 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3573 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3574 var spotRadius = self.data('sparkline-spotradius') || undefined;
3575 var valueSpots = self.data('sparkline-valuespots') || undefined;
3576 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3577 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3578 var lineWidth = self.data('sparkline-linewidth') || undefined;
3579 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3580 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3581 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3582 var xvalues = self.data('sparkline-xvalues') || undefined;
3583 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3584 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3585 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3586 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3587 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3588 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3589 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3590 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3591 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3592 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3593 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3594 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3595 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3596 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3597 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3598 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3599 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3600 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3601 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3602 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3603 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3604 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3606 if(spotColor === 'disable') spotColor='';
3607 if(minSpotColor === 'disable') minSpotColor='';
3608 if(maxSpotColor === 'disable') maxSpotColor='';
3610 state.sparkline_options = {
3612 lineColor: lineColor,
3613 fillColor: fillColor,
3614 chartRangeMin: chartRangeMin,
3615 chartRangeMax: chartRangeMax,
3616 composite: composite,
3617 enableTagOptions: enableTagOptions,
3618 tagOptionPrefix: tagOptionPrefix,
3619 tagValuesAttribute: tagValuesAttribute,
3620 disableHiddenCheck: disableHiddenCheck,
3621 defaultPixelsPerValue: defaultPixelsPerValue,
3622 spotColor: spotColor,
3623 minSpotColor: minSpotColor,
3624 maxSpotColor: maxSpotColor,
3625 spotRadius: spotRadius,
3626 valueSpots: valueSpots,
3627 highlightSpotColor: highlightSpotColor,
3628 highlightLineColor: highlightLineColor,
3629 lineWidth: lineWidth,
3630 normalRangeMin: normalRangeMin,
3631 normalRangeMax: normalRangeMax,
3632 drawNormalOnTop: drawNormalOnTop,
3634 chartRangeClip: chartRangeClip,
3635 chartRangeMinX: chartRangeMinX,
3636 chartRangeMaxX: chartRangeMaxX,
3637 disableInteraction: disableInteraction,
3638 disableTooltips: disableTooltips,
3639 disableHighlight: disableHighlight,
3640 highlightLighten: highlightLighten,
3641 highlightColor: highlightColor,
3642 tooltipContainer: tooltipContainer,
3643 tooltipClassname: tooltipClassname,
3644 tooltipChartTitle: state.title,
3645 tooltipFormat: tooltipFormat,
3646 tooltipPrefix: tooltipPrefix,
3647 tooltipSuffix: tooltipSuffix,
3648 tooltipSkipNull: tooltipSkipNull,
3649 tooltipValueLookups: tooltipValueLookups,
3650 tooltipFormatFieldlist: tooltipFormatFieldlist,
3651 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3652 numberFormatter: numberFormatter,
3653 numberDigitGroupSep: numberDigitGroupSep,
3654 numberDecimalMark: numberDecimalMark,
3655 numberDigitGroupCount: numberDigitGroupCount,
3656 animatedZooms: animatedZooms,
3657 width: state.chartWidth(),
3658 height: state.chartHeight()
3661 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3665 // ----------------------------------------------------------------------------------------------------------------
3672 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3673 if(after < state.netdata_first)
3674 after = state.netdata_first;
3676 if(before > state.netdata_last)
3677 before = state.netdata_last;
3679 state.setMode('zoom');
3680 state.globalSelectionSyncStop();
3681 state.globalSelectionSyncDelay();
3682 state.dygraph_user_action = true;
3683 state.dygraph_force_zoom = true;
3684 state.updateChartPanOrZoom(after, before);
3685 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3688 NETDATA.dygraphSetSelection = function(state, t) {
3689 if(typeof state.dygraph_instance !== 'undefined') {
3690 var r = state.calculateRowForTime(t);
3692 state.dygraph_instance.setSelection(r);
3694 state.dygraph_instance.clearSelection();
3695 state.legendShowUndefined();
3702 NETDATA.dygraphClearSelection = function(state, t) {
3703 if(typeof state.dygraph_instance !== 'undefined') {
3704 state.dygraph_instance.clearSelection();
3709 NETDATA.dygraphSmoothInitialize = function(callback) {
3711 url: NETDATA.dygraph_smooth_js,
3714 xhrFields: { withCredentials: true } // required for the cookie
3717 NETDATA.dygraph.smooth = true;
3718 smoothPlotter.smoothing = 0.3;
3721 NETDATA.dygraph.smooth = false;
3723 .always(function() {
3724 if(typeof callback === "function")
3729 NETDATA.dygraphInitialize = function(callback) {
3730 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3732 url: NETDATA.dygraph_js,
3735 xhrFields: { withCredentials: true } // required for the cookie
3738 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3741 NETDATA.chartLibraries.dygraph.enabled = false;
3742 NETDATA.error(100, NETDATA.dygraph_js);
3744 .always(function() {
3745 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3746 NETDATA.dygraphSmoothInitialize(callback);
3747 else if(typeof callback === "function")
3752 NETDATA.chartLibraries.dygraph.enabled = false;
3753 if(typeof callback === "function")
3758 NETDATA.dygraphChartUpdate = function(state, data) {
3759 var dygraph = state.dygraph_instance;
3761 if(typeof dygraph === 'undefined')
3762 return NETDATA.dygraphChartCreate(state, data);
3764 // when the chart is not visible, and hidden
3765 // if there is a window resize, dygraph detects
3766 // its element size as 0x0.
3767 // this will make it re-appear properly
3769 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3773 file: data.result.data,
3774 colors: state.chartColors(),
3775 labels: data.result.labels,
3776 labelsDivWidth: state.chartWidth() - 70,
3777 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3780 if(state.dygraph_force_zoom === true) {
3781 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3782 state.log('dygraphChartUpdate() forced zoom update');
3784 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3785 options.valueRange = state.dygraph_options.valueRange;
3786 options.isZoomedIgnoreProgrammaticZoom = true;
3787 state.dygraph_force_zoom = false;
3789 else if(state.current.name !== 'auto') {
3790 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3791 state.log('dygraphChartUpdate() loose update');
3793 options.valueRange = state.dygraph_options.valueRange;
3796 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3797 state.log('dygraphChartUpdate() strict update');
3799 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3800 options.valueRange = state.dygraph_options.valueRange;
3801 options.isZoomedIgnoreProgrammaticZoom = true;
3804 if(state.dygraph_smooth_eligible === true) {
3805 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3806 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3807 NETDATA.dygraphChartCreate(state, data);
3812 dygraph.updateOptions(options);
3814 state.dygraph_last_rendered = new Date().getTime();
3818 NETDATA.dygraphChartCreate = function(state, data) {
3819 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3820 state.log('dygraphChartCreate()');
3822 var self = $(state.element);
3824 var chart_type = state.chart.chart_type;
3825 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3826 chart_type = self.data('dygraph-type') || chart_type;
3828 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3829 smooth = self.data('dygraph-smooth') || smooth;
3831 if(NETDATA.dygraph.smooth === false)
3834 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3835 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3837 state.dygraph_options = {
3838 colors: self.data('dygraph-colors') || state.chartColors(),
3840 // leave a few pixels empty on the right of the chart
3841 rightGap: self.data('dygraph-rightgap') || 5,
3842 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3843 showRoller: self.data('dygraph-showroller') || false,
3845 title: self.data('dygraph-title') || state.title,
3846 titleHeight: self.data('dygraph-titleheight') || 19,
3848 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3849 labels: data.result.labels,
3850 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3851 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3852 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3853 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3854 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3857 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3858 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3860 includeZero: self.data('dygraph-includezero') || false,
3861 xRangePad: self.data('dygraph-xrangepad') || 0,
3862 yRangePad: self.data('dygraph-yrangepad') || 1,
3864 valueRange: self.data('dygraph-valuerange') || null,
3866 ylabel: state.units,
3867 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3869 // the function to plot the chart
3872 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3873 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3874 strokePattern: self.data('dygraph-strokepattern') || undefined,
3876 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3877 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3878 drawPoints: self.data('dygraph-drawpoints') || false,
3880 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3881 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3883 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3884 pointSize: self.data('dygraph-pointsize') || 1,
3886 // enabling this makes the chart with little square lines
3887 stepPlot: self.data('dygraph-stepplot') || false,
3889 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3890 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3891 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3893 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3894 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3895 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
3896 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
3898 drawAxis: self.data('dygraph-drawaxis') || true,
3899 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
3900 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
3901 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
3903 drawGrid: self.data('dygraph-drawgrid') || true,
3904 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
3905 drawYGrid: self.data('dygraph-drawygrid') || undefined,
3906 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
3907 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
3908 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
3910 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
3911 sigFigs: self.data('dygraph-sigfigs') || null,
3912 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
3913 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
3915 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
3916 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
3917 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
3919 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
3920 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
3924 ticker: Dygraph.dateTicker,
3925 axisLabelFormatter: function (d, gran) {
3926 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3928 valueFormatter: function (ms) {
3929 var d = new Date(ms);
3930 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
3931 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3936 valueFormatter: function (x) {
3937 // we format legends with the state object
3938 // no need to do anything here
3939 // return (Math.round(x*100) / 100).toLocaleString();
3940 // return state.legendFormatValue(x);
3945 legendFormatter: function(data) {
3946 var elements = state.element_legend_childs;
3948 // if the hidden div is not there
3949 // we are not managing the legend
3950 if(elements.hidden === null) return;
3952 if (typeof data.x !== 'undefined') {
3953 state.legendSetDate(data.x);
3954 var i = data.series.length;
3956 var series = data.series[i];
3957 if(!series.isVisible) continue;
3958 state.legendSetLabelValue(series.label, series.y);
3964 drawCallback: function(dygraph, is_initial) {
3965 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
3966 state.dygraph_user_action = false;
3968 var x_range = dygraph.xAxisRange();
3969 var after = Math.round(x_range[0]);
3970 var before = Math.round(x_range[1]);
3972 if(NETDATA.options.debug.dygraph === true)
3973 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
3975 if(before <= state.netdata_last && after >= state.netdata_first)
3976 state.updateChartPanOrZoom(after, before);
3979 zoomCallback: function(minDate, maxDate, yRanges) {
3980 if(NETDATA.options.debug.dygraph === true)
3981 state.log('dygraphZoomCallback()');
3983 state.globalSelectionSyncStop();
3984 state.globalSelectionSyncDelay();
3985 state.setMode('zoom');
3987 // refresh it to the greatest possible zoom level
3988 state.dygraph_user_action = true;
3989 state.dygraph_force_zoom = true;
3990 state.updateChartPanOrZoom(minDate, maxDate);
3992 highlightCallback: function(event, x, points, row, seriesName) {
3993 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3994 state.log('dygraphHighlightCallback()');
3998 // there is a bug in dygraph when the chart is zoomed enough
3999 // the time it thinks is selected is wrong
4000 // here we calculate the time t based on the row number selected
4002 var t = state.data_after + row * state.data_update_every;
4003 // 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);
4005 state.globalSelectionSync(x);
4007 // fix legend zIndex using the internal structures of dygraph legend module
4008 // this works, but it is a hack!
4009 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
4011 unhighlightCallback: function(event) {
4012 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4013 state.log('dygraphUnhighlightCallback()');
4015 state.unpauseChart();
4016 state.globalSelectionSyncStop();
4018 interactionModel : {
4019 mousedown: function(event, dygraph, context) {
4020 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4021 state.log('interactionModel.mousedown()');
4023 state.dygraph_user_action = true;
4024 state.globalSelectionSyncStop();
4026 if(NETDATA.options.debug.dygraph === true)
4027 state.log('dygraphMouseDown()');
4029 // Right-click should not initiate a zoom.
4030 if(event.button && event.button === 2) return;
4032 context.initializeMouseDown(event, dygraph, context);
4034 if(event.button && event.button === 1) {
4035 if (event.altKey || event.shiftKey) {
4036 state.setMode('pan');
4037 state.globalSelectionSyncDelay();
4038 Dygraph.startPan(event, dygraph, context);
4041 state.setMode('zoom');
4042 state.globalSelectionSyncDelay();
4043 Dygraph.startZoom(event, dygraph, context);
4047 if (event.altKey || event.shiftKey) {
4048 state.setMode('zoom');
4049 state.globalSelectionSyncDelay();
4050 Dygraph.startZoom(event, dygraph, context);
4053 state.setMode('pan');
4054 state.globalSelectionSyncDelay();
4055 Dygraph.startPan(event, dygraph, context);
4059 mousemove: function(event, dygraph, context) {
4060 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4061 state.log('interactionModel.mousemove()');
4063 if(context.isPanning) {
4064 state.dygraph_user_action = true;
4065 state.globalSelectionSyncStop();
4066 state.globalSelectionSyncDelay();
4067 state.setMode('pan');
4068 Dygraph.movePan(event, dygraph, context);
4070 else if(context.isZooming) {
4071 state.dygraph_user_action = true;
4072 state.globalSelectionSyncStop();
4073 state.globalSelectionSyncDelay();
4074 state.setMode('zoom');
4075 Dygraph.moveZoom(event, dygraph, context);
4078 mouseup: function(event, dygraph, context) {
4079 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4080 state.log('interactionModel.mouseup()');
4082 if (context.isPanning) {
4083 state.dygraph_user_action = true;
4084 state.globalSelectionSyncDelay();
4085 Dygraph.endPan(event, dygraph, context);
4087 else if (context.isZooming) {
4088 state.dygraph_user_action = true;
4089 state.globalSelectionSyncDelay();
4090 Dygraph.endZoom(event, dygraph, context);
4093 click: function(event, dygraph, context) {
4094 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4095 state.log('interactionModel.click()');
4097 event.preventDefault();
4099 dblclick: function(event, dygraph, context) {
4100 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4101 state.log('interactionModel.dblclick()');
4102 NETDATA.resetAllCharts(state);
4104 mousewheel: function(event, dygraph, context) {
4105 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4106 state.log('interactionModel.mousewheel()');
4108 // Take the offset of a mouse event on the dygraph canvas and
4109 // convert it to a pair of percentages from the bottom left.
4110 // (Not top left, bottom is where the lower value is.)
4111 function offsetToPercentage(g, offsetX, offsetY) {
4112 // This is calculating the pixel offset of the leftmost date.
4113 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4114 var yar0 = g.yAxisRange(0);
4116 // This is calculating the pixel of the higest value. (Top pixel)
4117 var yOffset = g.toDomCoords(null, yar0[1])[1];
4119 // x y w and h are relative to the corner of the drawing area,
4120 // so that the upper corner of the drawing area is (0, 0).
4121 var x = offsetX - xOffset;
4122 var y = offsetY - yOffset;
4124 // This is computing the rightmost pixel, effectively defining the
4126 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4128 // This is computing the lowest pixel, effectively defining the height.
4129 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4131 // Percentage from the left.
4132 var xPct = w === 0 ? 0 : (x / w);
4133 // Percentage from the top.
4134 var yPct = h === 0 ? 0 : (y / h);
4136 // The (1-) part below changes it from "% distance down from the top"
4137 // to "% distance up from the bottom".
4138 return [xPct, (1-yPct)];
4141 // Adjusts [x, y] toward each other by zoomInPercentage%
4142 // Split it so the left/bottom axis gets xBias/yBias of that change and
4143 // tight/top gets (1-xBias)/(1-yBias) of that change.
4145 // If a bias is missing it splits it down the middle.
4146 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4147 xBias = xBias || 0.5;
4148 yBias = yBias || 0.5;
4150 function adjustAxis(axis, zoomInPercentage, bias) {
4151 var delta = axis[1] - axis[0];
4152 var increment = delta * zoomInPercentage;
4153 var foo = [increment * bias, increment * (1-bias)];
4155 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4158 var yAxes = g.yAxisRanges();
4160 for (var i = 0; i < yAxes.length; i++) {
4161 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4164 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4167 if(event.altKey || event.shiftKey) {
4168 state.dygraph_user_action = true;
4170 state.globalSelectionSyncStop();
4171 state.globalSelectionSyncDelay();
4173 // http://dygraphs.com/gallery/interaction-api.js
4174 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4175 var percentage = normal / 50;
4177 if (!(event.offsetX && event.offsetY)){
4178 event.offsetX = event.layerX - event.target.offsetLeft;
4179 event.offsetY = event.layerY - event.target.offsetTop;
4182 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4183 var xPct = percentages[0];
4184 var yPct = percentages[1];
4186 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4188 var after = new_x_range[0];
4189 var before = new_x_range[1];
4191 var first = state.netdata_first + state.data_update_every;
4192 var last = state.netdata_last + state.data_update_every;
4195 after -= (before - last);
4202 state.setMode('zoom');
4203 if(state.updateChartPanOrZoom(after, before) === true)
4204 dygraph.updateOptions({ dateWindow: [ after, before ] });
4206 event.preventDefault();
4209 touchstart: function(event, dygraph, context) {
4210 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4211 state.log('interactionModel.touchstart()');
4213 state.dygraph_user_action = true;
4214 state.setMode('zoom');
4217 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4219 // we overwrite the touch directions at the end, to overwrite
4220 // the internal default of dygraphs
4221 context.touchDirections = { x: true, y: false };
4223 state.dygraph_last_touch_start = new Date().getTime();
4224 state.dygraph_last_touch_move = 0;
4226 if(typeof event.touches[0].pageX === 'number')
4227 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4229 state.dygraph_last_touch_page_x = 0;
4231 touchmove: function(event, dygraph, context) {
4232 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4233 state.log('interactionModel.touchmove()');
4235 state.dygraph_user_action = true;
4236 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4238 state.dygraph_last_touch_move = new Date().getTime();
4240 touchend: function(event, dygraph, context) {
4241 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4242 state.log('interactionModel.touchend()');
4244 state.dygraph_user_action = true;
4245 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4247 // if it didn't move, it is a selection
4248 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4249 // internal api of dygraphs
4250 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4251 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4252 if(NETDATA.dygraphSetSelection(state, t) === true)
4253 state.globalSelectionSync(t);
4256 // if it was double tap within double click time, reset the charts
4257 var now = new Date().getTime();
4258 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4259 if(state.dygraph_last_touch_move === 0) {
4260 var dt = now - state.dygraph_last_touch_end;
4261 if(dt <= NETDATA.options.current.double_click_speed)
4262 NETDATA.resetAllCharts(state);
4266 // remember the timestamp of the last touch end
4267 state.dygraph_last_touch_end = now;
4272 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4273 state.dygraph_options.drawGrid = false;
4274 state.dygraph_options.drawAxis = false;
4275 state.dygraph_options.title = undefined;
4276 state.dygraph_options.units = undefined;
4277 state.dygraph_options.ylabel = undefined;
4278 state.dygraph_options.yLabelWidth = 0;
4279 state.dygraph_options.labelsDivWidth = 120;
4280 state.dygraph_options.labelsDivStyles.width = '120px';
4281 state.dygraph_options.labelsSeparateLines = true;
4282 state.dygraph_options.rightGap = 0;
4283 state.dygraph_options.yRangePad = 1;
4286 if(smooth === true) {
4287 state.dygraph_smooth_eligible = true;
4289 if(NETDATA.options.current.smooth_plot === true)
4290 state.dygraph_options.plotter = smoothPlotter;
4292 else state.dygraph_smooth_eligible = false;
4294 state.dygraph_instance = new Dygraph(state.element_chart,
4295 data.result.data, state.dygraph_options);
4297 state.dygraph_force_zoom = false;
4298 state.dygraph_user_action = false;
4299 state.dygraph_last_rendered = new Date().getTime();
4303 // ----------------------------------------------------------------------------------------------------------------
4306 NETDATA.morrisInitialize = function(callback) {
4307 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4309 // morris requires raphael
4310 if(!NETDATA.chartLibraries.raphael.initialized) {
4311 if(NETDATA.chartLibraries.raphael.enabled) {
4312 NETDATA.raphaelInitialize(function() {
4313 NETDATA.morrisInitialize(callback);
4317 NETDATA.chartLibraries.morris.enabled = false;
4318 if(typeof callback === "function")
4323 NETDATA._loadCSS(NETDATA.morris_css);
4326 url: NETDATA.morris_js,
4329 xhrFields: { withCredentials: true } // required for the cookie
4332 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4335 NETDATA.chartLibraries.morris.enabled = false;
4336 NETDATA.error(100, NETDATA.morris_js);
4338 .always(function() {
4339 if(typeof callback === "function")
4345 NETDATA.chartLibraries.morris.enabled = false;
4346 if(typeof callback === "function")
4351 NETDATA.morrisChartUpdate = function(state, data) {
4352 state.morris_instance.setData(data.result.data);
4356 NETDATA.morrisChartCreate = function(state, data) {
4358 state.morris_options = {
4359 element: state.element_chart.id,
4360 data: data.result.data,
4362 ykeys: data.dimension_names,
4363 labels: data.dimension_names,
4369 continuousLine: false,
4370 behaveLikeLine: false
4373 if(state.chart.chart_type === 'line')
4374 state.morris_instance = new Morris.Line(state.morris_options);
4376 else if(state.chart.chart_type === 'area') {
4377 state.morris_options.behaveLikeLine = true;
4378 state.morris_instance = new Morris.Area(state.morris_options);
4381 state.morris_instance = new Morris.Area(state.morris_options);
4386 // ----------------------------------------------------------------------------------------------------------------
4389 NETDATA.raphaelInitialize = function(callback) {
4390 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4392 url: NETDATA.raphael_js,
4395 xhrFields: { withCredentials: true } // required for the cookie
4398 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4401 NETDATA.chartLibraries.raphael.enabled = false;
4402 NETDATA.error(100, NETDATA.raphael_js);
4404 .always(function() {
4405 if(typeof callback === "function")
4410 NETDATA.chartLibraries.raphael.enabled = false;
4411 if(typeof callback === "function")
4416 NETDATA.raphaelChartUpdate = function(state, data) {
4417 $(state.element_chart).raphael(data.result, {
4418 width: state.chartWidth(),
4419 height: state.chartHeight()
4425 NETDATA.raphaelChartCreate = function(state, data) {
4426 $(state.element_chart).raphael(data.result, {
4427 width: state.chartWidth(),
4428 height: state.chartHeight()
4434 // ----------------------------------------------------------------------------------------------------------------
4437 NETDATA.c3Initialize = function(callback) {
4438 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4441 if(!NETDATA.chartLibraries.d3.initialized) {
4442 if(NETDATA.chartLibraries.d3.enabled) {
4443 NETDATA.d3Initialize(function() {
4444 NETDATA.c3Initialize(callback);
4448 NETDATA.chartLibraries.c3.enabled = false;
4449 if(typeof callback === "function")
4454 NETDATA._loadCSS(NETDATA.c3_css);
4460 xhrFields: { withCredentials: true } // required for the cookie
4463 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4466 NETDATA.chartLibraries.c3.enabled = false;
4467 NETDATA.error(100, NETDATA.c3_js);
4469 .always(function() {
4470 if(typeof callback === "function")
4476 NETDATA.chartLibraries.c3.enabled = false;
4477 if(typeof callback === "function")
4482 NETDATA.c3ChartUpdate = function(state, data) {
4483 state.c3_instance.destroy();
4484 return NETDATA.c3ChartCreate(state, data);
4486 //state.c3_instance.load({
4487 // rows: data.result,
4494 NETDATA.c3ChartCreate = function(state, data) {
4496 state.element_chart.id = 'c3-' + state.uuid;
4497 // console.log('id = ' + state.element_chart.id);
4499 state.c3_instance = c3.generate({
4500 bindto: '#' + state.element_chart.id,
4502 width: state.chartWidth(),
4503 height: state.chartHeight()
4506 pattern: state.chartColors()
4511 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4517 format: function(x) {
4518 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4545 // console.log(state.c3_instance);
4550 // ----------------------------------------------------------------------------------------------------------------
4553 NETDATA.d3Initialize = function(callback) {
4554 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4559 xhrFields: { withCredentials: true } // required for the cookie
4562 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4565 NETDATA.chartLibraries.d3.enabled = false;
4566 NETDATA.error(100, NETDATA.d3_js);
4568 .always(function() {
4569 if(typeof callback === "function")
4574 NETDATA.chartLibraries.d3.enabled = false;
4575 if(typeof callback === "function")
4580 NETDATA.d3ChartUpdate = function(state, data) {
4584 NETDATA.d3ChartCreate = function(state, data) {
4588 // ----------------------------------------------------------------------------------------------------------------
4591 NETDATA.googleInitialize = function(callback) {
4592 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4594 url: NETDATA.google_js,
4597 xhrFields: { withCredentials: true } // required for the cookie
4600 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4601 google.load('visualization', '1.1', {
4602 'packages': ['corechart', 'controls'],
4603 'callback': callback
4607 NETDATA.chartLibraries.google.enabled = false;
4608 NETDATA.error(100, NETDATA.google_js);
4609 if(typeof callback === "function")
4614 NETDATA.chartLibraries.google.enabled = false;
4615 if(typeof callback === "function")
4620 NETDATA.googleChartUpdate = function(state, data) {
4621 var datatable = new google.visualization.DataTable(data.result);
4622 state.google_instance.draw(datatable, state.google_options);
4626 NETDATA.googleChartCreate = function(state, data) {
4627 var datatable = new google.visualization.DataTable(data.result);
4629 state.google_options = {
4630 colors: state.chartColors(),
4632 // do not set width, height - the chart resizes itself
4633 //width: state.chartWidth(),
4634 //height: state.chartHeight(),
4639 // title: "Time of Day",
4640 // format:'HH:mm:ss',
4641 viewWindowMode: 'maximized',
4653 viewWindowMode: 'pretty',
4668 focusTarget: 'category',
4675 titlePosition: 'out',
4686 curveType: 'function',
4691 switch(state.chart.chart_type) {
4693 state.google_options.vAxis.viewWindowMode = 'maximized';
4694 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4695 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4699 state.google_options.isStacked = true;
4700 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4701 state.google_options.vAxis.viewWindowMode = 'maximized';
4702 state.google_options.vAxis.minValue = null;
4703 state.google_options.vAxis.maxValue = null;
4704 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4709 state.google_options.lineWidth = 2;
4710 state.google_instance = new google.visualization.LineChart(state.element_chart);
4714 state.google_instance.draw(datatable, state.google_options);
4718 // ----------------------------------------------------------------------------------------------------------------
4720 NETDATA.percentFromValueMax = function(value, max) {
4721 if(value === null) value = 0;
4722 if(max < value) max = value;
4726 pcent = Math.round(value * 100 / max);
4727 if(pcent === 0 && value > 0) pcent = 1;
4733 // ----------------------------------------------------------------------------------------------------------------
4736 NETDATA.easypiechartInitialize = function(callback) {
4737 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4739 url: NETDATA.easypiechart_js,
4742 xhrFields: { withCredentials: true } // required for the cookie
4745 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4748 NETDATA.chartLibraries.easypiechart.enabled = false;
4749 NETDATA.error(100, NETDATA.easypiechart_js);
4751 .always(function() {
4752 if(typeof callback === "function")
4757 NETDATA.chartLibraries.easypiechart.enabled = false;
4758 if(typeof callback === "function")
4763 NETDATA.easypiechartClearSelection = function(state) {
4764 if(typeof state.easyPieChartEvent !== 'undefined') {
4765 if(state.easyPieChartEvent.timer !== null)
4766 clearTimeout(state.easyPieChartEvent.timer);
4768 state.easyPieChartEvent.timer = null;
4771 if(state.isAutoRefreshable() === true && state.data !== null) {
4772 NETDATA.easypiechartChartUpdate(state, state.data);
4775 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4776 state.easyPieChart_instance.update(0);
4778 state.easyPieChart_instance.enableAnimation();
4783 NETDATA.easypiechartSetSelection = function(state, t) {
4784 if(state.timeIsVisible(t) !== true)
4785 return NETDATA.easypiechartClearSelection(state);
4787 var slot = state.calculateRowForTime(t);
4788 if(slot < 0 || slot >= state.data.result.length)
4789 return NETDATA.easypiechartClearSelection(state);
4791 if(typeof state.easyPieChartEvent === 'undefined') {
4792 state.easyPieChartEvent = {
4799 var value = state.data.result[state.data.result.length - 1 - slot];
4800 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4801 var pcent = NETDATA.percentFromValueMax(value, max);
4803 state.easyPieChartEvent.value = value;
4804 state.easyPieChartEvent.pcent = pcent;
4805 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4807 if(state.easyPieChartEvent.timer === null) {
4808 state.easyPieChart_instance.disableAnimation();
4810 state.easyPieChartEvent.timer = setTimeout(function() {
4811 state.easyPieChartEvent.timer = null;
4812 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4813 }, NETDATA.options.current.charts_selection_animation_delay);
4819 NETDATA.easypiechartChartUpdate = function(state, data) {
4820 var value, max, pcent;
4822 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4828 value = data.result[0];
4829 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4830 pcent = NETDATA.percentFromValueMax(value, max);
4833 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4834 state.easyPieChart_instance.update(pcent);
4838 NETDATA.easypiechartChartCreate = function(state, data) {
4839 var self = $(state.element);
4840 var chart = $(state.element_chart);
4842 var value = data.result[0];
4843 var max = self.data('easypiechart-max-value') || null;
4844 var adjust = self.data('easypiechart-adjust') || null;
4848 state.easyPieChartMax = null;
4851 state.easyPieChartMax = max;
4853 var pcent = NETDATA.percentFromValueMax(value, max);
4855 chart.data('data-percent', pcent);
4859 case 'width': size = state.chartHeight(); break;
4860 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4861 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4863 default: size = state.chartWidth(); break;
4865 state.element.style.width = size + 'px';
4866 state.element.style.height = size + 'px';
4868 var stroke = Math.floor(size / 22);
4869 if(stroke < 3) stroke = 2;
4871 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4872 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4873 state.easyPieChartLabel = document.createElement('span');
4874 state.easyPieChartLabel.className = 'easyPieChartLabel';
4875 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4876 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4877 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4878 state.element_chart.appendChild(state.easyPieChartLabel);
4880 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4881 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4882 state.easyPieChartTitle = document.createElement('span');
4883 state.easyPieChartTitle.className = 'easyPieChartTitle';
4884 state.easyPieChartTitle.innerHTML = state.title;
4885 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4886 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4887 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4888 state.element_chart.appendChild(state.easyPieChartTitle);
4890 var unitfontsize = Math.round(titlefontsize * 0.9);
4891 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4892 state.easyPieChartUnits = document.createElement('span');
4893 state.easyPieChartUnits.className = 'easyPieChartUnits';
4894 state.easyPieChartUnits.innerHTML = state.units;
4895 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4896 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4897 state.element_chart.appendChild(state.easyPieChartUnits);
4899 chart.easyPieChart({
4900 barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
4901 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
4902 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
4903 scaleLength: self.data('easypiechart-scalelength') || 5,
4904 lineCap: self.data('easypiechart-linecap') || 'round',
4905 lineWidth: self.data('easypiechart-linewidth') || stroke,
4906 trackWidth: self.data('easypiechart-trackwidth') || undefined,
4907 size: self.data('easypiechart-size') || size,
4908 rotate: self.data('easypiechart-rotate') || 0,
4909 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
4910 easing: self.data('easypiechart-easing') || undefined
4913 // when we just re-create the chart
4914 // do not animate the first update
4916 if(typeof state.easyPieChart_instance !== 'undefined')
4919 state.easyPieChart_instance = chart.data('easyPieChart');
4920 if(animate === false) state.easyPieChart_instance.disableAnimation();
4921 state.easyPieChart_instance.update(pcent);
4922 if(animate === false) state.easyPieChart_instance.enableAnimation();
4926 // ----------------------------------------------------------------------------------------------------------------
4929 NETDATA.gaugeInitialize = function(callback) {
4930 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
4932 url: NETDATA.gauge_js,
4935 xhrFields: { withCredentials: true } // required for the cookie
4938 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
4941 NETDATA.chartLibraries.gauge.enabled = false;
4942 NETDATA.error(100, NETDATA.gauge_js);
4944 .always(function() {
4945 if(typeof callback === "function")
4950 NETDATA.chartLibraries.gauge.enabled = false;
4951 if(typeof callback === "function")
4956 NETDATA.gaugeAnimation = function(state, status) {
4959 if(typeof status === 'boolean' && status === false)
4961 else if(typeof status === 'number')
4964 state.gauge_instance.animationSpeed = speed;
4965 state.___gaugeOld__.speed = speed;
4968 NETDATA.gaugeSet = function(state, value, min, max) {
4969 if(typeof value !== 'number') value = 0;
4970 if(typeof min !== 'number') min = 0;
4971 if(typeof max !== 'number') max = 0;
4972 if(value > max) max = value;
4973 if(value < min) min = value;
4982 // gauge.js has an issue if the needle
4983 // is smaller than min or larger than max
4984 // when we set the new values
4985 // the needle will go crazy
4987 // to prevent it, we always feed it
4988 // with a percentage, so that the needle
4989 // is always between min and max
4990 var pcent = (value - min) * 100 / (max - min);
4992 // these should never happen
4993 if(pcent < 0) pcent = 0;
4994 if(pcent > 100) pcent = 100;
4996 state.gauge_instance.set(pcent);
4998 state.___gaugeOld__.value = value;
4999 state.___gaugeOld__.min = min;
5000 state.___gaugeOld__.max = max;
5003 NETDATA.gaugeSetLabels = function(state, value, min, max) {
5004 if(state.___gaugeOld__.valueLabel !== value) {
5005 state.___gaugeOld__.valueLabel = value;
5006 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
5008 if(state.___gaugeOld__.minLabel !== min) {
5009 state.___gaugeOld__.minLabel = min;
5010 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
5012 if(state.___gaugeOld__.maxLabel !== max) {
5013 state.___gaugeOld__.maxLabel = max;
5014 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
5018 NETDATA.gaugeClearSelection = function(state) {
5019 if(typeof state.gaugeEvent !== 'undefined') {
5020 if(state.gaugeEvent.timer !== null)
5021 clearTimeout(state.gaugeEvent.timer);
5023 state.gaugeEvent.timer = null;
5026 if(state.isAutoRefreshable() === true && state.data !== null) {
5027 NETDATA.gaugeChartUpdate(state, state.data);
5030 NETDATA.gaugeAnimation(state, false);
5031 NETDATA.gaugeSet(state, null, null, null);
5032 NETDATA.gaugeSetLabels(state, null, null, null);
5035 NETDATA.gaugeAnimation(state, true);
5039 NETDATA.gaugeSetSelection = function(state, t) {
5040 if(state.timeIsVisible(t) !== true)
5041 return NETDATA.gaugeClearSelection(state);
5043 var slot = state.calculateRowForTime(t);
5044 if(slot < 0 || slot >= state.data.result.length)
5045 return NETDATA.gaugeClearSelection(state);
5047 if(typeof state.gaugeEvent === 'undefined') {
5048 state.gaugeEvent = {
5056 var value = state.data.result[state.data.result.length - 1 - slot];
5057 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
5060 state.gaugeEvent.value = value;
5061 state.gaugeEvent.max = max;
5062 state.gaugeEvent.min = min;
5063 NETDATA.gaugeSetLabels(state, value, min, max);
5065 if(state.gaugeEvent.timer === null) {
5066 NETDATA.gaugeAnimation(state, false);
5068 state.gaugeEvent.timer = setTimeout(function() {
5069 state.gaugeEvent.timer = null;
5070 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
5071 }, NETDATA.options.current.charts_selection_animation_delay);
5077 NETDATA.gaugeChartUpdate = function(state, data) {
5078 var value, min, max;
5080 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
5084 NETDATA.gaugeSetLabels(state, null, null, null);
5087 value = data.result[0];
5089 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
5090 if(value > max) max = value;
5091 NETDATA.gaugeSetLabels(state, value, min, max);
5094 NETDATA.gaugeSet(state, value, min, max);
5098 NETDATA.gaugeChartCreate = function(state, data) {
5099 var self = $(state.element);
5100 // var chart = $(state.element_chart);
5102 var value = data.result[0];
5103 var max = self.data('gauge-max-value') || null;
5104 var adjust = self.data('gauge-adjust') || null;
5105 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5106 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5107 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5108 var stopColor = self.data('gauge-stop-color') || void 0;
5109 var generateGradient = self.data('gauge-generate-gradient') || false;
5113 state.gaugeMax = null;
5116 state.gaugeMax = max;
5118 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5120 // case 'width': width = height * ratio; break;
5122 // default: height = width / ratio; break;
5124 //state.element.style.width = width.toString() + 'px';
5125 //state.element.style.height = height.toString() + 'px';
5130 lines: 12, // The number of lines to draw
5131 angle: 0.15, // The length of each line
5132 lineWidth: 0.44, // 0.44 The line thickness
5134 length: 0.8, // 0.9 The radius of the inner circle
5135 strokeWidth: 0.035, // The rotation offset
5136 color: pointerColor // Fill color
5138 colorStart: startColor, // Colors
5139 colorStop: stopColor, // just experiment with them
5140 strokeColor: strokeColor, // to see which ones work best for you
5142 generateGradient: (generateGradient === true)?true:false,
5146 if (generateGradient.constructor === Array) {
5148 // data-gauge-generate-gradient="[0, 50, 100]"
5149 // data-gauge-gradient-percent-color-0="#FFFFFF"
5150 // data-gauge-gradient-percent-color-50="#999900"
5151 // data-gauge-gradient-percent-color-100="#000000"
5153 options.percentColors = new Array();
5154 var len = generateGradient.length;
5156 var pcent = generateGradient[len];
5157 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5158 if(color !== false) {
5159 var a = new Array();
5162 options.percentColors.unshift(a);
5165 if(options.percentColors.length === 0)
5166 delete options.percentColors;
5168 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5169 options.percentColors = [
5170 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5171 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5172 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5173 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5174 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5175 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5176 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5177 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5178 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5179 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5180 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5183 state.gauge_canvas = document.createElement('canvas');
5184 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5185 state.gauge_canvas.className = 'gaugeChart';
5186 state.gauge_canvas.width = width;
5187 state.gauge_canvas.height = height;
5188 state.element_chart.appendChild(state.gauge_canvas);
5190 var valuefontsize = Math.floor(height / 6);
5191 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5192 state.gaugeChartLabel = document.createElement('span');
5193 state.gaugeChartLabel.className = 'gaugeChartLabel';
5194 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5195 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5196 state.element_chart.appendChild(state.gaugeChartLabel);
5198 var titlefontsize = Math.round(valuefontsize / 2);
5200 state.gaugeChartTitle = document.createElement('span');
5201 state.gaugeChartTitle.className = 'gaugeChartTitle';
5202 state.gaugeChartTitle.innerHTML = state.title;
5203 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5204 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5205 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5206 state.element_chart.appendChild(state.gaugeChartTitle);
5208 var unitfontsize = Math.round(titlefontsize * 0.9);
5209 state.gaugeChartUnits = document.createElement('span');
5210 state.gaugeChartUnits.className = 'gaugeChartUnits';
5211 state.gaugeChartUnits.innerHTML = state.units;
5212 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5213 state.element_chart.appendChild(state.gaugeChartUnits);
5215 state.gaugeChartMin = document.createElement('span');
5216 state.gaugeChartMin.className = 'gaugeChartMin';
5217 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5218 state.element_chart.appendChild(state.gaugeChartMin);
5220 state.gaugeChartMax = document.createElement('span');
5221 state.gaugeChartMax.className = 'gaugeChartMax';
5222 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5223 state.element_chart.appendChild(state.gaugeChartMax);
5225 // when we just re-create the chart
5226 // do not animate the first update
5228 if(typeof state.gauge_instance !== 'undefined')
5231 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5233 state.___gaugeOld__ = {
5242 // we will always feed a percentage
5243 state.gauge_instance.minValue = 0;
5244 state.gauge_instance.maxValue = 100;
5246 NETDATA.gaugeAnimation(state, animate);
5247 NETDATA.gaugeSet(state, value, 0, max);
5248 NETDATA.gaugeSetLabels(state, value, 0, max);
5249 NETDATA.gaugeAnimation(state, true);
5253 // ----------------------------------------------------------------------------------------------------------------
5254 // Charts Libraries Registration
5256 NETDATA.chartLibraries = {
5258 initialize: NETDATA.dygraphInitialize,
5259 create: NETDATA.dygraphChartCreate,
5260 update: NETDATA.dygraphChartUpdate,
5261 resize: function(state) {
5262 if(typeof state.dygraph_instance.resize === 'function')
5263 state.dygraph_instance.resize();
5265 setSelection: NETDATA.dygraphSetSelection,
5266 clearSelection: NETDATA.dygraphClearSelection,
5267 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5270 format: function(state) { return 'json'; },
5271 options: function(state) { return 'ms|flip'; },
5272 legend: function(state) {
5273 if(this.isSparkline(state) === false)
5274 return 'right-side';
5278 autoresize: function(state) { return true; },
5279 max_updates_to_recreate: function(state) { return 5000; },
5280 track_colors: function(state) { return true; },
5281 pixels_per_point: function(state) {
5282 if(this.isSparkline(state) === false)
5288 isSparkline: function(state) {
5289 if(typeof state.dygraph_sparkline === 'undefined') {
5290 var t = $(state.element).data('dygraph-theme');
5291 if(t === 'sparkline')
5292 state.dygraph_sparkline = true;
5294 state.dygraph_sparkline = false;
5296 return state.dygraph_sparkline;
5300 initialize: NETDATA.sparklineInitialize,
5301 create: NETDATA.sparklineChartCreate,
5302 update: NETDATA.sparklineChartUpdate,
5304 setSelection: undefined, // function(state, t) { return true; },
5305 clearSelection: undefined, // function(state) { return true; },
5306 toolboxPanAndZoom: null,
5309 format: function(state) { return 'array'; },
5310 options: function(state) { return 'flip|abs'; },
5311 legend: function(state) { return null; },
5312 autoresize: function(state) { return false; },
5313 max_updates_to_recreate: function(state) { return 5000; },
5314 track_colors: function(state) { return false; },
5315 pixels_per_point: function(state) { return 3; }
5318 initialize: NETDATA.peityInitialize,
5319 create: NETDATA.peityChartCreate,
5320 update: NETDATA.peityChartUpdate,
5322 setSelection: undefined, // function(state, t) { return true; },
5323 clearSelection: undefined, // function(state) { return true; },
5324 toolboxPanAndZoom: null,
5327 format: function(state) { return 'ssvcomma'; },
5328 options: function(state) { return 'null2zero|flip|abs'; },
5329 legend: function(state) { return null; },
5330 autoresize: function(state) { return false; },
5331 max_updates_to_recreate: function(state) { return 5000; },
5332 track_colors: function(state) { return false; },
5333 pixels_per_point: function(state) { return 3; }
5336 initialize: NETDATA.morrisInitialize,
5337 create: NETDATA.morrisChartCreate,
5338 update: NETDATA.morrisChartUpdate,
5340 setSelection: undefined, // function(state, t) { return true; },
5341 clearSelection: undefined, // function(state) { return true; },
5342 toolboxPanAndZoom: null,
5345 format: function(state) { return 'json'; },
5346 options: function(state) { return 'objectrows|ms'; },
5347 legend: function(state) { return null; },
5348 autoresize: function(state) { return false; },
5349 max_updates_to_recreate: function(state) { return 50; },
5350 track_colors: function(state) { return false; },
5351 pixels_per_point: function(state) { return 15; }
5354 initialize: NETDATA.googleInitialize,
5355 create: NETDATA.googleChartCreate,
5356 update: NETDATA.googleChartUpdate,
5358 setSelection: undefined, //function(state, t) { return true; },
5359 clearSelection: undefined, //function(state) { return true; },
5360 toolboxPanAndZoom: null,
5363 format: function(state) { return 'datatable'; },
5364 options: function(state) { return ''; },
5365 legend: function(state) { return null; },
5366 autoresize: function(state) { return false; },
5367 max_updates_to_recreate: function(state) { return 300; },
5368 track_colors: function(state) { return false; },
5369 pixels_per_point: function(state) { return 4; }
5372 initialize: NETDATA.raphaelInitialize,
5373 create: NETDATA.raphaelChartCreate,
5374 update: NETDATA.raphaelChartUpdate,
5376 setSelection: undefined, // function(state, t) { return true; },
5377 clearSelection: undefined, // function(state) { return true; },
5378 toolboxPanAndZoom: null,
5381 format: function(state) { return 'json'; },
5382 options: function(state) { return ''; },
5383 legend: function(state) { return null; },
5384 autoresize: function(state) { return false; },
5385 max_updates_to_recreate: function(state) { return 5000; },
5386 track_colors: function(state) { return false; },
5387 pixels_per_point: function(state) { return 3; }
5390 initialize: NETDATA.c3Initialize,
5391 create: NETDATA.c3ChartCreate,
5392 update: NETDATA.c3ChartUpdate,
5394 setSelection: undefined, // function(state, t) { return true; },
5395 clearSelection: undefined, // function(state) { return true; },
5396 toolboxPanAndZoom: null,
5399 format: function(state) { return 'csvjsonarray'; },
5400 options: function(state) { return 'milliseconds'; },
5401 legend: function(state) { return null; },
5402 autoresize: function(state) { return false; },
5403 max_updates_to_recreate: function(state) { return 5000; },
5404 track_colors: function(state) { return false; },
5405 pixels_per_point: function(state) { return 15; }
5408 initialize: NETDATA.d3Initialize,
5409 create: NETDATA.d3ChartCreate,
5410 update: NETDATA.d3ChartUpdate,
5412 setSelection: undefined, // function(state, t) { return true; },
5413 clearSelection: undefined, // function(state) { return true; },
5414 toolboxPanAndZoom: null,
5417 format: function(state) { return 'json'; },
5418 options: function(state) { return ''; },
5419 legend: function(state) { return null; },
5420 autoresize: function(state) { return false; },
5421 max_updates_to_recreate: function(state) { return 5000; },
5422 track_colors: function(state) { return false; },
5423 pixels_per_point: function(state) { return 3; }
5426 initialize: NETDATA.easypiechartInitialize,
5427 create: NETDATA.easypiechartChartCreate,
5428 update: NETDATA.easypiechartChartUpdate,
5430 setSelection: NETDATA.easypiechartSetSelection,
5431 clearSelection: NETDATA.easypiechartClearSelection,
5432 toolboxPanAndZoom: null,
5435 format: function(state) { return 'array'; },
5436 options: function(state) { return 'absolute'; },
5437 legend: function(state) { return null; },
5438 autoresize: function(state) { return false; },
5439 max_updates_to_recreate: function(state) { return 5000; },
5440 track_colors: function(state) { return true; },
5441 pixels_per_point: function(state) { return 3; },
5445 initialize: NETDATA.gaugeInitialize,
5446 create: NETDATA.gaugeChartCreate,
5447 update: NETDATA.gaugeChartUpdate,
5449 setSelection: NETDATA.gaugeSetSelection,
5450 clearSelection: NETDATA.gaugeClearSelection,
5451 toolboxPanAndZoom: null,
5454 format: function(state) { return 'array'; },
5455 options: function(state) { return 'absolute'; },
5456 legend: function(state) { return null; },
5457 autoresize: function(state) { return false; },
5458 max_updates_to_recreate: function(state) { return 5000; },
5459 track_colors: function(state) { return true; },
5460 pixels_per_point: function(state) { return 3; },
5465 NETDATA.registerChartLibrary = function(library, url) {
5466 if(NETDATA.options.debug.libraries === true)
5467 console.log("registering chart library: " + library);
5469 NETDATA.chartLibraries[library].url = url;
5470 NETDATA.chartLibraries[library].initialized = true;
5471 NETDATA.chartLibraries[library].enabled = true;
5474 // ----------------------------------------------------------------------------------------------------------------
5475 // Load required JS libraries and CSS
5477 NETDATA.requiredJs = [
5479 url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
5480 isAlreadyLoaded: function() {
5481 // check if bootstrap is loaded
5482 if(typeof $().emulateTransitionEnd == 'function')
5485 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5493 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
5494 isAlreadyLoaded: function() { return false; }
5497 url: NETDATA.serverDefault + 'lib/bootstrap-toggle.min.js',
5498 isAlreadyLoaded: function() { return false; }
5502 NETDATA.requiredCSS = [
5504 url: NETDATA.themes.current.bootstrap_css,
5505 isAlreadyLoaded: function() {
5506 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5513 url: NETDATA.serverDefault + 'css/font-awesome.min.css',
5514 isAlreadyLoaded: function() { return false; }
5517 url: NETDATA.themes.current.dashboard_css,
5518 isAlreadyLoaded: function() { return false; }
5521 url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
5522 isAlreadyLoaded: function() { return false; }
5526 NETDATA.loadRequiredJs = function(index, callback) {
5527 if(index >= NETDATA.requiredJs.length) {
5528 if(typeof callback === 'function')
5533 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5534 NETDATA.loadRequiredJs(++index, callback);
5538 if(NETDATA.options.debug.main_loop === true)
5539 console.log('loading ' + NETDATA.requiredJs[index].url);
5542 url: NETDATA.requiredJs[index].url,
5545 xhrFields: { withCredentials: true } // required for the cookie
5547 .success(function() {
5548 if(NETDATA.options.debug.main_loop === true)
5549 console.log('loaded ' + NETDATA.requiredJs[index].url);
5551 NETDATA.loadRequiredJs(++index, callback);
5554 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5558 NETDATA.loadRequiredCSS = function(index) {
5559 if(index >= NETDATA.requiredCSS.length)
5562 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5563 NETDATA.loadRequiredCSS(++index);
5567 if(NETDATA.options.debug.main_loop === true)
5568 console.log('loading ' + NETDATA.requiredCSS[index].url);
5570 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5571 NETDATA.loadRequiredCSS(++index);
5575 // ----------------------------------------------------------------------------------------------------------------
5576 // Registry of netdata hosts
5583 get: function(what, callback) {
5585 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
5588 xhrFields: { withCredentials: true } // required for the cookie
5590 .done(function(data) {
5591 if(typeof callback === 'function')
5595 NETDATA.error(415, NETDATA.alarms.server);
5597 if(typeof callback === 'function')
5602 update_forever: function() {
5603 NETDATA.alarms.get('active', function(data) {
5605 if('Notification' in window && NETDATA.alarms.current != null) {
5606 if(Object.keys(NETDATA.alarms.current.alarms).length < Object.keys(data.alarms).length) {
5607 if (Notification.permission === 'granted') {
5608 new Notification('Netdata Alarm!', {body: 'Your Server needs attention!',
5609 icon: 'images/seo-performance-128.png'});
5614 NETDATA.alarms.current = data;
5616 if (typeof NETDATA.alarms.callback === 'function') {
5617 NETDATA.alarms.callback(data);
5621 setTimeout(NETDATA.alarms.update_forever, 10000);
5625 get_log: function(callback) {
5627 url: NETDATA.alarms.server + '/api/v1/alarm_log',
5630 xhrFields: { withCredentials: true } // required for the cookie
5632 .done(function(data) {
5633 if(typeof callback === 'function')
5637 NETDATA.error(416, NETDATA.alarms.server);
5639 if(typeof callback === 'function')
5645 var host = NETDATA.serverDefault;
5646 while(host.slice(-1) === '/')
5647 host = host.substring(0, host.length - 1);
5648 NETDATA.alarms.server = host;
5650 NETDATA.alarms.update_forever();
5651 if ('Notification' in window && Notification.permission === 'default') {
5652 Notification.requestPermission();
5657 // ----------------------------------------------------------------------------------------------------------------
5658 // Registry of netdata hosts
5660 NETDATA.registry = {
5661 server: null, // the netdata registry server
5662 person_guid: null, // the unique ID of this browser / user
5663 machine_guid: null, // the unique ID the netdata server that served dashboard.js
5664 hostname: null, // the hostname of the netdata server that served dashboard.js
5665 machines: null, // the user's other URLs
5666 machines_array: null, // the user's other URLs in an array
5669 parsePersonUrls: function(person_urls) {
5670 // console.log(person_urls);
5671 NETDATA.registry.person_urls = person_urls;
5674 NETDATA.registry.machines = {};
5675 NETDATA.registry.machines_array = new Array();
5677 var now = new Date().getTime();
5678 var apu = person_urls;
5681 if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
5682 // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
5688 accesses: apu[i][3],
5690 alternate_urls: new Array()
5692 obj.alternate_urls.push(apu[i][1]);
5694 NETDATA.registry.machines[apu[i][0]] = obj;
5695 NETDATA.registry.machines_array.push(obj);
5698 // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
5700 var pu = NETDATA.registry.machines[apu[i][0]];
5701 if(pu.last_t < apu[i][2]) {
5703 pu.last_t = apu[i][2];
5704 pu.name = apu[i][4];
5706 pu.accesses += apu[i][3];
5707 pu.alternate_urls.push(apu[i][1]);
5712 if(typeof netdataRegistryCallback === 'function')
5713 netdataRegistryCallback(NETDATA.registry.machines_array);
5717 if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry)
5720 NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
5722 NETDATA.registry.server = data.registry;
5723 NETDATA.registry.machine_guid = data.machine_guid;
5724 NETDATA.registry.hostname = data.hostname;
5726 NETDATA.registry.access(2, function (person_urls) {
5727 NETDATA.registry.parsePersonUrls(person_urls);
5734 hello: function(host, callback) {
5735 while(host.slice(-1) === '/')
5736 host = host.substring(0, host.length - 1);
5738 // send HELLO to a netdata server:
5739 // 1. verifies the server is reachable
5740 // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
5742 url: host + '/api/v1/registry?action=hello',
5745 xhrFields: { withCredentials: true } // required for the cookie
5747 .done(function(data) {
5748 if(typeof data.status !== 'string' || data.status !== 'ok') {
5749 NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
5753 if(typeof callback === 'function')
5757 NETDATA.error(407, host);
5759 if(typeof callback === 'function')
5764 access: function(max_redirects, callback) {
5765 // send ACCESS to a netdata registry:
5766 // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
5767 // 2. it responds with a list of netdata servers we know
5768 // the registry identifies us using a cookie it sets the first time we access it
5769 // the registry may respond with a redirect URL to send us to another registry
5771 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),
5774 xhrFields: { withCredentials: true } // required for the cookie
5776 .done(function(data) {
5777 var redirect = null;
5778 if(typeof data.registry === 'string')
5779 redirect = data.registry;
5781 if(typeof data.status !== 'string' || data.status !== 'ok') {
5782 NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
5787 if(redirect !== null && max_redirects > 0) {
5788 NETDATA.registry.server = redirect;
5789 NETDATA.registry.access(max_redirects - 1, callback);
5792 if(typeof callback === 'function')
5797 if(typeof data.person_guid === 'string')
5798 NETDATA.registry.person_guid = data.person_guid;
5800 if(typeof callback === 'function')
5801 callback(data.urls);
5805 NETDATA.error(410, NETDATA.registry.server);
5807 if(typeof callback === 'function')
5812 delete: function(delete_url, callback) {
5813 // send DELETE to a netdata registry:
5815 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),
5818 xhrFields: { withCredentials: true } // required for the cookie
5820 .done(function(data) {
5821 if(typeof data.status !== 'string' || data.status !== 'ok') {
5822 NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
5826 if(typeof callback === 'function')
5830 NETDATA.error(412, NETDATA.registry.server);
5832 if(typeof callback === 'function')
5837 switch: function(new_person_guid, callback) {
5840 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,
5843 xhrFields: { withCredentials: true } // required for the cookie
5845 .done(function(data) {
5846 if(typeof data.status !== 'string' || data.status !== 'ok') {
5847 NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
5851 if(typeof callback === 'function')
5855 NETDATA.error(414, NETDATA.registry.server);
5857 if(typeof callback === 'function')
5863 // ----------------------------------------------------------------------------------------------------------------
5866 NETDATA.errorReset();
5867 NETDATA.loadRequiredCSS(0);
5869 NETDATA._loadjQuery(function() {
5870 NETDATA.loadRequiredJs(0, function() {
5871 if(typeof $().emulateTransitionEnd !== 'function') {
5872 // bootstrap is not available
5873 NETDATA.options.current.show_help = false;
5876 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
5877 if(NETDATA.options.debug.main_loop === true)
5878 console.log('starting chart refresh thread');
5885 // window.NETDATA = NETDATA;
5886 // })(window, document);