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
16 // You can also set the default netdata server, using the following.
17 // When this variable is not set, we assume the page is hosted on your
18 // netdata server already.
19 // var netdataServer = "http://yourhost:19999"; // set your NetData server
21 //(function(window, document, undefined) {
23 // ------------------------------------------------------------------------
24 // compatibility fixes
26 // fix IE issue with console
27 if(!window.console) { window.console = { log: function(){} }; }
29 // if string.endsWith is not defined, define it
30 if(typeof String.prototype.endsWith !== 'function') {
31 String.prototype.endsWith = function(s) {
32 if(s.length > this.length) return false;
33 return this.slice(-s.length) === s;
37 // if string.startsWith is not defined, define it
38 if(typeof String.prototype.startsWith !== 'function') {
39 String.prototype.startsWith = function(s) {
40 if(s.length > this.length) return false;
41 return this.slice(s.length) === s;
46 var NETDATA = window.NETDATA || {};
48 // ----------------------------------------------------------------------------------------------------------------
49 // Detect the netdata server
51 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
52 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
53 NETDATA._scriptSource = function() {
56 if(typeof document.currentScript !== 'undefined') {
57 script = document.currentScript;
60 var all_scripts = document.getElementsByTagName('script');
61 script = all_scripts[all_scripts.length - 1];
64 if (typeof script.getAttribute.length !== 'undefined')
67 script = script.getAttribute('src', -1);
72 if(typeof netdataServer !== 'undefined')
73 NETDATA.serverDefault = netdataServer;
75 var s = NETDATA._scriptSource();
76 if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
78 console.log('WARNING: Cannot detect the URL of the netdata server.');
79 NETDATA.serverDefault = null;
83 if(NETDATA.serverDefault === null)
84 NETDATA.serverDefault = '';
85 else if(NETDATA.serverDefault.slice(-1) !== '/')
86 NETDATA.serverDefault += '/';
88 // default URLs for all the external files we need
89 // make them RELATIVE so that the whole thing can also be
90 // installed under a web server
91 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
92 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
93 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
94 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
95 NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge.min.js';
96 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
97 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
98 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
99 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
100 NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3.min.js';
101 NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3.min.js';
102 NETDATA.c3_css = NETDATA.serverDefault + 'css/c3.min.css';
103 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
104 NETDATA.google_js = 'https://www.google.com/jsapi';
108 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
109 dashboard_css: NETDATA.serverDefault + 'dashboard.css',
110 background: '#FFFFFF',
111 foreground: '#000000',
114 colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477',
115 '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6',
116 '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707',
117 '#329262', '#3B3EAC' ],
118 easypiechart_track: '#f0f0f0',
119 easypiechart_scale: '#dfe0e0',
120 gauge_pointer: '#C0C0C0',
121 gauge_stroke: '#F0F0F0',
122 gauge_gradient: false
125 bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
126 dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
127 background: '#272b30',
128 foreground: '#C8C8C8',
131 /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00',
132 '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0',
133 '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a',
134 '#a6a479', '#a66da8' ],
136 colors: [ '#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00',
137 '#3B3EAC', '#EE9911', '#BB44CC', '#C83E3E', '#990099', '#CC7700',
138 '#22AA99', '#109618', '#6633CC', '#DD4477', '#316395', '#8B0707',
139 '#329262', '#3B3EFF' ],
140 easypiechart_track: '#373b40',
141 easypiechart_scale: '#373b40',
142 gauge_pointer: '#474b50',
143 gauge_stroke: '#373b40',
144 gauge_gradient: false
148 if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
149 NETDATA.themes.current = NETDATA.themes[netdataTheme];
151 NETDATA.themes.current = NETDATA.themes.white;
153 NETDATA.colors = NETDATA.themes.current.colors;
155 // these are the colors Google Charts are using
156 // we have them here to attempt emulate their look and feel on the other chart libraries
157 // http://there4.io/2012/05/02/google-chart-color-list/
158 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
159 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
160 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
162 // an alternative set
163 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
164 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
165 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
167 // ----------------------------------------------------------------------------------------------------------------
168 // the defaults for all charts
170 // if the user does not specify any of these, the following will be used
172 NETDATA.chartDefaults = {
173 host: NETDATA.serverDefault, // the server to get data from
174 width: '100%', // the chart width - can be null
175 height: '100%', // the chart height - can be null
176 min_width: null, // the chart minimum width - can be null
177 library: 'dygraph', // the graphing library to use
178 method: 'average', // the grouping method
179 before: 0, // panning
180 after: -600, // panning
181 pixels_per_point: 1, // the detail of the chart
182 fill_luminance: 0.8 // luminance of colors in solit areas
185 // ----------------------------------------------------------------------------------------------------------------
189 pauseCallback: null, // a callback when we are really paused
191 pause: false, // when enabled we don't auto-refresh the charts
193 targets: null, // an array of all the state objects that are
194 // currently active (independently of their
195 // viewport visibility)
197 updated_dom: true, // when true, the DOM has been updated with
198 // new elements we have to check.
200 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
201 // rendering charts continiously.
202 // used with .current.fast_render_timeframe
204 page_is_visible: true, // when true, this page is visible
206 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
207 // auto-refresher for some time (when a chart is
208 // performing pan or zoom, we need to stop refreshing
209 // all other charts, to have the maximum speed for
210 // rendering the chart that is panned or zoomed).
211 // Used with .current.global_pan_sync_time
213 last_resized: new Date().getTime(), // the timestamp of the last resize request
215 last_page_scroll: 0, // the timestamp the last time the page was scrolled
217 // the current profile
218 // we may have many...
220 pixels_per_point: 1, // the minimum pixels per point for all charts
221 // increase this to speed javascript up
222 // each chart library has its own limit too
223 // the max of this and the chart library is used
224 // the final is calculated every time, so a change
225 // here will have immediate effect on the next chart
228 idle_between_charts: 100, // ms - how much time to wait between chart updates
230 fast_render_timeframe: 200, // ms - render continously until this time of continious
231 // rendering has been reached
232 // this setting is used to make it render e.g. 10
233 // charts at once, sleep idle_between_charts time
234 // and continue for another 10 charts.
236 idle_between_loops: 500, // ms - if all charts have been updated, wait this
237 // time before starting again.
239 idle_parallel_loops: 100, // ms - the time between parallel refresher updates
241 idle_lost_focus: 500, // ms - when the window does not have focus, check
242 // if focus has been regained, every this time
244 global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
245 // autorefreshing of charts is paused for this amount
248 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
249 // of time before setting up synchronized selections
252 sync_selection: true, // enable or disable selection sync
254 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
256 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
258 pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
260 update_only_visible: true, // enable or disable visibility management
262 parallel_refresher: true, // enable parallel refresh of charts
264 concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
266 destroy_on_hide: false, // destroy charts when they are not visible
268 show_help: true, // when enabled the charts will show some help
269 show_help_delay_show_ms: 500,
270 show_help_delay_hide_ms: 0,
272 eliminate_zero_dimensions: true, // do not show dimensions with just zeros
274 stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
275 stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts
277 double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap
279 smooth_plot: true, // enable smooth plot, where possible
281 charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
283 color_fill_opacity_line: 1.0,
284 color_fill_opacity_area: 0.2,
285 color_fill_opacity_stacked: 0.8,
287 pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
288 pan_and_zoom_factor_multiplier_control: 2.0,
289 pan_and_zoom_factor_multiplier_shift: 3.0,
290 pan_and_zoom_factor_multiplier_alt: 4.0,
292 setOptionCallback: function() { ; }
300 chart_data_url: false,
301 chart_errors: false, // FIXME
309 NETDATA.statistics = {
312 refreshes_active_max: 0
316 // ----------------------------------------------------------------------------------------------------------------
317 // local storage options
319 NETDATA.localStorage = {
322 callback: {} // only used for resetting back to defaults
325 NETDATA.localStorageGet = function(key, def, callback) {
328 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
329 NETDATA.localStorage.default[key.toString()] = def;
330 NETDATA.localStorage.callback[key.toString()] = callback;
333 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
335 // console.log('localStorage: loading "' + key.toString() + '"');
336 ret = localStorage.getItem(key.toString());
337 if(ret === null || ret === 'undefined') {
338 // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
339 localStorage.setItem(key.toString(), JSON.stringify(def));
343 // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
344 ret = JSON.parse(ret);
345 // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
349 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
354 if(typeof ret === 'undefined' || ret === 'undefined') {
355 console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
359 NETDATA.localStorage.current[key.toString()] = ret;
363 NETDATA.localStorageSet = function(key, value, callback) {
364 if(typeof value === 'undefined' || value === 'undefined') {
365 console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
368 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
369 NETDATA.localStorage.default[key.toString()] = value;
370 NETDATA.localStorage.current[key.toString()] = value;
371 NETDATA.localStorage.callback[key.toString()] = callback;
374 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
375 // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
377 localStorage.setItem(key.toString(), JSON.stringify(value));
380 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
384 NETDATA.localStorage.current[key.toString()] = value;
388 NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
390 if(typeof obj[i] === 'object') {
391 //console.log('object ' + prefix + '.' + i.toString());
392 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
396 obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
400 NETDATA.setOption = function(key, value) {
401 if(key.toString() === 'setOptionCallback') {
402 if(typeof NETDATA.options.current.setOptionCallback === 'function') {
403 NETDATA.options.current[key.toString()] = value;
404 NETDATA.options.current.setOptionCallback();
407 else if(NETDATA.options.current[key.toString()] !== value) {
408 var name = 'options.' + key.toString();
410 if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
411 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
413 //console.log(NETDATA.localStorage);
414 //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
415 //console.log(NETDATA.options);
416 NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
418 if(typeof NETDATA.options.current.setOptionCallback === 'function')
419 NETDATA.options.current.setOptionCallback();
425 NETDATA.getOption = function(key) {
426 return NETDATA.options.current[key.toString()];
429 // read settings from local storage
430 NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
432 // always start with this option enabled.
433 NETDATA.setOption('stop_updates_when_focus_is_lost', true);
435 NETDATA.resetOptions = function() {
436 for(var i in NETDATA.localStorage.default) {
437 var a = i.split('.');
439 if(a[0] === 'options') {
440 if(a[1] === 'setOptionCallback') continue;
441 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
442 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
444 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
446 else if(a[0] === 'chart_heights') {
447 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
448 NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
454 // ----------------------------------------------------------------------------------------------------------------
456 if(NETDATA.options.debug.main_loop === true)
457 console.log('welcome to NETDATA');
459 NETDATA.onresize = function() {
460 NETDATA.options.last_resized = new Date().getTime();
464 NETDATA.onscroll = function() {
465 // console.log('onscroll');
467 NETDATA.options.last_page_scroll = new Date().getTime();
468 if(NETDATA.options.targets === null) return;
470 // when the user scrolls he sees that we have
471 // hidden all the not-visible charts
472 // using this little function we try to switch
473 // the charts back to visible quickly
474 var targets = NETDATA.options.targets;
475 var len = targets.length;
476 while(len--) targets[len].isVisible();
479 window.onresize = NETDATA.onresize;
480 window.onscroll = NETDATA.onscroll;
482 // ----------------------------------------------------------------------------------------------------------------
485 NETDATA.errorCodes = {
486 100: { message: "Cannot load chart library", alert: true },
487 101: { message: "Cannot load jQuery", alert: true },
488 402: { message: "Chart library not found", alert: false },
489 403: { message: "Chart library not enabled/is failed", alert: false },
490 404: { message: "Chart not found", alert: false },
491 405: { message: "Cannot download charts index from server", alert: true },
492 406: { message: "Invalid charts index downloaded from server", alert: true }
494 NETDATA.errorLast = {
500 NETDATA.error = function(code, msg) {
501 NETDATA.errorLast.code = code;
502 NETDATA.errorLast.message = msg;
503 NETDATA.errorLast.datetime = new Date().getTime();
505 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
508 if(typeof netdataErrorCallback === 'function') {
509 ret = netdataErrorCallback('system', code, msg);
512 if(ret && NETDATA.errorCodes[code].alert)
513 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
516 NETDATA.errorReset = function() {
517 NETDATA.errorLast.code = 0;
518 NETDATA.errorLast.message = "You are doing fine!";
519 NETDATA.errorLast.datetime = 0;
522 // ----------------------------------------------------------------------------------------------------------------
525 // When multiple charts need the same chart, we avoid downloading it
526 // multiple times (and having it in browser memory multiple time)
527 // by using this registry.
529 // Every time we download a chart definition, we save it here with .add()
530 // Then we try to get it back with .get(). If that fails, we download it.
532 NETDATA.chartRegistry = {
535 fixid: function(id) {
536 return id.replace(/:/g, "_").replace(/\//g, "_");
539 add: function(host, id, data) {
540 host = this.fixid(host);
543 if(typeof this.charts[host] === 'undefined')
544 this.charts[host] = {};
546 //console.log('added ' + host + '/' + id);
547 this.charts[host][id] = data;
550 get: function(host, id) {
551 host = this.fixid(host);
554 if(typeof this.charts[host] === 'undefined')
557 if(typeof this.charts[host][id] === 'undefined')
560 //console.log('cached ' + host + '/' + id);
561 return this.charts[host][id];
564 downloadAll: function(host, callback) {
565 while(host.slice(-1) === '/')
566 host = host.substring(0, host.length - 1);
571 url: host + '/api/v1/charts',
575 .done(function(data) {
577 var h = NETDATA.chartRegistry.fixid(host);
578 self.charts[h] = data.charts;
580 else NETDATA.error(406, host + '/api/v1/charts');
582 if(typeof callback === 'function')
586 NETDATA.error(405, host + '/api/v1/charts');
588 if(typeof callback === 'function')
594 // ----------------------------------------------------------------------------------------------------------------
595 // Global Pan and Zoom on charts
597 // Using this structure are synchronize all the charts, so that
598 // when you pan or zoom one, all others are automatically refreshed
599 // to the same timespan.
601 NETDATA.globalPanAndZoom = {
602 seq: 0, // timestamp ms
603 // every time a chart is panned or zoomed
604 // we set the timestamp here
605 // then we use it as a sequence number
606 // to find if other charts are syncronized
609 master: null, // the master chart (state), to which all others
612 force_before_ms: null, // the timespan to sync all other charts
613 force_after_ms: null,
616 setMaster: function(state, after, before) {
617 if(NETDATA.options.current.sync_pan_and_zoom === false)
620 if(this.master !== null && this.master !== state)
621 this.master.resetChart(true, true);
623 var now = new Date().getTime();
626 this.force_after_ms = after;
627 this.force_before_ms = before;
628 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
632 clearMaster: function() {
633 if(this.master !== null) {
634 var st = this.master;
641 this.force_after_ms = null;
642 this.force_before_ms = null;
643 NETDATA.options.auto_refresher_stop_until = 0;
646 // is the given state the master of the global
647 // pan and zoom sync?
648 isMaster: function(state) {
649 if(this.master === state) return true;
653 // are we currently have a global pan and zoom sync?
654 isActive: function() {
655 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
659 // check if a chart, other than the master
660 // needs to be refreshed, due to the global pan and zoom
661 shouldBeAutoRefreshed: function(state) {
662 if(this.master === null || this.seq === 0)
665 //if(state.needsRecreation())
668 if(state.tm.pan_and_zoom_seq === this.seq)
675 // ----------------------------------------------------------------------------------------------------------------
676 // dimensions selection
679 // move color assignment to dimensions, here
681 dimensionStatus = function(parent, label, name_div, value_div, color) {
682 this.enabled = false;
683 this.parent = parent;
685 this.name_div = null;
686 this.value_div = null;
687 this.color = NETDATA.themes.current.foreground;
689 if(parent.selected_count > parent.unselected_count)
690 this.selected = true;
692 this.selected = false;
694 this.setOptions(name_div, value_div, color);
697 dimensionStatus.prototype.invalidate = function() {
698 this.name_div = null;
699 this.value_div = null;
700 this.enabled = false;
703 dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
706 if(this.name_div != name_div) {
707 this.name_div = name_div;
708 this.name_div.title = this.label;
709 this.name_div.style.color = this.color;
710 if(this.selected === false)
711 this.name_div.className = 'netdata-legend-name not-selected';
713 this.name_div.className = 'netdata-legend-name selected';
716 if(this.value_div != value_div) {
717 this.value_div = value_div;
718 this.value_div.title = this.label;
719 this.value_div.style.color = this.color;
720 if(this.selected === false)
721 this.value_div.className = 'netdata-legend-value not-selected';
723 this.value_div.className = 'netdata-legend-value selected';
730 dimensionStatus.prototype.setHandler = function() {
731 if(this.enabled === false) return;
735 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
736 this.name_div.onclick = this.value_div.onclick = function(e) {
738 if(ds.isSelected()) {
740 if(e.shiftKey === true || e.ctrlKey === true) {
741 // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
744 if(ds.parent.countSelected() === 0)
745 ds.parent.selectAll();
748 // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
749 if(ds.parent.countSelected() === 1) {
750 ds.parent.selectAll();
753 ds.parent.selectNone();
759 // this is not selected
760 if(e.shiftKey === true || e.ctrlKey === true) {
761 // control or shift key is pressed -> select this too
765 // no key is pressed -> select only this
766 ds.parent.selectNone();
771 ds.parent.state.redrawChart();
775 dimensionStatus.prototype.select = function() {
776 if(this.enabled === false) return;
778 this.name_div.className = 'netdata-legend-name selected';
779 this.value_div.className = 'netdata-legend-value selected';
780 this.selected = true;
783 dimensionStatus.prototype.unselect = function() {
784 if(this.enabled === false) return;
786 this.name_div.className = 'netdata-legend-name not-selected';
787 this.value_div.className = 'netdata-legend-value hidden';
788 this.selected = false;
791 dimensionStatus.prototype.isSelected = function() {
792 return(this.enabled === true && this.selected === true);
795 // ----------------------------------------------------------------------------------------------------------------
797 dimensionsVisibility = function(state) {
800 this.dimensions = {};
801 this.selected_count = 0;
802 this.unselected_count = 0;
805 dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
806 if(typeof this.dimensions[label] === 'undefined') {
808 this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
811 this.dimensions[label].setOptions(name_div, value_div, color);
813 return this.dimensions[label];
816 dimensionsVisibility.prototype.dimensionGet = function(label) {
817 return this.dimensions[label];
820 dimensionsVisibility.prototype.invalidateAll = function() {
821 for(var d in this.dimensions)
822 this.dimensions[d].invalidate();
825 dimensionsVisibility.prototype.selectAll = function() {
826 for(var d in this.dimensions)
827 this.dimensions[d].select();
830 dimensionsVisibility.prototype.countSelected = function() {
832 for(var d in this.dimensions)
833 if(this.dimensions[d].isSelected()) i++;
838 dimensionsVisibility.prototype.selectNone = function() {
839 for(var d in this.dimensions)
840 this.dimensions[d].unselect();
843 dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
844 var ret = new Array();
845 this.selected_count = 0;
846 this.unselected_count = 0;
848 for(var i = 0, len = array.length; i < len ; i++) {
849 var ds = this.dimensions[array[i]];
850 if(typeof ds === 'undefined') {
851 // console.log(array[i] + ' is not found');
856 if(ds.isSelected()) {
858 this.selected_count++;
862 this.unselected_count++;
866 if(this.selected_count === 0 && this.unselected_count !== 0) {
868 return this.selected2BooleanArray(array);
875 // ----------------------------------------------------------------------------------------------------------------
876 // global selection sync
878 NETDATA.globalSelectionSync = {
885 if(this.state !== null)
886 this.state.globalSelectionSyncStop();
890 if(this.state !== null) {
891 this.state.globalSelectionSyncDelay();
896 // ----------------------------------------------------------------------------------------------------------------
897 // Our state object, where all per-chart values are stored
899 chartState = function(element) {
900 var self = $(element);
901 this.element = element;
904 // all private functions should use 'that', instead of 'this'
908 * show an error instead of the chart
910 var error = function(msg) {
913 if(typeof netdataErrorCallback === 'function') {
914 ret = netdataErrorCallback('chart', that.id, msg);
918 that.element.innerHTML = that.id + ': ' + msg;
919 that.enabled = false;
920 that.current = that.pan;
924 // GUID - a unique identifier for the chart
925 this.uuid = NETDATA.guid();
927 // string - the name of chart
928 this.id = self.data('netdata');
930 // string - the key for localStorage settings
931 this.settings_id = self.data('id') || null;
933 // the user given dimensions of the element
934 this.width = self.data('width') || NETDATA.chartDefaults.width;
935 this.height = self.data('height') || NETDATA.chartDefaults.height;
937 if(this.settings_id !== null) {
938 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
939 // this is the callback that will be called
940 // if and when the user resets all localStorage variables
943 resizeChartToHeight(height);
947 // string - the netdata server URL, without any path
948 this.host = self.data('host') || NETDATA.chartDefaults.host;
950 // make sure the host does not end with /
951 // all netdata API requests use absolute paths
952 while(this.host.slice(-1) === '/')
953 this.host = this.host.substring(0, this.host.length - 1);
955 // string - the grouping method requested by the user
956 this.method = self.data('method') || NETDATA.chartDefaults.method;
958 // the time-range requested by the user
959 this.after = self.data('after') || NETDATA.chartDefaults.after;
960 this.before = self.data('before') || NETDATA.chartDefaults.before;
962 // the pixels per point requested by the user
963 this.pixels_per_point = self.data('pixels-per-point') || 1;
964 this.points = self.data('points') || null;
966 // the dimensions requested by the user
967 this.dimensions = self.data('dimensions') || null;
969 // the chart library requested by the user
970 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
972 // object - the chart library used
977 this.colors_assigned = {};
978 this.colors_available = null;
980 // the element already created by the user
981 this.element_message = null;
983 // the element with the chart
984 this.element_chart = null;
986 // the element with the legend of the chart (if created by us)
987 this.element_legend = null;
988 this.element_legend_childs = {
998 this.chart_url = null; // string - the url to download chart info
999 this.chart = null; // object - the chart as downloaded from the server
1001 this.title = self.data('title') || null; // the title of the chart
1002 this.units = self.data('units') || null; // the units of the chart dimensions
1003 this.append_options = self.data('append-options') || null; // the units of the chart dimensions
1005 this.running = false; // boolean - true when the chart is being refreshed now
1006 this.validated = false; // boolean - has the chart been validated?
1007 this.enabled = true; // boolean - is the chart enabled for refresh?
1008 this.paused = false; // boolean - is the chart paused for any reason?
1009 this.selected = false; // boolean - is the chart shown a selection?
1010 this.debug = false; // boolean - console.log() debug info about this chart
1012 this.netdata_first = 0; // milliseconds - the first timestamp in netdata
1013 this.netdata_last = 0; // milliseconds - the last timestamp in netdata
1014 this.requested_after = null; // milliseconds - the timestamp of the request after param
1015 this.requested_before = null; // milliseconds - the timestamp of the request before param
1016 this.requested_padding = null;
1017 this.view_after = 0;
1018 this.view_before = 0;
1023 force_update_at: 0, // the timestamp to force the update at
1024 force_before_ms: null,
1025 force_after_ms: null
1030 force_update_at: 0, // the timestamp to force the update at
1031 force_before_ms: null,
1032 force_after_ms: null
1037 force_update_at: 0, // the timestamp to force the update at
1038 force_before_ms: null,
1039 force_after_ms: null
1042 // this is a pointer to one of the sub-classes below
1044 this.current = this.auto;
1046 // check the requested library is available
1047 // we don't initialize it here - it will be initialized when
1048 // this chart will be first used
1049 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1050 NETDATA.error(402, that.library_name);
1051 error('chart library "' + that.library_name + '" is not found');
1054 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1055 NETDATA.error(403, that.library_name);
1056 error('chart library "' + that.library_name + '" is not enabled');
1060 that.library = NETDATA.chartLibraries[that.library_name];
1062 // milliseconds - the time the last refresh took
1063 this.refresh_dt_ms = 0;
1065 // if we need to report the rendering speed
1066 // find the element that needs to be updated
1067 var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
1069 if(refresh_dt_element_name !== null)
1070 this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1072 this.refresh_dt_element = null;
1074 this.dimensions_visibility = new dimensionsVisibility(this);
1076 this._updating = false;
1078 // ============================================================================================================
1079 // PRIVATE FUNCTIONS
1081 var createDOM = function() {
1082 if(that.enabled === false) return;
1084 if(that.element_message !== null) that.element_message.innerHTML = '';
1085 if(that.element_legend !== null) that.element_legend.innerHTML = '';
1086 if(that.element_chart !== null) that.element_chart.innerHTML = '';
1088 that.element.innerHTML = '';
1090 that.element_message = document.createElement('div');
1091 that.element_message.className = ' netdata-message hidden';
1092 that.element.appendChild(that.element_message);
1094 that.element_chart = document.createElement('div');
1095 that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1096 that.element.appendChild(that.element_chart);
1098 if(that.hasLegend() === true) {
1099 that.element.className = "netdata-container-with-legend";
1100 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1102 that.element_legend = document.createElement('div');
1103 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1104 that.element.appendChild(that.element_legend);
1107 that.element.className = "netdata-container";
1108 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1110 that.element_legend = null;
1112 that.element_legend_childs.series = null;
1114 if(typeof(that.width) === 'string')
1115 $(that.element).css('width', that.width);
1116 else if(typeof(that.width) === 'number')
1117 $(that.element).css('width', that.width + 'px');
1119 if(typeof(that.library.aspect_ratio) === 'undefined') {
1120 if(typeof(that.height) === 'string')
1121 $(that.element).css('height', that.height);
1122 else if(typeof(that.height) === 'number')
1123 $(that.element).css('height', that.height + 'px');
1126 var w = that.element.offsetWidth;
1127 if(w === null || w === 0) {
1128 // the div is hidden
1129 // this will resize the chart when next viewed
1130 that.tm.last_resized = 0;
1133 $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1136 if(NETDATA.chartDefaults.min_width !== null)
1137 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1139 that.tm.last_dom_created = new Date().getTime();
1145 * initialize state variables
1146 * destroy all (possibly) created state elements
1147 * create the basic DOM for a chart
1149 var init = function() {
1150 if(that.enabled === false) return;
1152 that.paused = false;
1153 that.selected = false;
1155 that.chart_created = false; // boolean - is the library.create() been called?
1156 that.updates_counter = 0; // numeric - the number of refreshes made so far
1157 that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden
1158 that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1161 last_initialized: 0, // milliseconds - the timestamp it was last initialized
1162 last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1163 last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1165 last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1166 last_updated: 0, // the timestamp the chart last updated with data
1167 pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1169 // Used with NETDATA.globalPanAndZoom.seq
1170 last_visible_check: 0, // the time we last checked if it is visible
1171 last_resized: 0, // the time the chart was resized
1172 last_hidden: 0, // the time the chart was hidden
1173 last_unhidden: 0, // the time the chart was unhidden
1174 last_autorefreshed: 0 // the time the chart was last refreshed
1177 that.data = null; // the last data as downloaded from the netdata server
1178 that.data_url = 'invalid://'; // string - the last url used to update the chart
1179 that.data_points = 0; // number - the number of points returned from netdata
1180 that.data_after = 0; // milliseconds - the first timestamp of the data
1181 that.data_before = 0; // milliseconds - the last timestamp of the data
1182 that.data_update_every = 0; // milliseconds - the frequency to update the data
1184 that.tm.last_initialized = new Date().getTime();
1187 that.setMode('auto');
1190 var maxMessageFontSize = function() {
1191 // normally we want a font size, as tall as the element
1192 var h = that.element_message.clientHeight;
1194 // but give it some air, 20% let's say, or 5 pixels min
1195 var lost = Math.max(h * 0.2, 5);
1198 // center the text, vertically
1199 var paddingTop = (lost - 5) / 2;
1201 // but check the width too
1202 // it should fit 10 characters in it
1203 var w = that.element_message.clientWidth / 10;
1205 paddingTop += (h - w) / 2;
1209 // and don't make it too huge
1210 // 5% of the screen size is good
1211 if(h > screen.height / 20) {
1212 paddingTop += (h - (screen.height / 20)) / 2;
1213 h = screen.height / 20;
1217 that.element_message.style.fontSize = h.toString() + 'px';
1218 that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1221 var showMessage = function(msg) {
1222 that.element_message.className = 'netdata-message';
1223 that.element_message.innerHTML = msg;
1224 that.element_message.style.fontSize = 'x-small';
1225 that.element_message.style.paddingTop = '0px';
1226 that.___messageHidden___ = undefined;
1229 var showMessageIcon = function(icon) {
1230 that.element_message.innerHTML = icon;
1231 that.element_message.className = 'netdata-message icon';
1232 maxMessageFontSize();
1233 that.___messageHidden___ = undefined;
1236 var hideMessage = function() {
1237 if(typeof that.___messageHidden___ === 'undefined') {
1238 that.___messageHidden___ = true;
1239 that.element_message.className = 'netdata-message hidden';
1243 var showRendering = function() {
1245 if(that.chart !== null) {
1246 if(that.chart.chart_type === 'line')
1247 icon = '<i class="fa fa-line-chart"></i>';
1249 icon = '<i class="fa fa-area-chart"></i>';
1252 icon = '<i class="fa fa-area-chart"></i>';
1254 showMessageIcon(icon + ' netdata');
1257 var showLoading = function() {
1258 if(that.chart_created === false) {
1259 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1265 var isHidden = function() {
1266 if(typeof that.___chartIsHidden___ !== 'undefined')
1272 // hide the chart, when it is not visible - called from isVisible()
1273 var hideChart = function() {
1274 // hide it, if it is not already hidden
1275 if(isHidden() === true) return;
1277 if(that.chart_created === true) {
1278 if(NETDATA.options.current.destroy_on_hide === true) {
1279 // we should destroy it
1284 that.element_chart.style.display = 'none';
1285 if(that.element_legend !== null) that.element_legend.style.display = 'none';
1286 that.tm.last_hidden = new Date().getTime();
1289 // This works, but I not sure there are no corner cases somewhere
1290 // so it is commented - if the user has memory issues he can
1291 // set Destroy on Hide for all charts
1292 // that.data = null;
1296 that.___chartIsHidden___ = true;
1299 // unhide the chart, when it is visible - called from isVisible()
1300 var unhideChart = function() {
1301 if(isHidden() === false) return;
1303 that.___chartIsHidden___ = undefined;
1304 that.updates_since_last_unhide = 0;
1306 if(that.chart_created === false) {
1307 // we need to re-initialize it, to show our background
1308 // logo in bootstrap tabs, until the chart loads
1312 that.tm.last_unhidden = new Date().getTime();
1313 that.element_chart.style.display = '';
1314 if(that.element_legend !== null) that.element_legend.style.display = '';
1320 var canBeRendered = function() {
1321 if(isHidden() === true || that.isVisible(true) === false)
1327 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1328 var callChartLibraryUpdateSafely = function(data) {
1331 if(canBeRendered() === false)
1334 if(NETDATA.options.debug.chart_errors === true)
1335 status = that.library.update(that, data);
1338 status = that.library.update(that, data);
1345 if(status === false) {
1346 error('chart failed to be updated as ' + that.library_name);
1353 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1354 var callChartLibraryCreateSafely = function(data) {
1357 if(canBeRendered() === false)
1360 if(NETDATA.options.debug.chart_errors === true)
1361 status = that.library.create(that, data);
1364 status = that.library.create(that, data);
1371 if(status === false) {
1372 error('chart failed to be created as ' + that.library_name);
1376 that.chart_created = true;
1377 that.updates_since_last_creation = 0;
1381 // ----------------------------------------------------------------------------------------------------------------
1384 // resizeChart() - private
1385 // to be called just before the chart library to make sure that
1386 // a properly sized dom is available
1387 var resizeChart = function() {
1388 if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1389 if(that.chart_created === false) return;
1391 if(that.needsRecreation()) {
1394 else if(typeof that.library.resize === 'function') {
1395 that.library.resize(that);
1397 if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1398 $(that.element_legend_childs.nano).nanoScroller();
1400 maxMessageFontSize();
1403 that.tm.last_resized = new Date().getTime();
1407 // this is the actual chart resize algorithm
1409 // - resize the entire container
1410 // - update the internal states
1411 // - resize the chart as the div changes height
1412 // - update the scrollbar of the legend
1413 var resizeChartToHeight = function(h) {
1415 that.element.style.height = h;
1417 if(that.settings_id !== null)
1418 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1420 var now = new Date().getTime();
1421 NETDATA.options.last_page_scroll = now;
1422 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1425 that.tm.last_resized = 0;
1429 this.resizeHandler = function(e) {
1432 if(typeof this.event_resize === 'undefined'
1433 || this.event_resize.chart_original_w === 'undefined'
1434 || this.event_resize.chart_original_h === 'undefined')
1435 this.event_resize = {
1436 chart_original_w: this.element.clientWidth,
1437 chart_original_h: this.element.clientHeight,
1441 if(e.type === 'touchstart') {
1442 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1443 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1446 this.event_resize.mouse_start_x = e.clientX;
1447 this.event_resize.mouse_start_y = e.clientY;
1450 this.event_resize.chart_start_w = this.element.clientWidth;
1451 this.event_resize.chart_start_h = this.element.clientHeight;
1452 this.event_resize.chart_last_w = this.element.clientWidth;
1453 this.event_resize.chart_last_h = this.element.clientHeight;
1455 var now = new Date().getTime();
1456 if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1457 // double click / double tap event
1459 // the optimal height of the chart
1460 // showing the entire legend
1461 var optimal = this.event_resize.chart_last_h
1462 + this.element_legend_childs.content.scrollHeight
1463 - this.element_legend_childs.content.clientHeight;
1465 // if we are not optimal, be optimal
1466 if(this.event_resize.chart_last_h != optimal)
1467 resizeChartToHeight(optimal.toString() + 'px');
1469 // else if we do not have the original height
1470 // reset to the original height
1471 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1472 resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1475 this.event_resize.last = now;
1477 // process movement event
1478 document.onmousemove =
1479 document.ontouchmove =
1480 this.element_legend_childs.resize_handler.onmousemove =
1481 this.element_legend_childs.resize_handler.ontouchmove =
1486 case 'mousemove': y = e.clientY; break;
1487 case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1491 var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1493 if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1494 resizeChartToHeight(newH.toString() + 'px');
1495 that.event_resize.chart_last_h = newH;
1500 // process end event
1501 document.onmouseup =
1502 document.ontouchend =
1503 this.element_legend_childs.resize_handler.onmouseup =
1504 this.element_legend_childs.resize_handler.ontouchend =
1506 // remove all the hooks
1507 document.onmouseup =
1508 document.onmousemove =
1509 document.ontouchmove =
1510 document.ontouchend =
1511 that.element_legend_childs.resize_handler.onmousemove =
1512 that.element_legend_childs.resize_handler.ontouchmove =
1513 that.element_legend_childs.resize_handler.onmouseout =
1514 that.element_legend_childs.resize_handler.onmouseup =
1515 that.element_legend_childs.resize_handler.ontouchend =
1518 // allow auto-refreshes
1519 NETDATA.options.auto_refresher_stop_until = 0;
1525 var noDataToShow = function() {
1526 showMessageIcon('<i class="fa fa-warning"></i> empty');
1527 that.legendUpdateDOM();
1528 that.tm.last_autorefreshed = new Date().getTime();
1529 // that.data_update_every = 30 * 1000;
1530 //that.element_chart.style.display = 'none';
1531 //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1532 //that.___chartIsHidden___ = true;
1535 // ============================================================================================================
1538 this.error = function(msg) {
1542 this.setMode = function(m) {
1543 if(this.current !== null && this.current.name === m) return;
1546 this.current = this.auto;
1547 else if(m === 'pan')
1548 this.current = this.pan;
1549 else if(m === 'zoom')
1550 this.current = this.zoom;
1552 this.current = this.auto;
1554 this.current.force_update_at = 0;
1555 this.current.force_before_ms = null;
1556 this.current.force_after_ms = null;
1558 this.tm.last_mode_switch = new Date().getTime();
1561 // ----------------------------------------------------------------------------------------------------------------
1562 // global selection sync
1564 // prevent to global selection sync for some time
1565 this.globalSelectionSyncDelay = function(ms) {
1566 if(NETDATA.options.current.sync_selection === false)
1569 if(typeof ms === 'number')
1570 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1572 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1575 // can we globally apply selection sync?
1576 this.globalSelectionSyncAbility = function() {
1577 if(NETDATA.options.current.sync_selection === false)
1580 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1586 this.globalSelectionSyncIsMaster = function() {
1587 if(NETDATA.globalSelectionSync.state === this)
1593 // this chart is the master of the global selection sync
1594 this.globalSelectionSyncBeMaster = function() {
1596 if(this.globalSelectionSyncIsMaster()) {
1597 if(this.debug === true)
1598 this.log('sync: I am the master already.');
1603 if(NETDATA.globalSelectionSync.state) {
1604 if(this.debug === true)
1605 this.log('sync: I am not the sync master. Resetting global sync.');
1607 this.globalSelectionSyncStop();
1610 // become the master
1611 if(this.debug === true)
1612 this.log('sync: becoming sync master.');
1614 this.selected = true;
1615 NETDATA.globalSelectionSync.state = this;
1617 // find the all slaves
1618 var targets = NETDATA.options.targets;
1619 var len = targets.length;
1624 if(this.debug === true)
1625 st.log('sync: not adding me to sync');
1627 else if(st.globalSelectionSyncIsEligible()) {
1628 if(this.debug === true)
1629 st.log('sync: adding to sync as slave');
1631 st.globalSelectionSyncBeSlave();
1635 // this.globalSelectionSyncDelay(100);
1638 // can the chart participate to the global selection sync as a slave?
1639 this.globalSelectionSyncIsEligible = function() {
1640 if(this.enabled === true
1641 && this.library !== null
1642 && typeof this.library.setSelection === 'function'
1643 && this.isVisible() === true
1644 && this.chart_created === true)
1650 // this chart becomes a slave of the global selection sync
1651 this.globalSelectionSyncBeSlave = function() {
1652 if(NETDATA.globalSelectionSync.state !== this)
1653 NETDATA.globalSelectionSync.slaves.push(this);
1656 // sync all the visible charts to the given time
1657 // this is to be called from the chart libraries
1658 this.globalSelectionSync = function(t) {
1659 if(this.globalSelectionSyncAbility() === false) {
1660 if(this.debug === true)
1661 this.log('sync: cannot sync (yet?).');
1666 if(this.globalSelectionSyncIsMaster() === false) {
1667 if(this.debug === true)
1668 this.log('sync: trying to be sync master.');
1670 this.globalSelectionSyncBeMaster();
1672 if(this.globalSelectionSyncAbility() === false) {
1673 if(this.debug === true)
1674 this.log('sync: cannot sync (yet?).');
1680 NETDATA.globalSelectionSync.last_t = t;
1681 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1686 // stop syncing all charts to the given time
1687 this.globalSelectionSyncStop = function() {
1688 if(NETDATA.globalSelectionSync.slaves.length) {
1689 if(this.debug === true)
1690 this.log('sync: cleaning up...');
1692 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1694 if(that.debug === true)
1695 st.log('sync: not adding me to sync stop');
1698 if(that.debug === true)
1699 st.log('sync: removed slave from sync');
1701 st.clearSelection();
1705 NETDATA.globalSelectionSync.last_t = 0;
1706 NETDATA.globalSelectionSync.slaves = [];
1707 NETDATA.globalSelectionSync.state = null;
1710 this.clearSelection();
1713 this.setSelection = function(t) {
1714 if(typeof this.library.setSelection === 'function') {
1715 if(this.library.setSelection(this, t) === true)
1716 this.selected = true;
1718 this.selected = false;
1720 else this.selected = true;
1722 if(this.selected === true && this.debug === true)
1723 this.log('selection set to ' + t.toString());
1725 return this.selected;
1728 this.clearSelection = function() {
1729 if(this.selected === true) {
1730 if(typeof this.library.clearSelection === 'function') {
1731 if(this.library.clearSelection(this) === true)
1732 this.selected = false;
1734 this.selected = true;
1736 else this.selected = false;
1738 if(this.selected === false && this.debug === true)
1739 this.log('selection cleared');
1744 return this.selected;
1747 // find if a timestamp (ms) is shown in the current chart
1748 this.timeIsVisible = function(t) {
1749 if(t >= this.data_after && t <= this.data_before)
1754 this.calculateRowForTime = function(t) {
1755 if(this.timeIsVisible(t) === false) return -1;
1756 return Math.floor((t - this.data_after) / this.data_update_every);
1759 // ----------------------------------------------------------------------------------------------------------------
1762 this.log = function(msg) {
1763 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1766 this.pauseChart = function() {
1767 if(this.paused === false) {
1768 if(this.debug === true)
1769 this.log('pauseChart()');
1775 this.unpauseChart = function() {
1776 if(this.paused === true) {
1777 if(this.debug === true)
1778 this.log('unpauseChart()');
1780 this.paused = false;
1784 this.resetChart = function(dont_clear_master, dont_update) {
1785 if(this.debug === true)
1786 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1788 if(typeof dont_clear_master === 'undefined')
1789 dont_clear_master = false;
1791 if(typeof dont_update === 'undefined')
1792 dont_update = false;
1794 if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1795 if(this.debug === true)
1796 this.log('resetChart() diverting to clearMaster().');
1797 // this will call us back with master === true
1798 NETDATA.globalPanAndZoom.clearMaster();
1802 this.clearSelection();
1804 this.tm.pan_and_zoom_seq = 0;
1806 this.setMode('auto');
1807 this.current.force_update_at = 0;
1808 this.current.force_before_ms = null;
1809 this.current.force_after_ms = null;
1810 this.tm.last_autorefreshed = 0;
1811 this.paused = false;
1812 this.selected = false;
1813 this.enabled = true;
1814 // this.debug = false;
1816 // do not update the chart here
1817 // or the chart will flip-flop when it is the master
1818 // of a selection sync and another chart becomes
1821 if(dont_update !== true && this.isVisible() === true) {
1826 this.updateChartPanOrZoom = function(after, before) {
1827 var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1830 if(this.debug === true)
1833 if(before < after) {
1834 if(this.debug === true)
1835 this.log(logme + 'flipped parameters, rejecting it.');
1840 if(typeof this.fixed_min_duration === 'undefined')
1841 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1843 var min_duration = this.fixed_min_duration;
1844 var current_duration = Math.round(this.view_before - this.view_after);
1846 // round the numbers
1847 after = Math.round(after);
1848 before = Math.round(before);
1850 // align them to update_every
1851 // stretching them further away
1852 after -= after % this.data_update_every;
1853 before += this.data_update_every - (before % this.data_update_every);
1855 // the final wanted duration
1856 var wanted_duration = before - after;
1858 // to allow panning, accept just a point below our minimum
1859 if((current_duration - this.data_update_every) < min_duration)
1860 min_duration = current_duration - this.data_update_every;
1862 // we do it, but we adjust to minimum size and return false
1863 // when the wanted size is below the current and the minimum
1865 if(wanted_duration < current_duration && wanted_duration < min_duration) {
1866 if(this.debug === true)
1867 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
1869 min_duration = this.fixed_min_duration;
1871 var dt = (min_duration - wanted_duration) / 2;
1874 wanted_duration = before - after;
1878 var tolerance = this.data_update_every * 2;
1879 var movement = Math.abs(before - this.view_before);
1881 if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
1882 if(this.debug === true)
1883 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);
1887 if(this.current.name === 'auto') {
1888 this.log(logme + 'caller called me with mode: ' + this.current.name);
1889 this.setMode('pan');
1892 if(this.debug === true)
1893 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);
1895 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1896 this.current.force_after_ms = after;
1897 this.current.force_before_ms = before;
1898 NETDATA.globalPanAndZoom.setMaster(this, after, before);
1902 this.legendFormatValue = function(value) {
1903 if(value === null || value === 'undefined') return '-';
1904 if(typeof value !== 'number') return value;
1906 var abs = Math.abs(value);
1907 if(abs >= 1000) return (Math.round(value)).toLocaleString();
1908 if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
1909 if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
1910 if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
1911 return (Math.round(value * 10000) / 10000).toLocaleString();
1914 this.legendSetLabelValue = function(label, value) {
1915 var series = this.element_legend_childs.series[label];
1916 if(typeof series === 'undefined') return;
1917 if(series.value === null && series.user === null) return;
1919 // if the value has not changed, skip DOM update
1920 //if(series.last === value) return;
1923 if(typeof value === 'number') {
1924 var v = Math.abs(value);
1925 s = r = this.legendFormatValue(value);
1927 if(typeof series.last === 'number') {
1928 if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1929 else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1930 else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1932 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1937 series.last = value;
1940 if(series.value !== null) series.value.innerHTML = s;
1941 if(series.user !== null) series.user.innerHTML = r;
1944 this.legendSetDate = function(ms) {
1945 if(typeof ms !== 'number') {
1946 this.legendShowUndefined();
1950 var d = new Date(ms);
1952 if(this.element_legend_childs.title_date)
1953 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
1955 if(this.element_legend_childs.title_time)
1956 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
1958 if(this.element_legend_childs.title_units)
1959 this.element_legend_childs.title_units.innerHTML = this.units;
1962 this.legendShowUndefined = function() {
1963 if(this.element_legend_childs.title_date)
1964 this.element_legend_childs.title_date.innerHTML = ' ';
1966 if(this.element_legend_childs.title_time)
1967 this.element_legend_childs.title_time.innerHTML = this.chart.name;
1969 if(this.element_legend_childs.title_units)
1970 this.element_legend_childs.title_units.innerHTML = ' ';
1972 if(this.data && this.element_legend_childs.series !== null) {
1973 var labels = this.data.dimension_names;
1974 var i = labels.length;
1976 var label = labels[i];
1978 if(typeof label === 'undefined') continue;
1979 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1980 this.legendSetLabelValue(label, null);
1985 this.legendShowLatestValues = function() {
1986 if(this.chart === null) return;
1987 if(this.selected) return;
1989 if(this.data === null || this.element_legend_childs.series === null) {
1990 this.legendShowUndefined();
1994 var show_undefined = true;
1995 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
1996 show_undefined = false;
1998 if(show_undefined) {
1999 this.legendShowUndefined();
2003 this.legendSetDate(this.view_before);
2005 var labels = this.data.dimension_names;
2006 var i = labels.length;
2008 var label = labels[i];
2010 if(typeof label === 'undefined') continue;
2011 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
2014 this.legendSetLabelValue(label, null);
2016 this.legendSetLabelValue(label, this.data.view_latest_values[i]);
2020 this.legendReset = function() {
2021 this.legendShowLatestValues();
2024 // this should be called just ONCE per dimension per chart
2025 this._chartDimensionColor = function(label) {
2026 if(this.colors === null) this.chartColors();
2028 if(typeof this.colors_assigned[label] === 'undefined') {
2029 if(this.colors_available.length === 0) {
2030 for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2031 this.colors_available.push(NETDATA.themes.current.colors[i]);
2034 this.colors_assigned[label] = this.colors_available.shift();
2036 if(this.debug === true)
2037 this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
2040 if(this.debug === true)
2041 this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
2044 this.colors.push(this.colors_assigned[label]);
2045 return this.colors_assigned[label];
2048 this.chartColors = function() {
2049 if(this.colors !== null) return this.colors;
2051 this.colors = new Array();
2052 this.colors_available = new Array();
2055 var c = $(this.element).data('colors');
2056 // this.log('read colors: ' + c);
2057 if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2058 if(typeof c !== 'string') {
2059 this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2066 for(i = 0, len = c.length; i < len ; i++) {
2068 this.colors_available.push(c[i]);
2069 // this.log('adding color: ' + c[i]);
2075 // push all the standard colors too
2076 for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2077 this.colors_available.push(NETDATA.themes.current.colors[i]);
2082 this.legendUpdateDOM = function() {
2085 // check that the legend DOM is up to date for the downloaded dimensions
2086 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2087 // this.log('the legend does not have any series - requesting legend update');
2090 else if(this.data === null) {
2091 // this.log('the chart does not have any data - requesting legend update');
2094 else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2098 var labels = this.data.dimension_names.toString();
2099 if(labels !== this.element_legend_childs.series.labels_key) {
2102 if(this.debug === true)
2103 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2107 if(needed === false) {
2108 // make sure colors available
2111 // do we have to update the current values?
2112 // we do this, only when the visible chart is current
2113 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2114 if(this.debug === true)
2115 this.log('chart is in latest position... updating values on legend...');
2117 //var labels = this.data.dimension_names;
2118 //var i = labels.length;
2120 // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2124 if(this.colors === null) {
2125 // this is the first time we update the chart
2126 // let's assign colors to all dimensions
2127 if(this.library.track_colors() === true)
2128 for(var dim in this.chart.dimensions)
2129 this._chartDimensionColor(this.chart.dimensions[dim].name);
2131 // we will re-generate the colors for the chart
2132 // based on the selected dimensions
2135 if(this.debug === true)
2136 this.log('updating Legend DOM');
2138 // mark all dimensions as invalid
2139 this.dimensions_visibility.invalidateAll();
2141 var genLabel = function(state, parent, name, count) {
2142 var color = state._chartDimensionColor(name);
2144 var user_element = null;
2145 var user_id = self.data('show-value-of-' + name + '-at') || null;
2146 if(user_id !== null) {
2147 user_element = document.getElementById(user_id) || null;
2148 if(user_element === null)
2149 state.log('Cannot find element with id: ' + user_id);
2152 state.element_legend_childs.series[name] = {
2153 name: document.createElement('span'),
2154 value: document.createElement('span'),
2159 var label = state.element_legend_childs.series[name];
2161 // create the dimension visibility tracking for this label
2162 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2164 var rgb = NETDATA.colorHex2Rgb(color);
2165 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2166 + state.chart.chart_type
2167 + '" style="background-color: '
2168 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2169 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2171 var text = document.createTextNode(' ' + name);
2172 label.name.appendChild(text);
2175 parent.appendChild(document.createElement('br'));
2177 parent.appendChild(label.name);
2178 parent.appendChild(label.value);
2181 var content = document.createElement('div');
2183 if(this.hasLegend()) {
2184 this.element_legend_childs = {
2186 resize_handler: document.createElement('div'),
2187 toolbox: document.createElement('div'),
2188 toolbox_left: document.createElement('div'),
2189 toolbox_right: document.createElement('div'),
2190 toolbox_reset: document.createElement('div'),
2191 toolbox_zoomin: document.createElement('div'),
2192 toolbox_zoomout: document.createElement('div'),
2193 toolbox_volume: document.createElement('div'),
2194 title_date: document.createElement('span'),
2195 title_time: document.createElement('span'),
2196 title_units: document.createElement('span'),
2197 nano: document.createElement('div'),
2199 paneClass: 'netdata-legend-series-pane',
2200 sliderClass: 'netdata-legend-series-slider',
2201 contentClass: 'netdata-legend-series-content',
2202 enabledClass: '__enabled',
2203 flashedClass: '__flashed',
2204 activeClass: '__active',
2206 alwaysVisible: true,
2212 this.element_legend.innerHTML = '';
2214 if(this.library.toolboxPanAndZoom !== null) {
2216 function get_pan_and_zoom_step(event) {
2218 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2220 else if (event.shiftKey)
2221 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2223 else if (event.altKey)
2224 return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2227 return NETDATA.options.current.pan_and_zoom_factor;
2230 this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2231 this.element.appendChild(this.element_legend_childs.toolbox);
2233 this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2234 this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2235 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2236 this.element_legend_childs.toolbox_left.onclick = function(e) {
2239 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2240 var before = that.view_before - step;
2241 var after = that.view_after - step;
2242 if(after >= that.netdata_first)
2243 that.library.toolboxPanAndZoom(that, after, before);
2245 if(NETDATA.options.current.show_help === true)
2246 $(this.element_legend_childs.toolbox_left).popover({
2251 placement: 'bottom',
2252 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2254 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>'
2258 this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2259 this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2260 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2261 this.element_legend_childs.toolbox_reset.onclick = function(e) {
2263 NETDATA.resetAllCharts(that);
2265 if(NETDATA.options.current.show_help === true)
2266 $(this.element_legend_childs.toolbox_reset).popover({
2271 placement: 'bottom',
2272 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2273 title: 'Chart Reset',
2274 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>'
2277 this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2278 this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2279 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2280 this.element_legend_childs.toolbox_right.onclick = function(e) {
2282 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2283 var before = that.view_before + step;
2284 var after = that.view_after + step;
2285 if(before <= that.netdata_last)
2286 that.library.toolboxPanAndZoom(that, after, before);
2288 if(NETDATA.options.current.show_help === true)
2289 $(this.element_legend_childs.toolbox_right).popover({
2294 placement: 'bottom',
2295 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2297 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>'
2301 this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2302 this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2303 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2304 this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2306 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2307 var before = that.view_before - dt;
2308 var after = that.view_after + dt;
2309 that.library.toolboxPanAndZoom(that, after, before);
2311 if(NETDATA.options.current.show_help === true)
2312 $(this.element_legend_childs.toolbox_zoomin).popover({
2317 placement: 'bottom',
2318 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2319 title: 'Chart Zoom In',
2320 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>'
2323 this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2324 this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2325 this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2326 this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2328 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);
2329 var before = that.view_before + dt;
2330 var after = that.view_after - dt;
2332 that.library.toolboxPanAndZoom(that, after, before);
2334 if(NETDATA.options.current.show_help === true)
2335 $(this.element_legend_childs.toolbox_zoomout).popover({
2340 placement: 'bottom',
2341 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2342 title: 'Chart Zoom Out',
2343 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>'
2346 //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2347 //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2348 //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2349 //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2350 //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2351 //e.preventDefault();
2352 //alert('clicked toolbox_volume on ' + that.id);
2356 this.element_legend_childs.toolbox = null;
2357 this.element_legend_childs.toolbox_left = null;
2358 this.element_legend_childs.toolbox_reset = null;
2359 this.element_legend_childs.toolbox_right = null;
2360 this.element_legend_childs.toolbox_zoomin = null;
2361 this.element_legend_childs.toolbox_zoomout = null;
2362 this.element_legend_childs.toolbox_volume = null;
2365 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2366 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2367 this.element.appendChild(this.element_legend_childs.resize_handler);
2368 if(NETDATA.options.current.show_help === true)
2369 $(this.element_legend_childs.resize_handler).popover({
2374 placement: 'bottom',
2375 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2376 title: 'Chart Resize',
2377 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>'
2381 this.element_legend_childs.resize_handler.onmousedown =
2383 that.resizeHandler(e);
2387 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2388 that.resizeHandler(e);
2391 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2392 this.element_legend.appendChild(this.element_legend_childs.title_date);
2394 this.element_legend.appendChild(document.createElement('br'));
2396 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2397 this.element_legend.appendChild(this.element_legend_childs.title_time);
2399 this.element_legend.appendChild(document.createElement('br'));
2401 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2402 this.element_legend.appendChild(this.element_legend_childs.title_units);
2404 this.element_legend.appendChild(document.createElement('br'));
2406 this.element_legend_childs.nano.className = 'netdata-legend-series';
2407 this.element_legend.appendChild(this.element_legend_childs.nano);
2409 content.className = 'netdata-legend-series-content';
2410 this.element_legend_childs.nano.appendChild(content);
2412 if(NETDATA.options.current.show_help === true)
2413 $(content).popover({
2418 placement: 'bottom',
2419 title: 'Chart Legend',
2420 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2421 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>'
2425 this.element_legend_childs = {
2427 resize_handler: null,
2430 toolbox_right: null,
2431 toolbox_reset: null,
2432 toolbox_zoomin: null,
2433 toolbox_zoomout: null,
2434 toolbox_volume: null,
2445 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2446 if(this.debug === true)
2447 this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2449 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2450 genLabel(this, content, this.data.dimension_names[i], i);
2454 var tmp = new Array();
2455 for(var dim in this.chart.dimensions) {
2456 tmp.push(this.chart.dimensions[dim].name);
2457 genLabel(this, content, this.chart.dimensions[dim].name, i);
2459 this.element_legend_childs.series.labels_key = tmp.toString();
2460 if(this.debug === true)
2461 this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2464 // create a hidden div to be used for hidding
2465 // the original legend of the chart library
2466 var el = document.createElement('div');
2467 if(this.element_legend !== null)
2468 this.element_legend.appendChild(el);
2469 el.style.display = 'none';
2471 this.element_legend_childs.hidden = document.createElement('div');
2472 el.appendChild(this.element_legend_childs.hidden);
2474 if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2475 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2477 this.legendShowLatestValues();
2480 this.hasLegend = function() {
2481 if(typeof this.___hasLegendCache___ !== 'undefined')
2482 return this.___hasLegendCache___;
2485 if(this.library && this.library.legend(this) === 'right-side') {
2486 var legend = $(this.element).data('legend') || 'yes';
2487 if(legend === 'yes') leg = true;
2490 this.___hasLegendCache___ = leg;
2494 this.legendWidth = function() {
2495 return (this.hasLegend())?140:0;
2498 this.legendHeight = function() {
2499 return $(this.element).height();
2502 this.chartWidth = function() {
2503 return $(this.element).width() - this.legendWidth();
2506 this.chartHeight = function() {
2507 return $(this.element).height();
2510 this.chartPixelsPerPoint = function() {
2511 // force an options provided detail
2512 var px = this.pixels_per_point;
2514 if(this.library && px < this.library.pixels_per_point(this))
2515 px = this.library.pixels_per_point(this);
2517 if(px < NETDATA.options.current.pixels_per_point)
2518 px = NETDATA.options.current.pixels_per_point;
2523 this.needsRecreation = function() {
2525 this.chart_created === true
2527 && this.library.autoresize() === false
2528 && this.tm.last_resized < NETDATA.options.last_resized
2532 this.chartURL = function() {
2533 var after, before, points_multiplier = 1;
2534 if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2535 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2537 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2538 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2539 this.view_after = after * 1000;
2540 this.view_before = before * 1000;
2542 this.requested_padding = null;
2543 points_multiplier = 1;
2545 else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2546 this.tm.pan_and_zoom_seq = 0;
2548 before = Math.round(this.current.force_before_ms / 1000);
2549 after = Math.round(this.current.force_after_ms / 1000);
2550 this.view_after = after * 1000;
2551 this.view_before = before * 1000;
2553 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2554 this.requested_padding = Math.round((before - after) / 2);
2555 after -= this.requested_padding;
2556 before += this.requested_padding;
2557 this.requested_padding *= 1000;
2558 points_multiplier = 2;
2561 this.current.force_before_ms = null;
2562 this.current.force_after_ms = null;
2565 this.tm.pan_and_zoom_seq = 0;
2567 before = this.before;
2569 this.view_after = after * 1000;
2570 this.view_before = before * 1000;
2572 this.requested_padding = null;
2573 points_multiplier = 1;
2576 this.requested_after = after * 1000;
2577 this.requested_before = before * 1000;
2579 this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2581 // build the data URL
2582 this.data_url = this.host + this.chart.data_url;
2583 this.data_url += "&format=" + this.library.format();
2584 this.data_url += "&points=" + (this.data_points * points_multiplier).toString();
2585 this.data_url += "&group=" + this.method;
2586 this.data_url += "&options=" + this.library.options(this);
2587 this.data_url += '|jsonwrap';
2589 if(NETDATA.options.current.eliminate_zero_dimensions === true)
2590 this.data_url += '|nonzero';
2592 if(this.append_options !== null)
2593 this.data_url += '|' + this.append_options.toString();
2596 this.data_url += "&after=" + after.toString();
2599 this.data_url += "&before=" + before.toString();
2602 this.data_url += "&dimensions=" + this.dimensions;
2604 if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2605 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2608 this.redrawChart = function() {
2609 if(this.data !== null)
2610 this.updateChartWithData(this.data);
2613 this.updateChartWithData = function(data) {
2614 if(this.debug === true)
2615 this.log('updateChartWithData() called.');
2617 // this may force the chart to be re-created
2621 this.updates_counter++;
2622 this.updates_since_last_unhide++;
2623 this.updates_since_last_creation++;
2625 var started = new Date().getTime();
2627 // if the result is JSON, find the latest update-every
2628 this.data_update_every = data.view_update_every * 1000;
2629 this.data_after = data.after * 1000;
2630 this.data_before = data.before * 1000;
2631 this.netdata_first = data.first_entry * 1000;
2632 this.netdata_last = data.last_entry * 1000;
2633 this.data_points = data.points;
2636 if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2637 if(this.view_after < this.data_after) {
2638 // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2639 this.view_after = this.data_after;
2642 if(this.view_before > this.data_before) {
2643 // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2644 this.view_before = this.data_before;
2648 this.view_after = this.data_after;
2649 this.view_before = this.data_before;
2652 if(this.debug === true) {
2653 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2655 if(this.current.force_after_ms)
2656 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2658 this.log('STATUS: forced : unset');
2660 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2661 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2662 this.log('STATUS: rendered : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2663 this.log('STATUS: points : ' + (this.data_points).toString());
2666 if(this.data_points === 0) {
2671 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2672 if(this.debug === true)
2673 this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2675 this.chart_created = false;
2678 // check and update the legend
2679 this.legendUpdateDOM();
2681 if(this.chart_created === true
2682 && typeof this.library.update === 'function') {
2684 if(this.debug === true)
2685 this.log('updating chart...');
2687 if(callChartLibraryUpdateSafely(data) === false)
2691 if(this.debug === true)
2692 this.log('creating chart...');
2694 if(callChartLibraryCreateSafely(data) === false)
2698 this.legendShowLatestValues();
2699 if(this.selected === true)
2700 NETDATA.globalSelectionSync.stop();
2702 // update the performance counters
2703 var now = new Date().getTime();
2704 this.tm.last_updated = now;
2706 // don't update last_autorefreshed if this chart is
2707 // forced to be updated with global PanAndZoom
2708 if(NETDATA.globalPanAndZoom.isActive())
2709 this.tm.last_autorefreshed = 0;
2711 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
2712 this.tm.last_autorefreshed = now - (now % this.data_update_every);
2714 this.tm.last_autorefreshed = now;
2717 this.refresh_dt_ms = now - started;
2718 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2720 if(this.refresh_dt_element !== null)
2721 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2724 this.updateChart = function(callback) {
2725 if(this.debug === true)
2726 this.log('updateChart() called.');
2728 if(this._updating === true) {
2729 if(this.debug === true)
2730 this.log('I am already updating...');
2732 if(typeof callback === 'function') callback();
2736 // due to late initialization of charts and libraries
2737 // we need to check this too
2738 if(this.enabled === false) {
2739 if(this.debug === true)
2740 this.log('I am not enabled');
2742 if(typeof callback === 'function') callback();
2746 if(canBeRendered() === false) {
2747 if(typeof callback === 'function') callback();
2751 if(this.chart === null) {
2752 this.getChart(function() { that.updateChart(callback); });
2756 if(this.library.initialized === false) {
2757 if(this.library.enabled === true) {
2758 this.library.initialize(function() { that.updateChart(callback); });
2762 error('chart library "' + this.library_name + '" is not available.');
2763 if(typeof callback === 'function') callback();
2768 this.clearSelection();
2771 if(this.debug === true)
2772 this.log('updating from ' + this.data_url);
2774 NETDATA.statistics.refreshes_total++;
2775 NETDATA.statistics.refreshes_active++;
2777 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
2778 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
2780 this._updating = true;
2782 this.xhr = $.ajax( {
2787 .success(function(data) {
2788 if(that.debug === true)
2789 that.log('data received. updating chart.');
2791 that.updateChartWithData(data);
2794 error('data download failed for url: ' + that.data_url);
2796 .always(function() {
2797 NETDATA.statistics.refreshes_active--;
2798 that._updating = false;
2799 if(typeof callback === 'function') callback();
2805 this.isVisible = function(nocache) {
2806 if(typeof nocache === 'undefined')
2809 // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2811 // caching - we do not evaluate the charts visibility
2812 // if the page has not been scrolled since the last check
2813 if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2814 return this.___isVisible___;
2816 this.tm.last_visible_check = new Date().getTime();
2818 var wh = window.innerHeight;
2819 var x = this.element.getBoundingClientRect();
2823 if(x.width === 0 || x.height === 0) {
2825 this.___isVisible___ = false;
2826 return this.___isVisible___;
2829 if(x.top < 0 && -x.top > x.height) {
2830 // the chart is entirely above
2831 ret = -x.top - x.height;
2833 else if(x.top > wh) {
2834 // the chart is entirely below
2838 if(ret > tolerance) {
2839 // the chart is too far
2842 this.___isVisible___ = false;
2843 return this.___isVisible___;
2846 // the chart is inside or very close
2849 this.___isVisible___ = true;
2850 return this.___isVisible___;
2854 this.isAutoRefreshable = function() {
2855 return (this.current.autorefresh);
2858 this.canBeAutoRefreshed = function() {
2859 var now = new Date().getTime();
2861 if(this.running === true) {
2862 if(this.debug === true)
2863 this.log('I am already running');
2868 if(this.enabled === false) {
2869 if(this.debug === true)
2870 this.log('I am not enabled');
2875 if(this.library === null || this.library.enabled === false) {
2876 error('charting library "' + this.library_name + '" is not available');
2877 if(this.debug === true)
2878 this.log('My chart library ' + this.library_name + ' is not available');
2883 if(this.isVisible() === false) {
2884 if(NETDATA.options.debug.visibility === true || this.debug === true)
2885 this.log('I am not visible');
2890 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
2891 if(this.debug === true)
2892 this.log('timed force update detected - allowing this update');
2894 this.current.force_update_at = 0;
2898 if(this.isAutoRefreshable() === true) {
2899 // allow the first update, even if the page is not visible
2900 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
2901 if(NETDATA.options.debug.focus === true || this.debug === true)
2902 this.log('canBeAutoRefreshed(): page does not have focus');
2907 if(this.needsRecreation() === true) {
2908 if(this.debug === true)
2909 this.log('canBeAutoRefreshed(): needs re-creation.');
2914 // options valid only for autoRefresh()
2915 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
2916 if(NETDATA.globalPanAndZoom.isActive()) {
2917 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
2918 if(this.debug === true)
2919 this.log('canBeAutoRefreshed(): global panning: I need an update.');
2924 if(this.debug === true)
2925 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
2931 if(this.selected === true) {
2932 if(this.debug === true)
2933 this.log('canBeAutoRefreshed(): I have a selection in place.');
2938 if(this.paused === true) {
2939 if(this.debug === true)
2940 this.log('canBeAutoRefreshed(): I am paused.');
2945 if(now - this.tm.last_autorefreshed >= this.data_update_every) {
2946 if(this.debug === true)
2947 this.log('canBeAutoRefreshed(): It is time to update me.');
2957 this.autoRefresh = function(callback) {
2958 if(this.canBeAutoRefreshed() === true && this.running === false) {
2961 state.running = true;
2962 state.updateChart(function() {
2963 state.running = false;
2965 if(typeof callback !== 'undefined')
2970 if(typeof callback !== 'undefined')
2975 this._defaultsFromDownloadedChart = function(chart) {
2977 this.chart_url = chart.url;
2978 this.data_update_every = chart.update_every * 1000;
2979 this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2980 this.tm.last_info_downloaded = new Date().getTime();
2982 if(this.title === null)
2983 this.title = chart.title;
2985 if(this.units === null)
2986 this.units = chart.units;
2989 // fetch the chart description from the netdata server
2990 this.getChart = function(callback) {
2991 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
2993 this._defaultsFromDownloadedChart(this.chart);
2994 if(typeof callback === 'function') callback();
2997 this.chart_url = "/api/v1/chart?chart=" + this.id;
2999 if(this.debug === true)
3000 this.log('downloading ' + this.chart_url);
3003 url: this.host + this.chart_url,
3007 .done(function(chart) {
3008 chart.url = that.chart_url;
3009 that._defaultsFromDownloadedChart(chart);
3010 NETDATA.chartRegistry.add(that.host, that.id, chart);
3013 NETDATA.error(404, that.chart_url);
3014 error('chart not found on url "' + that.chart_url + '"');
3016 .always(function() {
3017 if(typeof callback === 'function') callback();
3022 // ============================================================================================================
3028 NETDATA.resetAllCharts = function(state) {
3029 // first clear the global selection sync
3030 // to make sure no chart is in selected state
3031 state.globalSelectionSyncStop();
3033 // there are 2 possibilities here
3034 // a. state is the global Pan and Zoom master
3035 // b. state is not the global Pan and Zoom master
3037 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
3040 // clear the global Pan and Zoom
3041 // this will also refresh the master
3042 // and unblock any charts currently mirroring the master
3043 NETDATA.globalPanAndZoom.clearMaster();
3045 // if we were not the master, reset our status too
3046 // this is required because most probably the mouse
3047 // is over this chart, blocking it from auto-refreshing
3048 if(master === false && (state.paused === true || state.selected === true))
3052 // get or create a chart state, given a DOM element
3053 NETDATA.chartState = function(element) {
3054 var state = $(element).data('netdata-state-object') || null;
3055 if(state === null) {
3056 state = new chartState(element);
3057 $(element).data('netdata-state-object', state);
3062 // ----------------------------------------------------------------------------------------------------------------
3063 // Library functions
3065 // Load a script without jquery
3066 // This is used to load jquery - after it is loaded, we use jquery
3067 NETDATA._loadjQuery = function(callback) {
3068 if(typeof jQuery === 'undefined') {
3069 if(NETDATA.options.debug.main_loop === true)
3070 console.log('loading ' + NETDATA.jQuery);
3072 var script = document.createElement('script');
3073 script.type = 'text/javascript';
3074 script.async = true;
3075 script.src = NETDATA.jQuery;
3077 // script.onabort = onError;
3078 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3079 if(typeof callback === "function")
3080 script.onload = callback;
3082 var s = document.getElementsByTagName('script')[0];
3083 s.parentNode.insertBefore(script, s);
3085 else if(typeof callback === "function")
3089 NETDATA._loadCSS = function(filename) {
3090 // don't use jQuery here
3091 // styles are loaded before jQuery
3092 // to eliminate showing an unstyled page to the user
3094 var fileref = document.createElement("link");
3095 fileref.setAttribute("rel", "stylesheet");
3096 fileref.setAttribute("type", "text/css");
3097 fileref.setAttribute("href", filename);
3099 if (typeof fileref !== 'undefined')
3100 document.getElementsByTagName("head")[0].appendChild(fileref);
3103 NETDATA.colorHex2Rgb = function(hex) {
3104 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3105 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3106 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3107 return r + r + g + g + b + b;
3110 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3112 r: parseInt(result[1], 16),
3113 g: parseInt(result[2], 16),
3114 b: parseInt(result[3], 16)
3118 NETDATA.colorLuminance = function(hex, lum) {
3119 // validate hex string
3120 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3122 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3126 // convert to decimal and change luminosity
3127 var rgb = "#", c, i;
3128 for (i = 0; i < 3; i++) {
3129 c = parseInt(hex.substr(i*2,2), 16);
3130 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3131 rgb += ("00"+c).substr(c.length);
3137 NETDATA.guid = function() {
3139 return Math.floor((1 + Math.random()) * 0x10000)
3144 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3147 NETDATA.zeropad = function(x) {
3148 if(x > -10 && x < 10) return '0' + x.toString();
3149 else return x.toString();
3152 // user function to signal us the DOM has been
3154 NETDATA.updatedDom = function() {
3155 NETDATA.options.updated_dom = true;
3158 NETDATA.ready = function(callback) {
3159 NETDATA.options.pauseCallback = callback;
3162 NETDATA.pause = function(callback) {
3163 if(NETDATA.options.pause === true)
3166 NETDATA.options.pauseCallback = callback;
3169 NETDATA.unpause = function() {
3170 NETDATA.options.pauseCallback = null;
3171 NETDATA.options.updated_dom = true;
3172 NETDATA.options.pause = false;
3175 // ----------------------------------------------------------------------------------------------------------------
3177 // this is purely sequencial charts refresher
3178 // it is meant to be autonomous
3179 NETDATA.chartRefresherNoParallel = function(index) {
3180 if(NETDATA.options.debug.mail_loop === true)
3181 console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3183 if(NETDATA.options.updated_dom === true) {
3184 // the dom has been updated
3185 // get the dom parts again
3186 NETDATA.parseDom(NETDATA.chartRefresher);
3189 if(index >= NETDATA.options.targets.length) {
3190 if(NETDATA.options.debug.main_loop === true)
3191 console.log('waiting to restart main loop...');
3193 NETDATA.options.auto_refresher_fast_weight = 0;
3195 setTimeout(function() {
3196 NETDATA.chartRefresher();
3197 }, NETDATA.options.current.idle_between_loops);
3200 var state = NETDATA.options.targets[index];
3202 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3203 if(NETDATA.options.debug.main_loop === true)
3204 console.log('fast rendering...');
3206 state.autoRefresh(function() {
3207 NETDATA.chartRefresherNoParallel(++index);
3211 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3212 NETDATA.options.auto_refresher_fast_weight = 0;
3214 setTimeout(function() {
3215 state.autoRefresh(function() {
3216 NETDATA.chartRefresherNoParallel(++index);
3218 }, NETDATA.options.current.idle_between_charts);
3223 // this is part of the parallel refresher
3224 // its cause is to refresh sequencially all the charts
3225 // that depend on chart library initialization
3226 // it will call the parallel refresher back
3227 // as soon as it sees a chart that its chart library
3229 NETDATA.chartRefresher_uninitialized = function() {
3230 if(NETDATA.options.updated_dom === true) {
3231 // the dom has been updated
3232 // get the dom parts again
3233 NETDATA.parseDom(NETDATA.chartRefresher);
3237 if(NETDATA.options.sequencial.length === 0)
3238 NETDATA.chartRefresher();
3240 var state = NETDATA.options.sequencial.pop();
3241 if(state.library.initialized === true)
3242 NETDATA.chartRefresher();
3244 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3248 NETDATA.chartRefresherWaitTime = function() {
3249 return NETDATA.options.current.idle_parallel_loops;
3252 // the default refresher
3253 // it will create 2 sets of charts:
3254 // - the ones that can be refreshed in parallel
3255 // - the ones that depend on something else
3256 // the first set will be executed in parallel
3257 // the second will be given to NETDATA.chartRefresher_uninitialized()
3258 NETDATA.chartRefresher = function() {
3259 if(NETDATA.options.pause === true) {
3260 // console.log('auto-refresher is paused');
3261 setTimeout(NETDATA.chartRefresher,
3262 NETDATA.chartRefresherWaitTime());
3266 if(typeof NETDATA.options.pauseCallback === 'function') {
3267 // console.log('auto-refresher is calling pauseCallback');
3268 NETDATA.options.pause = true;
3269 NETDATA.options.pauseCallback();
3270 NETDATA.chartRefresher();
3274 if(NETDATA.options.current.parallel_refresher === false) {
3275 NETDATA.chartRefresherNoParallel(0);
3279 if(NETDATA.options.updated_dom === true) {
3280 // the dom has been updated
3281 // get the dom parts again
3282 NETDATA.parseDom(NETDATA.chartRefresher);
3286 var parallel = new Array();
3287 var targets = NETDATA.options.targets;
3288 var len = targets.length;
3291 state = targets[len];
3292 if(state.isVisible() === false || state.running === true)
3295 if(state.library.initialized === false) {
3296 if(state.library.enabled === true) {
3297 state.library.initialize(NETDATA.chartRefresher);
3301 state.error('chart library "' + state.library_name + '" is not enabled.');
3305 parallel.unshift(state);
3308 if(parallel.length > 0) {
3309 // this will execute the jobs in parallel
3310 $(parallel).each(function() {
3315 // run the next refresh iteration
3316 setTimeout(NETDATA.chartRefresher,
3317 NETDATA.chartRefresherWaitTime());
3320 NETDATA.parseDom = function(callback) {
3321 NETDATA.options.last_page_scroll = new Date().getTime();
3322 NETDATA.options.updated_dom = false;
3324 var targets = $('div[data-netdata]'); //.filter(':visible');
3326 if(NETDATA.options.debug.main_loop === true)
3327 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3329 NETDATA.options.targets = new Array();
3330 var len = targets.length;
3332 // the initialization will take care of sizing
3333 // and the "loading..." message
3334 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3337 if(typeof callback === 'function') callback();
3340 // this is the main function - where everything starts
3341 NETDATA.start = function() {
3342 // this should be called only once
3344 NETDATA.options.page_is_visible = true;
3346 $(window).blur(function() {
3347 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3348 NETDATA.options.page_is_visible = false;
3349 if(NETDATA.options.debug.focus === true)
3350 console.log('Lost Focus!');
3354 $(window).focus(function() {
3355 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3356 NETDATA.options.page_is_visible = true;
3357 if(NETDATA.options.debug.focus === true)
3358 console.log('Focus restored!');
3362 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3363 if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3364 NETDATA.options.page_is_visible = false;
3365 if(NETDATA.options.debug.focus === true)
3366 console.log('Document has no focus!');
3370 // bootstrap tab switching
3371 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3373 // bootstrap modal switching
3374 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3375 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3377 // bootstrap collapse switching
3378 $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
3379 $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
3381 NETDATA.parseDom(NETDATA.chartRefresher);
3384 // ----------------------------------------------------------------------------------------------------------------
3387 NETDATA.peityInitialize = function(callback) {
3388 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3390 url: NETDATA.peity_js,
3395 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3398 NETDATA.chartLibraries.peity.enabled = false;
3399 NETDATA.error(100, NETDATA.peity_js);
3401 .always(function() {
3402 if(typeof callback === "function")
3407 NETDATA.chartLibraries.peity.enabled = false;
3408 if(typeof callback === "function")
3413 NETDATA.peityChartUpdate = function(state, data) {
3414 state.peity_instance.innerHTML = data.result;
3416 if(state.peity_options.stroke !== state.chartColors()[0]) {
3417 state.peity_options.stroke = state.chartColors()[0];
3418 if(state.chart.chart_type === 'line')
3419 state.peity_options.fill = NETDATA.themes.current.background;
3421 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3424 $(state.peity_instance).peity('line', state.peity_options);
3428 NETDATA.peityChartCreate = function(state, data) {
3429 state.peity_instance = document.createElement('div');
3430 state.element_chart.appendChild(state.peity_instance);
3432 var self = $(state.element);
3433 state.peity_options = {
3434 stroke: NETDATA.themes.current.foreground,
3435 strokeWidth: self.data('peity-strokewidth') || 1,
3436 width: state.chartWidth(),
3437 height: state.chartHeight(),
3438 fill: NETDATA.themes.current.foreground
3441 NETDATA.peityChartUpdate(state, data);
3445 // ----------------------------------------------------------------------------------------------------------------
3448 NETDATA.sparklineInitialize = function(callback) {
3449 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3451 url: NETDATA.sparkline_js,
3456 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3459 NETDATA.chartLibraries.sparkline.enabled = false;
3460 NETDATA.error(100, NETDATA.sparkline_js);
3462 .always(function() {
3463 if(typeof callback === "function")
3468 NETDATA.chartLibraries.sparkline.enabled = false;
3469 if(typeof callback === "function")
3474 NETDATA.sparklineChartUpdate = function(state, data) {
3475 state.sparkline_options.width = state.chartWidth();
3476 state.sparkline_options.height = state.chartHeight();
3478 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3482 NETDATA.sparklineChartCreate = function(state, data) {
3483 var self = $(state.element);
3484 var type = self.data('sparkline-type') || 'line';
3485 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3486 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3487 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3488 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3489 var composite = self.data('sparkline-composite') || undefined;
3490 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3491 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3492 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3493 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3494 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3495 var spotColor = self.data('sparkline-spotcolor') || undefined;
3496 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3497 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3498 var spotRadius = self.data('sparkline-spotradius') || undefined;
3499 var valueSpots = self.data('sparkline-valuespots') || undefined;
3500 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3501 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3502 var lineWidth = self.data('sparkline-linewidth') || undefined;
3503 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3504 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3505 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3506 var xvalues = self.data('sparkline-xvalues') || undefined;
3507 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3508 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3509 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3510 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3511 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3512 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3513 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3514 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3515 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3516 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3517 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3518 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3519 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3520 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3521 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3522 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3523 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3524 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3525 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3526 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3527 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3528 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3530 state.sparkline_options = {
3532 lineColor: lineColor,
3533 fillColor: fillColor,
3534 chartRangeMin: chartRangeMin,
3535 chartRangeMax: chartRangeMax,
3536 composite: composite,
3537 enableTagOptions: enableTagOptions,
3538 tagOptionPrefix: tagOptionPrefix,
3539 tagValuesAttribute: tagValuesAttribute,
3540 disableHiddenCheck: disableHiddenCheck,
3541 defaultPixelsPerValue: defaultPixelsPerValue,
3542 spotColor: spotColor,
3543 minSpotColor: minSpotColor,
3544 maxSpotColor: maxSpotColor,
3545 spotRadius: spotRadius,
3546 valueSpots: valueSpots,
3547 highlightSpotColor: highlightSpotColor,
3548 highlightLineColor: highlightLineColor,
3549 lineWidth: lineWidth,
3550 normalRangeMin: normalRangeMin,
3551 normalRangeMax: normalRangeMax,
3552 drawNormalOnTop: drawNormalOnTop,
3554 chartRangeClip: chartRangeClip,
3555 chartRangeMinX: chartRangeMinX,
3556 chartRangeMaxX: chartRangeMaxX,
3557 disableInteraction: disableInteraction,
3558 disableTooltips: disableTooltips,
3559 disableHighlight: disableHighlight,
3560 highlightLighten: highlightLighten,
3561 highlightColor: highlightColor,
3562 tooltipContainer: tooltipContainer,
3563 tooltipClassname: tooltipClassname,
3564 tooltipChartTitle: state.title,
3565 tooltipFormat: tooltipFormat,
3566 tooltipPrefix: tooltipPrefix,
3567 tooltipSuffix: tooltipSuffix,
3568 tooltipSkipNull: tooltipSkipNull,
3569 tooltipValueLookups: tooltipValueLookups,
3570 tooltipFormatFieldlist: tooltipFormatFieldlist,
3571 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3572 numberFormatter: numberFormatter,
3573 numberDigitGroupSep: numberDigitGroupSep,
3574 numberDecimalMark: numberDecimalMark,
3575 numberDigitGroupCount: numberDigitGroupCount,
3576 animatedZooms: animatedZooms,
3577 width: state.chartWidth(),
3578 height: state.chartHeight()
3581 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3585 // ----------------------------------------------------------------------------------------------------------------
3592 NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3593 if(after < state.netdata_first)
3594 after = state.netdata_first;
3596 if(before > state.netdata_last)
3597 before = state.netdata_last;
3599 state.setMode('zoom');
3600 state.globalSelectionSyncStop();
3601 state.globalSelectionSyncDelay();
3602 state.dygraph_user_action = true;
3603 state.dygraph_force_zoom = true;
3604 state.updateChartPanOrZoom(after, before);
3605 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3608 NETDATA.dygraphSetSelection = function(state, t) {
3609 if(typeof state.dygraph_instance !== 'undefined') {
3610 var r = state.calculateRowForTime(t);
3612 state.dygraph_instance.setSelection(r);
3614 state.dygraph_instance.clearSelection();
3615 state.legendShowUndefined();
3622 NETDATA.dygraphClearSelection = function(state, t) {
3623 if(typeof state.dygraph_instance !== 'undefined') {
3624 state.dygraph_instance.clearSelection();
3629 NETDATA.dygraphSmoothInitialize = function(callback) {
3631 url: NETDATA.dygraph_smooth_js,
3636 NETDATA.dygraph.smooth = true;
3637 smoothPlotter.smoothing = 0.3;
3640 NETDATA.dygraph.smooth = false;
3642 .always(function() {
3643 if(typeof callback === "function")
3648 NETDATA.dygraphInitialize = function(callback) {
3649 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3651 url: NETDATA.dygraph_js,
3656 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3659 NETDATA.chartLibraries.dygraph.enabled = false;
3660 NETDATA.error(100, NETDATA.dygraph_js);
3662 .always(function() {
3663 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3664 NETDATA.dygraphSmoothInitialize(callback);
3665 else if(typeof callback === "function")
3670 NETDATA.chartLibraries.dygraph.enabled = false;
3671 if(typeof callback === "function")
3676 NETDATA.dygraphChartUpdate = function(state, data) {
3677 var dygraph = state.dygraph_instance;
3679 if(typeof dygraph === 'undefined')
3680 return NETDATA.dygraphChartCreate(state, data);
3682 // when the chart is not visible, and hidden
3683 // if there is a window resize, dygraph detects
3684 // its element size as 0x0.
3685 // this will make it re-appear properly
3687 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3691 file: data.result.data,
3692 colors: state.chartColors(),
3693 labels: data.result.labels,
3694 labelsDivWidth: state.chartWidth() - 70,
3695 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3698 if(state.dygraph_force_zoom === true) {
3699 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3700 state.log('dygraphChartUpdate() forced zoom update');
3702 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3703 options.valueRange = null;
3704 options.isZoomedIgnoreProgrammaticZoom = true;
3705 state.dygraph_force_zoom = false;
3707 else if(state.current.name !== 'auto') {
3708 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3709 state.log('dygraphChartUpdate() loose update');
3712 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3713 state.log('dygraphChartUpdate() strict update');
3715 options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3716 options.valueRange = null;
3717 options.isZoomedIgnoreProgrammaticZoom = true;
3720 if(state.dygraph_smooth_eligible === true) {
3721 if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3722 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3723 NETDATA.dygraphChartCreate(state, data);
3728 dygraph.updateOptions(options);
3730 state.dygraph_last_rendered = new Date().getTime();
3734 NETDATA.dygraphChartCreate = function(state, data) {
3735 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3736 state.log('dygraphChartCreate()');
3738 var self = $(state.element);
3740 var chart_type = state.chart.chart_type;
3741 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3742 chart_type = self.data('dygraph-type') || chart_type;
3744 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3745 smooth = self.data('dygraph-smooth') || smooth;
3747 if(NETDATA.dygraph.smooth === false)
3750 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3751 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3753 state.dygraph_options = {
3754 colors: self.data('dygraph-colors') || state.chartColors(),
3756 // leave a few pixels empty on the right of the chart
3757 rightGap: self.data('dygraph-rightgap') || 5,
3758 showRangeSelector: self.data('dygraph-showrangeselector') || false,
3759 showRoller: self.data('dygraph-showroller') || false,
3761 title: self.data('dygraph-title') || state.title,
3762 titleHeight: self.data('dygraph-titleheight') || 19,
3764 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3765 labels: data.result.labels,
3766 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3767 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3768 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3769 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3770 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3773 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3774 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3776 ylabel: state.units,
3777 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3779 // the function to plot the chart
3782 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3783 strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3784 strokePattern: self.data('dygraph-strokepattern') || undefined,
3786 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3787 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3788 drawPoints: self.data('dygraph-drawpoints') || false,
3790 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3791 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3793 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3794 pointSize: self.data('dygraph-pointsize') || 1,
3796 // enabling this makes the chart with little square lines
3797 stepPlot: self.data('dygraph-stepplot') || false,
3799 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3800 strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3801 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3803 fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3804 fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3805 stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
3806 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
3808 drawAxis: self.data('dygraph-drawaxis') || true,
3809 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
3810 axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
3811 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
3813 drawGrid: self.data('dygraph-drawgrid') || true,
3814 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
3815 drawYGrid: self.data('dygraph-drawygrid') || undefined,
3816 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
3817 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
3818 gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
3820 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
3821 sigFigs: self.data('dygraph-sigfigs') || null,
3822 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
3823 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
3825 highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
3826 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
3827 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
3829 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
3830 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
3834 ticker: Dygraph.dateTicker,
3835 axisLabelFormatter: function (d, gran) {
3836 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3838 valueFormatter: function (ms) {
3839 var d = new Date(ms);
3840 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
3841 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3846 valueFormatter: function (x) {
3847 // we format legends with the state object
3848 // no need to do anything here
3849 // return (Math.round(x*100) / 100).toLocaleString();
3850 // return state.legendFormatValue(x);
3855 legendFormatter: function(data) {
3856 var elements = state.element_legend_childs;
3858 // if the hidden div is not there
3859 // we are not managing the legend
3860 if(elements.hidden === null) return;
3862 if (typeof data.x !== 'undefined') {
3863 state.legendSetDate(data.x);
3864 var i = data.series.length;
3866 var series = data.series[i];
3867 if(!series.isVisible) continue;
3868 state.legendSetLabelValue(series.label, series.y);
3874 drawCallback: function(dygraph, is_initial) {
3875 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
3876 state.dygraph_user_action = false;
3878 var x_range = dygraph.xAxisRange();
3879 var after = Math.round(x_range[0]);
3880 var before = Math.round(x_range[1]);
3882 if(NETDATA.options.debug.dygraph === true)
3883 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
3885 if(before <= state.netdata_last && after >= state.netdata_first)
3886 state.updateChartPanOrZoom(after, before);
3889 zoomCallback: function(minDate, maxDate, yRanges) {
3890 if(NETDATA.options.debug.dygraph === true)
3891 state.log('dygraphZoomCallback()');
3893 state.globalSelectionSyncStop();
3894 state.globalSelectionSyncDelay();
3895 state.setMode('zoom');
3897 // refresh it to the greatest possible zoom level
3898 state.dygraph_user_action = true;
3899 state.dygraph_force_zoom = true;
3900 state.updateChartPanOrZoom(minDate, maxDate);
3902 highlightCallback: function(event, x, points, row, seriesName) {
3903 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3904 state.log('dygraphHighlightCallback()');
3908 // there is a bug in dygraph when the chart is zoomed enough
3909 // the time it thinks is selected is wrong
3910 // here we calculate the time t based on the row number selected
3912 var t = state.data_after + row * state.data_update_every;
3913 // 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);
3915 state.globalSelectionSync(x);
3917 // fix legend zIndex using the internal structures of dygraph legend module
3918 // this works, but it is a hack!
3919 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
3921 unhighlightCallback: function(event) {
3922 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3923 state.log('dygraphUnhighlightCallback()');
3925 state.unpauseChart();
3926 state.globalSelectionSyncStop();
3928 interactionModel : {
3929 mousedown: function(event, dygraph, context) {
3930 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3931 state.log('interactionModel.mousedown()');
3933 state.dygraph_user_action = true;
3934 state.globalSelectionSyncStop();
3936 if(NETDATA.options.debug.dygraph === true)
3937 state.log('dygraphMouseDown()');
3939 // Right-click should not initiate a zoom.
3940 if(event.button && event.button === 2) return;
3942 context.initializeMouseDown(event, dygraph, context);
3944 if(event.button && event.button === 1) {
3945 if (event.altKey || event.shiftKey) {
3946 state.setMode('pan');
3947 state.globalSelectionSyncDelay();
3948 Dygraph.startPan(event, dygraph, context);
3951 state.setMode('zoom');
3952 state.globalSelectionSyncDelay();
3953 Dygraph.startZoom(event, dygraph, context);
3957 if (event.altKey || event.shiftKey) {
3958 state.setMode('zoom');
3959 state.globalSelectionSyncDelay();
3960 Dygraph.startZoom(event, dygraph, context);
3963 state.setMode('pan');
3964 state.globalSelectionSyncDelay();
3965 Dygraph.startPan(event, dygraph, context);
3969 mousemove: function(event, dygraph, context) {
3970 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3971 state.log('interactionModel.mousemove()');
3973 if(context.isPanning) {
3974 state.dygraph_user_action = true;
3975 state.globalSelectionSyncStop();
3976 state.globalSelectionSyncDelay();
3977 state.setMode('pan');
3978 Dygraph.movePan(event, dygraph, context);
3980 else if(context.isZooming) {
3981 state.dygraph_user_action = true;
3982 state.globalSelectionSyncStop();
3983 state.globalSelectionSyncDelay();
3984 state.setMode('zoom');
3985 Dygraph.moveZoom(event, dygraph, context);
3988 mouseup: function(event, dygraph, context) {
3989 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3990 state.log('interactionModel.mouseup()');
3992 if (context.isPanning) {
3993 state.dygraph_user_action = true;
3994 state.globalSelectionSyncDelay();
3995 Dygraph.endPan(event, dygraph, context);
3997 else if (context.isZooming) {
3998 state.dygraph_user_action = true;
3999 state.globalSelectionSyncDelay();
4000 Dygraph.endZoom(event, dygraph, context);
4003 click: function(event, dygraph, context) {
4004 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4005 state.log('interactionModel.click()');
4007 event.preventDefault();
4009 dblclick: function(event, dygraph, context) {
4010 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4011 state.log('interactionModel.dblclick()');
4012 NETDATA.resetAllCharts(state);
4014 mousewheel: function(event, dygraph, context) {
4015 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4016 state.log('interactionModel.mousewheel()');
4018 // Take the offset of a mouse event on the dygraph canvas and
4019 // convert it to a pair of percentages from the bottom left.
4020 // (Not top left, bottom is where the lower value is.)
4021 function offsetToPercentage(g, offsetX, offsetY) {
4022 // This is calculating the pixel offset of the leftmost date.
4023 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
4024 var yar0 = g.yAxisRange(0);
4026 // This is calculating the pixel of the higest value. (Top pixel)
4027 var yOffset = g.toDomCoords(null, yar0[1])[1];
4029 // x y w and h are relative to the corner of the drawing area,
4030 // so that the upper corner of the drawing area is (0, 0).
4031 var x = offsetX - xOffset;
4032 var y = offsetY - yOffset;
4034 // This is computing the rightmost pixel, effectively defining the
4036 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
4038 // This is computing the lowest pixel, effectively defining the height.
4039 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
4041 // Percentage from the left.
4042 var xPct = w === 0 ? 0 : (x / w);
4043 // Percentage from the top.
4044 var yPct = h === 0 ? 0 : (y / h);
4046 // The (1-) part below changes it from "% distance down from the top"
4047 // to "% distance up from the bottom".
4048 return [xPct, (1-yPct)];
4051 // Adjusts [x, y] toward each other by zoomInPercentage%
4052 // Split it so the left/bottom axis gets xBias/yBias of that change and
4053 // tight/top gets (1-xBias)/(1-yBias) of that change.
4055 // If a bias is missing it splits it down the middle.
4056 function zoomRange(g, zoomInPercentage, xBias, yBias) {
4057 xBias = xBias || 0.5;
4058 yBias = yBias || 0.5;
4060 function adjustAxis(axis, zoomInPercentage, bias) {
4061 var delta = axis[1] - axis[0];
4062 var increment = delta * zoomInPercentage;
4063 var foo = [increment * bias, increment * (1-bias)];
4065 return [ axis[0] + foo[0], axis[1] - foo[1] ];
4068 var yAxes = g.yAxisRanges();
4070 for (var i = 0; i < yAxes.length; i++) {
4071 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4074 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4077 if(event.altKey || event.shiftKey) {
4078 state.dygraph_user_action = true;
4080 state.globalSelectionSyncStop();
4081 state.globalSelectionSyncDelay();
4083 // http://dygraphs.com/gallery/interaction-api.js
4084 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4085 var percentage = normal / 50;
4087 if (!(event.offsetX && event.offsetY)){
4088 event.offsetX = event.layerX - event.target.offsetLeft;
4089 event.offsetY = event.layerY - event.target.offsetTop;
4092 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4093 var xPct = percentages[0];
4094 var yPct = percentages[1];
4096 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4098 var after = new_x_range[0];
4099 var before = new_x_range[1];
4101 var first = state.netdata_first + state.data_update_every;
4102 var last = state.netdata_last + state.data_update_every;
4105 after -= (before - last);
4112 state.setMode('zoom');
4113 if(state.updateChartPanOrZoom(after, before) === true)
4114 dygraph.updateOptions({ dateWindow: [ after, before ] });
4116 event.preventDefault();
4119 touchstart: function(event, dygraph, context) {
4120 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4121 state.log('interactionModel.touchstart()');
4123 state.dygraph_user_action = true;
4124 state.setMode('zoom');
4127 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4129 // we overwrite the touch directions at the end, to overwrite
4130 // the internal default of dygraphs
4131 context.touchDirections = { x: true, y: false };
4133 state.dygraph_last_touch_start = new Date().getTime();
4134 state.dygraph_last_touch_move = 0;
4136 if(typeof event.touches[0].pageX === 'number')
4137 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4139 state.dygraph_last_touch_page_x = 0;
4141 touchmove: function(event, dygraph, context) {
4142 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4143 state.log('interactionModel.touchmove()');
4145 state.dygraph_user_action = true;
4146 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4148 state.dygraph_last_touch_move = new Date().getTime();
4150 touchend: function(event, dygraph, context) {
4151 if(NETDATA.options.debug.dygraph === true || state.debug === true)
4152 state.log('interactionModel.touchend()');
4154 state.dygraph_user_action = true;
4155 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4157 // if it didn't move, it is a selection
4158 if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4159 // internal api of dygraphs
4160 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4161 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4162 if(NETDATA.dygraphSetSelection(state, t) === true)
4163 state.globalSelectionSync(t);
4166 // if it was double tap within double click time, reset the charts
4167 var now = new Date().getTime();
4168 if(typeof state.dygraph_last_touch_end !== 'undefined') {
4169 if(state.dygraph_last_touch_move === 0) {
4170 var dt = now - state.dygraph_last_touch_end;
4171 if(dt <= NETDATA.options.current.double_click_speed)
4172 NETDATA.resetAllCharts(state);
4176 // remember the timestamp of the last touch end
4177 state.dygraph_last_touch_end = now;
4182 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4183 state.dygraph_options.drawGrid = false;
4184 state.dygraph_options.drawAxis = false;
4185 state.dygraph_options.title = undefined;
4186 state.dygraph_options.units = undefined;
4187 state.dygraph_options.ylabel = undefined;
4188 state.dygraph_options.yLabelWidth = 0;
4189 state.dygraph_options.labelsDivWidth = 120;
4190 state.dygraph_options.labelsDivStyles.width = '120px';
4191 state.dygraph_options.labelsSeparateLines = true;
4192 state.dygraph_options.rightGap = 0;
4195 if(smooth === true) {
4196 state.dygraph_smooth_eligible = true;
4198 if(NETDATA.options.current.smooth_plot === true)
4199 state.dygraph_options.plotter = smoothPlotter;
4201 else state.dygraph_smooth_eligible = false;
4203 state.dygraph_instance = new Dygraph(state.element_chart,
4204 data.result.data, state.dygraph_options);
4206 state.dygraph_force_zoom = false;
4207 state.dygraph_user_action = false;
4208 state.dygraph_last_rendered = new Date().getTime();
4212 // ----------------------------------------------------------------------------------------------------------------
4215 NETDATA.morrisInitialize = function(callback) {
4216 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4218 // morris requires raphael
4219 if(!NETDATA.chartLibraries.raphael.initialized) {
4220 if(NETDATA.chartLibraries.raphael.enabled) {
4221 NETDATA.raphaelInitialize(function() {
4222 NETDATA.morrisInitialize(callback);
4226 NETDATA.chartLibraries.morris.enabled = false;
4227 if(typeof callback === "function")
4232 NETDATA._loadCSS(NETDATA.morris_css);
4235 url: NETDATA.morris_js,
4240 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4243 NETDATA.chartLibraries.morris.enabled = false;
4244 NETDATA.error(100, NETDATA.morris_js);
4246 .always(function() {
4247 if(typeof callback === "function")
4253 NETDATA.chartLibraries.morris.enabled = false;
4254 if(typeof callback === "function")
4259 NETDATA.morrisChartUpdate = function(state, data) {
4260 state.morris_instance.setData(data.result.data);
4264 NETDATA.morrisChartCreate = function(state, data) {
4266 state.morris_options = {
4267 element: state.element_chart.id,
4268 data: data.result.data,
4270 ykeys: data.dimension_names,
4271 labels: data.dimension_names,
4277 continuousLine: false,
4278 behaveLikeLine: false
4281 if(state.chart.chart_type === 'line')
4282 state.morris_instance = new Morris.Line(state.morris_options);
4284 else if(state.chart.chart_type === 'area') {
4285 state.morris_options.behaveLikeLine = true;
4286 state.morris_instance = new Morris.Area(state.morris_options);
4289 state.morris_instance = new Morris.Area(state.morris_options);
4294 // ----------------------------------------------------------------------------------------------------------------
4297 NETDATA.raphaelInitialize = function(callback) {
4298 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4300 url: NETDATA.raphael_js,
4305 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4308 NETDATA.chartLibraries.raphael.enabled = false;
4309 NETDATA.error(100, NETDATA.raphael_js);
4311 .always(function() {
4312 if(typeof callback === "function")
4317 NETDATA.chartLibraries.raphael.enabled = false;
4318 if(typeof callback === "function")
4323 NETDATA.raphaelChartUpdate = function(state, data) {
4324 $(state.element_chart).raphael(data.result, {
4325 width: state.chartWidth(),
4326 height: state.chartHeight()
4332 NETDATA.raphaelChartCreate = function(state, data) {
4333 $(state.element_chart).raphael(data.result, {
4334 width: state.chartWidth(),
4335 height: state.chartHeight()
4341 // ----------------------------------------------------------------------------------------------------------------
4344 NETDATA.c3Initialize = function(callback) {
4345 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4348 if(!NETDATA.chartLibraries.d3.initialized) {
4349 if(NETDATA.chartLibraries.d3.enabled) {
4350 NETDATA.d3Initialize(function() {
4351 NETDATA.c3Initialize(callback);
4355 NETDATA.chartLibraries.c3.enabled = false;
4356 if(typeof callback === "function")
4361 NETDATA._loadCSS(NETDATA.c3_css);
4369 NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4372 NETDATA.chartLibraries.c3.enabled = false;
4373 NETDATA.error(100, NETDATA.c3_js);
4375 .always(function() {
4376 if(typeof callback === "function")
4382 NETDATA.chartLibraries.c3.enabled = false;
4383 if(typeof callback === "function")
4388 NETDATA.c3ChartUpdate = function(state, data) {
4389 state.c3_instance.destroy();
4390 return NETDATA.c3ChartCreate(state, data);
4392 //state.c3_instance.load({
4393 // rows: data.result,
4400 NETDATA.c3ChartCreate = function(state, data) {
4402 state.element_chart.id = 'c3-' + state.uuid;
4403 // console.log('id = ' + state.element_chart.id);
4405 state.c3_instance = c3.generate({
4406 bindto: '#' + state.element_chart.id,
4408 width: state.chartWidth(),
4409 height: state.chartHeight()
4412 pattern: state.chartColors()
4417 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4423 format: function(x) {
4424 return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4451 // console.log(state.c3_instance);
4456 // ----------------------------------------------------------------------------------------------------------------
4459 NETDATA.d3Initialize = function(callback) {
4460 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4467 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4470 NETDATA.chartLibraries.d3.enabled = false;
4471 NETDATA.error(100, NETDATA.d3_js);
4473 .always(function() {
4474 if(typeof callback === "function")
4479 NETDATA.chartLibraries.d3.enabled = false;
4480 if(typeof callback === "function")
4485 NETDATA.d3ChartUpdate = function(state, data) {
4489 NETDATA.d3ChartCreate = function(state, data) {
4493 // ----------------------------------------------------------------------------------------------------------------
4496 NETDATA.googleInitialize = function(callback) {
4497 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4499 url: NETDATA.google_js,
4504 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4505 google.load('visualization', '1.1', {
4506 'packages': ['corechart', 'controls'],
4507 'callback': callback
4511 NETDATA.chartLibraries.google.enabled = false;
4512 NETDATA.error(100, NETDATA.google_js);
4513 if(typeof callback === "function")
4518 NETDATA.chartLibraries.google.enabled = false;
4519 if(typeof callback === "function")
4524 NETDATA.googleChartUpdate = function(state, data) {
4525 var datatable = new google.visualization.DataTable(data.result);
4526 state.google_instance.draw(datatable, state.google_options);
4530 NETDATA.googleChartCreate = function(state, data) {
4531 var datatable = new google.visualization.DataTable(data.result);
4533 state.google_options = {
4534 colors: state.chartColors(),
4536 // do not set width, height - the chart resizes itself
4537 //width: state.chartWidth(),
4538 //height: state.chartHeight(),
4543 // title: "Time of Day",
4544 // format:'HH:mm:ss',
4545 viewWindowMode: 'maximized',
4557 viewWindowMode: 'pretty',
4572 focusTarget: 'category',
4579 titlePosition: 'out',
4590 curveType: 'function',
4595 switch(state.chart.chart_type) {
4597 state.google_options.vAxis.viewWindowMode = 'maximized';
4598 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4599 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4603 state.google_options.isStacked = true;
4604 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4605 state.google_options.vAxis.viewWindowMode = 'maximized';
4606 state.google_options.vAxis.minValue = null;
4607 state.google_options.vAxis.maxValue = null;
4608 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4613 state.google_options.lineWidth = 2;
4614 state.google_instance = new google.visualization.LineChart(state.element_chart);
4618 state.google_instance.draw(datatable, state.google_options);
4622 // ----------------------------------------------------------------------------------------------------------------
4624 NETDATA.percentFromValueMax = function(value, max) {
4625 if(value === null) value = 0;
4626 if(max < value) max = value;
4630 pcent = Math.round(value * 100 / max);
4631 if(pcent === 0 && value > 0) pcent = 1;
4637 // ----------------------------------------------------------------------------------------------------------------
4640 NETDATA.easypiechartInitialize = function(callback) {
4641 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4643 url: NETDATA.easypiechart_js,
4648 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4651 NETDATA.chartLibraries.easypiechart.enabled = false;
4652 NETDATA.error(100, NETDATA.easypiechart_js);
4654 .always(function() {
4655 if(typeof callback === "function")
4660 NETDATA.chartLibraries.easypiechart.enabled = false;
4661 if(typeof callback === "function")
4666 NETDATA.easypiechartClearSelection = function(state) {
4667 if(typeof state.easyPieChartEvent !== 'undefined') {
4668 if(state.easyPieChartEvent.timer !== null)
4669 clearTimeout(state.easyPieChartEvent.timer);
4671 state.easyPieChartEvent.timer = null;
4674 if(state.isAutoRefreshable() === true && state.data !== null) {
4675 NETDATA.easypiechartChartUpdate(state, state.data);
4678 state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4679 state.easyPieChart_instance.update(0);
4681 state.easyPieChart_instance.enableAnimation();
4686 NETDATA.easypiechartSetSelection = function(state, t) {
4687 if(state.timeIsVisible(t) !== true)
4688 return NETDATA.easypiechartClearSelection(state);
4690 var slot = state.calculateRowForTime(t);
4691 if(slot < 0 || slot >= state.data.result.length)
4692 return NETDATA.easypiechartClearSelection(state);
4694 if(typeof state.easyPieChartEvent === 'undefined') {
4695 state.easyPieChartEvent = {
4702 var value = state.data.result[state.data.result.length - 1 - slot];
4703 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4704 var pcent = NETDATA.percentFromValueMax(value, max);
4706 state.easyPieChartEvent.value = value;
4707 state.easyPieChartEvent.pcent = pcent;
4708 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4710 if(state.easyPieChartEvent.timer === null) {
4711 state.easyPieChart_instance.disableAnimation();
4713 state.easyPieChartEvent.timer = setTimeout(function() {
4714 state.easyPieChartEvent.timer = null;
4715 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4716 }, NETDATA.options.current.charts_selection_animation_delay);
4722 NETDATA.easypiechartChartUpdate = function(state, data) {
4723 var value, max, pcent;
4725 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4731 value = data.result[0];
4732 max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4733 pcent = NETDATA.percentFromValueMax(value, max);
4736 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4737 state.easyPieChart_instance.update(pcent);
4741 NETDATA.easypiechartChartCreate = function(state, data) {
4742 var self = $(state.element);
4743 var chart = $(state.element_chart);
4745 var value = data.result[0];
4746 var max = self.data('easypiechart-max-value') || null;
4747 var adjust = self.data('easypiechart-adjust') || null;
4751 state.easyPieChartMax = null;
4754 state.easyPieChartMax = max;
4756 var pcent = NETDATA.percentFromValueMax(value, max);
4758 chart.data('data-percent', pcent);
4762 case 'width': size = state.chartHeight(); break;
4763 case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4764 case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4766 default: size = state.chartWidth(); break;
4768 state.element.style.width = size + 'px';
4769 state.element.style.height = size + 'px';
4771 var stroke = Math.floor(size / 22);
4772 if(stroke < 3) stroke = 2;
4774 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4775 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4776 state.easyPieChartLabel = document.createElement('span');
4777 state.easyPieChartLabel.className = 'easyPieChartLabel';
4778 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4779 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4780 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4781 state.element_chart.appendChild(state.easyPieChartLabel);
4783 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4784 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4785 state.easyPieChartTitle = document.createElement('span');
4786 state.easyPieChartTitle.className = 'easyPieChartTitle';
4787 state.easyPieChartTitle.innerHTML = state.title;
4788 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4789 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4790 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4791 state.element_chart.appendChild(state.easyPieChartTitle);
4793 var unitfontsize = Math.round(titlefontsize * 0.9);
4794 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4795 state.easyPieChartUnits = document.createElement('span');
4796 state.easyPieChartUnits.className = 'easyPieChartUnits';
4797 state.easyPieChartUnits.innerHTML = state.units;
4798 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4799 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4800 state.element_chart.appendChild(state.easyPieChartUnits);
4802 chart.easyPieChart({
4803 barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
4804 trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
4805 scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
4806 scaleLength: self.data('easypiechart-scalelength') || 5,
4807 lineCap: self.data('easypiechart-linecap') || 'round',
4808 lineWidth: self.data('easypiechart-linewidth') || stroke,
4809 trackWidth: self.data('easypiechart-trackwidth') || undefined,
4810 size: self.data('easypiechart-size') || size,
4811 rotate: self.data('easypiechart-rotate') || 0,
4812 animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
4813 easing: self.data('easypiechart-easing') || undefined
4816 // when we just re-create the chart
4817 // do not animate the first update
4819 if(typeof state.easyPieChart_instance !== 'undefined')
4822 state.easyPieChart_instance = chart.data('easyPieChart');
4823 if(animate === false) state.easyPieChart_instance.disableAnimation();
4824 state.easyPieChart_instance.update(pcent);
4825 if(animate === false) state.easyPieChart_instance.enableAnimation();
4829 // ----------------------------------------------------------------------------------------------------------------
4832 NETDATA.gaugeInitialize = function(callback) {
4833 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
4835 url: NETDATA.gauge_js,
4840 NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
4843 NETDATA.chartLibraries.gauge.enabled = false;
4844 NETDATA.error(100, NETDATA.gauge_js);
4846 .always(function() {
4847 if(typeof callback === "function")
4852 NETDATA.chartLibraries.gauge.enabled = false;
4853 if(typeof callback === "function")
4858 NETDATA.gaugeAnimation = function(state, status) {
4861 if(typeof status === 'boolean' && status === false)
4863 else if(typeof status === 'number')
4866 state.gauge_instance.animationSpeed = speed;
4867 state.___gaugeOld__.speed = speed;
4870 NETDATA.gaugeSet = function(state, value, min, max) {
4871 if(typeof value !== 'number') value = 0;
4872 if(typeof min !== 'number') min = 0;
4873 if(typeof max !== 'number') max = 0;
4874 if(value > max) max = value;
4875 if(value < min) min = value;
4884 // gauge.js has an issue if the needle
4885 // is smaller than min or larger than max
4886 // when we set the new values
4887 // the needle will go crazy
4889 // to prevent it, we always feed it
4890 // with a percentage, so that the needle
4891 // is always between min and max
4892 var pcent = (value - min) * 100 / (max - min);
4894 // these should never happen
4895 if(pcent < 0) pcent = 0;
4896 if(pcent > 100) pcent = 100;
4898 state.gauge_instance.set(pcent);
4900 state.___gaugeOld__.value = value;
4901 state.___gaugeOld__.min = min;
4902 state.___gaugeOld__.max = max;
4905 NETDATA.gaugeSetLabels = function(state, value, min, max) {
4906 if(state.___gaugeOld__.valueLabel !== value) {
4907 state.___gaugeOld__.valueLabel = value;
4908 state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
4910 if(state.___gaugeOld__.minLabel !== min) {
4911 state.___gaugeOld__.minLabel = min;
4912 state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
4914 if(state.___gaugeOld__.maxLabel !== max) {
4915 state.___gaugeOld__.maxLabel = max;
4916 state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
4920 NETDATA.gaugeClearSelection = function(state) {
4921 if(typeof state.gaugeEvent !== 'undefined') {
4922 if(state.gaugeEvent.timer !== null)
4923 clearTimeout(state.gaugeEvent.timer);
4925 state.gaugeEvent.timer = null;
4928 if(state.isAutoRefreshable() === true && state.data !== null) {
4929 NETDATA.gaugeChartUpdate(state, state.data);
4932 NETDATA.gaugeAnimation(state, false);
4933 NETDATA.gaugeSet(state, null, null, null);
4934 NETDATA.gaugeSetLabels(state, null, null, null);
4937 NETDATA.gaugeAnimation(state, true);
4941 NETDATA.gaugeSetSelection = function(state, t) {
4942 if(state.timeIsVisible(t) !== true)
4943 return NETDATA.gaugeClearSelection(state);
4945 var slot = state.calculateRowForTime(t);
4946 if(slot < 0 || slot >= state.data.result.length)
4947 return NETDATA.gaugeClearSelection(state);
4949 if(typeof state.gaugeEvent === 'undefined') {
4950 state.gaugeEvent = {
4958 var value = state.data.result[state.data.result.length - 1 - slot];
4959 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
4962 state.gaugeEvent.value = value;
4963 state.gaugeEvent.max = max;
4964 state.gaugeEvent.min = min;
4965 NETDATA.gaugeSetLabels(state, value, min, max);
4967 if(state.gaugeEvent.timer === null) {
4968 NETDATA.gaugeAnimation(state, false);
4970 state.gaugeEvent.timer = setTimeout(function() {
4971 state.gaugeEvent.timer = null;
4972 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
4973 }, NETDATA.options.current.charts_selection_animation_delay);
4979 NETDATA.gaugeChartUpdate = function(state, data) {
4980 var value, min, max;
4982 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
4986 NETDATA.gaugeSetLabels(state, null, null, null);
4989 value = data.result[0];
4991 max = (state.gaugeMax === null)?data.max:state.gaugeMax;
4992 if(value > max) max = value;
4993 NETDATA.gaugeSetLabels(state, value, min, max);
4996 NETDATA.gaugeSet(state, value, min, max);
5000 NETDATA.gaugeChartCreate = function(state, data) {
5001 var self = $(state.element);
5002 // var chart = $(state.element_chart);
5004 var value = data.result[0];
5005 var max = self.data('gauge-max-value') || null;
5006 var adjust = self.data('gauge-adjust') || null;
5007 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
5008 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
5009 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
5010 var stopColor = self.data('gauge-stop-color') || void 0;
5011 var generateGradient = self.data('gauge-generate-gradient') || false;
5015 state.gaugeMax = null;
5018 state.gaugeMax = max;
5020 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
5022 // case 'width': width = height * ratio; break;
5024 // default: height = width / ratio; break;
5026 //state.element.style.width = width.toString() + 'px';
5027 //state.element.style.height = height.toString() + 'px';
5032 lines: 12, // The number of lines to draw
5033 angle: 0.15, // The length of each line
5034 lineWidth: 0.44, // 0.44 The line thickness
5036 length: 0.8, // 0.9 The radius of the inner circle
5037 strokeWidth: 0.035, // The rotation offset
5038 color: pointerColor // Fill color
5040 colorStart: startColor, // Colors
5041 colorStop: stopColor, // just experiment with them
5042 strokeColor: strokeColor, // to see which ones work best for you
5044 generateGradient: (generateGradient === true)?true:false,
5048 if (generateGradient.constructor === Array) {
5050 // data-gauge-generate-gradient="[0, 50, 100]"
5051 // data-gauge-gradient-percent-color-0="#FFFFFF"
5052 // data-gauge-gradient-percent-color-50="#999900"
5053 // data-gauge-gradient-percent-color-100="#000000"
5055 options.percentColors = new Array();
5056 var len = generateGradient.length;
5058 var pcent = generateGradient[len];
5059 var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
5060 if(color !== false) {
5061 var a = new Array();
5064 options.percentColors.unshift(a);
5067 if(options.percentColors.length === 0)
5068 delete options.percentColors;
5070 else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
5071 options.percentColors = [
5072 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
5073 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
5074 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
5075 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
5076 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
5077 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
5078 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
5079 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
5080 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
5081 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
5082 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
5085 state.gauge_canvas = document.createElement('canvas');
5086 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
5087 state.gauge_canvas.className = 'gaugeChart';
5088 state.gauge_canvas.width = width;
5089 state.gauge_canvas.height = height;
5090 state.element_chart.appendChild(state.gauge_canvas);
5092 var valuefontsize = Math.floor(height / 6);
5093 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5094 state.gaugeChartLabel = document.createElement('span');
5095 state.gaugeChartLabel.className = 'gaugeChartLabel';
5096 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5097 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5098 state.element_chart.appendChild(state.gaugeChartLabel);
5100 var titlefontsize = Math.round(valuefontsize / 2);
5102 state.gaugeChartTitle = document.createElement('span');
5103 state.gaugeChartTitle.className = 'gaugeChartTitle';
5104 state.gaugeChartTitle.innerHTML = state.title;
5105 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5106 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5107 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5108 state.element_chart.appendChild(state.gaugeChartTitle);
5110 var unitfontsize = Math.round(titlefontsize * 0.9);
5111 state.gaugeChartUnits = document.createElement('span');
5112 state.gaugeChartUnits.className = 'gaugeChartUnits';
5113 state.gaugeChartUnits.innerHTML = state.units;
5114 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5115 state.element_chart.appendChild(state.gaugeChartUnits);
5117 state.gaugeChartMin = document.createElement('span');
5118 state.gaugeChartMin.className = 'gaugeChartMin';
5119 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5120 state.element_chart.appendChild(state.gaugeChartMin);
5122 state.gaugeChartMax = document.createElement('span');
5123 state.gaugeChartMax.className = 'gaugeChartMax';
5124 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5125 state.element_chart.appendChild(state.gaugeChartMax);
5127 // when we just re-create the chart
5128 // do not animate the first update
5130 if(typeof state.gauge_instance !== 'undefined')
5133 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5135 state.___gaugeOld__ = {
5144 // we will always feed a percentage
5145 state.gauge_instance.minValue = 0;
5146 state.gauge_instance.maxValue = 100;
5148 NETDATA.gaugeAnimation(state, animate);
5149 NETDATA.gaugeSet(state, value, 0, max);
5150 NETDATA.gaugeSetLabels(state, value, 0, max);
5151 NETDATA.gaugeAnimation(state, true);
5155 // ----------------------------------------------------------------------------------------------------------------
5156 // Charts Libraries Registration
5158 NETDATA.chartLibraries = {
5160 initialize: NETDATA.dygraphInitialize,
5161 create: NETDATA.dygraphChartCreate,
5162 update: NETDATA.dygraphChartUpdate,
5163 resize: function(state) {
5164 if(typeof state.dygraph_instance.resize === 'function')
5165 state.dygraph_instance.resize();
5167 setSelection: NETDATA.dygraphSetSelection,
5168 clearSelection: NETDATA.dygraphClearSelection,
5169 toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5172 format: function(state) { return 'json'; },
5173 options: function(state) { return 'ms|flip'; },
5174 legend: function(state) {
5175 if(this.isSparkline(state) === false)
5176 return 'right-side';
5180 autoresize: function(state) { return true; },
5181 max_updates_to_recreate: function(state) { return 5000; },
5182 track_colors: function(state) { return true; },
5183 pixels_per_point: function(state) {
5184 if(this.isSparkline(state) === false)
5190 isSparkline: function(state) {
5191 if(typeof state.dygraph_sparkline === 'undefined') {
5192 var t = $(state.element).data('dygraph-theme');
5193 if(t === 'sparkline')
5194 state.dygraph_sparkline = true;
5196 state.dygraph_sparkline = false;
5198 return state.dygraph_sparkline;
5202 initialize: NETDATA.sparklineInitialize,
5203 create: NETDATA.sparklineChartCreate,
5204 update: NETDATA.sparklineChartUpdate,
5206 setSelection: undefined, // function(state, t) { return true; },
5207 clearSelection: undefined, // function(state) { return true; },
5208 toolboxPanAndZoom: null,
5211 format: function(state) { return 'array'; },
5212 options: function(state) { return 'flip|abs'; },
5213 legend: function(state) { return null; },
5214 autoresize: function(state) { return false; },
5215 max_updates_to_recreate: function(state) { return 5000; },
5216 track_colors: function(state) { return false; },
5217 pixels_per_point: function(state) { return 3; }
5220 initialize: NETDATA.peityInitialize,
5221 create: NETDATA.peityChartCreate,
5222 update: NETDATA.peityChartUpdate,
5224 setSelection: undefined, // function(state, t) { return true; },
5225 clearSelection: undefined, // function(state) { return true; },
5226 toolboxPanAndZoom: null,
5229 format: function(state) { return 'ssvcomma'; },
5230 options: function(state) { return 'null2zero|flip|abs'; },
5231 legend: function(state) { return null; },
5232 autoresize: function(state) { return false; },
5233 max_updates_to_recreate: function(state) { return 5000; },
5234 track_colors: function(state) { return false; },
5235 pixels_per_point: function(state) { return 3; }
5238 initialize: NETDATA.morrisInitialize,
5239 create: NETDATA.morrisChartCreate,
5240 update: NETDATA.morrisChartUpdate,
5242 setSelection: undefined, // function(state, t) { return true; },
5243 clearSelection: undefined, // function(state) { return true; },
5244 toolboxPanAndZoom: null,
5247 format: function(state) { return 'json'; },
5248 options: function(state) { return 'objectrows|ms'; },
5249 legend: function(state) { return null; },
5250 autoresize: function(state) { return false; },
5251 max_updates_to_recreate: function(state) { return 50; },
5252 track_colors: function(state) { return false; },
5253 pixels_per_point: function(state) { return 15; }
5256 initialize: NETDATA.googleInitialize,
5257 create: NETDATA.googleChartCreate,
5258 update: NETDATA.googleChartUpdate,
5260 setSelection: undefined, //function(state, t) { return true; },
5261 clearSelection: undefined, //function(state) { return true; },
5262 toolboxPanAndZoom: null,
5265 format: function(state) { return 'datatable'; },
5266 options: function(state) { return ''; },
5267 legend: function(state) { return null; },
5268 autoresize: function(state) { return false; },
5269 max_updates_to_recreate: function(state) { return 300; },
5270 track_colors: function(state) { return false; },
5271 pixels_per_point: function(state) { return 4; }
5274 initialize: NETDATA.raphaelInitialize,
5275 create: NETDATA.raphaelChartCreate,
5276 update: NETDATA.raphaelChartUpdate,
5278 setSelection: undefined, // function(state, t) { return true; },
5279 clearSelection: undefined, // function(state) { return true; },
5280 toolboxPanAndZoom: null,
5283 format: function(state) { return 'json'; },
5284 options: function(state) { return ''; },
5285 legend: function(state) { return null; },
5286 autoresize: function(state) { return false; },
5287 max_updates_to_recreate: function(state) { return 5000; },
5288 track_colors: function(state) { return false; },
5289 pixels_per_point: function(state) { return 3; }
5292 initialize: NETDATA.c3Initialize,
5293 create: NETDATA.c3ChartCreate,
5294 update: NETDATA.c3ChartUpdate,
5296 setSelection: undefined, // function(state, t) { return true; },
5297 clearSelection: undefined, // function(state) { return true; },
5298 toolboxPanAndZoom: null,
5301 format: function(state) { return 'csvjsonarray'; },
5302 options: function(state) { return 'milliseconds'; },
5303 legend: function(state) { return null; },
5304 autoresize: function(state) { return false; },
5305 max_updates_to_recreate: function(state) { return 5000; },
5306 track_colors: function(state) { return false; },
5307 pixels_per_point: function(state) { return 15; }
5310 initialize: NETDATA.d3Initialize,
5311 create: NETDATA.d3ChartCreate,
5312 update: NETDATA.d3ChartUpdate,
5314 setSelection: undefined, // function(state, t) { return true; },
5315 clearSelection: undefined, // function(state) { return true; },
5316 toolboxPanAndZoom: null,
5319 format: function(state) { return 'json'; },
5320 options: function(state) { return ''; },
5321 legend: function(state) { return null; },
5322 autoresize: function(state) { return false; },
5323 max_updates_to_recreate: function(state) { return 5000; },
5324 track_colors: function(state) { return false; },
5325 pixels_per_point: function(state) { return 3; }
5328 initialize: NETDATA.easypiechartInitialize,
5329 create: NETDATA.easypiechartChartCreate,
5330 update: NETDATA.easypiechartChartUpdate,
5332 setSelection: NETDATA.easypiechartSetSelection,
5333 clearSelection: NETDATA.easypiechartClearSelection,
5334 toolboxPanAndZoom: null,
5337 format: function(state) { return 'array'; },
5338 options: function(state) { return 'absolute'; },
5339 legend: function(state) { return null; },
5340 autoresize: function(state) { return false; },
5341 max_updates_to_recreate: function(state) { return 5000; },
5342 track_colors: function(state) { return true; },
5343 pixels_per_point: function(state) { return 3; },
5347 initialize: NETDATA.gaugeInitialize,
5348 create: NETDATA.gaugeChartCreate,
5349 update: NETDATA.gaugeChartUpdate,
5351 setSelection: NETDATA.gaugeSetSelection,
5352 clearSelection: NETDATA.gaugeClearSelection,
5353 toolboxPanAndZoom: null,
5356 format: function(state) { return 'array'; },
5357 options: function(state) { return 'absolute'; },
5358 legend: function(state) { return null; },
5359 autoresize: function(state) { return false; },
5360 max_updates_to_recreate: function(state) { return 5000; },
5361 track_colors: function(state) { return true; },
5362 pixels_per_point: function(state) { return 3; },
5367 NETDATA.registerChartLibrary = function(library, url) {
5368 if(NETDATA.options.debug.libraries === true)
5369 console.log("registering chart library: " + library);
5371 NETDATA.chartLibraries[library].url = url;
5372 NETDATA.chartLibraries[library].initialized = true;
5373 NETDATA.chartLibraries[library].enabled = true;
5376 // ----------------------------------------------------------------------------------------------------------------
5379 NETDATA.requiredJs = [
5381 url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
5382 isAlreadyLoaded: function() {
5383 // check if bootstrap is loaded
5384 if(typeof $().emulateTransitionEnd == 'function')
5387 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5395 url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
5396 isAlreadyLoaded: function() { return false; }
5399 url: NETDATA.serverDefault + 'lib/bootstrap-toggle.min.js',
5400 isAlreadyLoaded: function() { return false; }
5404 NETDATA.requiredCSS = [
5406 url: NETDATA.themes.current.bootstrap_css,
5407 isAlreadyLoaded: function() {
5408 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5415 url: NETDATA.serverDefault + 'css/font-awesome.min.css',
5416 isAlreadyLoaded: function() { return false; }
5419 url: NETDATA.themes.current.dashboard_css,
5420 isAlreadyLoaded: function() { return false; }
5423 url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
5424 isAlreadyLoaded: function() { return false; }
5428 NETDATA.loadRequiredJs = function(index, callback) {
5429 if(index >= NETDATA.requiredJs.length) {
5430 if(typeof callback === 'function')
5435 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5436 NETDATA.loadRequiredJs(++index, callback);
5440 if(NETDATA.options.debug.main_loop === true)
5441 console.log('loading ' + NETDATA.requiredJs[index].url);
5444 url: NETDATA.requiredJs[index].url,
5448 .success(function() {
5449 if(NETDATA.options.debug.main_loop === true)
5450 console.log('loaded ' + NETDATA.requiredJs[index].url);
5452 NETDATA.loadRequiredJs(++index, callback);
5455 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5459 NETDATA.loadRequiredCSS = function(index) {
5460 if(index >= NETDATA.requiredCSS.length)
5463 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5464 NETDATA.loadRequiredCSS(++index);
5468 if(NETDATA.options.debug.main_loop === true)
5469 console.log('loading ' + NETDATA.requiredCSS[index].url);
5471 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5472 NETDATA.loadRequiredCSS(++index);
5475 NETDATA.errorReset();
5476 NETDATA.loadRequiredCSS(0);
5478 NETDATA._loadjQuery(function() {
5479 NETDATA.loadRequiredJs(0, function() {
5480 if(typeof $().emulateTransitionEnd !== 'function') {
5481 // bootstrap is not available
5482 NETDATA.options.current.show_help = false;
5485 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
5486 if(NETDATA.options.debug.main_loop === true)
5487 console.log('starting chart refresh thread');
5494 // window.NETDATA = NETDATA;
5495 // })(window, document);