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
118 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
121 // ----------------------------------------------------------------------------------------------------------------
124 NETDATA.errorCodes = {
125 100: { message: "Cannot load chart library", alert: true },
126 101: { message: "Cannot load jQuery", alert: true },
127 402: { message: "Chart library not found", alert: false },
128 403: { message: "Chart library not enabled/is failed", alert: false },
129 404: { message: "Chart not found", alert: false }
131 NETDATA.errorLast = {
137 NETDATA.error = function(code, msg) {
138 NETDATA.errorLast.code = code;
139 NETDATA.errorLast.message = msg;
140 NETDATA.errorLast.datetime = new Date().getTime();
142 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
144 if(NETDATA.errorCodes[code].alert)
145 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
148 NETDATA.errorReset = function() {
149 NETDATA.errorLast.code = 0;
150 NETDATA.errorLast.message = "You are doing fine!";
151 NETDATA.errorLast.datetime = 0;
154 // ----------------------------------------------------------------------------------------------------------------
156 NETDATA.chartRegistry = {
159 add: function(host, id, data) {
160 host = host.replace(/:/g, "_").replace(/\//g, "_");
161 id = id.replace(/:/g, "_").replace(/\//g, "_");
163 if(typeof this.charts[host] == 'undefined')
164 this.charts[host] = {};
166 this.charts[host][id] = data;
169 get: function(host, id) {
170 host = host.replace(/:/g, "_").replace(/\//g, "_");
171 id = id.replace(/:/g, "_").replace(/\//g, "_");
173 if(typeof this.charts[host] == 'undefined')
176 if(typeof this.charts[host][id] == 'undefined')
179 return this.charts[host][id];
183 // ----------------------------------------------------------------------------------------------------------------
185 NETDATA.globalPanAndZoom = {
190 force_before_ms: null,
191 force_after_ms: null,
193 setMaster: function(state, after, before) {
194 if(this.state && this.state != state) this.state.resetChart();
197 this.seq = new Date().getTime();
198 this.force_after_ms = after;
199 this.force_before_ms = before;
201 clearMaster: function() {
203 var state = this.state;
204 this.state = null; // prevent infinite recursion
207 NETDATA.options.auto_refresher_stop_until = 0;
214 shouldBeAutoRefreshed: function(state) {
215 if(!this.state || !this.seq)
218 if(state.follows_global == this.seq)
225 // ----------------------------------------------------------------------------------------------------------------
226 // Our state object, where all per-chart values are stored
228 NETDATA.chartInitialState = function(element) {
232 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
233 id: self.data('netdata'), // string - the name of chart
235 // the user given dimensions of the element
236 width: self.data('width') || NETDATA.chartDefaults.width,
237 height: self.data('height') || NETDATA.chartDefaults.height,
239 // these are calculated every time the chart is refreshed
241 calculated_height: 0,
243 // string - the netdata server URL, without any path
244 host: self.data('host') || NETDATA.chartDefaults.host,
246 // string - the grouping method requested by the user
247 method: self.data('method') || NETDATA.chartDefaults.method,
249 // the time-range requested by the user
250 after: self.data('after') || NETDATA.chartDefaults.after,
251 before: self.data('before') || NETDATA.chartDefaults.before,
253 // the pixels per point requested by the user
255 points: self.data('points') || null,
257 // the dimensions requested by the user
258 dimensions: self.data('dimensions') || null,
260 // the chart library requested by the user
261 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
262 library: null, // object - the chart library used
264 element: element, // the element it is linked to
266 chart_url: null, // string - the url to download chart info
267 chart: null, // object - the chart as downloaded from the server
269 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
270 created_ms: 0, // boolean - the timestamp the chart was created
271 validated: false, // boolean - has the chart been validated?
272 enabled: true, // boolean - is the chart enabled for refresh?
273 paused: false, // boolean - is the chart paused for any reason?
275 updates_counter: 0, // numeric - the number of refreshes made so far
279 mode: null, // auto, pan, zoom
283 url: 'invalid://', // string - the last url used to update the chart
284 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
285 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
286 after_ms: 0, // milliseconds - the first timestamp of the data
287 before_ms: 0, // milliseconds - the last timestamp of the data
288 points: 0, // number - the number of points in the data
289 data: null, // the last downloaded data
290 force_before_ms: null,
291 force_after_ms: null,
292 requested_before_ms: null,
293 requested_after_ms: null,
294 first_entry_ms: null,
300 url: 'invalid://', // string - the last url used to update the chart
301 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
302 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
303 after_ms: 0, // milliseconds - the first timestamp of the data
304 before_ms: 0, // milliseconds - the last timestamp of the data
305 points: 0, // number - the number of points in the data
306 data: null, // the last downloaded data
307 force_before_ms: null,
308 force_after_ms: null,
309 requested_before_ms: null,
310 requested_after_ms: null,
311 first_entry_ms: null,
317 url: 'invalid://', // string - the last url used to update the chart
318 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
319 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
320 after_ms: 0, // milliseconds - the first timestamp of the data
321 before_ms: 0, // milliseconds - the last timestamp of the data
322 points: 0, // number - the number of points in the data
323 data: null, // the last downloaded data
324 force_before_ms: null,
325 force_after_ms: null,
326 requested_before_ms: null,
327 requested_after_ms: null,
328 first_entry_ms: null,
331 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
332 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
333 refresh_dt_element: null,
336 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
339 setSelection: function(t) {
340 if(typeof this.library.setSelection == 'function')
341 return this.library.setSelection(this, t);
346 clearSelection: function() {
347 if(typeof this.library.clearSelection == 'function')
348 return this.library.clearSelection(this);
353 timeIsVisible: function(t) {
354 if(t >= this.mode.after_ms && t <= this.mode.before_ms)
359 calculateRowForTime: function(t) {
360 if(!this.timeIsVisible(t)) return -1;
362 var r = Math.floor((t - this.mode.after_ms) / this.mode.view_update_every);
363 // console.log(this.mode.data);
368 pauseChart: function() {
372 unpauseChart: function() {
376 resetChart: function() {
377 if(NETDATA.globalPanAndZoom.state == this)
378 NETDATA.globalPanAndZoom.clearMaster();
380 if(state.mode.name != 'auto')
381 this.setMode('auto');
383 this.mode.force_before_ms = null;
384 this.mode.force_after_ms = null;
385 this.mode.last_updated_ms = 0;
386 this.follows_global = 0;
394 setMode: function(m) {
396 if(this.mode.name == m) return;
398 this[m].url = this.mode.url;
399 this[m].last_updated_ms = this.mode.last_updated_ms;
400 this[m].view_update_every = this.mode.view_update_every;
401 this[m].after_ms = this.mode.after_ms;
402 this[m].before_ms = this.mode.before_ms;
403 this[m].points = this.mode.points;
404 this[m].data = this.mode.data;
405 this[m].requested_before_ms = this.mode.requested_before_ms;
406 this[m].requested_after_ms = this.mode.requested_after_ms;
407 this[m].first_entry_ms = this.mode.first_entry_ms;
408 this[m].last_entry_ms = this.mode.last_entry_ms;
412 this.mode = this.auto;
414 this.mode = this.pan;
416 this.mode = this.zoom;
418 this.mode = this.auto;
420 this.mode.force_before_ms = null;
421 this.mode.force_after_ms = null;
423 if(this.debug) this.log('mode set to ' + this.mode.name);
426 _minPanOrZoomStep: function() {
427 return (((this.mode.before_ms - this.mode.after_ms) / this.mode.points) * ((this.mode.points * 5 / 100) + 1) );
428 // return this.mode.view_update_every * 10;
431 _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
432 var dt_after = Math.abs(old_after - new_after);
433 var dt_before = Math.abs(old_before - new_before);
434 var old_range = old_before - old_after;
436 var new_range = new_before - new_after;
437 var dt = Math.abs(old_range - new_range);
438 var step = Math.max(dt_after, dt_before, dt);
440 var min_step = this._minPanOrZoomStep();
441 if(new_range < old_range && new_range / this.calculated_width < 100) {
442 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');
446 if(step >= min_step) {
447 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');
451 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');
456 updateChartPanOrZoom: function(after, before, callback) {
459 if(this.mode.name == 'auto') {
460 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
464 if(!this.mode.force_after_ms || !this.mode.force_before_ms) {
465 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
468 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)) {
469 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());
472 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)) {
473 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());
478 var now = new Date().getTime();
479 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
480 NETDATA.globalPanAndZoom.setMaster(this, after, before);
482 this.mode.force_after_ms = after;
483 this.mode.force_before_ms = before;
484 this.updateChart(callback);
488 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
489 if(typeof callback != 'undefined') callback();
493 updateChart: function(callback) {
494 if(!this.library || !this.library.enabled) {
495 this.error('chart library "' + this.library_name + '" is not enabled');
496 if(typeof callback != 'undefined') callback();
501 if(this.debug) this.error('chart "' + this.id + '" is not enabled');
502 if(typeof callback != 'undefined') callback();
506 if(!self.visible(true)) {
507 if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
508 if(typeof callback != 'undefined') callback();
512 var this_state_object = this;
513 this.getChart(function() { this_state_object.updateChart(callback); });
517 if(!this.library.initialized) {
518 var this_state_object = this;
519 this.library.initialize(function() { this_state_object.updateChart(callback); });
523 this.clearSelection();
525 if(this.debug) this.log('updating from ' + this.mode.url);
527 var this_state_object = this;
529 url: this_state_object.mode.url,
532 .then(function(data) {
533 if(this_state_object.debug) this_state_object.log('got data from netdata server');
534 this_state_object.mode.data = data;
536 var started = new Date().getTime();
538 // if the result is JSON, find the latest update-every
539 if(typeof data == 'object' && this_state_object.library.jsonWrapper) {
540 if(!this_state_object.follows_global && typeof data.view_update_every != 'undefined')
541 this_state_object.mode.view_update_every = data.view_update_every * 1000;
543 if(typeof data.after != 'undefined')
544 this_state_object.mode.after_ms = data.after * 1000;
546 if(typeof data.before != 'undefined')
547 this_state_object.mode.before_ms = data.before * 1000;
549 if(typeof data.first_entry_t != 'undefined')
550 this_state_object.mode.first_entry_ms = data.first_entry_t * 1000;
552 if(typeof data.last_entry_t != 'undefined')
553 this_state_object.mode.last_entry_ms = data.last_entry_t * 1000;
555 if(typeof data.points != 'undefined')
556 this_state_object.mode.points = data.points;
558 data.state = this_state_object;
561 this_state_object.updates_counter++;
563 if(this_state_object.debug) {
564 this_state_object.log('UPDATE No ' + this_state_object.updates_counter + ' COMPLETED');
566 if(this_state_object.mode.force_after_ms)
567 this_state_object.log('STATUS: forced : ' + (this_state_object.mode.force_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.force_before_ms / 1000).toString());
569 this_state_object.log('STATUS: forced: unset');
571 this_state_object.log('STATUS: requested: ' + (this_state_object.mode.requested_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.requested_before_ms / 1000).toString());
572 this_state_object.log('STATUS: rendered : ' + (this_state_object.mode.after_ms / 1000).toString() + ' - ' + (this_state_object.mode.before_ms / 1000).toString());
573 this_state_object.log('STATUS: points : ' + (this_state_object.mode.points).toString() + ', min step: ' + (this_state_object._minPanOrZoomStep() / 1000).toString());
576 if(this_state_object.created_ms) {
577 if(this_state_object.debug) this_state_object.log('updating chart...');
579 if(NETDATA.options.debug.chart_errors) {
580 this_state_object.library.update(this_state_object.element, data);
584 this_state_object.library.update(this_state_object.element, data);
587 this_state_object.error('chart "' + state.id + '" failed to be updated as ' + state.library_name);
592 if(this_state_object.debug) this_state_object.log('creating chart...');
594 if(NETDATA.options.debug.chart_errors) {
595 this_state_object.library.create(this_state_object.element, data);
596 this_state_object.created_ms = new Date().getTime();
600 this_state_object.library.create(this_state_object.element, data);
601 this_state_object.created_ms = new Date().getTime();
604 this_state_object.error('chart "' + state.id + '" failed to be created as ' + state.library_name);
609 // update the performance counters
610 this_state_object.mode.last_updated_ms = new Date().getTime();
611 this_state_object.refresh_dt_ms = this_state_object.mode.last_updated_ms - started;
612 NETDATA.options.auto_refresher_fast_weight += this_state_object.refresh_dt_ms;
614 if(this_state_object.refresh_dt_element)
615 this_state_object.refresh_dt_element.innerHTML = this_state_object.refresh_dt_ms.toString();
618 this_state_object.error('cannot download chart from ' + this_state_object.mode.url);
621 if(typeof callback == 'function') callback();
625 chartURL: function() {
626 this.calculated_width = self.width();
627 this.calculated_height = self.height();
631 if(NETDATA.globalPanAndZoom.state) {
632 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
633 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
634 this.follows_global = NETDATA.globalPanAndZoom.seq;
637 before = this.mode.force_before_ms != null ? Math.round(this.mode.force_before_ms / 1000) : this.before;
638 after = this.mode.force_after_ms != null ? Math.round(this.mode.force_after_ms / 1000) : this.after;
639 this.follows_global = 0;
642 this.mode.requested_after_ms = after * 1000;
643 this.mode.requested_before_ms = before * 1000;
645 // force an options provided detail
646 var pixels_per_point = this.pixels_per_point;
647 if(pixels_per_point < NETDATA.options.current.pixels_per_point)
648 pixels_per_point = NETDATA.options.current.pixels_per_point
650 this.mode.points = this.points || Math.round(this.calculated_width / pixels_per_point);
652 // build the data URL
653 this.mode.url = this.chart.data_url;
654 this.mode.url += "&format=" + this.library.format;
655 this.mode.url += "&points=" + this.mode.points.toString();
656 this.mode.url += "&group=" + this.method;
657 this.mode.url += "&options=" + this.library.options;
658 if(this.library.jsonWrapper) this.mode.url += '|jsonwrap';
661 this.mode.url += "&after=" + after.toString();
664 this.mode.url += "&before=" + before.toString();
667 this.mode.url += "&dimensions=" + this.dimensions;
669 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);
672 canBeAutoRefreshed: function(auto_refresher) {
673 if(this.mode.autorefresh) {
674 var now = new Date().getTime();
676 if(this.updates_counter && !NETDATA.options.page_is_visible) {
677 if(NETDATA.options.debug.focus || this.debug) this.log('page does not have focus');
681 if(!auto_refresher) return true;
683 if(!self.visible(true)) return false;
685 // options valid only for autoRefresh()
686 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
687 if(NETDATA.globalPanAndZoom.state) {
688 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this))
694 if(this.paused) return false;
696 if(now - this.mode.last_updated_ms > this.mode.view_update_every)
704 autoRefresh: function(callback) {
705 if(this.canBeAutoRefreshed(true))
706 this.updateChart(callback);
707 else if(typeof callback != 'undefined')
711 // fetch the chart description from the netdata server
712 getChart: function(callback) {
713 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
715 if(typeof callback == 'function') callback();
718 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
719 if(this.debug) this.log('downloading ' + this.chart_url);
720 this_state_object = this;
726 .done(function(chart) {
727 chart.data_url = (this_state_object.host + chart.data_url);
728 this_state_object.chart = chart;
729 this_state_object.mode.view_update_every = chart.update_every * 1000;
730 this_state_object.mode.points = Math.round(self.width() / (chart.update_every / 1000));
732 chart.url = this_state_object.chart_url;
733 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
736 NETDATA.error(404, this_state_object.chart_url);
737 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
740 if(typeof callback == 'function') callback();
745 // resize the chart to its real dimensions
746 // as given by the caller
747 sizeChart: function() {
748 if(this.debug) this.log('sizing element');
749 self.css('width', this.width)
750 .css('height', this.height)
751 .css('display', 'inline-block')
752 .css('overflow', 'hidden');
755 // show a message in the chart
756 message: function(msg) {
758 if(NETDATA.options.debug.show_boxes)
759 bgcolor = " background-color: lightgrey;";
761 this.element.innerHTML = '<div style="font-size: x-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
765 // reset the creation datetime
766 // since we overwrote the whole element
768 if(this.debug) this.log(msg);
771 // show an error on the chart and stop it forever
772 error: function(msg) {
774 this.enabled = false;
777 // show a message indicating the chart is loading
778 loading: function() {
779 this.message('chart ' + this.id + ' is loading...');
783 if(state.debug) state.log('created');
787 if(typeof NETDATA.chartLibraries[state.library_name] == 'undefined') {
788 NETDATA.error(402, state.library_name);
789 state.error('chart library "' + state.library_name + '" is not found');
791 else if(!NETDATA.chartLibraries[state.library_name].enabled) {
792 NETDATA.error(403, state.library_name);
793 state.error('chart library "' + state.library_name + '" is not enabled');
796 state.library = NETDATA.chartLibraries[state.library_name];
797 state.pixels_per_point = self.data('pixels-per-point') || state.library.pixels_per_point;
800 if(state.refresh_dt_element_name)
801 state.refresh_dt_element = document.getElementById(state.refresh_dt_element_name) || null;
803 state.setMode('auto');
808 // get or create a chart state, given a DOM element
809 NETDATA.chartState = function(element) {
811 var state = self.data('state') || null;
813 state = NETDATA.chartInitialState(element);
814 self.data('state', state);
819 // ----------------------------------------------------------------------------------------------------------------
822 // Load a script without jquery
823 // This is used to load jquery - after it is loaded, we use jquery
824 NETDATA._loadjQuery = function(callback) {
825 if(typeof jQuery == 'undefined') {
826 var script = document.createElement('script');
827 script.type = 'text/javascript';
829 script.src = NETDATA.jQuery;
831 // script.onabort = onError;
832 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
833 if(typeof callback == "function")
834 script.onload = callback;
836 var s = document.getElementsByTagName('script')[0];
837 s.parentNode.insertBefore(script, s);
839 else if(typeof callback == "function")
843 NETDATA.ColorLuminance = function(hex, lum) {
844 // validate hex string
845 hex = String(hex).replace(/[^0-9a-f]/gi, '');
847 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
851 // convert to decimal and change luminosity
853 for (i = 0; i < 3; i++) {
854 c = parseInt(hex.substr(i*2,2), 16);
855 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
856 rgb += ("00"+c).substr(c.length);
862 NETDATA.guid = function() {
864 return Math.floor((1 + Math.random()) * 0x10000)
869 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
872 // user function to signal us the DOM has been
874 NETDATA.updatedDom = function() {
875 NETDATA.options.updated_dom = 1;
878 // ----------------------------------------------------------------------------------------------------------------
880 NETDATA.chartRefresher = function(index) {
881 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
883 if(NETDATA.options.updated_dom) {
884 // the dom has been updated
885 // get the dom parts again
886 NETDATA.getDomCharts(function() {
887 NETDATA.chartRefresher(0);
893 var target = NETDATA.options.targets.get(index);
895 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
896 NETDATA.options.auto_refresher_fast_weight = 0;
898 setTimeout(function() {
899 NETDATA.chartRefresher(0);
900 }, NETDATA.options.current.idle_between_loops);
903 var state = NETDATA.chartState(target);
905 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
906 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
908 state.autoRefresh(function() {
909 NETDATA.chartRefresher(++index);
913 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
914 NETDATA.options.auto_refresher_fast_weight = 0;
916 setTimeout(function() {
917 state.autoRefresh(function() {
918 NETDATA.chartRefresher(++index);
920 }, NETDATA.options.current.idle_between_charts);
925 NETDATA.getDomCharts = function(callback) {
926 NETDATA.options.updated_dom = 0;
928 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
930 if(NETDATA.options.debug.main_loop)
931 console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
933 // we need to re-size all the charts quickly
934 // before making any external calls
935 $.each(NETDATA.options.targets, function(i, target) {
936 // the initialization will take care of sizing
937 // and the "loading..." message
938 var state = NETDATA.chartState(target);
941 if(typeof callback == 'function') callback();
944 // this is the main function - where everything starts
945 NETDATA.init = function() {
946 // this should be called only once
948 NETDATA.options.page_is_visible = 1;
950 $(window).blur(function() {
951 NETDATA.options.page_is_visible = 0;
952 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
955 $(window).focus(function() {
956 NETDATA.options.page_is_visible = 1;
957 if(NETDATA.options.debug.focus) console.log('Focus restored!');
960 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
961 NETDATA.options.page_is_visible = 0;
962 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
965 NETDATA.getDomCharts(function() {
966 NETDATA.chartRefresher(0);
970 // ----------------------------------------------------------------------------------------------------------------
972 //var chart = function() {
975 //chart.prototype.color = function() {
979 //var c = new chart();
982 // ----------------------------------------------------------------------------------------------------------------
985 NETDATA.peityInitialize = function(callback) {
986 if(typeof netdataStopPeity == 'undefined') {
987 $.getScript(NETDATA.peity_js)
989 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
992 NETDATA.error(100, NETDATA.peity_js);
995 if(typeof callback == "function")
1000 NETDATA.chartLibraries.peity.enabled = false;
1001 if(typeof callback == "function")
1006 NETDATA.peityChartUpdate = function(element, data) {
1007 var peity = $(data.state.peity_element);
1008 peity.html(data.result);
1009 // peity.change() does not accept options
1010 // to pass width and height
1012 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1015 NETDATA.peityChartCreate = function(element, data) {
1016 element.innerHTML = '<div id="peity-' + data.state.uuid + '">' + data.result + '</div>';
1017 data.state.peity_element = document.getElementById('peity-' + data.state.uuid);
1018 var peity = $(data.state.peity_element);
1020 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1023 // ----------------------------------------------------------------------------------------------------------------
1026 NETDATA.sparklineInitialize = function(callback) {
1027 if(typeof netdataStopSparkline == 'undefined') {
1028 $.getScript(NETDATA.sparkline_js)
1030 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1033 NETDATA.error(100, NETDATA.sparkline_js);
1035 .always(function() {
1036 if(typeof callback == "function")
1041 NETDATA.chartLibraries.sparkline.enabled = false;
1042 if(typeof callback == "function")
1047 NETDATA.sparklineChartUpdate = function(element, data) {
1048 data.state.sparkline_options.width = data.state.calculated_width;
1049 data.state.sparkline_options.height = data.state.calculated_height;
1051 spark = $(data.state.sparkline_element);
1052 spark.sparkline(data.result, data.state.sparkline_options);
1055 NETDATA.sparklineChartCreate = function(element, data) {
1056 var self = $(element);
1057 var type = self.data('sparkline-type') || 'line';
1058 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1059 var fillColor = self.data('sparkline-fillcolor') || (data.state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
1060 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1061 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1062 var composite = self.data('sparkline-composite') || undefined;
1063 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1064 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1065 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1066 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1067 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1068 var spotColor = self.data('sparkline-spotcolor') || undefined;
1069 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1070 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1071 var spotRadius = self.data('sparkline-spotradius') || undefined;
1072 var valueSpots = self.data('sparkline-valuespots') || undefined;
1073 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1074 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1075 var lineWidth = self.data('sparkline-linewidth') || undefined;
1076 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1077 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1078 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1079 var xvalues = self.data('sparkline-xvalues') || undefined;
1080 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1081 var xvalues = self.data('sparkline-xvalues') || undefined;
1082 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1083 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1084 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1085 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1086 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1087 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1088 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1089 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1090 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1091 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1092 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1093 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + data.state.chart.units;
1094 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1095 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1096 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1097 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1098 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1099 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1100 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1101 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1102 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1104 data.state.sparkline_options = {
1106 lineColor: lineColor,
1107 fillColor: fillColor,
1108 chartRangeMin: chartRangeMin,
1109 chartRangeMax: chartRangeMax,
1110 composite: composite,
1111 enableTagOptions: enableTagOptions,
1112 tagOptionPrefix: tagOptionPrefix,
1113 tagValuesAttribute: tagValuesAttribute,
1114 disableHiddenCheck: disableHiddenCheck,
1115 defaultPixelsPerValue: defaultPixelsPerValue,
1116 spotColor: spotColor,
1117 minSpotColor: minSpotColor,
1118 maxSpotColor: maxSpotColor,
1119 spotRadius: spotRadius,
1120 valueSpots: valueSpots,
1121 highlightSpotColor: highlightSpotColor,
1122 highlightLineColor: highlightLineColor,
1123 lineWidth: lineWidth,
1124 normalRangeMin: normalRangeMin,
1125 normalRangeMax: normalRangeMax,
1126 drawNormalOnTop: drawNormalOnTop,
1128 chartRangeClip: chartRangeClip,
1129 chartRangeMinX: chartRangeMinX,
1130 chartRangeMaxX: chartRangeMaxX,
1131 disableInteraction: disableInteraction,
1132 disableTooltips: disableTooltips,
1133 disableHighlight: disableHighlight,
1134 highlightLighten: highlightLighten,
1135 highlightColor: highlightColor,
1136 tooltipContainer: tooltipContainer,
1137 tooltipClassname: tooltipClassname,
1138 tooltipChartTitle: data.state.chart.title,
1139 tooltipFormat: tooltipFormat,
1140 tooltipPrefix: tooltipPrefix,
1141 tooltipSuffix: tooltipSuffix,
1142 tooltipSkipNull: tooltipSkipNull,
1143 tooltipValueLookups: tooltipValueLookups,
1144 tooltipFormatFieldlist: tooltipFormatFieldlist,
1145 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1146 numberFormatter: numberFormatter,
1147 numberDigitGroupSep: numberDigitGroupSep,
1148 numberDecimalMark: numberDecimalMark,
1149 numberDigitGroupCount: numberDigitGroupCount,
1150 animatedZooms: animatedZooms,
1151 width: data.state.calculated_width,
1152 height: data.state.calculated_height
1155 element.innerHTML = '<div id="sparkline-' + data.state.uuid + '" style="display: inline-block; position: relative;"></div>';
1156 data.state.sparkline_element = document.getElementById('sparkline-' + data.state.uuid);
1158 spark = $(data.state.sparkline_element);
1159 spark.sparkline(data.result, data.state.sparkline_options);
1162 // ----------------------------------------------------------------------------------------------------------------
1170 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1171 if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStart()');
1174 var dygraph = state.dygraph_instance;
1176 if(!NETDATA.dygraph.sync) {
1177 $.each(NETDATA.options.targets, function(i, target) {
1178 var st = NETDATA.chartState(target);
1179 if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1180 NETDATA.dygraph.paused.push(st);
1185 $.each(NETDATA.dygraph.paused, function(i, st) {
1191 NETDATA.dygraph.syncStop = function(state) {
1192 if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStop()');
1194 if(!NETDATA.dygraph.sync) {
1195 $.each(NETDATA.dygraph.paused, function(i, st) {
1196 st.clearSelection();
1199 NETDATA.dygraph.sync = false;
1200 NETDATA.dygraph.paused = [];
1203 state.unpauseChart();
1206 NETDATA.dygraph.resetChart = function(element, dygraph) {
1207 if(NETDATA.options.debug.dygraph) console.log('dygraph.resetChart()');
1209 state = NETDATA.chartState(element);
1211 if(NETDATA.globalPanAndZoom.clearMaster());
1214 NETDATA.dygraph.zoomOrPan = function(element, dygraph, after, before) {
1215 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(element, dygraph, after:' + after + ', before: ' + before + ')');
1217 state = NETDATA.chartState(element);
1218 state.updateChartPanOrZoom(after, before);
1222 NETDATA.dygraphSetSelection = function(state, t) {
1223 var r = state.calculateRowForTime(t);
1225 state.dygraph_instance.setSelection(r);
1229 state.dygraph_instance.clearSelection();
1230 state.unpauseChart();
1234 NETDATA.dygraphclearSelection = function(state, t) {
1235 state.dygraph_instance.clearSelection();
1236 state.unpauseChart();
1239 NETDATA.dygraphInitialize = function(callback) {
1240 if(typeof netdataStopDygraph == 'undefined') {
1241 $.getScript(NETDATA.dygraph_js)
1243 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1246 NETDATA.error(100, NETDATA.dygraph_js);
1248 .always(function() {
1249 if(typeof callback == "function")
1254 NETDATA.chartLibraries.dygraph.enabled = false;
1255 if(typeof callback == "function")
1260 NETDATA.dygraphChartUpdate = function(element, data) {
1261 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate()');
1263 var dygraph = data.state.dygraph_instance;
1265 if(data.state.mode.name == 'pan') {
1266 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() loose update');
1267 dygraph.updateOptions({
1268 file: data.result.data,
1269 labels: data.result.labels,
1270 labelsDivWidth: data.state.calculated_width - 70
1274 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() strict update');
1275 dygraph.updateOptions({
1276 file: data.result.data,
1277 labels: data.result.labels,
1278 labelsDivWidth: data.state.calculated_width - 70,
1285 NETDATA.dygraphChartCreate = function(element, data) {
1286 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartCreate()');
1288 var self = $(element);
1289 var title = self.data('dygraph-title') || data.state.chart.title;
1290 var titleHeight = self.data('dygraph-titleheight') || 20;
1291 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1292 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1293 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
1294 var stackedGraph = self.data('dygraph-stackedgraph') || (data.state.chart.chart_type == 'stacked')?true:false;
1295 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1296 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
1297 var fillGraph = self.data('dygraph-fillgraph') || (data.state.chart.chart_type == 'area')?true:false;
1298 var drawPoints = self.data('dygraph-drawpoints') || false;
1299 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1300 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
1301 var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
1302 var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
1303 var legend = self.data('dygraph-legend') || 'onmouseover';
1304 var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
1305 var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
1306 var axisLineColor = self.data('dygraph-axislinecolor') || '#EEE';
1307 var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
1308 var sigFigs = self.data('dygraph-sigfigs') || null;
1309 var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
1310 var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
1311 var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
1312 var drawAxis = self.data('dygraph-drawaxis') || true;
1313 var strokeWidth = self.data('dygraph-strokewidth') || 1.0;
1314 var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
1315 var colors = self.data('dygraph-colors') || NETDATA.colors;
1316 var pointSize = self.data('dygraph-pointsize') || 1;
1317 var stepPlot = self.data('dygraph-stepplot') || false;
1318 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
1319 var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (data.state.chart.chart_type == 'stacked')?1.0:0.0;
1320 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1321 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1322 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || { strokeWidth: 1.5 };
1323 var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (data.state.chart.chart_type == 'stacked')?0.7:0.5;
1324 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1325 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1326 var showRoller = self.data('dygraph-showroller') || false;
1327 var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
1328 var rightGap = self.data('dygraph-rightgap') || 5;
1329 var drawGrid = self.data('dygraph-drawgrid') || true;
1330 var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
1331 var drawYGrid = self.data('dygraph-drawygrid') || undefined;
1332 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1333 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1335 data.state.dygraph_options = {
1337 titleHeight: titleHeight,
1338 ylabel: data.state.chart.units,
1339 yLabelWidth: yLabelWidth,
1340 connectSeparatedPoints: connectSeparatedPoints,
1341 drawPoints: drawPoints,
1342 fillGraph: fillGraph,
1343 stackedGraph: stackedGraph,
1344 stackedGraphNaNFill: stackedGraphNaNFill,
1346 drawXGrid: drawXGrid,
1347 drawYGrid: drawYGrid,
1348 gridLinePattern: gridLinePattern,
1349 gridLineWidth: gridLineWidth,
1350 gridLineColor: gridLineColor,
1351 axisLineColor: axisLineColor,
1352 axisLineWidth: axisLineWidth,
1354 hideOverlayOnMouseOut: hideOverlayOnMouseOut,
1355 labelsDiv: labelsDiv,
1356 labelsDivStyles: labelsDivStyles,
1357 labelsDivWidth: labelsDivWidth,
1358 labelsSeparateLines: labelsSeparateLines,
1359 labelsShowZeroValues: labelsShowZeroValues,
1363 showLabelsOnHighlight: showLabelsOnHighlight,
1364 maxNumberWidth: maxNumberWidth,
1366 digitsAfterDecimal: digitsAfterDecimal,
1367 axisLabelFontSize: axisLabelFontSize,
1369 strokeWidth: strokeWidth,
1370 drawGapEdgePoints: drawGapEdgePoints,
1371 pointSize: pointSize,
1373 strokeBorderColor: strokeBorderColor,
1374 strokeBorderWidth: strokeBorderWidth,
1375 strokePattern: strokePattern,
1376 highlightCircleSize: highlightCircleSize,
1377 highlightSeriesOpts: highlightSeriesOpts,
1378 highlightSeriesBackgroundAlpha: highlightSeriesBackgroundAlpha,
1379 pointClickCallback: pointClickCallback,
1380 showRangeSelector: showRangeSelector,
1381 showRoller: showRoller,
1382 valueFormatter: valueFormatter,
1384 labels: data.result.labels,
1388 ticker: Dygraph.dateTicker,
1389 axisLabelFormatter: function (d, gran) {
1390 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1392 valueFormatter :function (ms) {
1393 var d = new Date(ms);
1394 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1401 drawCallback: function(dygraph, is_initial) {
1402 if(data.state.mode.name != 'auto') {
1403 if(NETDATA.options.debug.dygraph) console.log('dygraphDrawCallback()');
1405 var x_range = dygraph.xAxisRange();
1406 var after = Math.round(x_range[0]);
1407 var before = Math.round(x_range[1]);
1409 NETDATA.dygraph.zoomOrPan(element, this, after, before);
1412 zoomCallback: function(minDate, maxDate, yRanges) {
1413 if(NETDATA.options.debug.dygraph) console.log('dygraphZoomCallback()');
1414 NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1416 highlightCallback: function(event, x, points, row, seriesName) {
1417 if(NETDATA.options.debug.dygraph) console.log('dygraphHighlightCallback()');
1418 NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1420 unhighlightCallback: function(event) {
1421 if(NETDATA.options.debug.dygraph) console.log('dygraphUnhighlightCallback()');
1422 NETDATA.dygraph.syncStop(data.state);
1424 interactionModel : {
1425 mousedown: function(event, dygraph, context) {
1426 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDown()');
1428 // Right-click should not initiate a zoom.
1429 if (event.button && event.button == 2) return;
1431 context.initializeMouseDown(event, dygraph, context);
1433 if (event.altKey || event.shiftKey) {
1434 data.state.setMode('zoom');
1435 Dygraph.startZoom(event, dygraph, context);
1438 data.state.setMode('pan');
1439 Dygraph.startPan(event, dygraph, context);
1442 mousemove: function(event, dygraph, context) {
1443 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseMove()');
1445 if (context.isPanning) {
1446 data.state.setMode('pan');
1447 Dygraph.movePan(event, dygraph, context);
1449 else if (context.isZooming) {
1450 data.state.setMode('zoom');
1451 Dygraph.moveZoom(event, dygraph, context);
1454 mouseup: function(event, dygraph, context) {
1455 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseUp()');
1457 if (context.isPanning)
1458 Dygraph.endPan(event, dygraph, context);
1459 else if (context.isZooming)
1460 Dygraph.endZoom(event, dygraph, context);
1462 click: function(event, dygraph, context) {
1463 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseClick()');
1464 Dygraph.cancelEvent(event);
1466 dblclick: function(event, dygraph, context) {
1467 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDoubleClick()');
1468 NETDATA.dygraph.resetChart(element, dygraph);
1470 mousewheel: function(event, dygraph, context) {
1471 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseWheel()');
1473 if(event.altKey || event.shiftKey) {
1474 // http://dygraphs.com/gallery/interaction-api.js
1475 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1476 var percentage = normal / 25;
1478 var before_old = data.state.mode.before_ms;
1479 var after_old = data.state.mode.after_ms;
1480 var range_old = before_old - after_old;
1482 var range = range_old * ( 1 - percentage );
1483 var dt = Math.round((range_old - range) / 2);
1485 var before = before_old - dt;
1486 var after = after_old + dt;
1488 if(NETDATA.options.debug.dygraph) console.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
1490 data.state.setMode('zoom');
1491 NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1494 touchstart: function(event, dygraph, context) {
1495 Dygraph.Interaction.startTouch(event, dygraph, context);
1496 context.touchDirections = { x: true, y: false };
1497 data.state.setMode('zoom');
1499 touchmove: function(event, dygraph, context) {
1500 //Dygraph.cancelEvent(event);
1501 Dygraph.Interaction.moveTouch(event, dygraph, context);
1503 touchend: function(event, dygraph, context) {
1504 Dygraph.Interaction.endTouch(event, dygraph, context);
1509 self.html('<div id="dygraph-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>');
1511 data.state.dygraph_instance = new Dygraph(document.getElementById('dygraph-' + data.state.uuid),
1512 data.result.data, data.state.dygraph_options);
1515 // ----------------------------------------------------------------------------------------------------------------
1518 NETDATA.morrisInitialize = function(callback) {
1519 if(typeof netdataStopMorris == 'undefined') {
1521 // morris requires raphael
1522 if(!NETDATA.chartLibraries.raphael.initialized) {
1523 if(NETDATA.chartLibraries.raphael.enabled) {
1524 NETDATA.raphaelInitialize(function() {
1525 NETDATA.morrisInitialize(callback);
1529 NETDATA.chartLibraries.morris.enabled = false;
1530 if(typeof callback == "function")
1535 var fileref = document.createElement("link");
1536 fileref.setAttribute("rel", "stylesheet");
1537 fileref.setAttribute("type", "text/css");
1538 fileref.setAttribute("href", NETDATA.morris_css);
1540 if (typeof fileref != "undefined")
1541 document.getElementsByTagName("head")[0].appendChild(fileref);
1543 $.getScript(NETDATA.morris_js)
1545 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1548 NETDATA.error(100, NETDATA.morris_js);
1550 .always(function() {
1551 if(typeof callback == "function")
1557 NETDATA.chartLibraries.morris.enabled = false;
1558 if(typeof callback == "function")
1563 NETDATA.morrisChartUpdate = function(element, data) {
1564 data.state.morris_instance.setData(data.result.data);
1567 NETDATA.morrisChartCreate = function(element, data) {
1569 element.innerHTML = '<div id="morris-' + data.state.uuid + '" style="width: ' + data.state.calculated_width + 'px; height: ' + data.state.calculated_height + 'px;"></div>';
1572 element: 'morris-' + data.state.uuid,
1573 data: data.result.data,
1575 ykeys: data.dimension_names,
1576 labels: data.dimension_names,
1582 continuousLine: false,
1583 behaveLikeLine: false
1587 if(data.state.chart.chart_type == 'line')
1588 morris = new Morris.Line(options);
1590 else if(data.state.chart.chart_type == 'area') {
1591 options.behaveLikeLine = true;
1592 morris = new Morris.Area(options);
1595 morris = new Morris.Area(options);
1597 data.state.morris_instance = morris;
1598 data.state.morris_options = options;
1601 // ----------------------------------------------------------------------------------------------------------------
1604 NETDATA.raphaelInitialize = function(callback) {
1605 if(typeof netdataStopRaphael == 'undefined') {
1606 $.getScript(NETDATA.raphael_js)
1608 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
1611 NETDATA.error(100, NETDATA.raphael_js);
1613 .always(function() {
1614 if(typeof callback == "function")
1619 NETDATA.chartLibraries.raphael.enabled = false;
1620 if(typeof callback == "function")
1625 NETDATA.raphaelChartUpdate = function(element, data) {
1626 var self = $(element);
1628 self.raphael(data, {
1629 width: data.state.calculated_width,
1630 height: data.state.calculated_height
1634 NETDATA.raphaelChartCreate = function(element, data) {
1635 var self = $(element);
1637 self.raphael(data, {
1638 width: data.state.calculated_width,
1639 height: data.state.calculated_height
1643 // ----------------------------------------------------------------------------------------------------------------
1646 NETDATA.googleInitialize = function(callback) {
1647 if(typeof netdataStopGoogleCharts == 'undefined') {
1648 $.getScript(NETDATA.google_js)
1650 NETDATA.registerChartLibrary('google', NETDATA.google_js);
1652 google.load('visualization', '1.1', {
1653 'packages': ['corechart', 'controls'],
1654 'callback': callback
1658 NETDATA.error(100, NETDATA.google_js);
1659 if(typeof callback == "function")
1664 NETDATA.chartLibraries.google.enabled = false;
1665 if(typeof callback == "function")
1670 NETDATA.googleChartUpdate = function(element, data) {
1671 var datatable = new google.visualization.DataTable(data.result);
1672 data.state.google_instance.draw(datatable, data.state.google_options);
1675 NETDATA.googleChartCreate = function(element, data) {
1676 var datatable = new google.visualization.DataTable(data.result);
1679 // do not set width, height - the chart resizes itself
1680 //width: data.state.calculated_width,
1681 //height: data.state.calculated_height,
1683 title: data.state.chart.title,
1686 // title: "Time of Day",
1687 // format:'HH:mm:ss',
1688 viewWindowMode: 'maximized',
1699 title: data.state.chart.units,
1700 viewWindowMode: 'pretty',
1715 focusTarget: 'category',
1722 titlePosition: 'out',
1733 curveType: 'function',
1738 element.innerHTML = '<div id="google-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>';
1741 switch(data.state.chart.chart_type) {
1743 options.vAxis.viewWindowMode = 'maximized';
1744 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1748 options.isStacked = true;
1749 options.areaOpacity = 0.85;
1750 options.vAxis.viewWindowMode = 'maximized';
1751 options.vAxis.minValue = null;
1752 options.vAxis.maxValue = null;
1753 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1758 options.lineWidth = 2;
1759 gchart = new google.visualization.LineChart(document.getElementById('google-' + data.state.uuid));
1763 gchart.draw(datatable, options);
1765 data.state.google_instance = gchart;
1766 data.state.google_options = options;
1769 // ----------------------------------------------------------------------------------------------------------------
1770 // Charts Libraries Registration
1772 NETDATA.chartLibraries = {
1774 initialize: NETDATA.dygraphInitialize,
1775 create: NETDATA.dygraphChartCreate,
1776 update: NETDATA.dygraphChartUpdate,
1777 setSelection: NETDATA.dygraphSetSelection,
1778 clearSelection: NETDATA.dygraphClearSelection,
1784 pixels_per_point: 2,
1785 detects_dimensions_on_update: false
1788 initialize: NETDATA.sparklineInitialize,
1789 create: NETDATA.sparklineChartCreate,
1790 update: NETDATA.sparklineChartUpdate,
1792 clearSelection: null,
1796 options: 'flip|abs',
1798 pixels_per_point: 2,
1799 detects_dimensions_on_update: false
1802 initialize: NETDATA.peityInitialize,
1803 create: NETDATA.peityChartCreate,
1804 update: NETDATA.peityChartUpdate,
1806 clearSelection: null,
1810 options: 'null2zero|flip|abs',
1812 pixels_per_point: 2,
1813 detects_dimensions_on_update: false
1816 initialize: NETDATA.morrisInitialize,
1817 create: NETDATA.morrisChartCreate,
1818 update: NETDATA.morrisChartUpdate,
1820 clearSelection: null,
1824 options: 'objectrows|ms',
1826 pixels_per_point: 10,
1827 detects_dimensions_on_update: false
1830 initialize: NETDATA.googleInitialize,
1831 create: NETDATA.googleChartCreate,
1832 update: NETDATA.googleChartUpdate,
1834 clearSelection: null,
1837 format: 'datatable',
1840 pixels_per_point: 2,
1841 detects_dimensions_on_update: true
1844 initialize: NETDATA.raphaelInitialize,
1845 create: NETDATA.raphaelChartCreate,
1846 update: NETDATA.raphaelChartUpdate,
1848 clearSelection: null,
1854 pixels_per_point: 1,
1855 detects_dimensions_on_update: false
1859 NETDATA.registerChartLibrary = function(library, url) {
1860 console.log("registering chart library: " + library);
1862 NETDATA.chartLibraries[library].url = url;
1863 NETDATA.chartLibraries[library].initialized = true;
1864 NETDATA.chartLibraries[library].enabled = true;
1866 // console.log(NETDATA.chartLibraries);
1869 // ----------------------------------------------------------------------------------------------------------------
1870 // load all libraries and initialize
1872 NETDATA.errorReset();
1874 NETDATA._loadjQuery(function() {
1875 $.getScript(NETDATA.serverDefault + 'lib/visible.js').then(function() {