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.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
74 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
75 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
76 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
77 NETDATA.dashboard_css = NETDATA.serverDefault + 'dashboard.css';
78 NETDATA.google_js = 'https://www.google.com/jsapi';
80 // these are the colors Google Charts are using
81 // we have them here to attempt emulate their look and feel on the other chart libraries
82 // http://there4.io/2012/05/02/google-chart-color-list/
83 NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
84 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
85 '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
88 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
89 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
90 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
92 // ----------------------------------------------------------------------------------------------------------------
93 // the defaults for all charts
95 // if the user does not specify any of these, the following will be used
97 NETDATA.chartDefaults = {
98 host: NETDATA.serverDefault, // the server to get data from
99 width: '100%', // the chart width - can be null
100 height: '100%', // the chart height - can be null
101 min_width: null, // the chart minimum width - can be null
102 library: 'dygraph', // the graphing library to use
103 method: 'average', // the grouping method
104 before: 0, // panning
105 after: -600, // panning
106 pixels_per_point: 1, // the detail of the chart
107 fill_luminance: 0.8 // luminance of colors in solit areas
110 // ----------------------------------------------------------------------------------------------------------------
114 targets: null, // an array of all the DOM elements that are
115 // currently visible (independently of their
116 // viewport visibility)
118 updated_dom: true, // when true, the DOM has been updated with
119 // new elements we have to check.
121 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
122 // rendering charts continiously.
123 // used with .current.fast_render_timeframe
125 page_is_visible: true, // when true, this page is visible
127 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
128 // auto-refresher for some time (when a chart is
129 // performing pan or zoom, we need to stop refreshing
130 // all other charts, to have the maximum speed for
131 // rendering the chart that is panned or zoomed).
132 // Used with .current.global_pan_sync_time
134 last_resized: 0, // the timestamp of the last resize request
136 // the current profile
137 // we may have many...
139 pixels_per_point: 1, // the minimum pixels per point for all charts
140 // increase this to speed javascript up
141 // each chart library has its own limit too
142 // the max of this and the chart library is used
143 // the final is calculated every time, so a change
144 // here will have immediate effect on the next chart
147 idle_between_charts: 50, // ms - how much time to wait between chart updates
149 fast_render_timeframe: 200, // ms - render continously until this time of continious
150 // rendering has been reached
151 // this setting is used to make it render e.g. 10
152 // charts at once, sleep idle_between_charts time
153 // and continue for another 10 charts.
155 idle_between_loops: 200, // ms - if all charts have been updated, wait this
156 // time before starting again.
158 idle_lost_focus: 500, // ms - when the window does not have focus, check
159 // if focus has been regained, every this time
161 global_pan_sync_time: 500, // ms - when you pan or zoon a chart, the background
162 // autorefreshing of charts is paused for this amount
165 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
166 // of time before setting up synchronized selections
169 sync_selection: true, // enable or disable selection sync
171 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
173 update_only_visible: true // enable or disable visibility management
190 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
192 window.onresize = function(event) {
193 NETDATA.options.last_resized = new Date().getTime();
196 // ----------------------------------------------------------------------------------------------------------------
199 NETDATA.errorCodes = {
200 100: { message: "Cannot load chart library", alert: true },
201 101: { message: "Cannot load jQuery", alert: true },
202 402: { message: "Chart library not found", alert: false },
203 403: { message: "Chart library not enabled/is failed", alert: false },
204 404: { message: "Chart not found", alert: false }
206 NETDATA.errorLast = {
212 NETDATA.error = function(code, msg) {
213 NETDATA.errorLast.code = code;
214 NETDATA.errorLast.message = msg;
215 NETDATA.errorLast.datetime = new Date().getTime();
217 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
219 if(NETDATA.errorCodes[code].alert)
220 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
223 NETDATA.errorReset = function() {
224 NETDATA.errorLast.code = 0;
225 NETDATA.errorLast.message = "You are doing fine!";
226 NETDATA.errorLast.datetime = 0;
229 // ----------------------------------------------------------------------------------------------------------------
232 // When multiple charts need the same chart, we avoid downloading it
233 // multiple times (and having it in browser memory multiple time)
234 // by using this registry.
236 // Every time we download a chart definition, we save it here with .add()
237 // Then we try to get it back with .get(). If that fails, we download it.
239 NETDATA.chartRegistry = {
242 add: function(host, id, data) {
243 host = host.replace(/:/g, "_").replace(/\//g, "_");
244 id = id.replace(/:/g, "_").replace(/\//g, "_");
246 if(typeof this.charts[host] == 'undefined')
247 this.charts[host] = {};
249 this.charts[host][id] = data;
252 get: function(host, id) {
253 host = host.replace(/:/g, "_").replace(/\//g, "_");
254 id = id.replace(/:/g, "_").replace(/\//g, "_");
256 if(typeof this.charts[host] == 'undefined')
259 if(typeof this.charts[host][id] == 'undefined')
262 return this.charts[host][id];
266 // ----------------------------------------------------------------------------------------------------------------
267 // Global Pan and Zoom on charts
269 // Using this structure are synchronize all the charts, so that
270 // when you pan or zoom one, all others are automatically refreshed
271 // to the same timespan.
273 NETDATA.globalPanAndZoom = {
274 seq: 0, // timestamp ms
275 // every time a chart is panned or zoomed
276 // we set the timestamp here
277 // then we use it as a sequence number
278 // to find if other charts are syncronized
281 master: null, // the master chart (state), to which all others
284 force_before_ms: null, // the timespan to sync all other charts
285 force_after_ms: null,
288 setMaster: function(state, after, before) {
289 if(!NETDATA.options.current.sync_pan_and_zoom) return;
291 if(this.master && this.master != state)
292 this.master.resetChart();
295 this.seq = new Date().getTime();
296 this.force_after_ms = after;
297 this.force_before_ms = before;
301 clearMaster: function() {
302 if(!NETDATA.options.current.sync_pan_and_zoom) return;
305 var state = this.master;
306 this.master = null; // prevent infinite recursion
309 NETDATA.options.auto_refresher_stop_until = 0;
316 this.force_after_ms = 0;
317 this.force_before_ms = 0;
320 // is the given state the master of the global
321 // pan and zoom sync?
322 isMaster: function(state) {
323 if(this.master == state) return true;
327 // are we currently have a global pan and zoom sync?
328 isActive: function() {
329 if(this.master) return true;
333 // check if a chart, other than the master
334 // needs to be refreshed, due to the global pan and zoom
335 shouldBeAutoRefreshed: function(state) {
336 if(!this.master || !this.seq)
339 if(state.needsResize())
342 if(state.follows_global == this.seq)
349 // ----------------------------------------------------------------------------------------------------------------
350 // Our state object, where all per-chart values are stored
352 NETDATA.chartInitialState = function(element) {
356 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
357 id: self.data('netdata'), // string - the name of chart
359 // the user given dimensions of the element
360 width: self.data('width') || NETDATA.chartDefaults.width,
361 height: self.data('height') || NETDATA.chartDefaults.height,
363 // string - the netdata server URL, without any path
364 host: self.data('host') || NETDATA.chartDefaults.host,
366 // string - the grouping method requested by the user
367 method: self.data('method') || NETDATA.chartDefaults.method,
369 // the time-range requested by the user
370 after: self.data('after') || NETDATA.chartDefaults.after,
371 before: self.data('before') || NETDATA.chartDefaults.before,
373 // the pixels per point requested by the user
374 pixels_per_point: self.data('pixels-per-point') || 1,
375 points: self.data('points') || null,
377 // the dimensions requested by the user
378 dimensions: self.data('dimensions') || null,
380 // the chart library requested by the user
381 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
382 library: null, // object - the chart library used
384 element: element, // the element already created by the user
385 element_chart: null, // the element with the chart
386 element_chart_id: null,
387 element_legend: null, // the element with the legend of the chart (if created by us)
388 element_legend_id: null,
389 element_legend_childs: { hidden: null },
391 chart_url: null, // string - the url to download chart info
392 chart: null, // object - the chart as downloaded from the server
394 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
395 created_ms: 0, // boolean - the timestamp the chart was created
396 validated: false, // boolean - has the chart been validated?
397 enabled: true, // boolean - is the chart enabled for refresh?
398 paused: false, // boolean - is the chart paused for any reason?
399 selected: false, // boolean - is the chart shown a selection?
400 debug: false, // boolena - console.log() debug info about this chart
402 updates_counter: 0, // numeric - the number of refreshes made so far
403 updates_since_last_creation: 0,
405 follows_global: 0, // the sequence number of the global synchronization
407 // Used with NETDATA.globalPanAndZoom.seq
409 last_resized: 0, // the last time the chart was resized
411 mode: null, // auto, pan, zoom
412 // this is a pointer to one of the sub-classes below
417 url: 'invalid://', // string - the last url used to update the chart
418 last_autorefreshed: 0, // milliseconds - the timestamp of last automatic refresh
419 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
420 after_ms: 0, // milliseconds - the first timestamp of the data
421 before_ms: 0, // milliseconds - the last timestamp of the data
422 points: 0, // number - the number of points in the data
423 data: null, // the last downloaded data
424 force_before_ms: null,
425 force_after_ms: null,
426 requested_before_ms: null,
427 requested_after_ms: null,
428 first_entry_ms: null,
434 url: 'invalid://', // string - the last url used to update the chart
435 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
436 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
437 after_ms: 0, // milliseconds - the first timestamp of the data
438 before_ms: 0, // milliseconds - the last timestamp of the data
439 points: 0, // number - the number of points in the data
440 data: null, // the last downloaded data
441 force_before_ms: null,
442 force_after_ms: null,
443 requested_before_ms: null,
444 requested_after_ms: null,
445 first_entry_ms: null,
451 url: 'invalid://', // string - the last url used to update the chart
452 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
453 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
454 after_ms: 0, // milliseconds - the first timestamp of the data
455 before_ms: 0, // milliseconds - the last timestamp of the data
456 points: 0, // number - the number of points in the data
457 data: null, // the last downloaded data
458 force_before_ms: null,
459 force_after_ms: null,
460 requested_before_ms: null,
461 requested_after_ms: null,
462 first_entry_ms: null,
466 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
467 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
468 refresh_dt_element: null,
471 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
474 delaySelectionSync: function() {
475 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
478 setSelection: function(t) {
479 if(typeof this.library.setSelection == 'function') {
480 if(this.library.setSelection(this, t))
481 this.selected = true;
483 this.selected = false;
485 else this.selected = true;
487 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
489 return this.selected;
492 clearSelection: function() {
494 if(typeof this.library.clearSelection == 'function') {
495 if(this.library.clearSelection(this))
496 this.selected = false;
498 this.selected = true;
500 else this.selected = false;
502 if(!this.selected && this.debug) this.log('selection cleared');
505 return this.selected;
508 // find if a timestamp (ms) is shown in the current chart
509 timeIsVisible: function(t) {
510 if(t >= this.current.after_ms && t <= this.current.before_ms)
515 calculateRowForTime: function(t) {
516 if(!this.timeIsVisible(t)) return -1;
517 return Math.floor((t - this.current.after_ms) / this.current.view_update_every);
520 pauseChart: function() {
522 if(this.debug) this.log('paused');
527 unpauseChart: function() {
529 if(this.debug) this.log('unpaused');
534 resetChart: function() {
535 NETDATA.globalPanAndZoom.clearMaster();
536 this.follows_global = 0;
538 this.clearSelection();
540 this.setMode('auto');
541 this.current.force_before_ms = null;
542 this.current.force_after_ms = null;
543 this.current.last_autorefreshed = 0;
545 this.selected = false;
549 // do not update the chart here
550 // or the chart will flip-flop when it is the master
551 // of a selection sync and another chart becomes
553 if(!NETDATA.options.current.sync_pan_and_zoom)
557 setMode: function(m) {
559 if(this.current.name == m) return;
561 this[m].url = this.current.url;
562 this[m].last_autorefreshed = this.current.last_autorefreshed;
563 this[m].view_update_every = this.current.view_update_every;
564 this[m].after_ms = this.current.after_ms;
565 this[m].before_ms = this.current.before_ms;
566 this[m].points = this.current.points;
567 this[m].data = this.current.data;
568 this[m].requested_before_ms = this.current.requested_before_ms;
569 this[m].requested_after_ms = this.current.requested_after_ms;
570 this[m].first_entry_ms = this.current.first_entry_ms;
571 this[m].last_entry_ms = this.current.last_entry_ms;
575 this.current = this.auto;
577 this.current = this.pan;
579 this.current = this.zoom;
581 this.current = this.auto;
583 this.current.force_before_ms = null;
584 this.current.force_after_ms = null;
586 if(this.debug) this.log('mode set to ' + this.current.name);
589 _minPanOrZoomStep: function() {
590 return (((this.current.before_ms - this.current.after_ms) / this.current.points) * ((this.current.points * 5 / 100) + 1) );
591 // return this.current.view_update_every * 10;
594 _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
595 var dt_after = Math.abs(old_after - new_after);
596 var dt_before = Math.abs(old_before - new_before);
597 var old_range = old_before - old_after;
599 var new_range = new_before - new_after;
600 var dt = Math.abs(old_range - new_range);
601 var step = Math.max(dt_after, dt_before, dt);
603 var min_step = this._minPanOrZoomStep();
604 if(new_range < old_range && new_range / this.chartWidth() < 100) {
605 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.chartWidth() / 1000).toString() + ': TOO SMALL RANGE');
609 if(step >= min_step) {
610 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');
614 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');
619 updateChartPanOrZoom: function(after, before, callback) {
622 if(this.current.name == 'auto') {
623 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
627 if(!this.current.force_after_ms || !this.current.force_before_ms) {
628 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
631 else if(this._shouldBeMoved(this.current.force_after_ms, this.current.force_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
632 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
635 else if(this._shouldBeMoved(this.current.requested_after_ms, this.current.requested_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
636 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
641 var now = new Date().getTime();
642 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
643 NETDATA.globalPanAndZoom.setMaster(this, after, before);
645 this.current.force_after_ms = after;
646 this.current.force_before_ms = before;
647 this.updateChart(callback);
651 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
652 if(typeof callback != 'undefined') callback();
656 updateLegendDOM: function() {
657 if(!this.hasLegend()) return;
661 // check that the legend DOM is up to date for the downloaded dimensions
662 if(typeof this.element_legend_childs.series != 'object')
664 else if(!this.current.data)
667 for(var i = 0; i < this.current.data.dimension_names.length; i++) {
668 if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] == 'undefined') {
672 else if(!this.follows_global) {
673 // FIXME update to latest value
681 if(this.debug) this.log('updating Legend DOM');
683 this.element_legend_childs = {
684 title_date: document.createElement('span'),
685 title_time: document.createElement('span'),
686 title_units: document.createElement('span'),
690 this.element_legend_childs.title_date.className += "netdata-legend-title-date";
691 this.element_legend.appendChild(this.element_legend_childs.title_date);
693 this.element_legend.appendChild(document.createElement('br'));
695 this.element_legend_childs.title_time.className += "netdata-legend-title-time";
696 this.element_legend.appendChild(this.element_legend_childs.title_time);
698 this.element_legend.appendChild(document.createElement('br'));
700 this.element_legend_childs.title_units.className += "netdata-legend-title-units";
701 this.element_legend.appendChild(this.element_legend_childs.title_units);
703 this.element_legend.appendChild(document.createElement('br'));
705 var el = document.createElement('div');
706 el.className += "netdata-legend-series";
707 this.element_legend.appendChild(el);
709 if(this.current.data) {
711 $.each(me.current.data.dimension_names, function(i, d) {
712 me.element_legend_childs.series[d] = {
713 name: document.createElement('span'),
714 value: document.createElement('span')
716 me.element_legend_childs.series[d].name.className += 'netdata-legend-name';
717 me.element_legend_childs.series[d].value.className += 'netdata-legend-value';
718 me.element_legend_childs.series[d].value.title = d;
720 var c = i % NETDATA.colors.length;
721 me.element_legend_childs.series[d].name.style.color = NETDATA.colors[c];
722 me.element_legend_childs.series[d].value.style.color = NETDATA.colors[c];
725 el.appendChild(document.createElement('br'));
727 el.appendChild(me.element_legend_childs.series[d].name);
728 el.appendChild(me.element_legend_childs.series[d].value);
733 $.each(me.chart.dimensions, function(i, d) {
734 me.element_legend_childs.series[d.name] = {
735 name: document.createElement('span'),
736 value: document.createElement('span')
738 me.element_legend_childs.series[d.name].name.className += 'netdata-legend-name';
739 me.element_legend_childs.series[d.name].value.className += 'netdata-legend-value';
740 me.element_legend_childs.series[d.name].value.title = d;
743 while(c >= NETDATA.colors.length) c -= NETDATA.colors.length;
744 me.element_legend_childs.series[d.name].name.style.color = NETDATA.colors[c];
745 me.element_legend_childs.series[d.name].value.style.color = NETDATA.colors[c];
748 el.appendChild(document.createElement('br'));
750 el.appendChild(me.element_legend_childs.series[d.name].name);
751 el.appendChild(me.element_legend_childs.series[d.name].value);
755 // create a hidden div to be used for hidding
756 // the original legend of the chart library
757 el = document.createElement('div');
758 this.element_legend.appendChild(el);
759 el.style.display = 'none';
761 this.element_legend_childs.hidden = document.createElement('div');
762 el.appendChild(this.element_legend_childs.hidden);
765 createChartDOM: function() {
768 if(this.hasLegend()) {
769 this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
770 this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
772 html += '<div class="netdata-chart-with-legend-right netdata-'
773 + this.library_name + '-chart-with-legend-right" id="'
774 + this.element_chart_id
777 html += '<div class="netdata-chart-legend netdata-'
778 + this.library_name + '-legend" id="'
779 + this.element_legend_id
783 this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
784 html += '<div class="netdata-chart netdata-'
785 + this.library_name + '-chart" id="'
786 + this.element_chart_id
790 element.innerHTML = html;
791 this.element_chart = document.getElementById(this.element_chart_id);
792 $(this.element_chart).data('netdata-state-object', this);
794 if(this.hasLegend()) {
795 this.element_legend = document.getElementById(this.element_legend_id);
796 $(this.element_legend).data('netdata-state-object', this);
797 this.updateLegendDOM();
801 hasLegend: function() {
802 if(this.element_legend) return true;
804 if(this.library && this.library.legend == 'right-side') {
805 var legend = $(this.element).data('legend') || 'yes';
806 if(legend == 'no') return false;
813 legendWidth: function() {
814 return (this.hasLegend())?110:0;
817 legendHeight: function() {
818 return $(this.element).height();
821 chartWidth: function() {
822 return $(this.element).width() - this.legendWidth();
825 chartHeight: function() {
826 return $(this.element).height();
829 chartPixelsPerPoint: function() {
830 // force an options provided detail
831 var px = this.pixels_per_point;
833 if(this.library && px < this.library.pixels_per_point)
834 px = this.library.pixels_per_point;
836 if(px < NETDATA.options.current.pixels_per_point)
837 px = NETDATA.options.current.pixels_per_point;
842 needsResize: function() {
843 return (this.library && !this.library.autoresize && this.last_resized < NETDATA.options.last_resized);
846 resizeChart: function() {
847 if(this.needsResize()) {
848 if(this.debug) this.log('forcing re-generation due to window resize.');
850 this.last_resized = new Date().getTime();
854 chartURL: function() {
857 if(NETDATA.globalPanAndZoom.isActive()) {
858 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
859 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
860 this.follows_global = NETDATA.globalPanAndZoom.seq;
863 before = this.current.force_before_ms != null ? Math.round(this.current.force_before_ms / 1000) : this.before;
864 after = this.current.force_after_ms != null ? Math.round(this.current.force_after_ms / 1000) : this.after;
865 this.follows_global = 0;
868 this.current.requested_after_ms = after * 1000;
869 this.current.requested_before_ms = before * 1000;
871 this.current.points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
873 // build the data URL
874 this.current.url = this.chart.data_url;
875 this.current.url += "&format=" + this.library.format;
876 this.current.url += "&points=" + this.current.points.toString();
877 this.current.url += "&group=" + this.method;
878 this.current.url += "&options=" + this.library.options;
879 if(this.library.jsonWrapper) this.current.url += '|jsonwrap';
882 this.current.url += "&after=" + after.toString();
885 this.current.url += "&before=" + before.toString();
888 this.current.url += "&dimensions=" + this.dimensions;
890 if(NETDATA.options.debug.chart_data_url || this.debug) this.log('chartURL(): ' + this.current.url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.current.points + ' library: ' + this.library_name);
893 updateChartWithData: function(data) {
894 if(this.debug) this.log('got data from netdata server');
895 this.current.data = data;
897 var started = new Date().getTime();
899 // if the result is JSON, find the latest update-every
900 if(typeof data == 'object' && this.library.jsonWrapper) {
901 if(typeof data.view_update_every != 'undefined')
902 this.current.view_update_every = data.view_update_every * 1000;
904 if(typeof data.after != 'undefined')
905 this.current.after_ms = data.after * 1000;
907 if(typeof data.before != 'undefined')
908 this.current.before_ms = data.before * 1000;
910 if(typeof data.first_entry_t != 'undefined')
911 this.current.first_entry_ms = data.first_entry_t * 1000;
913 if(typeof data.last_entry_t != 'undefined')
914 this.current.last_entry_ms = data.last_entry_t * 1000;
916 if(typeof data.points != 'undefined')
917 this.current.points = data.points;
922 this.updates_counter++;
925 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
927 if(this.current.force_after_ms)
928 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
930 this.log('STATUS: forced: unset');
932 this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
933 this.log('STATUS: rendered : ' + (this.current.after_ms / 1000).toString() + ' - ' + (this.current.before_ms / 1000).toString());
934 this.log('STATUS: points : ' + (this.current.points).toString() + ', min step: ' + (this._minPanOrZoomStep() / 1000).toString());
937 // this may force the chart to be re-created
940 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate) {
941 if(this.debug) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
945 if(this.created_ms && typeof this.library.update == 'function') {
946 if(this.debug) this.log('updating chart...');
948 // check and update the legend
949 this.updateLegendDOM();
951 this.updates_since_last_creation++;
952 if(NETDATA.options.debug.chart_errors) {
953 this.library.update(this, data);
957 this.library.update(this, data);
960 this.error('chart "' + this.id + '" failed to be updated as ' + this.library_name);
965 if(this.debug) this.log('creating chart...');
967 this.createChartDOM();
968 this.updates_since_last_creation = 0;
970 if(NETDATA.options.debug.chart_errors) {
971 this.library.create(this, data);
972 this.created_ms = new Date().getTime();
976 this.library.create(this, data);
977 this.created_ms = new Date().getTime();
980 this.error('chart "' + this.id + '" failed to be created as ' + this.library_name);
985 // update the performance counters
986 var now = new Date().getTime();
988 // don't update last_autorefreshed if this chart is
989 // forced to be updated with global PanAndZoom
990 if(NETDATA.globalPanAndZoom.isActive())
991 this.current.last_autorefreshed = 0;
993 this.current.last_autorefreshed = now;
995 this.refresh_dt_ms = now - started;
996 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
998 if(this.refresh_dt_element)
999 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
1002 updateChart: function(callback) {
1003 if(!this.library || !this.library.enabled) {
1004 this.error('charting library "' + this.library_name + '" is not available');
1005 if(typeof callback != 'undefined') callback();
1010 if(this.debug) this.error('chart "' + this.id + '" is not enabled');
1011 if(typeof callback != 'undefined') callback();
1015 if(!this.isVisible()) {
1016 if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
1017 if(typeof callback != 'undefined') callback();
1021 var this_state_object = this;
1022 this.getChart(function() { this_state_object.updateChart(callback); });
1026 if(!this.library.initialized) {
1027 var this_state_object = this;
1028 this.library.initialize(function() { this_state_object.updateChart(callback); });
1032 this.clearSelection();
1034 if(this.debug) this.log('updating from ' + this.current.url);
1036 var this_state_object = this;
1038 url: this.current.url,
1042 .then(function(data) {
1043 this_state_object.updateChartWithData(data);
1046 this_state_object.error('cannot download chart from ' + this_state_object.current.url);
1048 .always(function() {
1049 if(typeof callback == 'function') callback();
1053 isVisible: function() {
1054 if(NETDATA.options.current.update_only_visible)
1055 return $(this.element).visible(true);
1060 isAutoRefreshed: function() {
1061 return this.current.autorefresh;
1064 canBeAutoRefreshed: function() {
1065 if(this.isAutoRefreshed()) {
1066 var now = new Date().getTime();
1068 if(this.updates_counter && !NETDATA.options.page_is_visible) {
1069 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
1073 if(!this.isVisible()) {
1074 if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
1078 // options valid only for autoRefresh()
1079 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
1080 if(NETDATA.globalPanAndZoom.isActive()) {
1081 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
1082 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
1086 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
1092 if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
1097 if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
1101 if(now - this.current.last_autorefreshed > this.current.view_update_every) {
1102 if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
1111 autoRefresh: function(callback) {
1112 if(this.canBeAutoRefreshed()) {
1113 this.updateChart(callback);
1116 if(typeof callback != 'undefined')
1121 _defaultsFromDownloadedChart: function(chart) {
1123 this.chart_url = chart.url;
1124 this.current.view_update_every = chart.update_every * 1000;
1125 this.current.points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1128 // fetch the chart description from the netdata server
1129 getChart: function(callback) {
1130 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
1132 this._defaultsFromDownloadedChart(this.chart);
1133 if(typeof callback == 'function') callback();
1136 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
1137 if(this.debug) this.log('downloading ' + this.chart_url);
1138 var this_state_object = this;
1141 url: this.chart_url,
1145 .done(function(chart) {
1146 chart.url = this_state_object.chart_url;
1147 chart.data_url = (this_state_object.host + chart.data_url);
1148 this_state_object._defaultsFromDownloadedChart(chart);
1149 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
1152 NETDATA.error(404, this_state_object.chart_url);
1153 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
1155 .always(function() {
1156 if(typeof callback == 'function') callback();
1161 // resize the chart to its real dimensions
1162 // as given by the caller
1163 sizeChart: function() {
1164 element.className += "netdata-container";
1166 if(this.debug) this.log('sizing element');
1169 $(this.element).css('width', this.width);
1172 $(this.element).css('height', this.height);
1174 // these should be left to css
1175 //$(this.element).css('display', 'inline-block')
1176 //$(this.element).css('overflow', 'hidden');
1178 if(NETDATA.chartDefaults.min_width)
1179 $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
1182 // show a message in the chart
1183 message: function(type, msg) {
1184 this.element.innerHTML = '<div class="netdata-message netdata-' + type + '-message" style="font-size: x-small; overflow: hidden; width: 100%; height: 100%;"><small>'
1188 // reset the creation datetime
1189 // since we overwrote the whole element
1191 if(this.debug) this.log(msg);
1194 // show an error on the chart and stop it forever
1195 error: function(msg) {
1196 this.message('error', this.id + ': ' + msg);
1197 this.enabled = false;
1200 // show a message indicating the chart is loading
1201 info: function(msg) {
1202 this.message('info', this.id + ': ' + msg);
1206 if(this.debug) this.log('created');
1208 this.info("loading...");
1210 // make sure the host does not end with /
1211 // all netdata API requests use absolute paths
1212 while(this.host.slice(-1) == '/')
1213 this.host = this.host.substring(0, this.host.length - 1);
1215 // check the requested library is available
1216 // we don't initialize it here - it will be initialized when
1217 // this chart will be first used
1218 if(typeof NETDATA.chartLibraries[this.library_name] == 'undefined') {
1219 NETDATA.error(402, this.library_name);
1220 this.error('chart library "' + this.library_name + '" is not found');
1222 else if(!NETDATA.chartLibraries[this.library_name].enabled) {
1223 NETDATA.error(403, this.library_name);
1224 this.error('chart library "' + this.library_name + '" is not enabled');
1227 this.library = NETDATA.chartLibraries[this.library_name];
1229 // if we need to report the rendering speed
1230 // find the element that needs to be updated
1231 if(this.refresh_dt_element_name)
1232 this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
1234 // the default mode for all charts
1235 this.setMode('auto');
1243 // get or create a chart state, given a DOM element
1244 NETDATA.chartState = function(element) {
1245 var state = $(element).data('netdata-state-object') || null;
1247 state = NETDATA.chartInitialState(element);
1248 $(element).data('netdata-state-object', state);
1253 // ----------------------------------------------------------------------------------------------------------------
1254 // Library functions
1256 // Load a script without jquery
1257 // This is used to load jquery - after it is loaded, we use jquery
1258 NETDATA._loadjQuery = function(callback) {
1259 if(typeof jQuery == 'undefined') {
1260 var script = document.createElement('script');
1261 script.type = 'text/javascript';
1262 script.async = true;
1263 script.src = NETDATA.jQuery;
1265 // script.onabort = onError;
1266 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
1267 if(typeof callback == "function")
1268 script.onload = callback;
1270 var s = document.getElementsByTagName('script')[0];
1271 s.parentNode.insertBefore(script, s);
1273 else if(typeof callback == "function")
1277 NETDATA._loadCSS = function(filename) {
1278 var fileref = document.createElement("link");
1279 fileref.setAttribute("rel", "stylesheet");
1280 fileref.setAttribute("type", "text/css");
1281 fileref.setAttribute("href", filename);
1283 if (typeof fileref != "undefined")
1284 document.getElementsByTagName("head")[0].appendChild(fileref);
1287 NETDATA.ColorLuminance = function(hex, lum) {
1288 // validate hex string
1289 hex = String(hex).replace(/[^0-9a-f]/gi, '');
1291 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
1295 // convert to decimal and change luminosity
1296 var rgb = "#", c, i;
1297 for (i = 0; i < 3; i++) {
1298 c = parseInt(hex.substr(i*2,2), 16);
1299 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
1300 rgb += ("00"+c).substr(c.length);
1306 NETDATA.guid = function() {
1308 return Math.floor((1 + Math.random()) * 0x10000)
1313 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
1316 NETDATA.zeropad = function(x) {
1317 if(x > -10 && x < 10) return '0' + x.toString();
1318 else return x.toString();
1321 // user function to signal us the DOM has been
1323 NETDATA.updatedDom = function() {
1324 NETDATA.options.updated_dom = true;
1327 // ----------------------------------------------------------------------------------------------------------------
1329 NETDATA.chartRefresher = function(index) {
1330 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
1332 if(NETDATA.options.updated_dom) {
1333 // the dom has been updated
1334 // get the dom parts again
1335 NETDATA.getDomCharts(function() {
1336 NETDATA.chartRefresher(0);
1341 var target = NETDATA.options.targets.get(index);
1342 if(target == null) {
1343 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
1344 NETDATA.options.auto_refresher_fast_weight = 0;
1346 setTimeout(function() {
1347 NETDATA.chartRefresher(0);
1348 }, NETDATA.options.current.idle_between_loops);
1351 var state = NETDATA.chartState(target);
1353 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
1354 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
1356 state.autoRefresh(function() {
1357 NETDATA.chartRefresher(++index);
1361 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
1362 NETDATA.options.auto_refresher_fast_weight = 0;
1364 setTimeout(function() {
1365 state.autoRefresh(function() {
1366 NETDATA.chartRefresher(++index);
1368 }, NETDATA.options.current.idle_between_charts);
1373 NETDATA.getDomCharts = function(callback) {
1374 NETDATA.options.updated_dom = false;
1376 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
1378 if(NETDATA.options.debug.main_loop)
1379 console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
1381 // we need to re-size all the charts quickly
1382 // before making any external calls
1383 $.each(NETDATA.options.targets, function(i, target) {
1384 // the initialization will take care of sizing
1385 // and the "loading..." message
1386 var state = NETDATA.chartState(target);
1389 if(typeof callback == 'function') callback();
1392 // this is the main function - where everything starts
1393 NETDATA.start = function() {
1394 // this should be called only once
1396 NETDATA.options.page_is_visible = true;
1398 $(window).blur(function() {
1399 NETDATA.options.page_is_visible = false;
1400 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
1403 $(window).focus(function() {
1404 NETDATA.options.page_is_visible = true;
1405 if(NETDATA.options.debug.focus) console.log('Focus restored!');
1408 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
1409 NETDATA.options.page_is_visible = false;
1410 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1413 NETDATA.getDomCharts(function() {
1414 NETDATA.chartRefresher(0);
1418 // ----------------------------------------------------------------------------------------------------------------
1421 NETDATA.peityInitialize = function(callback) {
1422 if(typeof netdataNoPeitys == 'undefined' || !netdataNoPeitys) {
1424 url: NETDATA.peity_js,
1429 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1432 NETDATA.error(100, NETDATA.peity_js);
1434 .always(function() {
1435 if(typeof callback == "function")
1440 NETDATA.chartLibraries.peity.enabled = false;
1441 if(typeof callback == "function")
1446 NETDATA.peityChartUpdate = function(state, data) {
1447 $(state.element_chart).html(data.result);
1448 // $(state.element_chart).change() does not accept options
1449 // to pass width and height
1450 //$(state.element_chart).change();
1451 $(state.element_chart).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
1454 NETDATA.peityChartCreate = function(state, data) {
1455 $(state.element_chart).html(data.result);
1456 $(state.element_chart).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
1459 // ----------------------------------------------------------------------------------------------------------------
1462 NETDATA.sparklineInitialize = function(callback) {
1463 if(typeof netdataNoSparklines == 'undefined' || !netdataNoSparklines) {
1465 url: NETDATA.sparkline_js,
1470 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1473 NETDATA.error(100, NETDATA.sparkline_js);
1475 .always(function() {
1476 if(typeof callback == "function")
1481 NETDATA.chartLibraries.sparkline.enabled = false;
1482 if(typeof callback == "function")
1487 NETDATA.sparklineChartUpdate = function(state, data) {
1488 state.sparkline_options.width = state.chartWidth();
1489 state.sparkline_options.height = state.chartHeight();
1491 $(state.element_chart).sparkline(data.result, state.sparkline_options);
1494 NETDATA.sparklineChartCreate = function(state, data) {
1495 var self = $(state.element);
1496 var type = self.data('sparkline-type') || 'line';
1497 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1498 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
1499 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1500 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1501 var composite = self.data('sparkline-composite') || undefined;
1502 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1503 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1504 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1505 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1506 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1507 var spotColor = self.data('sparkline-spotcolor') || undefined;
1508 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1509 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1510 var spotRadius = self.data('sparkline-spotradius') || undefined;
1511 var valueSpots = self.data('sparkline-valuespots') || undefined;
1512 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1513 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1514 var lineWidth = self.data('sparkline-linewidth') || undefined;
1515 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1516 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1517 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1518 var xvalues = self.data('sparkline-xvalues') || undefined;
1519 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1520 var xvalues = self.data('sparkline-xvalues') || undefined;
1521 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1522 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1523 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1524 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1525 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1526 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1527 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1528 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1529 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1530 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1531 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1532 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.chart.units;
1533 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1534 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1535 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1536 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1537 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1538 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1539 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1540 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1541 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1543 state.sparkline_options = {
1545 lineColor: lineColor,
1546 fillColor: fillColor,
1547 chartRangeMin: chartRangeMin,
1548 chartRangeMax: chartRangeMax,
1549 composite: composite,
1550 enableTagOptions: enableTagOptions,
1551 tagOptionPrefix: tagOptionPrefix,
1552 tagValuesAttribute: tagValuesAttribute,
1553 disableHiddenCheck: disableHiddenCheck,
1554 defaultPixelsPerValue: defaultPixelsPerValue,
1555 spotColor: spotColor,
1556 minSpotColor: minSpotColor,
1557 maxSpotColor: maxSpotColor,
1558 spotRadius: spotRadius,
1559 valueSpots: valueSpots,
1560 highlightSpotColor: highlightSpotColor,
1561 highlightLineColor: highlightLineColor,
1562 lineWidth: lineWidth,
1563 normalRangeMin: normalRangeMin,
1564 normalRangeMax: normalRangeMax,
1565 drawNormalOnTop: drawNormalOnTop,
1567 chartRangeClip: chartRangeClip,
1568 chartRangeMinX: chartRangeMinX,
1569 chartRangeMaxX: chartRangeMaxX,
1570 disableInteraction: disableInteraction,
1571 disableTooltips: disableTooltips,
1572 disableHighlight: disableHighlight,
1573 highlightLighten: highlightLighten,
1574 highlightColor: highlightColor,
1575 tooltipContainer: tooltipContainer,
1576 tooltipClassname: tooltipClassname,
1577 tooltipChartTitle: state.chart.title,
1578 tooltipFormat: tooltipFormat,
1579 tooltipPrefix: tooltipPrefix,
1580 tooltipSuffix: tooltipSuffix,
1581 tooltipSkipNull: tooltipSkipNull,
1582 tooltipValueLookups: tooltipValueLookups,
1583 tooltipFormatFieldlist: tooltipFormatFieldlist,
1584 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1585 numberFormatter: numberFormatter,
1586 numberDigitGroupSep: numberDigitGroupSep,
1587 numberDecimalMark: numberDecimalMark,
1588 numberDigitGroupCount: numberDigitGroupCount,
1589 animatedZooms: animatedZooms,
1590 width: state.chartWidth(),
1591 height: state.chartHeight()
1594 $(state.element_chart).sparkline(data.result, state.sparkline_options);
1597 // ----------------------------------------------------------------------------------------------------------------
1604 dont_sync_before: 0,
1608 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1609 if(!NETDATA.options.current.sync_selection) return;
1610 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1612 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1613 // state.log('sync: I am not the sync master.');
1616 // state.debug = true;
1618 var t = state.current.after_ms + row * state.current.view_update_every;
1619 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT') + ', rows in db: ' + state.current.data.points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.current.after_ms + ' - ' + state.current.before_ms + ' real: ' + state.current.data.after + ' - ' + state.current.data.before + ' every: ' + state.current.view_update_every);
1621 now = new Date().getTime();
1622 if(now < NETDATA.dygraph.dont_sync_before) {
1623 if(state.debug) state.log('sync: cannot sync yet.');
1627 // since we are the sync master, we should not call state.setSelection()
1628 // dygraphs is taking care of visualizing our selection.
1629 state.selected = true;
1631 var dygraph = state.dygraph_instance;
1633 if(!NETDATA.dygraph.sync) {
1634 if(state.debug) state.log('sync: setting up...');
1635 $.each(NETDATA.options.targets, function(i, target) {
1636 var st = NETDATA.chartState(target);
1638 if(state.debug) st.log('sync: not adding me to sync');
1641 if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.isVisible()) {
1642 NETDATA.dygraph.slaves.push(st);
1643 if(state.debug) st.log('sync: added slave to sync');
1647 NETDATA.dygraph.sync = true;
1650 $.each(NETDATA.dygraph.slaves, function(i, st) {
1652 if(state.debug) st.log('sync: ignoring me from set selection');
1655 if(state.debug) st.log('sync: showing master selection');
1661 NETDATA.dygraph.syncStop = function(state) {
1662 if(!NETDATA.options.current.sync_selection) return;
1664 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1666 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1667 // state.log('sync: I am not the sync master.');
1671 if(NETDATA.dygraph.sync) {
1672 if(state.debug) st.log('sync: cleaning up...');
1673 $.each(NETDATA.dygraph.slaves, function(i, st) {
1675 if(state.debug) st.log('sync: not adding me to sync stop');
1678 if(state.debug) st.log('sync: removed slave from sync');
1679 st.clearSelection();
1683 NETDATA.dygraph.slaves = [];
1684 NETDATA.dygraph.sync = false;
1687 // since we are the sync master, we should not call state.clearSelection()
1688 // dygraphs is taking care of visualizing our selection.
1689 state.selected = false;
1692 NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1693 if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1698 NETDATA.dygraph.chartPanOrZoom = function(state, dygraph, after, before) {
1699 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(state, dygraph, after:' + after + ', before: ' + before + ')');
1701 //console.log(state.dygraph_instance);
1702 state.updateChartPanOrZoom(after, before);
1706 NETDATA.dygraphSetSelection = function(state, t) {
1707 if(typeof state.dygraph_instance != 'undefined') {
1708 var r = state.calculateRowForTime(t);
1710 state.dygraph_instance.setSelection(r);
1714 state.dygraph_instance.clearSelection();
1720 NETDATA.dygraphClearSelection = function(state, t) {
1721 if(typeof state.dygraph_instance != 'undefined') {
1722 state.dygraph_instance.clearSelection();
1727 NETDATA.dygraphSmoothInitialize = function(callback) {
1729 url: NETDATA.dygraph_smooth_js,
1734 NETDATA.dygraph.smooth = true;
1735 smoothPlotter.smoothing = 0.3;
1737 .always(function() {
1738 if(typeof callback == "function")
1743 NETDATA.dygraphInitialize = function(callback) {
1744 if(typeof netdataNoDygraphs == 'undefined' || !netdataNoDygraphs) {
1746 url: NETDATA.dygraph_js,
1751 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1752 NETDATA.dygraphSmoothInitialize(callback);
1755 NETDATA.error(100, NETDATA.dygraph_js);
1756 if(typeof callback == "function")
1761 NETDATA.chartLibraries.dygraph.enabled = false;
1762 if(typeof callback == "function")
1767 NETDATA.dygraphChartUpdate = function(state, data) {
1768 var dygraph = state.dygraph_instance;
1770 if(state.current.name == 'pan') {
1771 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() loose update');
1772 dygraph.updateOptions({
1773 file: data.result.data,
1774 labels: data.result.labels,
1775 labelsDivWidth: state.chartWidth() - 70
1779 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() strict update');
1780 dygraph.updateOptions({
1781 file: data.result.data,
1782 labels: data.result.labels,
1783 labelsDivWidth: state.chartWidth() - 70,
1790 NETDATA.dygraphChartCreate = function(state, data) {
1791 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartCreate()');
1793 var self = $(state.element);
1795 state.dygraph_options = {
1796 colors: self.data('dygraph-colors') || NETDATA.colors,
1798 // leave a few pixels empty on the right of the chart
1799 rightGap: self.data('dygraph-rightgap') || 5,
1800 showRangeSelector: self.data('dygraph-showrangeselector') || false,
1801 showRoller: self.data('dygraph-showroller') || false,
1803 title: self.data('dygraph-title') || state.chart.title,
1804 titleHeight: self.data('dygraph-titleheight') || 19,
1806 legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
1807 labels: data.result.labels,
1808 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
1809 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px', 'zIndex': 10000 },
1810 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
1811 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
1812 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
1815 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
1816 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
1818 ylabel: state.chart.units,
1819 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
1821 // the function to plot the chart
1824 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
1825 strokeWidth: self.data('dygraph-strokewidth') || (state.chart.chart_type == 'stacked')?0.0:1.0,
1826 strokePattern: self.data('dygraph-strokepattern') || undefined,
1828 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
1829 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
1830 drawPoints: self.data('dygraph-drawpoints') || false,
1832 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
1833 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
1835 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
1836 pointSize: self.data('dygraph-pointsize') || 1,
1838 // enabling this makes the chart with little square lines
1839 stepPlot: self.data('dygraph-stepplot') || false,
1841 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
1842 strokeBorderColor: self.data('dygraph-strokebordercolor') || 'white',
1843 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (state.chart.chart_type == 'stacked')?0.0:0.0,
1845 fillGraph: self.data('dygraph-fillgraph') || (state.chart.chart_type == 'area')?true:false,
1846 fillAlpha: self.data('dygraph-fillalpha') || (state.chart.chart_type == 'stacked')?0.8:0.2,
1847 stackedGraph: self.data('dygraph-stackedgraph') || (state.chart.chart_type == 'stacked')?true:false,
1848 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
1850 drawAxis: self.data('dygraph-drawaxis') || true,
1851 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
1852 axisLineColor: self.data('dygraph-axislinecolor') || '#DDD',
1853 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
1855 drawGrid: self.data('dygraph-drawgrid') || true,
1856 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
1857 drawYGrid: self.data('dygraph-drawygrid') || undefined,
1858 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
1859 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
1860 gridLineColor: self.data('dygraph-gridlinecolor') || '#EEE',
1862 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
1863 sigFigs: self.data('dygraph-sigfigs') || null,
1864 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
1865 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
1867 highlightCircleSize: self.data('dygraph-highlightcirclesize') || 4,
1868 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
1869 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (state.chart.chart_type == 'stacked')?0.7:0.5,
1871 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
1875 ticker: Dygraph.dateTicker,
1876 axisLabelFormatter: function (d, gran) {
1877 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
1879 valueFormatter: function (ms) {
1880 var d = new Date(ms);
1881 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
1882 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
1887 valueFormatter: function (x) {
1888 return (Math.round(x*100) / 100).toLocaleString();
1892 legendFormatter: function(data) {
1893 var g = data.dygraph;
1895 var elements = state.element_legend_childs;
1897 // if the hidden div is not there
1898 // state is not managing the legend
1899 if(elements.hidden == null) return;
1901 if (typeof(data.x) === 'undefined') {
1902 elements.title_date.innerHTML = ' ';
1903 elements.title_time.innerHTML = state.chart.name;
1904 elements.title_units.innerHTML = ' ';
1906 for (var i = 0; i < data.series.length; i++) {
1907 var series = data.series[i];
1908 elements.series[series.label].name.innerHTML = series.dashHTML + ' ' + series.labelHTML;
1909 elements.series[series.label].value.innerHTML = '';
1913 var d = new Date(data.x);
1914 elements.title_date.innerHTML = d.toLocaleDateString();
1915 elements.title_time.innerHTML = d.toLocaleTimeString();
1916 elements.title_units.innerHTML = state.chart.units;
1918 for (var i = 0; i < data.series.length; i++) {
1919 var series = data.series[i];
1920 if(!series.isVisible) continue;
1921 elements.series[series.label].value.innerHTML = series.yHTML;
1925 return 'nothing interesting here';
1927 if (typeof(data.x) === 'undefined') {
1928 html = '<span class="netdata-legend-title-date"> </span><br/><span class="netdata-legend-title-time">' + state.chart.name + '</span><br/><span class="netdata-legend-title-units"> </span>';
1929 for (var i = 0; i < data.series.length; i++) {
1930 var series = data.series[i];
1931 if (!series.isVisible) continue;
1934 html += "<span class='netdata-legend-series' style='color: " + series.color + ";'>" + series.dashHTML + " " + series.labelHTML + "</span>"
1935 html += "<span class='netdata-legend-value'> </span>";
1940 html = data.xHTML + '<br/><span class="netdata-legend-title-units">' + state.chart.units + '</span>';
1941 for (var i = 0; i < data.series.length; i++) {
1942 var series = data.series[i];
1943 if (!series.isVisible) continue;
1945 html += "<span class='netdata-legend-series' style='color: " + series.color + ";'>" + series.dashHTML + " " + series.labelHTML + "</span>"
1946 html += "<span class='netdata-legend-value' style='color: " + series.color + ";'>" + series.yHTML + "</span>";
1951 drawCallback: function(dygraph, is_initial) {
1952 if(state.current.name != 'auto') {
1953 if(NETDATA.options.debug.dygraph) state.log('dygraphDrawCallback()');
1955 var x_range = dygraph.xAxisRange();
1956 var after = Math.round(x_range[0]);
1957 var before = Math.round(x_range[1]);
1959 NETDATA.dygraph.chartPanOrZoom(state, this, after, before);
1962 zoomCallback: function(minDate, maxDate, yRanges) {
1963 if(NETDATA.options.debug.dygraph) state.log('dygraphZoomCallback()');
1964 NETDATA.dygraph.syncStop(state);
1965 state.delaySelectionSync();
1966 NETDATA.dygraph.chartPanOrZoom(state, this, minDate, maxDate);
1968 highlightCallback: function(event, x, points, row, seriesName) {
1969 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphHighlightCallback()');
1971 NETDATA.dygraph.syncStart(state, event, x, points, row, seriesName);
1972 // fix legend zIndex using the internal structures of dygraph legend module
1973 // this works, but it is a hack!
1974 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
1976 unhighlightCallback: function(event) {
1977 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphUnhighlightCallback()');
1978 state.unpauseChart();
1979 NETDATA.dygraph.syncStop(state);
1981 interactionModel : {
1982 mousedown: function(event, dygraph, context) {
1983 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousedown()');
1984 NETDATA.dygraph.syncStop(state);
1986 if(NETDATA.options.debug.dygraph) state.log('dygraphMouseDown()');
1988 // Right-click should not initiate a zoom.
1989 if(event.button && event.button == 2) return;
1991 context.initializeMouseDown(event, dygraph, context);
1993 if(event.button && event.button == 1) {
1994 if (event.altKey || event.shiftKey) {
1995 state.setMode('pan');
1996 state.delaySelectionSync();
1997 Dygraph.startPan(event, dygraph, context);
2000 state.setMode('zoom');
2001 state.delaySelectionSync();
2002 Dygraph.startZoom(event, dygraph, context);
2006 if (event.altKey || event.shiftKey) {
2007 state.setMode('zoom');
2008 state.delaySelectionSync();
2009 Dygraph.startZoom(event, dygraph, context);
2012 state.setMode('pan');
2013 state.delaySelectionSync();
2014 Dygraph.startPan(event, dygraph, context);
2018 mousemove: function(event, dygraph, context) {
2019 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousemove()');
2021 if(context.isPanning) {
2022 NETDATA.dygraph.syncStop(state);
2023 state.delaySelectionSync();
2024 state.setMode('pan');
2025 Dygraph.movePan(event, dygraph, context);
2027 else if(context.isZooming) {
2028 NETDATA.dygraph.syncStop(state);
2029 state.delaySelectionSync();
2030 state.setMode('zoom');
2031 Dygraph.moveZoom(event, dygraph, context);
2034 mouseup: function(event, dygraph, context) {
2035 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mouseup()');
2037 if (context.isPanning) {
2038 state.delaySelectionSync();
2039 Dygraph.endPan(event, dygraph, context);
2041 else if (context.isZooming) {
2042 state.delaySelectionSync();
2043 Dygraph.endZoom(event, dygraph, context);
2046 click: function(event, dygraph, context) {
2047 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.click()');
2048 /*Dygraph.cancelEvent(event);*/
2050 dblclick: function(event, dygraph, context) {
2051 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.dblclick()');
2052 NETDATA.dygraph.syncStop(state);
2053 NETDATA.dygraph.resetChart(state, dygraph, context);
2055 mousewheel: function(event, dygraph, context) {
2056 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousewheel()');
2058 NETDATA.dygraph.syncStop(state);
2059 state.delaySelectionSync();
2061 if(event.altKey || event.shiftKey) {
2062 // http://dygraphs.com/gallery/interaction-api.js
2063 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
2064 var percentage = normal / 25;
2066 var before_old = state.current.before_ms;
2067 var after_old = state.current.after_ms;
2068 var range_old = before_old - after_old;
2070 var range = range_old * ( 1 - percentage );
2071 var dt = Math.round((range_old - range) / 2);
2073 var before = before_old - dt;
2074 var after = after_old + dt;
2076 if(NETDATA.options.debug.dygraph) state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
2078 state.setMode('zoom');
2079 NETDATA.dygraph.chartPanOrZoom(state, dygraph, after, before);
2082 touchstart: function(event, dygraph, context) {
2083 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchstart()');
2084 NETDATA.dygraph.syncStop(state);
2085 state.delaySelectionSync();
2086 Dygraph.Interaction.startTouch(event, dygraph, context);
2087 context.touchDirections = { x: true, y: false };
2088 state.setMode('zoom');
2090 touchmove: function(event, dygraph, context) {
2091 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchmove()');
2092 //Dygraph.cancelEvent(event);
2093 NETDATA.dygraph.syncStop(state);
2094 Dygraph.Interaction.moveTouch(event, dygraph, context);
2096 touchend: function(event, dygraph, context) {
2097 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchend()');
2098 Dygraph.Interaction.endTouch(event, dygraph, context);
2103 // smooth lines patch
2104 if(NETDATA.dygraph.smooth && state.chart.chart_type == 'line') {
2105 state.dygraph_options.plotter = smoothPlotter;
2106 state.dygraph_options.strokeWidth = 1.5;
2109 var theme = self.data('dygraph-theme') || null;
2110 if(theme == 'sparkline') {
2111 state.dygraph_options.drawGrid = false;
2112 state.dygraph_options.drawAxis = false;
2113 state.dygraph_options.title = undefined;
2114 state.dygraph_options.units = undefined;
2115 state.dygraph_options.legend = 'never'; // 'follow'
2116 state.dygraph_options.ylabel = undefined;
2117 state.dygraph_options.yLabelWidth = 0;
2118 state.dygraph_options.labelsDivWidth = 120;
2119 state.dygraph_options.labelsDivStyles.width = '120px';
2120 state.dygraph_options.labelsSeparateLines = true;
2121 state.dygraph_options.highlightCircleSize = 3;
2122 state.dygraph_options.rightGap = 0;
2126 state.dygraph_instance = new Dygraph(state.element_chart,
2127 data.result.data, state.dygraph_options);
2130 // ----------------------------------------------------------------------------------------------------------------
2133 NETDATA.morrisInitialize = function(callback) {
2134 if(typeof netdataNoMorris == 'undefined' || !netdataNoMorris) {
2136 // morris requires raphael
2137 if(!NETDATA.chartLibraries.raphael.initialized) {
2138 if(NETDATA.chartLibraries.raphael.enabled) {
2139 NETDATA.raphaelInitialize(function() {
2140 NETDATA.morrisInitialize(callback);
2144 NETDATA.chartLibraries.morris.enabled = false;
2145 if(typeof callback == "function")
2150 NETDATA._loadCSS(NETDATA.morris_css);
2153 url: NETDATA.morris_js,
2158 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
2161 NETDATA.error(100, NETDATA.morris_js);
2163 .always(function() {
2164 if(typeof callback == "function")
2170 NETDATA.chartLibraries.morris.enabled = false;
2171 if(typeof callback == "function")
2176 NETDATA.morrisChartUpdate = function(state, data) {
2177 state.morris_instance.setData(data.result.data);
2180 NETDATA.morrisChartCreate = function(state, data) {
2182 state.morris_options = {
2183 element: state.element_chart_id,
2184 data: data.result.data,
2186 ykeys: data.dimension_names,
2187 labels: data.dimension_names,
2193 continuousLine: false,
2194 behaveLikeLine: false
2197 if(state.chart.chart_type == 'line')
2198 state.morris_instance = new Morris.Line(state.morris_options);
2200 else if(state.chart.chart_type == 'area') {
2201 state.morris_options.behaveLikeLine = true;
2202 state.morris_instance = new Morris.Area(state.morris_options);
2205 state.morris_instance = new Morris.Area(state.morris_options);
2208 // ----------------------------------------------------------------------------------------------------------------
2211 NETDATA.raphaelInitialize = function(callback) {
2212 if(typeof netdataStopRaphael == 'undefined') {
2214 url: NETDATA.raphael_js,
2219 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
2222 NETDATA.error(100, NETDATA.raphael_js);
2224 .always(function() {
2225 if(typeof callback == "function")
2230 NETDATA.chartLibraries.raphael.enabled = false;
2231 if(typeof callback == "function")
2236 NETDATA.raphaelChartUpdate = function(state, data) {
2237 $(state.element_chart).raphael(data.result, {
2238 width: state.chartWidth(),
2239 height: state.chartHeight()
2243 NETDATA.raphaelChartCreate = function(state, data) {
2244 $(state.element_chart).raphael(data.result, {
2245 width: state.chartWidth(),
2246 height: state.chartHeight()
2250 // ----------------------------------------------------------------------------------------------------------------
2253 NETDATA.googleInitialize = function(callback) {
2254 if(typeof netdataNoGoogleCharts == 'undefined' || !netdataNoGoogleCharts) {
2256 url: NETDATA.google_js,
2261 NETDATA.registerChartLibrary('google', NETDATA.google_js);
2263 google.load('visualization', '1.1', {
2264 'packages': ['corechart', 'controls'],
2265 'callback': callback
2269 NETDATA.error(100, NETDATA.google_js);
2270 if(typeof callback == "function")
2275 NETDATA.chartLibraries.google.enabled = false;
2276 if(typeof callback == "function")
2281 NETDATA.googleChartUpdate = function(state, data) {
2282 var datatable = new google.visualization.DataTable(data.result);
2283 state.google_instance.draw(datatable, state.google_options);
2286 NETDATA.googleChartCreate = function(state, data) {
2287 var datatable = new google.visualization.DataTable(data.result);
2289 state.google_options = {
2290 // do not set width, height - the chart resizes itself
2291 //width: state.chartWidth(),
2292 //height: state.chartHeight(),
2294 title: state.chart.title,
2297 // title: "Time of Day",
2298 // format:'HH:mm:ss',
2299 viewWindowMode: 'maximized',
2310 title: state.chart.units,
2311 viewWindowMode: 'pretty',
2326 focusTarget: 'category',
2333 titlePosition: 'out',
2344 curveType: 'function',
2349 switch(state.chart.chart_type) {
2351 state.google_options.vAxis.viewWindowMode = 'maximized';
2352 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2356 state.google_options.isStacked = true;
2357 state.google_options.areaOpacity = 0.85;
2358 state.google_options.vAxis.viewWindowMode = 'maximized';
2359 state.google_options.vAxis.minValue = null;
2360 state.google_options.vAxis.maxValue = null;
2361 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2366 state.google_options.lineWidth = 2;
2367 state.google_instance = new google.visualization.LineChart(state.element_chart);
2371 state.google_instance.draw(datatable, state.google_options);
2374 // ----------------------------------------------------------------------------------------------------------------
2375 // Charts Libraries Registration
2377 NETDATA.chartLibraries = {
2379 initialize: NETDATA.dygraphInitialize,
2380 create: NETDATA.dygraphChartCreate,
2381 update: NETDATA.dygraphChartUpdate,
2382 setSelection: NETDATA.dygraphSetSelection,
2383 clearSelection: NETDATA.dygraphClearSelection,
2389 legend: 'right-side',
2391 max_updates_to_recreate: 5000,
2392 pixels_per_point: 3,
2393 detects_dimensions_on_update: false
2396 initialize: NETDATA.sparklineInitialize,
2397 create: NETDATA.sparklineChartCreate,
2398 update: NETDATA.sparklineChartUpdate,
2400 clearSelection: null,
2404 options: 'flip|abs',
2408 max_updates_to_recreate: 5000,
2409 pixels_per_point: 3,
2410 detects_dimensions_on_update: false
2413 initialize: NETDATA.peityInitialize,
2414 create: NETDATA.peityChartCreate,
2415 update: NETDATA.peityChartUpdate,
2417 clearSelection: null,
2421 options: 'null2zero|flip|abs',
2425 max_updates_to_recreate: 5000,
2426 pixels_per_point: 3,
2427 detects_dimensions_on_update: false
2430 initialize: NETDATA.morrisInitialize,
2431 create: NETDATA.morrisChartCreate,
2432 update: NETDATA.morrisChartUpdate,
2434 clearSelection: null,
2438 options: 'objectrows|ms',
2442 max_updates_to_recreate: 50,
2443 pixels_per_point: 15,
2444 detects_dimensions_on_update: false
2447 initialize: NETDATA.googleInitialize,
2448 create: NETDATA.googleChartCreate,
2449 update: NETDATA.googleChartUpdate,
2451 clearSelection: null,
2454 format: 'datatable',
2459 max_updates_to_recreate: 300,
2460 pixels_per_point: 4,
2461 detects_dimensions_on_update: true
2464 initialize: NETDATA.raphaelInitialize,
2465 create: NETDATA.raphaelChartCreate,
2466 update: NETDATA.raphaelChartUpdate,
2468 clearSelection: null,
2476 max_updates_to_recreate: 1000,
2477 pixels_per_point: 1,
2478 detects_dimensions_on_update: false
2482 NETDATA.registerChartLibrary = function(library, url) {
2483 if(NETDATA.options.debug.libraries)
2484 console.log("registering chart library: " + library);
2486 NETDATA.chartLibraries[library].url = url;
2487 NETDATA.chartLibraries[library].initialized = true;
2488 NETDATA.chartLibraries[library].enabled = true;
2491 // ----------------------------------------------------------------------------------------------------------------
2494 NETDATA.errorReset();
2495 NETDATA._loadjQuery(function() {
2497 url: NETDATA.serverDefault + 'lib/visible.js',
2502 NETDATA._loadCSS(NETDATA.dashboard_css);
2504 if(typeof netdataDontStart == 'undefined' || !netdataDontStart)