1 // You can set the following variables before loading this script:
3 // var netdataStopDygraph = 1; // do not use dygraph
4 // var netdataStopSparkline = 1; // do not use sparkline
5 // var netdataStopPeity = 1; // do not use peity
6 // var netdataStopGoogleCharts = 1; // do not use google
7 // var netdataStopMorris = 1; // do not use morris
9 // You can also set the default netdata server, using the following.
10 // When this variable is not set, we assume the page is hosted on your
11 // netdata server already.
12 // var netdataServer = "http://yourhost:19999"; // set your NetData server
16 // fix IE bug with console
17 if(!window.console){ window.console = {log: function(){} }; }
19 var NETDATA = window.NETDATA || {};
21 // ----------------------------------------------------------------------------------------------------------------
22 // Detect the netdata server
24 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
25 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
26 NETDATA._scriptSource = function(scripts) {
27 var script = null, base = null;
29 if(typeof document.currentScript != 'undefined') {
30 script = document.currentScript;
33 var all_scripts = document.getElementsByTagName('script');
34 script = all_scripts[all_scripts.length - 1];
37 if (script.getAttribute.length != 'undefined')
40 script = script.getAttribute('src', -1);
42 var link = document.createElement('a');
43 link.setAttribute('href', script);
45 if(!link.protocol || !link.hostname) return null;
48 if(base) base += "//";
49 base += link.hostname;
51 if(link.port) base += ":" + link.port;
57 if(typeof netdataServer != 'undefined')
58 NETDATA.serverDefault = netdataServer + "/";
60 NETDATA.serverDefault = NETDATA._scriptSource();
62 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
63 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
64 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
65 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
66 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
67 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
68 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
69 NETDATA.google_js = 'https://www.google.com/jsapi';
70 NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
71 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
72 '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
74 // ----------------------------------------------------------------------------------------------------------------
75 // the defaults for all charts
77 NETDATA.chartDefaults = {
78 host: NETDATA.serverDefault, // the server to get data from
79 width: '100%', // the chart width
80 height: '100%', // the chart height
81 library: 'dygraph', // the graphing library to use
82 method: 'max', // the grouping method
84 after: -600, // panning
85 pixels_per_point: 1 // the detail of the chart
91 auto_refresher_fast_weight: 0,
94 auto_refresher_stop_until: 0,
98 idle_between_charts: 50,
99 idle_between_loops: 200,
100 idle_lost_focus: 500,
101 global_pan_sync_time: 500,
102 fast_render_timeframe: 200, // render continously for these many ms
103 sync_delay: 1500 // how much time after an operation to setup synced selections?
119 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
122 // ----------------------------------------------------------------------------------------------------------------
125 NETDATA.errorCodes = {
126 100: { message: "Cannot load chart library", alert: true },
127 101: { message: "Cannot load jQuery", alert: true },
128 402: { message: "Chart library not found", alert: false },
129 403: { message: "Chart library not enabled/is failed", alert: false },
130 404: { message: "Chart not found", alert: false }
132 NETDATA.errorLast = {
138 NETDATA.error = function(code, msg) {
139 NETDATA.errorLast.code = code;
140 NETDATA.errorLast.message = msg;
141 NETDATA.errorLast.datetime = new Date().getTime();
143 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
145 if(NETDATA.errorCodes[code].alert)
146 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
149 NETDATA.errorReset = function() {
150 NETDATA.errorLast.code = 0;
151 NETDATA.errorLast.message = "You are doing fine!";
152 NETDATA.errorLast.datetime = 0;
155 // ----------------------------------------------------------------------------------------------------------------
157 NETDATA.chartRegistry = {
160 add: function(host, id, data) {
161 host = host.replace(/:/g, "_").replace(/\//g, "_");
162 id = id.replace(/:/g, "_").replace(/\//g, "_");
164 if(typeof this.charts[host] == 'undefined')
165 this.charts[host] = {};
167 this.charts[host][id] = data;
170 get: function(host, id) {
171 host = host.replace(/:/g, "_").replace(/\//g, "_");
172 id = id.replace(/:/g, "_").replace(/\//g, "_");
174 if(typeof this.charts[host] == 'undefined')
177 if(typeof this.charts[host][id] == 'undefined')
180 return this.charts[host][id];
184 // ----------------------------------------------------------------------------------------------------------------
186 NETDATA.globalPanAndZoom = {
191 force_before_ms: null,
192 force_after_ms: null,
194 setMaster: function(state, after, before) {
195 if(this.state && this.state != state) this.state.resetChart();
198 this.seq = new Date().getTime();
199 this.force_after_ms = after;
200 this.force_before_ms = before;
202 clearMaster: function() {
204 var state = this.state;
205 this.state = null; // prevent infinite recursion
208 NETDATA.options.auto_refresher_stop_until = 0;
215 shouldBeAutoRefreshed: function(state) {
216 if(!this.state || !this.seq)
219 if(state.follows_global == this.seq)
226 // ----------------------------------------------------------------------------------------------------------------
227 // Our state object, where all per-chart values are stored
229 NETDATA.chartInitialState = function(element) {
233 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
234 id: self.data('netdata'), // string - the name of chart
236 // the user given dimensions of the element
237 width: self.data('width') || NETDATA.chartDefaults.width,
238 height: self.data('height') || NETDATA.chartDefaults.height,
240 // these are calculated every time the chart is refreshed
242 calculated_height: 0,
244 // string - the netdata server URL, without any path
245 host: self.data('host') || NETDATA.chartDefaults.host,
247 // string - the grouping method requested by the user
248 method: self.data('method') || NETDATA.chartDefaults.method,
250 // the time-range requested by the user
251 after: self.data('after') || NETDATA.chartDefaults.after,
252 before: self.data('before') || NETDATA.chartDefaults.before,
254 // the pixels per point requested by the user
256 points: self.data('points') || null,
258 // the dimensions requested by the user
259 dimensions: self.data('dimensions') || null,
261 // the chart library requested by the user
262 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
263 library: null, // object - the chart library used
265 element: element, // the element it is linked to
267 chart_url: null, // string - the url to download chart info
268 chart: null, // object - the chart as downloaded from the server
270 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
271 created_ms: 0, // boolean - the timestamp the chart was created
272 validated: false, // boolean - has the chart been validated?
273 enabled: true, // boolean - is the chart enabled for refresh?
274 paused: false, // boolean - is the chart paused for any reason?
275 selected: false, // boolean - is the chart shown a selection?
277 updates_counter: 0, // numeric - the number of refreshes made so far
281 mode: null, // auto, pan, zoom
285 url: 'invalid://', // string - the last url used to update the chart
286 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
287 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
288 after_ms: 0, // milliseconds - the first timestamp of the data
289 before_ms: 0, // milliseconds - the last timestamp of the data
290 points: 0, // number - the number of points in the data
291 data: null, // the last downloaded data
292 force_before_ms: null,
293 force_after_ms: null,
294 requested_before_ms: null,
295 requested_after_ms: null,
296 first_entry_ms: null,
302 url: 'invalid://', // string - the last url used to update the chart
303 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
304 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
305 after_ms: 0, // milliseconds - the first timestamp of the data
306 before_ms: 0, // milliseconds - the last timestamp of the data
307 points: 0, // number - the number of points in the data
308 data: null, // the last downloaded data
309 force_before_ms: null,
310 force_after_ms: null,
311 requested_before_ms: null,
312 requested_after_ms: null,
313 first_entry_ms: null,
319 url: 'invalid://', // string - the last url used to update the chart
320 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
321 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
322 after_ms: 0, // milliseconds - the first timestamp of the data
323 before_ms: 0, // milliseconds - the last timestamp of the data
324 points: 0, // number - the number of points in the data
325 data: null, // the last downloaded data
326 force_before_ms: null,
327 force_after_ms: null,
328 requested_before_ms: null,
329 requested_after_ms: null,
330 first_entry_ms: null,
333 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
334 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
335 refresh_dt_element: null,
338 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
341 setSelection: function(t) {
342 if(typeof this.library.setSelection == 'function') {
343 if(this.library.setSelection(this, t))
344 this.selected = true;
346 this.selected = false;
348 else this.selected = true;
350 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
352 return this.selected;
355 clearSelection: function() {
357 if(typeof this.library.clearSelection == 'function') {
358 if(this.library.clearSelection(this))
359 this.selected = false;
361 this.selected = true;
363 else this.selected = false;
365 if(!this.selected && this.debug) this.log('selection cleared');
368 return this.selected;
371 timeIsVisible: function(t) {
372 if(t >= this.mode.after_ms && t <= this.mode.before_ms)
377 calculateRowForTime: function(t) {
378 if(!this.timeIsVisible(t)) return -1;
380 var r = Math.floor((t - this.mode.after_ms) / this.mode.view_update_every);
381 // console.log(this.mode.data);
386 pauseChart: function() {
388 if(this.debug) this.log('paused');
393 unpauseChart: function() {
395 if(this.debug) this.log('unpaused');
400 resetChart: function() {
401 if(NETDATA.globalPanAndZoom.state == this)
402 NETDATA.globalPanAndZoom.clearMaster();
404 if(state.mode.name != 'auto')
405 this.setMode('auto');
407 this.mode.force_before_ms = null;
408 this.mode.force_after_ms = null;
409 this.mode.last_updated_ms = 0;
410 this.follows_global = 0;
412 this.selected = false;
419 setMode: function(m) {
421 if(this.mode.name == m) return;
423 this[m].url = this.mode.url;
424 this[m].last_updated_ms = this.mode.last_updated_ms;
425 this[m].view_update_every = this.mode.view_update_every;
426 this[m].after_ms = this.mode.after_ms;
427 this[m].before_ms = this.mode.before_ms;
428 this[m].points = this.mode.points;
429 this[m].data = this.mode.data;
430 this[m].requested_before_ms = this.mode.requested_before_ms;
431 this[m].requested_after_ms = this.mode.requested_after_ms;
432 this[m].first_entry_ms = this.mode.first_entry_ms;
433 this[m].last_entry_ms = this.mode.last_entry_ms;
437 this.mode = this.auto;
439 this.mode = this.pan;
441 this.mode = this.zoom;
443 this.mode = this.auto;
445 this.mode.force_before_ms = null;
446 this.mode.force_after_ms = null;
448 if(this.debug) this.log('mode set to ' + this.mode.name);
451 _minPanOrZoomStep: function() {
452 return (((this.mode.before_ms - this.mode.after_ms) / this.mode.points) * ((this.mode.points * 5 / 100) + 1) );
453 // return this.mode.view_update_every * 10;
456 _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
457 var dt_after = Math.abs(old_after - new_after);
458 var dt_before = Math.abs(old_before - new_before);
459 var old_range = old_before - old_after;
461 var new_range = new_before - new_after;
462 var dt = Math.abs(old_range - new_range);
463 var step = Math.max(dt_after, dt_before, dt);
465 var min_step = this._minPanOrZoomStep();
466 if(new_range < old_range && new_range / this.calculated_width < 100) {
467 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum point size: 0.10, wanted point size: ' + (new_range / this.calculated_width / 1000).toString() + ': TOO SMALL RANGE');
471 if(step >= min_step) {
472 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': YES');
476 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': NO');
481 updateChartPanOrZoom: function(after, before, callback) {
484 if(this.mode.name == 'auto') {
485 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
489 if(!this.mode.force_after_ms || !this.mode.force_before_ms) {
490 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
493 else if(this._shouldBeMoved(this.mode.force_after_ms, this.mode.force_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
494 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.mode.force_after_ms / 1000).toString() + ' - ' + (this.mode.force_before_ms / 1000).toString());
497 else if(this._shouldBeMoved(this.mode.requested_after_ms, this.mode.requested_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
498 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.mode.requested_after_ms / 1000).toString() + ' - ' + (this.mode.requested_before_ms / 1000).toString());
503 var now = new Date().getTime();
504 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
505 NETDATA.globalPanAndZoom.setMaster(this, after, before);
507 this.mode.force_after_ms = after;
508 this.mode.force_before_ms = before;
509 this.updateChart(callback);
513 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
514 if(typeof callback != 'undefined') callback();
518 updateChart: function(callback) {
519 if(!this.library || !this.library.enabled) {
520 this.error('chart library "' + this.library_name + '" is not enabled');
521 if(typeof callback != 'undefined') callback();
526 if(this.debug) this.error('chart "' + this.id + '" is not enabled');
527 if(typeof callback != 'undefined') callback();
531 if(!self.visible(true)) {
532 if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
533 if(typeof callback != 'undefined') callback();
537 var this_state_object = this;
538 this.getChart(function() { this_state_object.updateChart(callback); });
542 if(!this.library.initialized) {
543 var this_state_object = this;
544 this.library.initialize(function() { this_state_object.updateChart(callback); });
548 this.clearSelection();
550 if(this.debug) this.log('updating from ' + this.mode.url);
552 var this_state_object = this;
554 url: this_state_object.mode.url,
557 .then(function(data) {
558 if(this_state_object.debug) this_state_object.log('got data from netdata server');
559 this_state_object.mode.data = data;
561 var started = new Date().getTime();
563 // if the result is JSON, find the latest update-every
564 if(typeof data == 'object' && this_state_object.library.jsonWrapper) {
565 if(!this_state_object.follows_global && typeof data.view_update_every != 'undefined')
566 this_state_object.mode.view_update_every = data.view_update_every * 1000;
568 if(typeof data.after != 'undefined')
569 this_state_object.mode.after_ms = data.after * 1000;
571 if(typeof data.before != 'undefined')
572 this_state_object.mode.before_ms = data.before * 1000;
574 if(typeof data.first_entry_t != 'undefined')
575 this_state_object.mode.first_entry_ms = data.first_entry_t * 1000;
577 if(typeof data.last_entry_t != 'undefined')
578 this_state_object.mode.last_entry_ms = data.last_entry_t * 1000;
580 if(typeof data.points != 'undefined')
581 this_state_object.mode.points = data.points;
583 data.state = this_state_object;
586 this_state_object.updates_counter++;
588 if(this_state_object.debug) {
589 this_state_object.log('UPDATE No ' + this_state_object.updates_counter + ' COMPLETED');
591 if(this_state_object.mode.force_after_ms)
592 this_state_object.log('STATUS: forced : ' + (this_state_object.mode.force_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.force_before_ms / 1000).toString());
594 this_state_object.log('STATUS: forced: unset');
596 this_state_object.log('STATUS: requested: ' + (this_state_object.mode.requested_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.requested_before_ms / 1000).toString());
597 this_state_object.log('STATUS: rendered : ' + (this_state_object.mode.after_ms / 1000).toString() + ' - ' + (this_state_object.mode.before_ms / 1000).toString());
598 this_state_object.log('STATUS: points : ' + (this_state_object.mode.points).toString() + ', min step: ' + (this_state_object._minPanOrZoomStep() / 1000).toString());
601 if(this_state_object.created_ms) {
602 if(this_state_object.debug) this_state_object.log('updating chart...');
604 if(NETDATA.options.debug.chart_errors) {
605 this_state_object.library.update(this_state_object.element, data);
609 this_state_object.library.update(this_state_object.element, data);
612 this_state_object.error('chart "' + state.id + '" failed to be updated as ' + state.library_name);
617 if(this_state_object.debug) this_state_object.log('creating chart...');
619 if(NETDATA.options.debug.chart_errors) {
620 this_state_object.library.create(this_state_object.element, data);
621 this_state_object.created_ms = new Date().getTime();
625 this_state_object.library.create(this_state_object.element, data);
626 this_state_object.created_ms = new Date().getTime();
629 this_state_object.error('chart "' + state.id + '" failed to be created as ' + state.library_name);
634 // update the performance counters
635 this_state_object.mode.last_updated_ms = new Date().getTime();
636 this_state_object.refresh_dt_ms = this_state_object.mode.last_updated_ms - started;
637 NETDATA.options.auto_refresher_fast_weight += this_state_object.refresh_dt_ms;
639 if(this_state_object.refresh_dt_element)
640 this_state_object.refresh_dt_element.innerHTML = this_state_object.refresh_dt_ms.toString();
643 this_state_object.error('cannot download chart from ' + this_state_object.mode.url);
646 if(typeof callback == 'function') callback();
650 chartURL: function() {
651 this.calculated_width = self.width();
652 this.calculated_height = self.height();
656 if(NETDATA.globalPanAndZoom.state) {
657 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
658 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
659 this.follows_global = NETDATA.globalPanAndZoom.seq;
662 before = this.mode.force_before_ms != null ? Math.round(this.mode.force_before_ms / 1000) : this.before;
663 after = this.mode.force_after_ms != null ? Math.round(this.mode.force_after_ms / 1000) : this.after;
664 this.follows_global = 0;
667 this.mode.requested_after_ms = after * 1000;
668 this.mode.requested_before_ms = before * 1000;
670 // force an options provided detail
671 var pixels_per_point = this.pixels_per_point;
672 if(pixels_per_point < NETDATA.options.current.pixels_per_point)
673 pixels_per_point = NETDATA.options.current.pixels_per_point
675 this.mode.points = this.points || Math.round(this.calculated_width / pixels_per_point);
677 // build the data URL
678 this.mode.url = this.chart.data_url;
679 this.mode.url += "&format=" + this.library.format;
680 this.mode.url += "&points=" + this.mode.points.toString();
681 this.mode.url += "&group=" + this.method;
682 this.mode.url += "&options=" + this.library.options;
683 if(this.library.jsonWrapper) this.mode.url += '|jsonwrap';
686 this.mode.url += "&after=" + after.toString();
689 this.mode.url += "&before=" + before.toString();
692 this.mode.url += "&dimensions=" + this.dimensions;
694 if(NETDATA.options.debug.chart_data_url) this.log('chartURL(): ' + this.mode.url + ' WxH:' + this.calculated_width + 'x' + this.calculated_height + ' points: ' + this.mode.points + ' library: ' + this.library_name);
697 canBeAutoRefreshed: function(auto_refresher) {
698 if(this.mode.autorefresh) {
699 var now = new Date().getTime();
701 if(this.updates_counter && !NETDATA.options.page_is_visible) {
702 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
706 if(!auto_refresher) return true;
708 if(!self.visible(true)) {
709 if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
713 // options valid only for autoRefresh()
714 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
715 if(NETDATA.globalPanAndZoom.state) {
716 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
717 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
721 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
727 if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
732 if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
736 if(now - this.mode.last_updated_ms > this.mode.view_update_every) {
737 if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
746 autoRefresh: function(callback) {
747 if(this.canBeAutoRefreshed(true))
748 this.updateChart(callback);
749 else if(typeof callback != 'undefined')
753 // fetch the chart description from the netdata server
754 getChart: function(callback) {
755 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
757 if(typeof callback == 'function') callback();
760 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
761 if(this.debug) this.log('downloading ' + this.chart_url);
762 this_state_object = this;
768 .done(function(chart) {
769 chart.data_url = (this_state_object.host + chart.data_url);
770 this_state_object.chart = chart;
771 this_state_object.mode.view_update_every = chart.update_every * 1000;
772 this_state_object.mode.points = Math.round(self.width() / (chart.update_every / 1000));
774 chart.url = this_state_object.chart_url;
775 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
778 NETDATA.error(404, this_state_object.chart_url);
779 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
782 if(typeof callback == 'function') callback();
787 // resize the chart to its real dimensions
788 // as given by the caller
789 sizeChart: function() {
790 if(this.debug) this.log('sizing element');
791 self.css('width', this.width)
792 .css('height', this.height)
793 .css('display', 'inline-block')
794 .css('overflow', 'hidden');
797 // show a message in the chart
798 message: function(msg) {
800 if(NETDATA.options.debug.show_boxes)
801 bgcolor = " background-color: lightgrey;";
803 this.element.innerHTML = '<div style="font-size: x-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
807 // reset the creation datetime
808 // since we overwrote the whole element
810 if(this.debug) this.log(msg);
813 // show an error on the chart and stop it forever
814 error: function(msg) {
816 this.enabled = false;
819 // show a message indicating the chart is loading
820 loading: function() {
821 this.message('chart ' + this.id + ' is loading...');
825 if(state.debug) state.log('created');
829 if(typeof NETDATA.chartLibraries[state.library_name] == 'undefined') {
830 NETDATA.error(402, state.library_name);
831 state.error('chart library "' + state.library_name + '" is not found');
833 else if(!NETDATA.chartLibraries[state.library_name].enabled) {
834 NETDATA.error(403, state.library_name);
835 state.error('chart library "' + state.library_name + '" is not enabled');
838 state.library = NETDATA.chartLibraries[state.library_name];
839 state.pixels_per_point = self.data('pixels-per-point') || state.library.pixels_per_point;
842 if(state.refresh_dt_element_name)
843 state.refresh_dt_element = document.getElementById(state.refresh_dt_element_name) || null;
845 state.setMode('auto');
850 // get or create a chart state, given a DOM element
851 NETDATA.chartState = function(element) {
853 var state = self.data('state') || null;
855 state = NETDATA.chartInitialState(element);
856 self.data('state', state);
861 // ----------------------------------------------------------------------------------------------------------------
864 // Load a script without jquery
865 // This is used to load jquery - after it is loaded, we use jquery
866 NETDATA._loadjQuery = function(callback) {
867 if(typeof jQuery == 'undefined') {
868 var script = document.createElement('script');
869 script.type = 'text/javascript';
871 script.src = NETDATA.jQuery;
873 // script.onabort = onError;
874 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
875 if(typeof callback == "function")
876 script.onload = callback;
878 var s = document.getElementsByTagName('script')[0];
879 s.parentNode.insertBefore(script, s);
881 else if(typeof callback == "function")
885 NETDATA.ColorLuminance = function(hex, lum) {
886 // validate hex string
887 hex = String(hex).replace(/[^0-9a-f]/gi, '');
889 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
893 // convert to decimal and change luminosity
895 for (i = 0; i < 3; i++) {
896 c = parseInt(hex.substr(i*2,2), 16);
897 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
898 rgb += ("00"+c).substr(c.length);
904 NETDATA.guid = function() {
906 return Math.floor((1 + Math.random()) * 0x10000)
911 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
914 // user function to signal us the DOM has been
916 NETDATA.updatedDom = function() {
917 NETDATA.options.updated_dom = 1;
920 // ----------------------------------------------------------------------------------------------------------------
922 NETDATA.chartRefresher = function(index) {
923 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
925 if(NETDATA.options.updated_dom) {
926 // the dom has been updated
927 // get the dom parts again
928 NETDATA.getDomCharts(function() {
929 NETDATA.chartRefresher(0);
935 var target = NETDATA.options.targets.get(index);
937 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
938 NETDATA.options.auto_refresher_fast_weight = 0;
940 setTimeout(function() {
941 NETDATA.chartRefresher(0);
942 }, NETDATA.options.current.idle_between_loops);
945 var state = NETDATA.chartState(target);
947 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
948 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
950 state.autoRefresh(function() {
951 NETDATA.chartRefresher(++index);
955 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
956 NETDATA.options.auto_refresher_fast_weight = 0;
958 setTimeout(function() {
959 state.autoRefresh(function() {
960 NETDATA.chartRefresher(++index);
962 }, NETDATA.options.current.idle_between_charts);
967 NETDATA.getDomCharts = function(callback) {
968 NETDATA.options.updated_dom = 0;
970 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
972 if(NETDATA.options.debug.main_loop)
973 console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
975 // we need to re-size all the charts quickly
976 // before making any external calls
977 $.each(NETDATA.options.targets, function(i, target) {
978 // the initialization will take care of sizing
979 // and the "loading..." message
980 var state = NETDATA.chartState(target);
983 if(typeof callback == 'function') callback();
986 // this is the main function - where everything starts
987 NETDATA.init = function() {
988 // this should be called only once
990 NETDATA.options.page_is_visible = 1;
992 $(window).blur(function() {
993 NETDATA.options.page_is_visible = 0;
994 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
997 $(window).focus(function() {
998 NETDATA.options.page_is_visible = 1;
999 if(NETDATA.options.debug.focus) console.log('Focus restored!');
1002 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
1003 NETDATA.options.page_is_visible = 0;
1004 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1007 NETDATA.getDomCharts(function() {
1008 NETDATA.chartRefresher(0);
1012 // ----------------------------------------------------------------------------------------------------------------
1014 //var chart = function() {
1017 //chart.prototype.color = function() {
1021 //var c = new chart();
1024 // ----------------------------------------------------------------------------------------------------------------
1027 NETDATA.peityInitialize = function(callback) {
1028 if(typeof netdataStopPeity == 'undefined') {
1029 $.getScript(NETDATA.peity_js)
1031 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1034 NETDATA.error(100, NETDATA.peity_js);
1036 .always(function() {
1037 if(typeof callback == "function")
1042 NETDATA.chartLibraries.peity.enabled = false;
1043 if(typeof callback == "function")
1048 NETDATA.peityChartUpdate = function(element, data) {
1049 var peity = $(data.state.peity_element);
1050 peity.html(data.result);
1051 // peity.change() does not accept options
1052 // to pass width and height
1054 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1057 NETDATA.peityChartCreate = function(element, data) {
1058 element.innerHTML = '<div id="peity-' + data.state.uuid + '">' + data.result + '</div>';
1059 data.state.peity_element = document.getElementById('peity-' + data.state.uuid);
1060 var peity = $(data.state.peity_element);
1062 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1065 // ----------------------------------------------------------------------------------------------------------------
1068 NETDATA.sparklineInitialize = function(callback) {
1069 if(typeof netdataStopSparkline == 'undefined') {
1070 $.getScript(NETDATA.sparkline_js)
1072 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1075 NETDATA.error(100, NETDATA.sparkline_js);
1077 .always(function() {
1078 if(typeof callback == "function")
1083 NETDATA.chartLibraries.sparkline.enabled = false;
1084 if(typeof callback == "function")
1089 NETDATA.sparklineChartUpdate = function(element, data) {
1090 data.state.sparkline_options.width = data.state.calculated_width;
1091 data.state.sparkline_options.height = data.state.calculated_height;
1093 spark = $(data.state.sparkline_element);
1094 spark.sparkline(data.result, data.state.sparkline_options);
1097 NETDATA.sparklineChartCreate = function(element, data) {
1098 var self = $(element);
1099 var type = self.data('sparkline-type') || 'line';
1100 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1101 var fillColor = self.data('sparkline-fillcolor') || (data.state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
1102 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1103 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1104 var composite = self.data('sparkline-composite') || undefined;
1105 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1106 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1107 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1108 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1109 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1110 var spotColor = self.data('sparkline-spotcolor') || undefined;
1111 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1112 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1113 var spotRadius = self.data('sparkline-spotradius') || undefined;
1114 var valueSpots = self.data('sparkline-valuespots') || undefined;
1115 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1116 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1117 var lineWidth = self.data('sparkline-linewidth') || undefined;
1118 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1119 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1120 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1121 var xvalues = self.data('sparkline-xvalues') || undefined;
1122 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1123 var xvalues = self.data('sparkline-xvalues') || undefined;
1124 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1125 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1126 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1127 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1128 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1129 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1130 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1131 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1132 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1133 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1134 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1135 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + data.state.chart.units;
1136 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1137 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1138 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1139 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1140 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1141 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1142 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1143 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1144 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1146 data.state.sparkline_options = {
1148 lineColor: lineColor,
1149 fillColor: fillColor,
1150 chartRangeMin: chartRangeMin,
1151 chartRangeMax: chartRangeMax,
1152 composite: composite,
1153 enableTagOptions: enableTagOptions,
1154 tagOptionPrefix: tagOptionPrefix,
1155 tagValuesAttribute: tagValuesAttribute,
1156 disableHiddenCheck: disableHiddenCheck,
1157 defaultPixelsPerValue: defaultPixelsPerValue,
1158 spotColor: spotColor,
1159 minSpotColor: minSpotColor,
1160 maxSpotColor: maxSpotColor,
1161 spotRadius: spotRadius,
1162 valueSpots: valueSpots,
1163 highlightSpotColor: highlightSpotColor,
1164 highlightLineColor: highlightLineColor,
1165 lineWidth: lineWidth,
1166 normalRangeMin: normalRangeMin,
1167 normalRangeMax: normalRangeMax,
1168 drawNormalOnTop: drawNormalOnTop,
1170 chartRangeClip: chartRangeClip,
1171 chartRangeMinX: chartRangeMinX,
1172 chartRangeMaxX: chartRangeMaxX,
1173 disableInteraction: disableInteraction,
1174 disableTooltips: disableTooltips,
1175 disableHighlight: disableHighlight,
1176 highlightLighten: highlightLighten,
1177 highlightColor: highlightColor,
1178 tooltipContainer: tooltipContainer,
1179 tooltipClassname: tooltipClassname,
1180 tooltipChartTitle: data.state.chart.title,
1181 tooltipFormat: tooltipFormat,
1182 tooltipPrefix: tooltipPrefix,
1183 tooltipSuffix: tooltipSuffix,
1184 tooltipSkipNull: tooltipSkipNull,
1185 tooltipValueLookups: tooltipValueLookups,
1186 tooltipFormatFieldlist: tooltipFormatFieldlist,
1187 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1188 numberFormatter: numberFormatter,
1189 numberDigitGroupSep: numberDigitGroupSep,
1190 numberDecimalMark: numberDecimalMark,
1191 numberDigitGroupCount: numberDigitGroupCount,
1192 animatedZooms: animatedZooms,
1193 width: data.state.calculated_width,
1194 height: data.state.calculated_height
1197 element.innerHTML = '<div id="sparkline-' + data.state.uuid + '" style="display: inline-block; position: relative;"></div>';
1198 data.state.sparkline_element = document.getElementById('sparkline-' + data.state.uuid);
1200 spark = $(data.state.sparkline_element);
1201 spark.sparkline(data.result, data.state.sparkline_options);
1204 // ----------------------------------------------------------------------------------------------------------------
1210 dont_sync_before: 0,
1214 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1215 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1217 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1218 // state.log('sync: I am not the sync master.');
1221 // state.debug = true;
1223 var t = state.mode.after_ms + row * state.mode.view_update_every;
1224 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT'));
1226 now = new Date().getTime();
1227 if(now < NETDATA.dygraph.dont_sync_before) {
1228 if(state.debug) st.log('sync: cannot sync yet.');
1232 // since we are the sync master, we should not call state.setSelection()
1233 // dygraphs is taking care of visualizing our selection.
1234 state.selected = true;
1236 var dygraph = state.dygraph_instance;
1238 if(!NETDATA.dygraph.sync) {
1239 if(state.debug) st.log('sync: setting up...');
1240 $.each(NETDATA.options.targets, function(i, target) {
1241 var st = NETDATA.chartState(target);
1243 if(state.debug) st.log('sync: not adding me to sync');
1246 if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1247 NETDATA.dygraph.slaves.push(st);
1248 if(state.debug) st.log('sync: added slave to sync');
1252 NETDATA.dygraph.sync = true;
1255 $.each(NETDATA.dygraph.slaves, function(i, st) {
1257 if(state.debug) st.log('sync: ignoring me from set selection');
1260 if(state.debug) st.log('sync: showing master selection');
1266 NETDATA.dygraph.syncStop = function(state) {
1267 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1269 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1270 // state.log('sync: I am not the sync master.');
1274 if(NETDATA.dygraph.sync) {
1275 if(state.debug) st.log('sync: cleaning up...');
1276 $.each(NETDATA.dygraph.slaves, function(i, st) {
1278 if(state.debug) st.log('sync: not adding me to sync stop');
1281 if(state.debug) st.log('sync: removed slave from sync');
1282 st.clearSelection();
1286 NETDATA.dygraph.slaves = [];
1287 NETDATA.dygraph.sync = false;
1290 // since we are the sync master, we should not call state.clearSelection()
1291 // dygraphs is taking care of visualizing our selection.
1292 state.selected = false;
1295 NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1296 if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1299 if(NETDATA.globalPanAndZoom.clearMaster());
1302 NETDATA.dygraph.zoomOrPan = function(element, dygraph, after, before) {
1303 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(element, dygraph, after:' + after + ', before: ' + before + ')');
1305 state = NETDATA.chartState(element);
1306 state.updateChartPanOrZoom(after, before);
1310 NETDATA.dygraphSetSelection = function(state, t) {
1311 if(typeof state.dygraph_instance != 'undefined') {
1312 var r = state.calculateRowForTime(t);
1314 state.dygraph_instance.setSelection(r);
1318 state.dygraph_instance.clearSelection();
1324 NETDATA.dygraphClearSelection = function(state, t) {
1325 if(typeof state.dygraph_instance != 'undefined') {
1326 state.dygraph_instance.clearSelection();
1331 NETDATA.dygraphInitialize = function(callback) {
1332 if(typeof netdataStopDygraph == 'undefined') {
1333 $.getScript(NETDATA.dygraph_js)
1335 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1338 NETDATA.error(100, NETDATA.dygraph_js);
1340 .always(function() {
1341 if(typeof callback == "function")
1346 NETDATA.chartLibraries.dygraph.enabled = false;
1347 if(typeof callback == "function")
1352 NETDATA.dygraphChartUpdate = function(element, data) {
1353 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate()');
1355 var dygraph = data.state.dygraph_instance;
1357 if(data.state.mode.name == 'pan') {
1358 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() loose update');
1359 dygraph.updateOptions({
1360 file: data.result.data,
1361 labels: data.result.labels,
1362 labelsDivWidth: data.state.calculated_width - 70
1366 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() strict update');
1367 dygraph.updateOptions({
1368 file: data.result.data,
1369 labels: data.result.labels,
1370 labelsDivWidth: data.state.calculated_width - 70,
1377 NETDATA.dygraphChartCreate = function(element, data) {
1378 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartCreate()');
1380 var self = $(element);
1381 var title = self.data('dygraph-title') || data.state.chart.title;
1382 var titleHeight = self.data('dygraph-titleheight') || 20;
1383 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1384 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1385 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
1386 var stackedGraph = self.data('dygraph-stackedgraph') || (data.state.chart.chart_type == 'stacked')?true:false;
1387 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1388 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
1389 var fillGraph = self.data('dygraph-fillgraph') || (data.state.chart.chart_type == 'area')?true:false;
1390 var drawPoints = self.data('dygraph-drawpoints') || false;
1391 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1392 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
1393 var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
1394 var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
1395 var legend = self.data('dygraph-legend') || 'onmouseover';
1396 var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
1397 var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
1398 var axisLineColor = self.data('dygraph-axislinecolor') || '#EEE';
1399 var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
1400 var sigFigs = self.data('dygraph-sigfigs') || null;
1401 var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
1402 var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
1403 var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
1404 var drawAxis = self.data('dygraph-drawaxis') || true;
1405 var strokeWidth = self.data('dygraph-strokewidth') || 1.0;
1406 var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
1407 var colors = self.data('dygraph-colors') || NETDATA.colors;
1408 var pointSize = self.data('dygraph-pointsize') || 1;
1409 var stepPlot = self.data('dygraph-stepplot') || false;
1410 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
1411 var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (data.state.chart.chart_type == 'stacked')?1.0:0.0;
1412 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1413 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1414 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || { strokeWidth: 1.5 };
1415 var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (data.state.chart.chart_type == 'stacked')?0.7:0.5;
1416 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1417 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1418 var showRoller = self.data('dygraph-showroller') || false;
1419 var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
1420 var rightGap = self.data('dygraph-rightgap') || 5;
1421 var drawGrid = self.data('dygraph-drawgrid') || true;
1422 var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
1423 var drawYGrid = self.data('dygraph-drawygrid') || undefined;
1424 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1425 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1427 data.state.dygraph_options = {
1429 titleHeight: titleHeight,
1430 ylabel: data.state.chart.units,
1431 yLabelWidth: yLabelWidth,
1432 connectSeparatedPoints: connectSeparatedPoints,
1433 drawPoints: drawPoints,
1434 fillGraph: fillGraph,
1435 stackedGraph: stackedGraph,
1436 stackedGraphNaNFill: stackedGraphNaNFill,
1438 drawXGrid: drawXGrid,
1439 drawYGrid: drawYGrid,
1440 gridLinePattern: gridLinePattern,
1441 gridLineWidth: gridLineWidth,
1442 gridLineColor: gridLineColor,
1443 axisLineColor: axisLineColor,
1444 axisLineWidth: axisLineWidth,
1446 hideOverlayOnMouseOut: hideOverlayOnMouseOut,
1447 labelsDiv: labelsDiv,
1448 labelsDivStyles: labelsDivStyles,
1449 labelsDivWidth: labelsDivWidth,
1450 labelsSeparateLines: labelsSeparateLines,
1451 labelsShowZeroValues: labelsShowZeroValues,
1455 showLabelsOnHighlight: showLabelsOnHighlight,
1456 maxNumberWidth: maxNumberWidth,
1458 digitsAfterDecimal: digitsAfterDecimal,
1459 axisLabelFontSize: axisLabelFontSize,
1461 strokeWidth: strokeWidth,
1462 drawGapEdgePoints: drawGapEdgePoints,
1463 pointSize: pointSize,
1465 strokeBorderColor: strokeBorderColor,
1466 strokeBorderWidth: strokeBorderWidth,
1467 strokePattern: strokePattern,
1468 highlightCircleSize: highlightCircleSize,
1469 highlightSeriesOpts: highlightSeriesOpts,
1470 highlightSeriesBackgroundAlpha: highlightSeriesBackgroundAlpha,
1471 pointClickCallback: pointClickCallback,
1472 showRangeSelector: showRangeSelector,
1473 showRoller: showRoller,
1474 valueFormatter: valueFormatter,
1476 labels: data.result.labels,
1480 ticker: Dygraph.dateTicker,
1481 axisLabelFormatter: function (d, gran) {
1482 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1484 valueFormatter :function (ms) {
1485 var d = new Date(ms);
1486 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1493 drawCallback: function(dygraph, is_initial) {
1494 if(data.state.mode.name != 'auto') {
1495 if(NETDATA.options.debug.dygraph) data.state.log('dygraphDrawCallback()');
1497 var x_range = dygraph.xAxisRange();
1498 var after = Math.round(x_range[0]);
1499 var before = Math.round(x_range[1]);
1501 NETDATA.dygraph.zoomOrPan(element, this, after, before);
1504 zoomCallback: function(minDate, maxDate, yRanges) {
1505 if(NETDATA.options.debug.dygraph) data.state.log('dygraphZoomCallback()');
1506 NETDATA.dygraph.syncStop(data.state);
1507 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1508 NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1510 highlightCallback: function(event, x, points, row, seriesName) {
1511 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphHighlightCallback()');
1512 data.state.pauseChart();
1513 NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1515 unhighlightCallback: function(event) {
1516 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphUnhighlightCallback()');
1517 data.state.unpauseChart();
1518 NETDATA.dygraph.syncStop(data.state);
1520 interactionModel : {
1521 mousedown: function(event, dygraph, context) {
1522 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousedown()');
1523 NETDATA.dygraph.syncStop(data.state);
1525 if(NETDATA.options.debug.dygraph) data.state.log('dygraphMouseDown()');
1527 // Right-click should not initiate a zoom.
1528 if(event.button && event.button == 2) return;
1530 context.initializeMouseDown(event, dygraph, context);
1532 if(event.button && event.button == 1) {
1533 if (event.altKey || event.shiftKey) {
1534 data.state.setMode('pan');
1535 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1536 Dygraph.startPan(event, dygraph, context);
1539 data.state.setMode('zoom');
1540 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1541 Dygraph.startZoom(event, dygraph, context);
1545 if (event.altKey || event.shiftKey) {
1546 data.state.setMode('zoom');
1547 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1548 Dygraph.startZoom(event, dygraph, context);
1551 data.state.setMode('pan');
1552 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1553 Dygraph.startPan(event, dygraph, context);
1557 mousemove: function(event, dygraph, context) {
1558 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousemove()');
1560 if(context.isPanning) {
1561 NETDATA.dygraph.syncStop(data.state);
1562 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1563 data.state.setMode('pan');
1564 Dygraph.movePan(event, dygraph, context);
1566 else if(context.isZooming) {
1567 NETDATA.dygraph.syncStop(data.state);
1568 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1569 data.state.setMode('zoom');
1570 Dygraph.moveZoom(event, dygraph, context);
1573 mouseup: function(event, dygraph, context) {
1574 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mouseup()');
1576 if (context.isPanning) {
1577 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1578 Dygraph.endPan(event, dygraph, context);
1580 else if (context.isZooming) {
1581 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1582 Dygraph.endZoom(event, dygraph, context);
1585 click: function(event, dygraph, context) {
1586 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.click()');
1587 Dygraph.cancelEvent(event);
1589 dblclick: function(event, dygraph, context) {
1590 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.dblclick()');
1591 NETDATA.dygraph.syncStop(data.state);
1592 NETDATA.dygraph.resetChart(data.state, dygraph, context);
1594 mousewheel: function(event, dygraph, context) {
1595 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousewheel()');
1597 NETDATA.dygraph.syncStop(data.state);
1598 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1600 if(event.altKey || event.shiftKey) {
1601 // http://dygraphs.com/gallery/interaction-api.js
1602 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1603 var percentage = normal / 25;
1605 var before_old = data.state.mode.before_ms;
1606 var after_old = data.state.mode.after_ms;
1607 var range_old = before_old - after_old;
1609 var range = range_old * ( 1 - percentage );
1610 var dt = Math.round((range_old - range) / 2);
1612 var before = before_old - dt;
1613 var after = after_old + dt;
1615 if(NETDATA.options.debug.dygraph) data.state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
1617 data.state.setMode('zoom');
1618 NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1621 touchstart: function(event, dygraph, context) {
1622 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchstart()');
1623 NETDATA.dygraph.syncStop(data.state);
1624 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1625 Dygraph.Interaction.startTouch(event, dygraph, context);
1626 context.touchDirections = { x: true, y: false };
1627 data.state.setMode('zoom');
1629 touchmove: function(event, dygraph, context) {
1630 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchmove()');
1631 //Dygraph.cancelEvent(event);
1632 NETDATA.dygraph.syncStop(data.state);
1633 Dygraph.Interaction.moveTouch(event, dygraph, context);
1635 touchend: function(event, dygraph, context) {
1636 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchend()');
1637 Dygraph.Interaction.endTouch(event, dygraph, context);
1642 self.html('<div id="dygraph-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>');
1644 data.state.dygraph_instance = new Dygraph(document.getElementById('dygraph-' + data.state.uuid),
1645 data.result.data, data.state.dygraph_options);
1648 // ----------------------------------------------------------------------------------------------------------------
1651 NETDATA.morrisInitialize = function(callback) {
1652 if(typeof netdataStopMorris == 'undefined') {
1654 // morris requires raphael
1655 if(!NETDATA.chartLibraries.raphael.initialized) {
1656 if(NETDATA.chartLibraries.raphael.enabled) {
1657 NETDATA.raphaelInitialize(function() {
1658 NETDATA.morrisInitialize(callback);
1662 NETDATA.chartLibraries.morris.enabled = false;
1663 if(typeof callback == "function")
1668 var fileref = document.createElement("link");
1669 fileref.setAttribute("rel", "stylesheet");
1670 fileref.setAttribute("type", "text/css");
1671 fileref.setAttribute("href", NETDATA.morris_css);
1673 if (typeof fileref != "undefined")
1674 document.getElementsByTagName("head")[0].appendChild(fileref);
1676 $.getScript(NETDATA.morris_js)
1678 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1681 NETDATA.error(100, NETDATA.morris_js);
1683 .always(function() {
1684 if(typeof callback == "function")
1690 NETDATA.chartLibraries.morris.enabled = false;
1691 if(typeof callback == "function")
1696 NETDATA.morrisChartUpdate = function(element, data) {
1697 data.state.morris_instance.setData(data.result.data);
1700 NETDATA.morrisChartCreate = function(element, data) {
1702 element.innerHTML = '<div id="morris-' + data.state.uuid + '" style="width: ' + data.state.calculated_width + 'px; height: ' + data.state.calculated_height + 'px;"></div>';
1705 element: 'morris-' + data.state.uuid,
1706 data: data.result.data,
1708 ykeys: data.dimension_names,
1709 labels: data.dimension_names,
1715 continuousLine: false,
1716 behaveLikeLine: false
1720 if(data.state.chart.chart_type == 'line')
1721 morris = new Morris.Line(options);
1723 else if(data.state.chart.chart_type == 'area') {
1724 options.behaveLikeLine = true;
1725 morris = new Morris.Area(options);
1728 morris = new Morris.Area(options);
1730 data.state.morris_instance = morris;
1731 data.state.morris_options = options;
1734 // ----------------------------------------------------------------------------------------------------------------
1737 NETDATA.raphaelInitialize = function(callback) {
1738 if(typeof netdataStopRaphael == 'undefined') {
1739 $.getScript(NETDATA.raphael_js)
1741 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
1744 NETDATA.error(100, NETDATA.raphael_js);
1746 .always(function() {
1747 if(typeof callback == "function")
1752 NETDATA.chartLibraries.raphael.enabled = false;
1753 if(typeof callback == "function")
1758 NETDATA.raphaelChartUpdate = function(element, data) {
1759 var self = $(element);
1761 self.raphael(data, {
1762 width: data.state.calculated_width,
1763 height: data.state.calculated_height
1767 NETDATA.raphaelChartCreate = function(element, data) {
1768 var self = $(element);
1770 self.raphael(data, {
1771 width: data.state.calculated_width,
1772 height: data.state.calculated_height
1776 // ----------------------------------------------------------------------------------------------------------------
1779 NETDATA.googleInitialize = function(callback) {
1780 if(typeof netdataStopGoogleCharts == 'undefined') {
1781 $.getScript(NETDATA.google_js)
1783 NETDATA.registerChartLibrary('google', NETDATA.google_js);
1785 google.load('visualization', '1.1', {
1786 'packages': ['corechart', 'controls'],
1787 'callback': callback
1791 NETDATA.error(100, NETDATA.google_js);
1792 if(typeof callback == "function")
1797 NETDATA.chartLibraries.google.enabled = false;
1798 if(typeof callback == "function")
1803 NETDATA.googleChartUpdate = function(element, data) {
1804 var datatable = new google.visualization.DataTable(data.result);
1805 data.state.google_instance.draw(datatable, data.state.google_options);
1808 NETDATA.googleChartCreate = function(element, data) {
1809 var datatable = new google.visualization.DataTable(data.result);
1812 // do not set width, height - the chart resizes itself
1813 //width: data.state.calculated_width,
1814 //height: data.state.calculated_height,
1816 title: data.state.chart.title,
1819 // title: "Time of Day",
1820 // format:'HH:mm:ss',
1821 viewWindowMode: 'maximized',
1832 title: data.state.chart.units,
1833 viewWindowMode: 'pretty',
1848 focusTarget: 'category',
1855 titlePosition: 'out',
1866 curveType: 'function',
1871 element.innerHTML = '<div id="google-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>';
1874 switch(data.state.chart.chart_type) {
1876 options.vAxis.viewWindowMode = 'maximized';
1877 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1881 options.isStacked = true;
1882 options.areaOpacity = 0.85;
1883 options.vAxis.viewWindowMode = 'maximized';
1884 options.vAxis.minValue = null;
1885 options.vAxis.maxValue = null;
1886 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1891 options.lineWidth = 2;
1892 gchart = new google.visualization.LineChart(document.getElementById('google-' + data.state.uuid));
1896 gchart.draw(datatable, options);
1898 data.state.google_instance = gchart;
1899 data.state.google_options = options;
1902 // ----------------------------------------------------------------------------------------------------------------
1903 // Charts Libraries Registration
1905 NETDATA.chartLibraries = {
1907 initialize: NETDATA.dygraphInitialize,
1908 create: NETDATA.dygraphChartCreate,
1909 update: NETDATA.dygraphChartUpdate,
1910 setSelection: NETDATA.dygraphSetSelection,
1911 clearSelection: NETDATA.dygraphClearSelection,
1917 pixels_per_point: 2,
1918 detects_dimensions_on_update: false
1921 initialize: NETDATA.sparklineInitialize,
1922 create: NETDATA.sparklineChartCreate,
1923 update: NETDATA.sparklineChartUpdate,
1925 clearSelection: null,
1929 options: 'flip|abs',
1931 pixels_per_point: 2,
1932 detects_dimensions_on_update: false
1935 initialize: NETDATA.peityInitialize,
1936 create: NETDATA.peityChartCreate,
1937 update: NETDATA.peityChartUpdate,
1939 clearSelection: null,
1943 options: 'null2zero|flip|abs',
1945 pixels_per_point: 2,
1946 detects_dimensions_on_update: false
1949 initialize: NETDATA.morrisInitialize,
1950 create: NETDATA.morrisChartCreate,
1951 update: NETDATA.morrisChartUpdate,
1953 clearSelection: null,
1957 options: 'objectrows|ms',
1959 pixels_per_point: 10,
1960 detects_dimensions_on_update: false
1963 initialize: NETDATA.googleInitialize,
1964 create: NETDATA.googleChartCreate,
1965 update: NETDATA.googleChartUpdate,
1967 clearSelection: null,
1970 format: 'datatable',
1973 pixels_per_point: 2,
1974 detects_dimensions_on_update: true
1977 initialize: NETDATA.raphaelInitialize,
1978 create: NETDATA.raphaelChartCreate,
1979 update: NETDATA.raphaelChartUpdate,
1981 clearSelection: null,
1987 pixels_per_point: 1,
1988 detects_dimensions_on_update: false
1992 NETDATA.registerChartLibrary = function(library, url) {
1993 console.log("registering chart library: " + library);
1995 NETDATA.chartLibraries[library].url = url;
1996 NETDATA.chartLibraries[library].initialized = true;
1997 NETDATA.chartLibraries[library].enabled = true;
1999 // console.log(NETDATA.chartLibraries);
2002 // ----------------------------------------------------------------------------------------------------------------
2003 // load all libraries and initialize
2005 NETDATA.errorReset();
2007 NETDATA._loadjQuery(function() {
2008 $.getScript(NETDATA.serverDefault + 'lib/visible.js').then(function() {