1 // You can set the following variables before loading this script:
3 // var netdataNoDygraphs = true; // do not use dygraph
4 // var netdataNoSparklines = true; // do not use sparkline
5 // var netdataNoPeitys = true; // do not use peity
6 // var netdataNoGoogleCharts = true; // do not use google
7 // var netdataNoMorris = true; // do not use morris
8 // var netdataDontStart = true; // do not start the thread to process the charts
10 // You can also set the default netdata server, using the following.
11 // When this variable is not set, we assume the page is hosted on your
12 // netdata server already.
13 // var netdataServer = "http://yourhost:19999"; // set your NetData server
17 // fix IE bug with console
18 if(!window.console){ window.console = {log: function(){} }; }
20 var NETDATA = window.NETDATA || {};
22 // ----------------------------------------------------------------------------------------------------------------
23 // Detect the netdata server
25 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
26 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
27 NETDATA._scriptSource = function(scripts) {
28 var script = null, base = null;
30 if(typeof document.currentScript != 'undefined') {
31 script = document.currentScript;
34 var all_scripts = document.getElementsByTagName('script');
35 script = all_scripts[all_scripts.length - 1];
38 if (script.getAttribute.length != 'undefined')
41 script = script.getAttribute('src', -1);
43 var link = document.createElement('a');
44 link.setAttribute('href', script);
46 if(!link.protocol || !link.hostname) return null;
49 if(base) base += "//";
50 base += link.hostname;
52 if(link.port) base += ":" + link.port;
58 if(typeof netdataServer != 'undefined')
59 NETDATA.serverDefault = netdataServer;
61 NETDATA.serverDefault = NETDATA._scriptSource();
63 if(NETDATA.serverDefault.slice(-1) != '/')
64 NETDATA.serverDefault += '/';
66 // default URLs for all the external files we need
67 // make them RELATIVE so that the whole thing can also be
68 // installed under a web server
69 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
70 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
71 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
72 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
73 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
74 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
75 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
76 NETDATA.dashboard_css = NETDATA.serverDefault + 'dashboard.css';
77 NETDATA.google_js = 'https://www.google.com/jsapi';
79 // these are the colors Google Charts are using
80 // we have them here to attempt emulate their look and feel on the other chart libraries
81 // http://there4.io/2012/05/02/google-chart-color-list/
82 NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
83 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
84 '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
87 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
88 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
89 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
91 // ----------------------------------------------------------------------------------------------------------------
92 // the defaults for all charts
94 // if the user does not specify any of these, the following will be used
96 NETDATA.chartDefaults = {
97 host: NETDATA.serverDefault, // the server to get data from
98 width: '100%', // the chart width - can be null
99 height: '100%', // the chart height - can be null
100 min_width: null, // the chart minimum width - can be null
101 library: 'dygraph', // the graphing library to use
102 method: 'average', // the grouping method
103 before: 0, // panning
104 after: -600, // panning
105 pixels_per_point: 1, // the detail of the chart
106 fill_luminance: 0.8 // luminance of colors in solit areas
109 // ----------------------------------------------------------------------------------------------------------------
113 targets: null, // an array of all the DOM elements that are
114 // currently visible (independently of their
115 // viewport visibility)
117 updated_dom: true, // when true, the DOM has been updated with
118 // new elements we have to check.
120 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
121 // rendering charts continiously.
122 // used with .current.fast_render_timeframe
124 page_is_visible: true, // when true, this page is visible
126 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
127 // auto-refresher for some time (when a chart is
128 // performing pan or zoom, we need to stop refreshing
129 // all other charts, to have the maximum speed for
130 // rendering the chart that is panned or zoomed).
131 // Used with .current.global_pan_sync_time
133 // the current profile
134 // we may have many...
136 pixels_per_point: 1, // the minimum pixels per point for all charts
137 // increase this to speed javascript up
138 // each chart library has its own limit too
139 // the max of this and the chart library is used
140 // the final is calculated every time, so a change
141 // here will have immediate effect on the next chart
144 idle_between_charts: 50, // ms - how much time to wait between chart updates
146 fast_render_timeframe: 200, // ms - render continously until this time of continious
147 // rendering has been reached
148 // this setting is used to make it render e.g. 10
149 // charts at once, sleep idle_between_charts time
150 // and continue for another 10 charts.
152 idle_between_loops: 200, // ms - if all charts have been updated, wait this
153 // time before starting again.
155 idle_lost_focus: 500, // ms - when the window does not have focus, check
156 // if focus has been regained, every this time
158 global_pan_sync_time: 500, // ms - when you pan or zoon a chart, the background
159 // autorefreshing of charts is paused for this amount
162 sync_delay: 1500 // ms - when you pan or zoom a chart, wait this amount
163 // of time before setting up synchronized selections
181 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
184 // ----------------------------------------------------------------------------------------------------------------
187 NETDATA.errorCodes = {
188 100: { message: "Cannot load chart library", alert: true },
189 101: { message: "Cannot load jQuery", alert: true },
190 402: { message: "Chart library not found", alert: false },
191 403: { message: "Chart library not enabled/is failed", alert: false },
192 404: { message: "Chart not found", alert: false }
194 NETDATA.errorLast = {
200 NETDATA.error = function(code, msg) {
201 NETDATA.errorLast.code = code;
202 NETDATA.errorLast.message = msg;
203 NETDATA.errorLast.datetime = new Date().getTime();
205 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
207 if(NETDATA.errorCodes[code].alert)
208 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
211 NETDATA.errorReset = function() {
212 NETDATA.errorLast.code = 0;
213 NETDATA.errorLast.message = "You are doing fine!";
214 NETDATA.errorLast.datetime = 0;
217 // ----------------------------------------------------------------------------------------------------------------
220 // When multiple charts need the same chart, we avoid downloading it
221 // multiple times (and having it in browser memory multiple time)
222 // by using this registry.
224 // Every time we download a chart definition, we save it here with .add()
225 // Then we try to get it back with .get(). If that fails, we download it.
227 NETDATA.chartRegistry = {
230 add: function(host, id, data) {
231 host = host.replace(/:/g, "_").replace(/\//g, "_");
232 id = id.replace(/:/g, "_").replace(/\//g, "_");
234 if(typeof this.charts[host] == 'undefined')
235 this.charts[host] = {};
237 this.charts[host][id] = data;
240 get: function(host, id) {
241 host = host.replace(/:/g, "_").replace(/\//g, "_");
242 id = id.replace(/:/g, "_").replace(/\//g, "_");
244 if(typeof this.charts[host] == 'undefined')
247 if(typeof this.charts[host][id] == 'undefined')
250 return this.charts[host][id];
254 // ----------------------------------------------------------------------------------------------------------------
255 // Global Pan and Zoom on charts
257 // Using this structure are synchronize all the charts, so that
258 // when you pan or zoom one, all others are automatically refreshed
259 // to the same timespan.
261 NETDATA.globalPanAndZoom = {
262 seq: 0, // timestamp ms
263 // every time a chart is panned or zoomed
264 // we set the timestamp here
265 // then we use it as a sequence number
266 // to find if other charts are syncronized
269 master: null, // the master chart (state), to which all others
272 force_before_ms: null, // the timespan to sync all other charts
273 force_after_ms: null,
276 setMaster: function(state, after, before) {
277 if(this.master && this.master != state)
278 this.master.resetChart();
281 this.seq = new Date().getTime();
282 this.force_after_ms = after;
283 this.force_before_ms = before;
287 clearMaster: function() {
289 var state = this.master;
290 this.master = null; // prevent infinite recursion
293 NETDATA.options.auto_refresher_stop_until = 0;
300 this.force_after_ms = 0;
301 this.force_before_ms = 0;
304 // is the given state the master of the global
305 // pan and zoom sync?
306 isMaster: function(state) {
307 if(this.master == state) return true;
311 // are we currently have a global pan and zoom sync?
312 isActive: function() {
313 if(this.master) return true;
317 // check if a chart, other than the master
318 // needs to be refreshed, due to the global pan and zoom
319 shouldBeAutoRefreshed: function(state) {
320 if(!this.master || !this.seq)
323 if(state.follows_global == this.seq)
330 // ----------------------------------------------------------------------------------------------------------------
331 // Our state object, where all per-chart values are stored
333 NETDATA.chartInitialState = function(element) {
337 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
338 id: self.data('netdata'), // string - the name of chart
340 // the user given dimensions of the element
341 width: self.data('width') || NETDATA.chartDefaults.width,
342 height: self.data('height') || NETDATA.chartDefaults.height,
344 // these are calculated every time the chart is refreshed
346 calculated_height: 0,
348 // string - the netdata server URL, without any path
349 host: self.data('host') || NETDATA.chartDefaults.host,
351 // string - the grouping method requested by the user
352 method: self.data('method') || NETDATA.chartDefaults.method,
354 // the time-range requested by the user
355 after: self.data('after') || NETDATA.chartDefaults.after,
356 before: self.data('before') || NETDATA.chartDefaults.before,
358 // the pixels per point requested by the user
360 points: self.data('points') || null,
362 // the dimensions requested by the user
363 dimensions: self.data('dimensions') || null,
365 // the chart library requested by the user
366 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
367 library: null, // object - the chart library used
369 element: element, // the element this chart appears in
371 chart_url: null, // string - the url to download chart info
372 chart: null, // object - the chart as downloaded from the server
374 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
375 created_ms: 0, // boolean - the timestamp the chart was created
376 validated: false, // boolean - has the chart been validated?
377 enabled: true, // boolean - is the chart enabled for refresh?
378 paused: false, // boolean - is the chart paused for any reason?
379 selected: false, // boolean - is the chart shown a selection?
380 debug: false, // boolena - console.log() debug info about this chart
381 updates_counter: 0, // numeric - the number of refreshes made so far
383 follows_global: 0, // the sequence number of the global synchronization
385 // Used with NETDATA.globalPanAndZoom.seq
387 mode: null, // auto, pan, zoom
388 // this is a pointer to one of the sub-classes below
393 url: 'invalid://', // string - the last url used to update the chart
394 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
395 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
396 after_ms: 0, // milliseconds - the first timestamp of the data
397 before_ms: 0, // milliseconds - the last timestamp of the data
398 points: 0, // number - the number of points in the data
399 data: null, // the last downloaded data
400 force_before_ms: null,
401 force_after_ms: null,
402 requested_before_ms: null,
403 requested_after_ms: null,
404 first_entry_ms: null,
410 url: 'invalid://', // string - the last url used to update the chart
411 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
412 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
413 after_ms: 0, // milliseconds - the first timestamp of the data
414 before_ms: 0, // milliseconds - the last timestamp of the data
415 points: 0, // number - the number of points in the data
416 data: null, // the last downloaded data
417 force_before_ms: null,
418 force_after_ms: null,
419 requested_before_ms: null,
420 requested_after_ms: null,
421 first_entry_ms: null,
427 url: 'invalid://', // string - the last url used to update the chart
428 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
429 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
430 after_ms: 0, // milliseconds - the first timestamp of the data
431 before_ms: 0, // milliseconds - the last timestamp of the data
432 points: 0, // number - the number of points in the data
433 data: null, // the last downloaded data
434 force_before_ms: null,
435 force_after_ms: null,
436 requested_before_ms: null,
437 requested_after_ms: null,
438 first_entry_ms: null,
442 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
443 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
444 refresh_dt_element: null,
447 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
450 setSelection: function(t) {
451 if(typeof this.library.setSelection == 'function') {
452 if(this.library.setSelection(this, t))
453 this.selected = true;
455 this.selected = false;
457 else this.selected = true;
459 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
461 return this.selected;
464 clearSelection: function() {
466 if(typeof this.library.clearSelection == 'function') {
467 if(this.library.clearSelection(this))
468 this.selected = false;
470 this.selected = true;
472 else this.selected = false;
474 if(!this.selected && this.debug) this.log('selection cleared');
477 return this.selected;
480 timeIsVisible: function(t) {
481 if(t >= this.mode.after_ms && t <= this.mode.before_ms)
486 calculateRowForTime: function(t) {
487 if(!this.timeIsVisible(t)) return -1;
489 var r = Math.floor((t - this.mode.after_ms) / this.mode.view_update_every);
490 // console.log(this.mode.data);
495 pauseChart: function() {
497 if(this.debug) this.log('paused');
502 unpauseChart: function() {
504 if(this.debug) this.log('unpaused');
509 resetChart: function() {
510 if(NETDATA.globalPanAndZoom.isMaster(this))
511 NETDATA.globalPanAndZoom.clearMaster();
513 if(state.mode.name != 'auto')
514 this.setMode('auto');
516 this.mode.force_before_ms = null;
517 this.mode.force_after_ms = null;
518 this.mode.last_updated_ms = 0;
519 this.follows_global = 0;
521 this.selected = false;
525 // do not update the chart here
526 // or the chart will flip-flop when it is the master
527 // of a selection sync and another chart becomes
529 // state.updateChart();
532 setMode: function(m) {
534 if(this.mode.name == m) return;
536 this[m].url = this.mode.url;
537 this[m].last_updated_ms = this.mode.last_updated_ms;
538 this[m].view_update_every = this.mode.view_update_every;
539 this[m].after_ms = this.mode.after_ms;
540 this[m].before_ms = this.mode.before_ms;
541 this[m].points = this.mode.points;
542 this[m].data = this.mode.data;
543 this[m].requested_before_ms = this.mode.requested_before_ms;
544 this[m].requested_after_ms = this.mode.requested_after_ms;
545 this[m].first_entry_ms = this.mode.first_entry_ms;
546 this[m].last_entry_ms = this.mode.last_entry_ms;
550 this.mode = this.auto;
552 this.mode = this.pan;
554 this.mode = this.zoom;
556 this.mode = this.auto;
558 this.mode.force_before_ms = null;
559 this.mode.force_after_ms = null;
561 if(this.debug) this.log('mode set to ' + this.mode.name);
564 _minPanOrZoomStep: function() {
565 return (((this.mode.before_ms - this.mode.after_ms) / this.mode.points) * ((this.mode.points * 5 / 100) + 1) );
566 // return this.mode.view_update_every * 10;
569 _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
570 var dt_after = Math.abs(old_after - new_after);
571 var dt_before = Math.abs(old_before - new_before);
572 var old_range = old_before - old_after;
574 var new_range = new_before - new_after;
575 var dt = Math.abs(old_range - new_range);
576 var step = Math.max(dt_after, dt_before, dt);
578 var min_step = this._minPanOrZoomStep();
579 if(new_range < old_range && new_range / this.calculated_width < 100) {
580 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');
584 if(step >= min_step) {
585 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');
589 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');
594 updateChartPanOrZoom: function(after, before, callback) {
597 if(this.mode.name == 'auto') {
598 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
602 if(!this.mode.force_after_ms || !this.mode.force_before_ms) {
603 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
606 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)) {
607 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());
610 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)) {
611 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());
616 var now = new Date().getTime();
617 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
618 NETDATA.globalPanAndZoom.setMaster(this, after, before);
620 this.mode.force_after_ms = after;
621 this.mode.force_before_ms = before;
622 this.updateChart(callback);
626 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
627 if(typeof callback != 'undefined') callback();
631 updateChart: function(callback) {
632 if(!this.library || !this.library.enabled) {
633 this.error('charting library "' + this.library_name + '" is not available');
634 if(typeof callback != 'undefined') callback();
639 if(this.debug) this.error('chart "' + this.id + '" is not enabled');
640 if(typeof callback != 'undefined') callback();
644 if(!self.visible(true)) {
645 if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
646 if(typeof callback != 'undefined') callback();
650 var this_state_object = this;
651 this.getChart(function() { this_state_object.updateChart(callback); });
655 if(!this.library.initialized) {
656 var this_state_object = this;
657 this.library.initialize(function() { this_state_object.updateChart(callback); });
661 this.clearSelection();
663 if(this.debug) this.log('updating from ' + this.mode.url);
665 var this_state_object = this;
667 url: this_state_object.mode.url,
670 .then(function(data) {
671 if(this_state_object.debug) this_state_object.log('got data from netdata server');
672 this_state_object.mode.data = data;
674 var started = new Date().getTime();
676 // if the result is JSON, find the latest update-every
677 if(typeof data == 'object' && this_state_object.library.jsonWrapper) {
678 if(!this_state_object.follows_global && typeof data.view_update_every != 'undefined')
679 this_state_object.mode.view_update_every = data.view_update_every * 1000;
681 if(typeof data.after != 'undefined')
682 this_state_object.mode.after_ms = data.after * 1000;
684 if(typeof data.before != 'undefined')
685 this_state_object.mode.before_ms = data.before * 1000;
687 if(typeof data.first_entry_t != 'undefined')
688 this_state_object.mode.first_entry_ms = data.first_entry_t * 1000;
690 if(typeof data.last_entry_t != 'undefined')
691 this_state_object.mode.last_entry_ms = data.last_entry_t * 1000;
693 if(typeof data.points != 'undefined')
694 this_state_object.mode.points = data.points;
696 data.state = this_state_object;
699 this_state_object.updates_counter++;
701 if(this_state_object.debug) {
702 this_state_object.log('UPDATE No ' + this_state_object.updates_counter + ' COMPLETED');
704 if(this_state_object.mode.force_after_ms)
705 this_state_object.log('STATUS: forced : ' + (this_state_object.mode.force_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.force_before_ms / 1000).toString());
707 this_state_object.log('STATUS: forced: unset');
709 this_state_object.log('STATUS: requested: ' + (this_state_object.mode.requested_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.requested_before_ms / 1000).toString());
710 this_state_object.log('STATUS: rendered : ' + (this_state_object.mode.after_ms / 1000).toString() + ' - ' + (this_state_object.mode.before_ms / 1000).toString());
711 this_state_object.log('STATUS: points : ' + (this_state_object.mode.points).toString() + ', min step: ' + (this_state_object._minPanOrZoomStep() / 1000).toString());
714 if(this_state_object.created_ms) {
715 if(this_state_object.debug) this_state_object.log('updating chart...');
717 if(NETDATA.options.debug.chart_errors) {
718 this_state_object.library.update(this_state_object.element, data);
722 this_state_object.library.update(this_state_object.element, data);
725 this_state_object.error('chart "' + state.id + '" failed to be updated as ' + state.library_name);
730 if(this_state_object.debug) this_state_object.log('creating chart...');
732 if(NETDATA.options.debug.chart_errors) {
733 this_state_object.library.create(this_state_object.element, data);
734 this_state_object.created_ms = new Date().getTime();
738 this_state_object.library.create(this_state_object.element, data);
739 this_state_object.created_ms = new Date().getTime();
742 this_state_object.error('chart "' + state.id + '" failed to be created as ' + state.library_name);
747 // update the performance counters
748 this_state_object.mode.last_updated_ms = new Date().getTime();
749 this_state_object.refresh_dt_ms = this_state_object.mode.last_updated_ms - started;
750 NETDATA.options.auto_refresher_fast_weight += this_state_object.refresh_dt_ms;
752 if(this_state_object.refresh_dt_element)
753 this_state_object.refresh_dt_element.innerHTML = this_state_object.refresh_dt_ms.toString();
756 this_state_object.error('cannot download chart from ' + this_state_object.mode.url);
759 if(typeof callback == 'function') callback();
763 chartURL: function() {
764 this.calculated_width = self.width();
765 this.calculated_height = self.height();
769 if(NETDATA.globalPanAndZoom.isActive()) {
770 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
771 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
772 this.follows_global = NETDATA.globalPanAndZoom.seq;
775 before = this.mode.force_before_ms != null ? Math.round(this.mode.force_before_ms / 1000) : this.before;
776 after = this.mode.force_after_ms != null ? Math.round(this.mode.force_after_ms / 1000) : this.after;
777 this.follows_global = 0;
780 this.mode.requested_after_ms = after * 1000;
781 this.mode.requested_before_ms = before * 1000;
783 // force an options provided detail
784 var pixels_per_point = this.pixels_per_point;
785 if(pixels_per_point < NETDATA.options.current.pixels_per_point)
786 pixels_per_point = NETDATA.options.current.pixels_per_point
788 this.mode.points = this.points || Math.round(this.calculated_width / pixels_per_point);
790 // build the data URL
791 this.mode.url = this.chart.data_url;
792 this.mode.url += "&format=" + this.library.format;
793 this.mode.url += "&points=" + this.mode.points.toString();
794 this.mode.url += "&group=" + this.method;
795 this.mode.url += "&options=" + this.library.options;
796 if(this.library.jsonWrapper) this.mode.url += '|jsonwrap';
799 this.mode.url += "&after=" + after.toString();
802 this.mode.url += "&before=" + before.toString();
805 this.mode.url += "&dimensions=" + this.dimensions;
807 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);
810 isVisible: function() {
811 return self.visible(true);
814 isAutoRefreshed: function() {
815 return this.mode.autorefresh;
818 canBeAutoRefreshed: function() {
819 if(this.isAutoRefreshed()) {
820 var now = new Date().getTime();
822 if(this.updates_counter && !NETDATA.options.page_is_visible) {
823 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
827 if(!this.isVisible()) {
828 if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
832 // options valid only for autoRefresh()
833 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
834 if(NETDATA.globalPanAndZoom.isActive()) {
835 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
836 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
840 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
846 if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
851 if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
855 if(now - this.mode.last_updated_ms > this.mode.view_update_every) {
856 if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
865 autoRefresh: function(callback) {
866 if(this.canBeAutoRefreshed())
867 this.updateChart(callback);
868 else if(typeof callback != 'undefined')
872 // fetch the chart description from the netdata server
873 getChart: function(callback) {
874 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
876 if(typeof callback == 'function') callback();
879 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
880 if(this.debug) this.log('downloading ' + this.chart_url);
881 this_state_object = this;
887 .done(function(chart) {
888 chart.data_url = (this_state_object.host + chart.data_url);
889 this_state_object.chart = chart;
890 this_state_object.mode.view_update_every = chart.update_every * 1000;
891 this_state_object.mode.points = Math.round(self.width() / (chart.update_every / 1000));
893 chart.url = this_state_object.chart_url;
894 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
897 NETDATA.error(404, this_state_object.chart_url);
898 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
901 if(typeof callback == 'function') callback();
906 // resize the chart to its real dimensions
907 // as given by the caller
908 sizeChart: function() {
909 element.className += "netdata-container";
911 if(this.debug) this.log('sizing element');
914 self.css('width', this.width);
917 self.css('height', this.height);
919 // these should be left to css
920 //self.css('display', 'inline-block')
921 //self.css('overflow', 'hidden');
923 if(NETDATA.chartDefaults.min_width)
924 self.css('min-width', NETDATA.chartDefaults.min_width);
927 // show a message in the chart
928 message: function(type, msg) {
929 this.element.innerHTML = '<div class="netdata-message netdata-' + type + '-message" style="font-size: x-small; overflow: hidden; width: 100%; height: 100%;"><small>'
933 // reset the creation datetime
934 // since we overwrote the whole element
936 if(this.debug) this.log(msg);
939 // show an error on the chart and stop it forever
940 error: function(msg) {
941 this.message('error', this.id + ': ' + msg);
942 this.enabled = false;
945 // show a message indicating the chart is loading
946 info: function(msg) {
947 this.message('info', this.id + ': ' + msg);
951 if(this.debug) this.log('created');
953 this.info("loading...");
955 // make sure the host does not end with /
956 // all netdata API requests use absolute paths
957 while(this.host.slice(-1) == '/')
958 this.host = this.host.substring(0, this.host.length - 1);
960 // check the requested library is available
961 // we don't initialize it here - it will be initialized when
962 // this chart will be first used
963 if(typeof NETDATA.chartLibraries[this.library_name] == 'undefined') {
964 NETDATA.error(402, this.library_name);
965 this.error('chart library "' + this.library_name + '" is not found');
967 else if(!NETDATA.chartLibraries[this.library_name].enabled) {
968 NETDATA.error(403, this.library_name);
969 this.error('chart library "' + this.library_name + '" is not enabled');
972 this.library = NETDATA.chartLibraries[this.library_name];
973 this.pixels_per_point = self.data('pixels-per-point') || this.library.pixels_per_point;
976 // if we need to report the rendering speed
977 // find the element that needs to be updated
978 if(this.refresh_dt_element_name)
979 this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
981 // the default mode for all charts
982 this.setMode('auto');
990 // get or create a chart state, given a DOM element
991 NETDATA.chartState = function(element) {
993 var state = self.data('state') || null;
995 state = NETDATA.chartInitialState(element);
996 self.data('state', state);
1001 // ----------------------------------------------------------------------------------------------------------------
1002 // Library functions
1004 // Load a script without jquery
1005 // This is used to load jquery - after it is loaded, we use jquery
1006 NETDATA._loadjQuery = function(callback) {
1007 if(typeof jQuery == 'undefined') {
1008 var script = document.createElement('script');
1009 script.type = 'text/javascript';
1010 script.async = true;
1011 script.src = NETDATA.jQuery;
1013 // script.onabort = onError;
1014 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
1015 if(typeof callback == "function")
1016 script.onload = callback;
1018 var s = document.getElementsByTagName('script')[0];
1019 s.parentNode.insertBefore(script, s);
1021 else if(typeof callback == "function")
1025 NETDATA._loadCSS = function(filename) {
1026 var fileref = document.createElement("link");
1027 fileref.setAttribute("rel", "stylesheet");
1028 fileref.setAttribute("type", "text/css");
1029 fileref.setAttribute("href", filename);
1031 if (typeof fileref != "undefined")
1032 document.getElementsByTagName("head")[0].appendChild(fileref);
1035 NETDATA.ColorLuminance = function(hex, lum) {
1036 // validate hex string
1037 hex = String(hex).replace(/[^0-9a-f]/gi, '');
1039 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
1043 // convert to decimal and change luminosity
1044 var rgb = "#", c, i;
1045 for (i = 0; i < 3; i++) {
1046 c = parseInt(hex.substr(i*2,2), 16);
1047 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
1048 rgb += ("00"+c).substr(c.length);
1054 NETDATA.guid = function() {
1056 return Math.floor((1 + Math.random()) * 0x10000)
1061 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
1064 // user function to signal us the DOM has been
1066 NETDATA.updatedDom = function() {
1067 NETDATA.options.updated_dom = true;
1070 // ----------------------------------------------------------------------------------------------------------------
1072 NETDATA.chartRefresher = function(index) {
1073 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
1075 if(NETDATA.options.updated_dom) {
1076 // the dom has been updated
1077 // get the dom parts again
1078 NETDATA.getDomCharts(function() {
1079 NETDATA.chartRefresher(0);
1085 var target = NETDATA.options.targets.get(index);
1086 if(target == null) {
1087 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
1088 NETDATA.options.auto_refresher_fast_weight = 0;
1090 setTimeout(function() {
1091 NETDATA.chartRefresher(0);
1092 }, NETDATA.options.current.idle_between_loops);
1095 var state = NETDATA.chartState(target);
1097 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
1098 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
1100 state.autoRefresh(function() {
1101 NETDATA.chartRefresher(++index);
1105 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
1106 NETDATA.options.auto_refresher_fast_weight = 0;
1108 setTimeout(function() {
1109 state.autoRefresh(function() {
1110 NETDATA.chartRefresher(++index);
1112 }, NETDATA.options.current.idle_between_charts);
1117 NETDATA.getDomCharts = function(callback) {
1118 NETDATA.options.updated_dom = false;
1120 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
1122 if(NETDATA.options.debug.main_loop)
1123 console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
1125 // we need to re-size all the charts quickly
1126 // before making any external calls
1127 $.each(NETDATA.options.targets, function(i, target) {
1128 // the initialization will take care of sizing
1129 // and the "loading..." message
1130 var state = NETDATA.chartState(target);
1133 if(typeof callback == 'function') callback();
1136 // this is the main function - where everything starts
1137 NETDATA.start = function() {
1138 // this should be called only once
1140 NETDATA.options.page_is_visible = true;
1142 $(window).blur(function() {
1143 NETDATA.options.page_is_visible = false;
1144 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
1147 $(window).focus(function() {
1148 NETDATA.options.page_is_visible = true;
1149 if(NETDATA.options.debug.focus) console.log('Focus restored!');
1152 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
1153 NETDATA.options.page_is_visible = false;
1154 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1157 NETDATA.getDomCharts(function() {
1158 NETDATA.chartRefresher(0);
1162 // ----------------------------------------------------------------------------------------------------------------
1165 NETDATA.peityInitialize = function(callback) {
1166 if(typeof netdataNoPeitys == 'undefined' || !netdataNoPeitys) {
1167 $.getScript(NETDATA.peity_js)
1169 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1172 NETDATA.error(100, NETDATA.peity_js);
1174 .always(function() {
1175 if(typeof callback == "function")
1180 NETDATA.chartLibraries.peity.enabled = false;
1181 if(typeof callback == "function")
1186 NETDATA.peityChartUpdate = function(element, data) {
1187 var peity = $(data.state.peity_element);
1188 peity.html(data.result);
1189 // peity.change() does not accept options
1190 // to pass width and height
1192 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1195 NETDATA.peityChartCreate = function(element, data) {
1196 element.innerHTML = '<div class="netdata-chart-container netdata-peity-container" id="peity-' + data.state.uuid + '">' + data.result + '</div>';
1197 data.state.peity_element = document.getElementById('peity-' + data.state.uuid);
1198 var peity = $(data.state.peity_element);
1200 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1203 // ----------------------------------------------------------------------------------------------------------------
1206 NETDATA.sparklineInitialize = function(callback) {
1207 if(typeof netdataNoSparklines == 'undefined' || !netdataNoSparklines) {
1208 $.getScript(NETDATA.sparkline_js)
1210 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1213 NETDATA.error(100, NETDATA.sparkline_js);
1215 .always(function() {
1216 if(typeof callback == "function")
1221 NETDATA.chartLibraries.sparkline.enabled = false;
1222 if(typeof callback == "function")
1227 NETDATA.sparklineChartUpdate = function(element, data) {
1228 data.state.sparkline_options.width = data.state.calculated_width;
1229 data.state.sparkline_options.height = data.state.calculated_height;
1231 spark = $(data.state.sparkline_element);
1232 spark.sparkline(data.result, data.state.sparkline_options);
1235 NETDATA.sparklineChartCreate = function(element, data) {
1236 var self = $(element);
1237 var type = self.data('sparkline-type') || 'line';
1238 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1239 var fillColor = self.data('sparkline-fillcolor') || (data.state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
1240 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1241 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1242 var composite = self.data('sparkline-composite') || undefined;
1243 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1244 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1245 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1246 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1247 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1248 var spotColor = self.data('sparkline-spotcolor') || undefined;
1249 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1250 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1251 var spotRadius = self.data('sparkline-spotradius') || undefined;
1252 var valueSpots = self.data('sparkline-valuespots') || undefined;
1253 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1254 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1255 var lineWidth = self.data('sparkline-linewidth') || undefined;
1256 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1257 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1258 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1259 var xvalues = self.data('sparkline-xvalues') || undefined;
1260 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1261 var xvalues = self.data('sparkline-xvalues') || undefined;
1262 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1263 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1264 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1265 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1266 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1267 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1268 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1269 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1270 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1271 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1272 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1273 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + data.state.chart.units;
1274 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1275 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1276 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1277 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1278 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1279 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1280 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1281 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1282 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1284 data.state.sparkline_options = {
1286 lineColor: lineColor,
1287 fillColor: fillColor,
1288 chartRangeMin: chartRangeMin,
1289 chartRangeMax: chartRangeMax,
1290 composite: composite,
1291 enableTagOptions: enableTagOptions,
1292 tagOptionPrefix: tagOptionPrefix,
1293 tagValuesAttribute: tagValuesAttribute,
1294 disableHiddenCheck: disableHiddenCheck,
1295 defaultPixelsPerValue: defaultPixelsPerValue,
1296 spotColor: spotColor,
1297 minSpotColor: minSpotColor,
1298 maxSpotColor: maxSpotColor,
1299 spotRadius: spotRadius,
1300 valueSpots: valueSpots,
1301 highlightSpotColor: highlightSpotColor,
1302 highlightLineColor: highlightLineColor,
1303 lineWidth: lineWidth,
1304 normalRangeMin: normalRangeMin,
1305 normalRangeMax: normalRangeMax,
1306 drawNormalOnTop: drawNormalOnTop,
1308 chartRangeClip: chartRangeClip,
1309 chartRangeMinX: chartRangeMinX,
1310 chartRangeMaxX: chartRangeMaxX,
1311 disableInteraction: disableInteraction,
1312 disableTooltips: disableTooltips,
1313 disableHighlight: disableHighlight,
1314 highlightLighten: highlightLighten,
1315 highlightColor: highlightColor,
1316 tooltipContainer: tooltipContainer,
1317 tooltipClassname: tooltipClassname,
1318 tooltipChartTitle: data.state.chart.title,
1319 tooltipFormat: tooltipFormat,
1320 tooltipPrefix: tooltipPrefix,
1321 tooltipSuffix: tooltipSuffix,
1322 tooltipSkipNull: tooltipSkipNull,
1323 tooltipValueLookups: tooltipValueLookups,
1324 tooltipFormatFieldlist: tooltipFormatFieldlist,
1325 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1326 numberFormatter: numberFormatter,
1327 numberDigitGroupSep: numberDigitGroupSep,
1328 numberDecimalMark: numberDecimalMark,
1329 numberDigitGroupCount: numberDigitGroupCount,
1330 animatedZooms: animatedZooms,
1331 width: data.state.calculated_width,
1332 height: data.state.calculated_height
1335 element.innerHTML = '<div class="netdata-chart-container netdata-sparkline-container" id="sparkline-' + data.state.uuid + '" style="display: inline-block; position: relative;"></div>';
1336 data.state.sparkline_element = document.getElementById('sparkline-' + data.state.uuid);
1338 spark = $(data.state.sparkline_element);
1339 spark.sparkline(data.result, data.state.sparkline_options);
1342 // ----------------------------------------------------------------------------------------------------------------
1348 dont_sync_before: 0,
1352 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1353 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1355 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1356 // state.log('sync: I am not the sync master.');
1359 // state.debug = true;
1361 var t = state.mode.after_ms + row * state.mode.view_update_every;
1362 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT'));
1364 now = new Date().getTime();
1365 if(now < NETDATA.dygraph.dont_sync_before) {
1366 if(state.debug) st.log('sync: cannot sync yet.');
1370 // since we are the sync master, we should not call state.setSelection()
1371 // dygraphs is taking care of visualizing our selection.
1372 state.selected = true;
1374 var dygraph = state.dygraph_instance;
1376 if(!NETDATA.dygraph.sync) {
1377 if(state.debug) st.log('sync: setting up...');
1378 $.each(NETDATA.options.targets, function(i, target) {
1379 var st = NETDATA.chartState(target);
1381 if(state.debug) st.log('sync: not adding me to sync');
1384 if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.isVisible()) {
1385 NETDATA.dygraph.slaves.push(st);
1386 if(state.debug) st.log('sync: added slave to sync');
1390 NETDATA.dygraph.sync = true;
1393 $.each(NETDATA.dygraph.slaves, function(i, st) {
1395 if(state.debug) st.log('sync: ignoring me from set selection');
1398 if(state.debug) st.log('sync: showing master selection');
1404 NETDATA.dygraph.syncStop = function(state) {
1405 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1407 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1408 // state.log('sync: I am not the sync master.');
1412 if(NETDATA.dygraph.sync) {
1413 if(state.debug) st.log('sync: cleaning up...');
1414 $.each(NETDATA.dygraph.slaves, function(i, st) {
1416 if(state.debug) st.log('sync: not adding me to sync stop');
1419 if(state.debug) st.log('sync: removed slave from sync');
1420 st.clearSelection();
1424 NETDATA.dygraph.slaves = [];
1425 NETDATA.dygraph.sync = false;
1428 // since we are the sync master, we should not call state.clearSelection()
1429 // dygraphs is taking care of visualizing our selection.
1430 state.selected = false;
1433 NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1434 if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1437 if(NETDATA.globalPanAndZoom.clearMaster());
1440 NETDATA.dygraph.zoomOrPan = function(element, dygraph, after, before) {
1441 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(element, dygraph, after:' + after + ', before: ' + before + ')');
1443 state = NETDATA.chartState(element);
1444 state.updateChartPanOrZoom(after, before);
1448 NETDATA.dygraphSetSelection = function(state, t) {
1449 if(typeof state.dygraph_instance != 'undefined') {
1450 var r = state.calculateRowForTime(t);
1452 state.dygraph_instance.setSelection(r);
1456 state.dygraph_instance.clearSelection();
1462 NETDATA.dygraphClearSelection = function(state, t) {
1463 if(typeof state.dygraph_instance != 'undefined') {
1464 state.dygraph_instance.clearSelection();
1469 NETDATA.dygraphInitialize = function(callback) {
1470 if(typeof netdataNoDygraphs == 'undefined' || !netdataNoDygraphs) {
1471 $.getScript(NETDATA.dygraph_js)
1473 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1476 NETDATA.error(100, NETDATA.dygraph_js);
1478 .always(function() {
1479 if(typeof callback == "function")
1484 NETDATA.chartLibraries.dygraph.enabled = false;
1485 if(typeof callback == "function")
1490 NETDATA.dygraphChartUpdate = function(element, data) {
1491 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate()');
1493 var dygraph = data.state.dygraph_instance;
1495 if(data.state.mode.name == 'pan') {
1496 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() loose update');
1497 dygraph.updateOptions({
1498 file: data.result.data,
1499 labels: data.result.labels,
1500 labelsDivWidth: data.state.calculated_width - 70
1504 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() strict update');
1505 dygraph.updateOptions({
1506 file: data.result.data,
1507 labels: data.result.labels,
1508 labelsDivWidth: data.state.calculated_width - 70,
1515 NETDATA.dygraphChartCreate = function(element, data) {
1516 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartCreate()');
1518 var self = $(element);
1519 var title = self.data('dygraph-title') || data.state.chart.title;
1520 var titleHeight = self.data('dygraph-titleheight') || 19;
1521 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1522 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1523 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
1524 var stackedGraph = self.data('dygraph-stackedgraph') || (data.state.chart.chart_type == 'stacked')?true:false;
1525 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1526 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
1527 var fillGraph = self.data('dygraph-fillgraph') || (data.state.chart.chart_type == 'area')?true:false;
1528 var drawPoints = self.data('dygraph-drawpoints') || false;
1529 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1530 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
1531 var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
1532 var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
1533 var legend = self.data('dygraph-legend') || 'onmouseover';
1534 var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
1535 var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
1536 var axisLineColor = self.data('dygraph-axislinecolor') || '#EEE';
1537 var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
1538 var sigFigs = self.data('dygraph-sigfigs') || null;
1539 var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
1540 var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
1541 var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
1542 var drawAxis = self.data('dygraph-drawaxis') || true;
1543 var strokeWidth = self.data('dygraph-strokewidth') || 1.0;
1544 var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
1545 var colors = self.data('dygraph-colors') || NETDATA.colors;
1546 var pointSize = self.data('dygraph-pointsize') || 1;
1547 var stepPlot = self.data('dygraph-stepplot') || false;
1548 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
1549 var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (data.state.chart.chart_type == 'stacked')?0.1:0.0;
1550 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1551 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1552 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || null; // TOO SLOW: { strokeWidth: 1.5 };
1553 var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || null; // TOO SLOW: (data.state.chart.chart_type == 'stacked')?0.7:0.5;
1554 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1555 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1556 var showRoller = self.data('dygraph-showroller') || false;
1557 var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
1558 var rightGap = self.data('dygraph-rightgap') || 5;
1559 var drawGrid = self.data('dygraph-drawgrid') || true;
1560 var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
1561 var drawYGrid = self.data('dygraph-drawygrid') || undefined;
1562 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1563 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1565 data.state.dygraph_options = {
1567 titleHeight: titleHeight,
1568 ylabel: data.state.chart.units,
1569 yLabelWidth: yLabelWidth,
1570 connectSeparatedPoints: connectSeparatedPoints,
1571 drawPoints: drawPoints,
1572 fillGraph: fillGraph,
1573 stackedGraph: stackedGraph,
1574 stackedGraphNaNFill: stackedGraphNaNFill,
1576 drawXGrid: drawXGrid,
1577 drawYGrid: drawYGrid,
1578 gridLinePattern: gridLinePattern,
1579 gridLineWidth: gridLineWidth,
1580 gridLineColor: gridLineColor,
1581 axisLineColor: axisLineColor,
1582 axisLineWidth: axisLineWidth,
1584 hideOverlayOnMouseOut: hideOverlayOnMouseOut,
1585 labelsDiv: labelsDiv,
1586 labelsDivStyles: labelsDivStyles,
1587 labelsDivWidth: labelsDivWidth,
1588 labelsSeparateLines: labelsSeparateLines,
1589 labelsShowZeroValues: labelsShowZeroValues,
1593 showLabelsOnHighlight: showLabelsOnHighlight,
1594 maxNumberWidth: maxNumberWidth,
1596 digitsAfterDecimal: digitsAfterDecimal,
1597 axisLabelFontSize: axisLabelFontSize,
1599 strokeWidth: strokeWidth,
1600 drawGapEdgePoints: drawGapEdgePoints,
1601 pointSize: pointSize,
1603 strokeBorderColor: strokeBorderColor,
1604 strokeBorderWidth: strokeBorderWidth,
1605 strokePattern: strokePattern,
1606 highlightCircleSize: highlightCircleSize,
1607 highlightSeriesOpts: highlightSeriesOpts,
1608 highlightSeriesBackgroundAlpha: highlightSeriesBackgroundAlpha,
1609 pointClickCallback: pointClickCallback,
1610 showRangeSelector: showRangeSelector,
1611 showRoller: showRoller,
1612 valueFormatter: valueFormatter,
1614 labels: data.result.labels,
1618 ticker: Dygraph.dateTicker,
1619 axisLabelFormatter: function (d, gran) {
1620 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1622 valueFormatter :function (ms) {
1623 var d = new Date(ms);
1624 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1631 drawCallback: function(dygraph, is_initial) {
1632 if(data.state.mode.name != 'auto') {
1633 if(NETDATA.options.debug.dygraph) data.state.log('dygraphDrawCallback()');
1635 var x_range = dygraph.xAxisRange();
1636 var after = Math.round(x_range[0]);
1637 var before = Math.round(x_range[1]);
1639 NETDATA.dygraph.zoomOrPan(element, this, after, before);
1642 zoomCallback: function(minDate, maxDate, yRanges) {
1643 if(NETDATA.options.debug.dygraph) data.state.log('dygraphZoomCallback()');
1644 NETDATA.dygraph.syncStop(data.state);
1645 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1646 NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1648 highlightCallback: function(event, x, points, row, seriesName) {
1649 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphHighlightCallback()');
1650 data.state.pauseChart();
1651 NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1653 unhighlightCallback: function(event) {
1654 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphUnhighlightCallback()');
1655 data.state.unpauseChart();
1656 NETDATA.dygraph.syncStop(data.state);
1658 interactionModel : {
1659 mousedown: function(event, dygraph, context) {
1660 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousedown()');
1661 NETDATA.dygraph.syncStop(data.state);
1663 if(NETDATA.options.debug.dygraph) data.state.log('dygraphMouseDown()');
1665 // Right-click should not initiate a zoom.
1666 if(event.button && event.button == 2) return;
1668 context.initializeMouseDown(event, dygraph, context);
1670 if(event.button && event.button == 1) {
1671 if (event.altKey || event.shiftKey) {
1672 data.state.setMode('pan');
1673 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1674 Dygraph.startPan(event, dygraph, context);
1677 data.state.setMode('zoom');
1678 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1679 Dygraph.startZoom(event, dygraph, context);
1683 if (event.altKey || event.shiftKey) {
1684 data.state.setMode('zoom');
1685 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1686 Dygraph.startZoom(event, dygraph, context);
1689 data.state.setMode('pan');
1690 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1691 Dygraph.startPan(event, dygraph, context);
1695 mousemove: function(event, dygraph, context) {
1696 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousemove()');
1698 if(context.isPanning) {
1699 NETDATA.dygraph.syncStop(data.state);
1700 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1701 data.state.setMode('pan');
1702 Dygraph.movePan(event, dygraph, context);
1704 else if(context.isZooming) {
1705 NETDATA.dygraph.syncStop(data.state);
1706 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1707 data.state.setMode('zoom');
1708 Dygraph.moveZoom(event, dygraph, context);
1711 mouseup: function(event, dygraph, context) {
1712 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mouseup()');
1714 if (context.isPanning) {
1715 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1716 Dygraph.endPan(event, dygraph, context);
1718 else if (context.isZooming) {
1719 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1720 Dygraph.endZoom(event, dygraph, context);
1723 click: function(event, dygraph, context) {
1724 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.click()');
1725 Dygraph.cancelEvent(event);
1727 dblclick: function(event, dygraph, context) {
1728 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.dblclick()');
1729 NETDATA.dygraph.syncStop(data.state);
1730 NETDATA.dygraph.resetChart(data.state, dygraph, context);
1732 mousewheel: function(event, dygraph, context) {
1733 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousewheel()');
1735 NETDATA.dygraph.syncStop(data.state);
1736 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1738 if(event.altKey || event.shiftKey) {
1739 // http://dygraphs.com/gallery/interaction-api.js
1740 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1741 var percentage = normal / 25;
1743 var before_old = data.state.mode.before_ms;
1744 var after_old = data.state.mode.after_ms;
1745 var range_old = before_old - after_old;
1747 var range = range_old * ( 1 - percentage );
1748 var dt = Math.round((range_old - range) / 2);
1750 var before = before_old - dt;
1751 var after = after_old + dt;
1753 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());
1755 data.state.setMode('zoom');
1756 NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1759 touchstart: function(event, dygraph, context) {
1760 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchstart()');
1761 NETDATA.dygraph.syncStop(data.state);
1762 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1763 Dygraph.Interaction.startTouch(event, dygraph, context);
1764 context.touchDirections = { x: true, y: false };
1765 data.state.setMode('zoom');
1767 touchmove: function(event, dygraph, context) {
1768 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchmove()');
1769 //Dygraph.cancelEvent(event);
1770 NETDATA.dygraph.syncStop(data.state);
1771 Dygraph.Interaction.moveTouch(event, dygraph, context);
1773 touchend: function(event, dygraph, context) {
1774 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchend()');
1775 Dygraph.Interaction.endTouch(event, dygraph, context);
1780 self.html('<div class="netdata-chart-container netdata-dygraph-container" id="dygraph-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>');
1782 data.state.dygraph_instance = new Dygraph(document.getElementById('dygraph-' + data.state.uuid),
1783 data.result.data, data.state.dygraph_options);
1786 // ----------------------------------------------------------------------------------------------------------------
1789 NETDATA.morrisInitialize = function(callback) {
1790 if(typeof netdataNoMorris == 'undefined' || !netdataNoMorris) {
1792 // morris requires raphael
1793 if(!NETDATA.chartLibraries.raphael.initialized) {
1794 if(NETDATA.chartLibraries.raphael.enabled) {
1795 NETDATA.raphaelInitialize(function() {
1796 NETDATA.morrisInitialize(callback);
1800 NETDATA.chartLibraries.morris.enabled = false;
1801 if(typeof callback == "function")
1806 NETDATA._loadCSS(NETDATA.morris_css);
1808 $.getScript(NETDATA.morris_js)
1810 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1813 NETDATA.error(100, NETDATA.morris_js);
1815 .always(function() {
1816 if(typeof callback == "function")
1822 NETDATA.chartLibraries.morris.enabled = false;
1823 if(typeof callback == "function")
1828 NETDATA.morrisChartUpdate = function(element, data) {
1829 data.state.morris_instance.setData(data.result.data);
1832 NETDATA.morrisChartCreate = function(element, data) {
1834 element.innerHTML = '<div class="netdata-chart-container netdata-morris-container" id="morris-' + data.state.uuid + '" style="width: ' + data.state.calculated_width + 'px; height: ' + data.state.calculated_height + 'px;"></div>';
1837 element: 'morris-' + data.state.uuid,
1838 data: data.result.data,
1840 ykeys: data.dimension_names,
1841 labels: data.dimension_names,
1847 continuousLine: false,
1848 behaveLikeLine: false
1852 if(data.state.chart.chart_type == 'line')
1853 morris = new Morris.Line(options);
1855 else if(data.state.chart.chart_type == 'area') {
1856 options.behaveLikeLine = true;
1857 morris = new Morris.Area(options);
1860 morris = new Morris.Area(options);
1862 data.state.morris_instance = morris;
1863 data.state.morris_options = options;
1866 // ----------------------------------------------------------------------------------------------------------------
1869 NETDATA.raphaelInitialize = function(callback) {
1870 if(typeof netdataStopRaphael == 'undefined') {
1871 $.getScript(NETDATA.raphael_js)
1873 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
1876 NETDATA.error(100, NETDATA.raphael_js);
1878 .always(function() {
1879 if(typeof callback == "function")
1884 NETDATA.chartLibraries.raphael.enabled = false;
1885 if(typeof callback == "function")
1890 NETDATA.raphaelChartUpdate = function(element, data) {
1891 var self = $(element);
1893 self.raphael(data, {
1894 width: data.state.calculated_width,
1895 height: data.state.calculated_height
1899 NETDATA.raphaelChartCreate = function(element, data) {
1900 var self = $(element);
1902 self.raphael(data, {
1903 width: data.state.calculated_width,
1904 height: data.state.calculated_height
1908 // ----------------------------------------------------------------------------------------------------------------
1911 NETDATA.googleInitialize = function(callback) {
1912 if(typeof netdataNoGoogleCharts == 'undefined' || !netdataNoGoogleCharts) {
1913 $.getScript(NETDATA.google_js)
1915 NETDATA.registerChartLibrary('google', NETDATA.google_js);
1917 google.load('visualization', '1.1', {
1918 'packages': ['corechart', 'controls'],
1919 'callback': callback
1923 NETDATA.error(100, NETDATA.google_js);
1924 if(typeof callback == "function")
1929 NETDATA.chartLibraries.google.enabled = false;
1930 if(typeof callback == "function")
1935 NETDATA.googleChartUpdate = function(element, data) {
1936 var datatable = new google.visualization.DataTable(data.result);
1937 data.state.google_instance.draw(datatable, data.state.google_options);
1940 NETDATA.googleChartCreate = function(element, data) {
1941 var datatable = new google.visualization.DataTable(data.result);
1944 // do not set width, height - the chart resizes itself
1945 //width: data.state.calculated_width,
1946 //height: data.state.calculated_height,
1948 title: data.state.chart.title,
1951 // title: "Time of Day",
1952 // format:'HH:mm:ss',
1953 viewWindowMode: 'maximized',
1964 title: data.state.chart.units,
1965 viewWindowMode: 'pretty',
1980 focusTarget: 'category',
1987 titlePosition: 'out',
1998 curveType: 'function',
2003 element.innerHTML = '<div class="netdata-chart-container netdata-google-container" id="google-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>';
2006 switch(data.state.chart.chart_type) {
2008 options.vAxis.viewWindowMode = 'maximized';
2009 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
2013 options.isStacked = true;
2014 options.areaOpacity = 0.85;
2015 options.vAxis.viewWindowMode = 'maximized';
2016 options.vAxis.minValue = null;
2017 options.vAxis.maxValue = null;
2018 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
2023 options.lineWidth = 2;
2024 gchart = new google.visualization.LineChart(document.getElementById('google-' + data.state.uuid));
2028 gchart.draw(datatable, options);
2030 data.state.google_instance = gchart;
2031 data.state.google_options = options;
2034 // ----------------------------------------------------------------------------------------------------------------
2035 // Charts Libraries Registration
2037 NETDATA.chartLibraries = {
2039 initialize: NETDATA.dygraphInitialize,
2040 create: NETDATA.dygraphChartCreate,
2041 update: NETDATA.dygraphChartUpdate,
2042 setSelection: NETDATA.dygraphSetSelection,
2043 clearSelection: NETDATA.dygraphClearSelection,
2049 pixels_per_point: 2,
2050 detects_dimensions_on_update: false
2053 initialize: NETDATA.sparklineInitialize,
2054 create: NETDATA.sparklineChartCreate,
2055 update: NETDATA.sparklineChartUpdate,
2057 clearSelection: null,
2061 options: 'flip|abs',
2063 pixels_per_point: 2,
2064 detects_dimensions_on_update: false
2067 initialize: NETDATA.peityInitialize,
2068 create: NETDATA.peityChartCreate,
2069 update: NETDATA.peityChartUpdate,
2071 clearSelection: null,
2075 options: 'null2zero|flip|abs',
2077 pixels_per_point: 2,
2078 detects_dimensions_on_update: false
2081 initialize: NETDATA.morrisInitialize,
2082 create: NETDATA.morrisChartCreate,
2083 update: NETDATA.morrisChartUpdate,
2085 clearSelection: null,
2089 options: 'objectrows|ms',
2091 pixels_per_point: 15,
2092 detects_dimensions_on_update: false
2095 initialize: NETDATA.googleInitialize,
2096 create: NETDATA.googleChartCreate,
2097 update: NETDATA.googleChartUpdate,
2099 clearSelection: null,
2102 format: 'datatable',
2105 pixels_per_point: 4,
2106 detects_dimensions_on_update: true
2109 initialize: NETDATA.raphaelInitialize,
2110 create: NETDATA.raphaelChartCreate,
2111 update: NETDATA.raphaelChartUpdate,
2113 clearSelection: null,
2119 pixels_per_point: 1,
2120 detects_dimensions_on_update: false
2124 NETDATA.registerChartLibrary = function(library, url) {
2125 if(NETDATA.options.debug.libraries)
2126 console.log("registering chart library: " + library);
2128 NETDATA.chartLibraries[library].url = url;
2129 NETDATA.chartLibraries[library].initialized = true;
2130 NETDATA.chartLibraries[library].enabled = true;
2133 // ----------------------------------------------------------------------------------------------------------------
2136 NETDATA.errorReset();
2137 NETDATA._loadjQuery(function() {
2138 $.getScript(NETDATA.serverDefault + 'lib/visible.js').then(function() {
2139 NETDATA._loadCSS(NETDATA.dashboard_css);
2141 if(typeof netdataDontStart == 'undefined' || !netdataDontStart)