]> arthur.barton.de Git - netdata.git/blob - web/dashboard.js
fixed syncronization of dygraphs on zoom-out
[netdata.git] / web / dashboard.js
1 // You can set the following variables before loading this script:
2 //
3 // var netdataStopDygraph = 1;                  // do not use dygraph
4 // var netdataStopSparkline = 1;                // do not use sparkline
5 // var netdataStopPeity = 1;                    // do not use peity
6 // var netdataStopGoogleCharts = 1;             // do not use google
7 // var netdataStopMorris = 1;                   // do not use morris
8 //
9 // You can also set the default netdata server, using the following.
10 // When this variable is not set, we assume the page is hosted on your
11 // netdata server already.
12 // var netdataServer = "http://yourhost:19999"; // set your NetData server
13
14 (function(window)
15 {
16         // fix IE bug with console
17         if(!window.console){ window.console = {log: function(){} }; }
18
19         var NETDATA = window.NETDATA || {};
20
21         // ----------------------------------------------------------------------------------------------------------------
22         // Detect the netdata server
23
24         // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
25         // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
26         NETDATA._scriptSource = function(scripts) {
27                 var script = null, base = null;
28
29                 if(typeof document.currentScript != 'undefined') {
30                         script = document.currentScript;
31                 }
32                 else {
33                         var all_scripts = document.getElementsByTagName('script');
34                         script = all_scripts[all_scripts.length - 1];
35                 }
36
37                 if (script.getAttribute.length != 'undefined')
38                         script = script.src;
39                 else
40                         script = script.getAttribute('src', -1);
41
42                 var link = document.createElement('a');
43                 link.setAttribute('href', script);
44
45                 if(!link.protocol || !link.hostname) return null;
46
47                 base = link.protocol;
48                 if(base) base += "//";
49                 base += link.hostname;
50
51                 if(link.port) base += ":" + link.port;
52                 base += "/";
53
54                 return base;
55         };
56
57         if(typeof netdataServer != 'undefined')
58                 NETDATA.serverDefault = netdataServer + "/";
59         else
60                 NETDATA.serverDefault = NETDATA._scriptSource();
61
62         NETDATA.jQuery       = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
63         NETDATA.peity_js     = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
64         NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
65         NETDATA.dygraph_js   = NETDATA.serverDefault + 'lib/dygraph-combined.js';
66         NETDATA.raphael_js   = NETDATA.serverDefault + 'lib/raphael-min.js';
67         NETDATA.morris_js    = NETDATA.serverDefault + 'lib/morris.min.js';
68         NETDATA.morris_css   = NETDATA.serverDefault + 'css/morris.css';
69         NETDATA.google_js    = 'https://www.google.com/jsapi';
70         NETDATA.colors  = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
71                                                 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
72                                                 '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
73
74         // ----------------------------------------------------------------------------------------------------------------
75         // the defaults for all charts
76
77         NETDATA.chartDefaults = {
78                 host: NETDATA.serverDefault,    // the server to get data from
79                 width: '100%',                                  // the chart width
80                 height: '100%',                                 // the chart height
81                 library: 'dygraph',                             // the graphing library to use
82                 method: 'max',                                  // the grouping method
83                 before: 0,                                              // panning
84                 after: -600,                                    // panning
85                 pixels_per_point: 1                             // the detail of the chart
86         }
87
88         NETDATA.options = {
89                 targets: null,                          
90                 updated_dom: 1,
91                 auto_refresher_fast_weight: 0,
92                 page_is_visible: 1,
93                 double_click_ms: 100,
94                 auto_refresher_stop_until: 0,
95
96                 current: {
97                         pixels_per_point: 1,
98                         idle_between_charts: 50,
99                         idle_between_loops: 200,
100                         idle_lost_focus: 500,
101                         global_pan_sync_time: 500,
102                         fast_render_timeframe: 200, // render continously for these many ms
103                         sync_delay: 1500                        // how much time after an operation to setup synced selections?
104                 },
105
106                 debug: {
107                         show_boxes:             0,
108                         main_loop:                      0,
109                         focus:                          0,
110                         visibility:             0,
111                         chart_data_url:         0,
112                         chart_errors:           0,
113                         chart_timing:           0,
114                         chart_calls:            0,
115                         dygraph:                        0
116                 }
117         }
118
119         if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
120
121
122         // ----------------------------------------------------------------------------------------------------------------
123         // Error Handling
124
125         NETDATA.errorCodes = {
126                 100: { message: "Cannot load chart library", alert: true },
127                 101: { message: "Cannot load jQuery", alert: true },
128                 402: { message: "Chart library not found", alert: false },
129                 403: { message: "Chart library not enabled/is failed", alert: false },
130                 404: { message: "Chart not found", alert: false }
131         };
132         NETDATA.errorLast = {
133                 code: 0,
134                 message: "",
135                 datetime: 0
136         };
137
138         NETDATA.error = function(code, msg) {
139                 NETDATA.errorLast.code = code;
140                 NETDATA.errorLast.message = msg;
141                 NETDATA.errorLast.datetime = new Date().getTime();
142
143                 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
144
145                 if(NETDATA.errorCodes[code].alert)
146                         alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
147         }
148
149         NETDATA.errorReset = function() {
150                 NETDATA.errorLast.code = 0;
151                 NETDATA.errorLast.message = "You are doing fine!";
152                 NETDATA.errorLast.datetime = 0;
153         };
154
155         // ----------------------------------------------------------------------------------------------------------------
156
157         NETDATA.chartRegistry = {
158                 charts: {},
159
160                 add: function(host, id, data) {
161                         host = host.replace(/:/g, "_").replace(/\//g, "_");
162                         id   =   id.replace(/:/g, "_").replace(/\//g, "_");
163
164                         if(typeof this.charts[host] == 'undefined')
165                                 this.charts[host] = {};
166
167                         this.charts[host][id] = data;
168                 },
169
170                 get: function(host, id) {
171                         host = host.replace(/:/g, "_").replace(/\//g, "_");
172                         id   =   id.replace(/:/g, "_").replace(/\//g, "_");
173
174                         if(typeof this.charts[host] == 'undefined')
175                                 return null;
176
177                         if(typeof this.charts[host][id] == 'undefined')
178                                 return null;
179
180                         return this.charts[host][id];
181                 }
182         };
183
184         // ----------------------------------------------------------------------------------------------------------------
185
186         NETDATA.globalPanAndZoom = {
187                 seq: 0,
188                 state: null,
189                 before_ms: null,
190                 after_ms: null,
191                 force_before_ms: null,
192                 force_after_ms: null,
193
194                 setMaster: function(state, after, before) {
195                         if(this.state && this.state != state) this.state.resetChart();
196
197                         this.state = state;
198                         this.seq = new Date().getTime();
199                         this.force_after_ms = after;
200                         this.force_before_ms = before;
201                 },
202                 clearMaster: function() {
203                         if(this.state) {
204                                 var state = this.state;
205                                 this.state = null; // prevent infinite recursion
206                                 this.seq = 0;
207                                 state.resetChart();
208                                 NETDATA.options.auto_refresher_stop_until = 0;
209                         }
210                         else {
211                                 this.state = null;
212                                 this.seq = 0;
213                         }
214                 },
215                 shouldBeAutoRefreshed: function(state) {
216                         if(!this.state || !this.seq)
217                                 return false;
218
219                         if(state.follows_global == this.seq)
220                                 return false;
221
222                         return true;
223                 }
224         }
225
226         // ----------------------------------------------------------------------------------------------------------------
227         // Our state object, where all per-chart values are stored
228
229         NETDATA.chartInitialState = function(element) {
230                 self = $(element);
231
232                 var state = {
233                         uuid: NETDATA.guid(),   // GUID - a unique identifier for the chart
234                         id: self.data('netdata'),       // string - the name of chart
235
236                         // the user given dimensions of the element
237                         width: self.data('width') || NETDATA.chartDefaults.width,
238                         height: self.data('height') || NETDATA.chartDefaults.height,
239
240                         // these are calculated every time the chart is refreshed
241                         calculated_width: 0,
242                         calculated_height: 0,
243
244                         // string - the netdata server URL, without any path
245                         host: self.data('host') || NETDATA.chartDefaults.host,
246
247                         // string - the grouping method requested by the user
248                         method: self.data('method') || NETDATA.chartDefaults.method,
249
250                         // the time-range requested by the user
251                         after: self.data('after') || NETDATA.chartDefaults.after,
252                         before: self.data('before') || NETDATA.chartDefaults.before,
253
254                         // the pixels per point requested by the user
255                         pixels_per_point: 1,
256                         points: self.data('points') || null,
257
258                         // the dimensions requested by the user
259                         dimensions: self.data('dimensions') || null,
260
261                         // the chart library requested by the user
262                         library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
263                         library: null,                  // object - the chart library used
264
265                         element: element,               // the element it is linked to
266                         
267                         chart_url: null,                // string - the url to download chart info
268                         chart: null,                    // object - the chart as downloaded from the server
269
270                         downloaded_ms: 0,               // milliseconds - the timestamp we downloaded the chart
271                         created_ms: 0,                  // boolean - the timestamp the chart was created
272                         validated: false,               // boolean - has the chart been validated?
273                         enabled: true,                  // boolean - is the chart enabled for refresh?
274                         paused: false,                  // boolean - is the chart paused for any reason?
275                         selected: false,                // boolean - is the chart shown a selection?
276                         debug: false,
277                         updates_counter: 0,             // numeric - the number of refreshes made so far
278
279                         follows_global: 0,
280
281                         mode: null,                     // auto, pan, zoom
282                         auto: {
283                                 name: 'auto',
284                                 autorefresh: true,
285                                 url: 'invalid://',      // string - the last url used to update the chart
286                                 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
287                                 view_update_every: 0,   // milliseconds - the minimum acceptable refresh duration
288                                 after_ms: 0,            // milliseconds - the first timestamp of the data
289                                 before_ms: 0,           // milliseconds - the last timestamp of the data
290                                 points: 0,                      // number - the number of points in the data
291                                 data: null,                     // the last downloaded data
292                                 force_before_ms: null,
293                                 force_after_ms: null,
294                                 requested_before_ms: null,
295                                 requested_after_ms: null,
296                                 first_entry_ms: null,
297                                 last_entry_ms: null,
298                         },
299                         pan: {
300                                 name: 'pan',
301                                 autorefresh: false,
302                                 url: 'invalid://',      // string - the last url used to update the chart
303                                 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
304                                 view_update_every: 0,   // milliseconds - the minimum acceptable refresh duration
305                                 after_ms: 0,            // milliseconds - the first timestamp of the data
306                                 before_ms: 0,           // milliseconds - the last timestamp of the data
307                                 points: 0,                      // number - the number of points in the data
308                                 data: null,                     // the last downloaded data
309                                 force_before_ms: null,
310                                 force_after_ms: null,
311                                 requested_before_ms: null,
312                                 requested_after_ms: null,
313                                 first_entry_ms: null,
314                                 last_entry_ms: null,
315                         },
316                         zoom: {
317                                 name: 'zoom',
318                                 autorefresh: false,
319                                 url: 'invalid://',      // string - the last url used to update the chart
320                                 last_updated_ms: 0, // milliseconds - the timestamp of last refresh
321                                 view_update_every: 0,   // milliseconds - the minimum acceptable refresh duration
322                                 after_ms: 0,            // milliseconds - the first timestamp of the data
323                                 before_ms: 0,           // milliseconds - the last timestamp of the data
324                                 points: 0,                      // number - the number of points in the data
325                                 data: null,                     // the last downloaded data
326                                 force_before_ms: null,
327                                 force_after_ms: null,
328                                 requested_before_ms: null,
329                                 requested_after_ms: null,
330                                 first_entry_ms: null,
331                                 last_entry_ms: null,
332                         },
333                         refresh_dt_ms: 0,               // milliseconds - the time the last refresh took
334                         refresh_dt_element_name: self.data('dt-element-name') || null,  // string - the element to print refresh_dt_ms
335                         refresh_dt_element: null,
336
337                         log(msg) {
338                                 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
339                         },
340
341                         setSelection: function(t) {
342                                 if(typeof this.library.setSelection == 'function') {
343                                         if(this.library.setSelection(this, t))
344                                                 this.selected = true;
345                                         else
346                                                 this.selected = false;
347                                 }
348                                 else this.selected = true;
349
350                                 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
351
352                                 return this.selected;
353                         },
354
355                         clearSelection: function() {
356                                 if(this.selected) {
357                                         if(typeof this.library.clearSelection == 'function') {
358                                                 if(this.library.clearSelection(this))
359                                                         this.selected = false;
360                                                 else
361                                                         this.selected = true;
362                                         }
363                                         else this.selected = false;
364                                         
365                                         if(!this.selected && this.debug) this.log('selection cleared');
366                                 }
367
368                                 return this.selected;
369                         },
370
371                         timeIsVisible: function(t) {
372                                 if(t >= this.mode.after_ms && t <= this.mode.before_ms)
373                                         return true;
374                                 return false;
375                         },
376
377                         calculateRowForTime: function(t) {
378                                 if(!this.timeIsVisible(t)) return -1;
379
380                                 var r = Math.floor((t - this.mode.after_ms) / this.mode.view_update_every);
381                                 // console.log(this.mode.data);
382
383                                 return r;
384                         },
385
386                         pauseChart: function() {
387                                 if(!this.paused) {
388                                         if(this.debug) this.log('paused');
389                                         this.paused = true;
390                                 }
391                         },
392
393                         unpauseChart: function() {
394                                 if(this.paused) {
395                                         if(this.debug) this.log('unpaused');
396                                         this.paused = false;
397                                 }
398                         },
399
400                         resetChart: function() {
401                                 if(NETDATA.globalPanAndZoom.state == this)
402                                         NETDATA.globalPanAndZoom.clearMaster();
403
404                                 if(state.mode.name != 'auto')
405                                         this.setMode('auto');
406
407                                 this.mode.force_before_ms = null;
408                                 this.mode.force_after_ms = null;
409                                 this.mode.last_updated_ms = 0;
410                                 this.follows_global = 0;
411                                 this.paused = false;
412                                 this.selected = false;
413                                 this.enabled = true;
414                                 this.debug = false;
415
416                                 state.updateChart();
417                         },
418
419                         setMode: function(m) {
420                                 if(this.mode) {
421                                         if(this.mode.name == m) return;
422
423                                         this[m].url = this.mode.url;
424                                         this[m].last_updated_ms = this.mode.last_updated_ms;
425                                         this[m].view_update_every = this.mode.view_update_every;
426                                         this[m].after_ms = this.mode.after_ms;
427                                         this[m].before_ms = this.mode.before_ms;
428                                         this[m].points = this.mode.points;
429                                         this[m].data = this.mode.data;
430                                         this[m].requested_before_ms = this.mode.requested_before_ms;
431                                         this[m].requested_after_ms = this.mode.requested_after_ms;
432                                         this[m].first_entry_ms = this.mode.first_entry_ms;
433                                         this[m].last_entry_ms = this.mode.last_entry_ms;
434                                 }
435
436                                 if(m == 'auto')
437                                         this.mode = this.auto;
438                                 else if(m == 'pan')
439                                         this.mode = this.pan;
440                                 else if(m == 'zoom')
441                                         this.mode = this.zoom;
442                                 else
443                                         this.mode = this.auto;
444
445                                 this.mode.force_before_ms = null;
446                                 this.mode.force_after_ms = null;
447
448                                 if(this.debug) this.log('mode set to ' + this.mode.name);
449                         },
450
451                         _minPanOrZoomStep: function() {
452                                 return (((this.mode.before_ms - this.mode.after_ms) / this.mode.points) * ((this.mode.points * 5 / 100) + 1) );
453                                 // return this.mode.view_update_every * 10;
454                         },
455
456                         _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
457                                 var dt_after = Math.abs(old_after - new_after);
458                                 var dt_before = Math.abs(old_before - new_before);
459                                 var old_range = old_before - old_after;
460
461                                 var new_range = new_before - new_after;
462                                 var dt = Math.abs(old_range - new_range);
463                                 var step = Math.max(dt_after, dt_before, dt);
464
465                                 var min_step = this._minPanOrZoomStep();
466                                 if(new_range < old_range && new_range / this.calculated_width < 100) {
467                                         if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum point size: 0.10, wanted point size: ' + (new_range / this.calculated_width / 1000).toString() + ': TOO SMALL RANGE');
468                                         return false;
469                                 }
470
471                                 if(step >= min_step) {
472                                         if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': YES');
473                                         return true;
474                                 }
475                                 else {
476                                         if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': NO');
477                                         return false;
478                                 }
479                         },
480
481                         updateChartPanOrZoom: function(after, before, callback) {
482                                 var move = false;
483
484                                 if(this.mode.name == 'auto') {
485                                         if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
486                                         this.setMode('pan');
487                                 }
488                                         
489                                 if(!this.mode.force_after_ms || !this.mode.force_before_ms) {
490                                         if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
491                                         move = true;
492                                 }
493                                 else if(this._shouldBeMoved(this.mode.force_after_ms, this.mode.force_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
494                                         if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.mode.force_after_ms / 1000).toString() + ' - ' + (this.mode.force_before_ms / 1000).toString());
495                                         move = true;
496                                 }
497                                 else if(this._shouldBeMoved(this.mode.requested_after_ms, this.mode.requested_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
498                                         if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.mode.requested_after_ms / 1000).toString() + ' - ' + (this.mode.requested_before_ms / 1000).toString());
499                                         move = true;
500                                 }
501
502                                 if(move) {
503                                         var now = new Date().getTime();
504                                         NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
505                                         NETDATA.globalPanAndZoom.setMaster(this, after, before);
506
507                                         this.mode.force_after_ms = after;
508                                         this.mode.force_before_ms = before;
509                                         this.updateChart(callback);
510                                         return true;
511                                 }
512
513                                 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
514                                 if(typeof callback != 'undefined') callback();
515                                 return false;
516                         },
517
518                         updateChart: function(callback) {
519                                 if(!this.library || !this.library.enabled) {
520                                         this.error('chart library "' + this.library_name + '" is not enabled');
521                                         if(typeof callback != 'undefined') callback();
522                                         return;
523                                 }
524
525                                 if(!this.enabled) {
526                                         if(this.debug) this.error('chart "' + this.id + '" is not enabled');
527                                         if(typeof callback != 'undefined') callback();
528                                         return;
529                                 }
530
531                                 if(!self.visible(true)) {
532                                         if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
533                                         if(typeof callback != 'undefined') callback();
534                                         return;
535                                 }
536                                 if(!this.chart) {
537                                         var this_state_object = this;
538                                         this.getChart(function() { this_state_object.updateChart(callback); });
539                                         return;
540                                 }
541
542                                 if(!this.library.initialized) {
543                                         var this_state_object = this;
544                                         this.library.initialize(function() { this_state_object.updateChart(callback); });
545                                         return;
546                                 }
547                                 
548                                 this.clearSelection();
549                                 this.chartURL();
550                                 if(this.debug) this.log('updating from ' + this.mode.url);
551
552                                 var this_state_object = this;
553                                 $.ajax( {
554                                         url: this_state_object.mode.url,
555                                         crossDomain: true
556                                 })
557                                 .then(function(data) {
558                                         if(this_state_object.debug) this_state_object.log('got data from netdata server');
559                                         this_state_object.mode.data = data;
560
561                                         var started = new Date().getTime();
562
563                                         // if the result is JSON, find the latest update-every
564                                         if(typeof data == 'object' && this_state_object.library.jsonWrapper) {
565                                                 if(!this_state_object.follows_global && typeof data.view_update_every != 'undefined')
566                                                         this_state_object.mode.view_update_every = data.view_update_every * 1000;
567
568                                                 if(typeof data.after != 'undefined')
569                                                         this_state_object.mode.after_ms = data.after * 1000;
570
571                                                 if(typeof data.before != 'undefined')
572                                                         this_state_object.mode.before_ms = data.before * 1000;
573
574                                                 if(typeof data.first_entry_t != 'undefined')
575                                                         this_state_object.mode.first_entry_ms = data.first_entry_t * 1000;
576
577                                                 if(typeof data.last_entry_t != 'undefined')
578                                                         this_state_object.mode.last_entry_ms = data.last_entry_t * 1000;
579
580                                                 if(typeof data.points != 'undefined')
581                                                         this_state_object.mode.points = data.points;
582
583                                                 data.state = this_state_object;
584                                         }
585
586                                         this_state_object.updates_counter++;
587
588                                         if(this_state_object.debug) {
589                                                 this_state_object.log('UPDATE No ' + this_state_object.updates_counter + ' COMPLETED');
590
591                                                 if(this_state_object.mode.force_after_ms)
592                                                         this_state_object.log('STATUS: forced   : ' + (this_state_object.mode.force_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.force_before_ms / 1000).toString());
593                                                 else
594                                                         this_state_object.log('STATUS: forced: unset');
595
596                                                 this_state_object.log('STATUS: requested: ' + (this_state_object.mode.requested_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.requested_before_ms / 1000).toString());
597                                                 this_state_object.log('STATUS: rendered : ' + (this_state_object.mode.after_ms / 1000).toString() + ' - ' + (this_state_object.mode.before_ms / 1000).toString());
598                                                 this_state_object.log('STATUS: points   : ' + (this_state_object.mode.points).toString() + ', min step: ' + (this_state_object._minPanOrZoomStep() / 1000).toString());
599                                         }
600
601                                         if(this_state_object.created_ms) {
602                                                 if(this_state_object.debug) this_state_object.log('updating chart...');
603
604                                                 if(NETDATA.options.debug.chart_errors) {
605                                                         this_state_object.library.update(this_state_object.element, data);
606                                                 }
607                                                 else {
608                                                         try {
609                                                                 this_state_object.library.update(this_state_object.element, data);
610                                                         }
611                                                         catch(err) {
612                                                                 this_state_object.error('chart "' + state.id + '" failed to be updated as ' + state.library_name);
613                                                         }
614                                                 }
615                                         }
616                                         else {
617                                                 if(this_state_object.debug) this_state_object.log('creating chart...');
618
619                                                 if(NETDATA.options.debug.chart_errors) {
620                                                         this_state_object.library.create(this_state_object.element, data);
621                                                         this_state_object.created_ms = new Date().getTime();
622                                                 }
623                                                 else {
624                                                         try {
625                                                                 this_state_object.library.create(this_state_object.element, data);
626                                                                 this_state_object.created_ms = new Date().getTime();
627                                                         }
628                                                         catch(err) {
629                                                                 this_state_object.error('chart "' + state.id + '" failed to be created as ' + state.library_name);
630                                                         }
631                                                 }
632                                         }
633
634                                         // update the performance counters
635                                         this_state_object.mode.last_updated_ms = new Date().getTime();
636                                         this_state_object.refresh_dt_ms = this_state_object.mode.last_updated_ms - started;
637                                         NETDATA.options.auto_refresher_fast_weight += this_state_object.refresh_dt_ms;
638
639                                         if(this_state_object.refresh_dt_element)
640                                                 this_state_object.refresh_dt_element.innerHTML = this_state_object.refresh_dt_ms.toString();
641                                 })
642                                 .fail(function() {
643                                         this_state_object.error('cannot download chart from ' + this_state_object.mode.url);
644                                 })
645                                 .always(function() {
646                                         if(typeof callback == 'function') callback();
647                                 });
648                         },
649
650                         chartURL: function() {
651                                 this.calculated_width = self.width();
652                                 this.calculated_height = self.height();
653
654                                 var before;
655                                 var after;
656                                 if(NETDATA.globalPanAndZoom.state) {
657                                         after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
658                                         before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
659                                         this.follows_global = NETDATA.globalPanAndZoom.seq;
660                                 }
661                                 else {
662                                         before = this.mode.force_before_ms != null ? Math.round(this.mode.force_before_ms / 1000) : this.before;
663                                         after  = this.mode.force_after_ms  != null ? Math.round(this.mode.force_after_ms / 1000) : this.after;
664                                         this.follows_global = 0;
665                                 }
666
667                                 this.mode.requested_after_ms = after * 1000;
668                                 this.mode.requested_before_ms = before * 1000;
669
670                                 // force an options provided detail
671                                 var pixels_per_point = this.pixels_per_point;
672                                 if(pixels_per_point < NETDATA.options.current.pixels_per_point)
673                                         pixels_per_point = NETDATA.options.current.pixels_per_point
674
675                                 this.mode.points = this.points || Math.round(this.calculated_width / pixels_per_point);
676
677                                 // build the data URL
678                                 this.mode.url = this.chart.data_url;
679                                 this.mode.url += "&format="  + this.library.format;
680                                 this.mode.url += "&points="  + this.mode.points.toString();
681                                 this.mode.url += "&group="   + this.method;
682                                 this.mode.url += "&options=" + this.library.options;
683                                 if(this.library.jsonWrapper) this.mode.url += '|jsonwrap';
684
685                                 if(after)
686                                         this.mode.url += "&after="  + after.toString();
687
688                                 if(before)
689                                         this.mode.url += "&before=" + before.toString();
690
691                                 if(this.dimensions)
692                                         this.mode.url += "&dimensions=" + this.dimensions;
693
694                                 if(NETDATA.options.debug.chart_data_url) this.log('chartURL(): ' + this.mode.url + ' WxH:' + this.calculated_width + 'x' + this.calculated_height + ' points: ' + this.mode.points + ' library: ' + this.library_name);
695                         },
696
697                         canBeAutoRefreshed: function(auto_refresher) {
698                                 if(this.mode.autorefresh) {
699                                         var now = new Date().getTime();
700
701                                         if(this.updates_counter && !NETDATA.options.page_is_visible) {
702                                                 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
703                                                 return false;
704                                         }
705
706                                         if(!auto_refresher) return true;
707
708                                         if(!self.visible(true)) {
709                                                 if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
710                                                 return false;
711                                         }
712
713                                         // options valid only for autoRefresh()
714                                         if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
715                                                 if(NETDATA.globalPanAndZoom.state) {
716                                                         if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
717                                                                 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
718                                                                 return true;
719                                                         }
720                                                         else {
721                                                                 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
722                                                                 return false;
723                                                         }
724                                                 }
725
726                                                 if(this.selected) {
727                                                         if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
728                                                         return false;
729                                                 }
730
731                                                 if(this.paused) {
732                                                         if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
733                                                         return false;
734                                                 }
735
736                                                 if(now - this.mode.last_updated_ms > this.mode.view_update_every) {
737                                                         if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
738                                                         return true;
739                                                 }
740                                         }
741                                 }
742
743                                 return false;
744                         },
745
746                         autoRefresh: function(callback) {
747                                 if(this.canBeAutoRefreshed(true))
748                                         this.updateChart(callback);
749                                 else if(typeof callback != 'undefined')
750                                         callback();
751                         },
752
753                         // fetch the chart description from the netdata server
754                         getChart: function(callback) {
755                                 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
756                                 if(this.chart) {
757                                         if(typeof callback == 'function') callback();
758                                 }
759                                 else {
760                                         this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
761                                         if(this.debug) this.log('downloading ' + this.chart_url);
762                                         this_state_object = this;
763
764                                         $.ajax( {
765                                                 url:  this.chart_url,
766                                                 crossDomain: true
767                                         })
768                                         .done(function(chart) {
769                                                 chart.data_url = (this_state_object.host + chart.data_url);
770                                                 this_state_object.chart = chart;
771                                                 this_state_object.mode.view_update_every = chart.update_every * 1000;
772                                                 this_state_object.mode.points = Math.round(self.width() / (chart.update_every / 1000));
773
774                                                 chart.url = this_state_object.chart_url;
775                                                 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
776                                         })
777                                         .fail(function() {
778                                                 NETDATA.error(404, this_state_object.chart_url);
779                                                 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
780                                         })
781                                         .always(function() {
782                                                 if(typeof callback == 'function') callback();
783                                         });
784                                 }
785                         },
786
787                         // resize the chart to its real dimensions
788                         // as given by the caller
789                         sizeChart: function() {
790                                 if(this.debug) this.log('sizing element');
791                                 self.css('width', this.width)
792                                         .css('height', this.height)
793                                         .css('display', 'inline-block')
794                                         .css('overflow', 'hidden');
795                         },
796
797                         // show a message in the chart
798                         message: function(msg) {
799                                 var bgcolor = ""
800                                 if(NETDATA.options.debug.show_boxes)
801                                         bgcolor = " background-color: lightgrey;";
802
803                                 this.element.innerHTML = '<div style="font-size: x-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
804                                         + msg
805                                         + '</small></div>';
806                                 
807                                 // reset the creation datetime
808                                 // since we overwrote the whole element
809                                 this.created_ms = 0
810                                 if(this.debug) this.log(msg);
811                         },
812
813                         // show an error on the chart and stop it forever
814                         error: function(msg) {
815                                 this.message(msg);
816                                 this.enabled = false;
817                         },
818
819                         // show a message indicating the chart is loading
820                         loading: function() {
821                                 this.message('chart ' + this.id + ' is loading...');
822                         }
823                 };
824
825                 if(state.debug) state.log('created');
826                 state.sizeChart();
827                 state.loading();
828
829                 if(typeof NETDATA.chartLibraries[state.library_name] == 'undefined') {
830                         NETDATA.error(402, state.library_name);
831                         state.error('chart library "' + state.library_name + '" is not found');
832                 }
833                 else if(!NETDATA.chartLibraries[state.library_name].enabled) {
834                         NETDATA.error(403, state.library_name);
835                         state.error('chart library "' + state.library_name + '" is not enabled');
836                 }
837                 else {
838                         state.library = NETDATA.chartLibraries[state.library_name];
839                         state.pixels_per_point = self.data('pixels-per-point') || state.library.pixels_per_point;
840                 }
841
842                 if(state.refresh_dt_element_name)
843                         state.refresh_dt_element = document.getElementById(state.refresh_dt_element_name) || null;
844
845                 state.setMode('auto');
846
847                 return state;
848         }
849
850         // get or create a chart state, given a DOM element
851         NETDATA.chartState = function(element) {
852                 self = $(element);
853                 var state = self.data('state') || null;
854                 if(!state) {
855                         state = NETDATA.chartInitialState(element);
856                         self.data('state', state);
857                 }
858                 return state;
859         }
860
861         // ----------------------------------------------------------------------------------------------------------------
862         // Library functions
863
864         // Load a script without jquery
865         // This is used to load jquery - after it is loaded, we use jquery
866         NETDATA._loadjQuery = function(callback) {
867                 if(typeof jQuery == 'undefined') {
868                         var script = document.createElement('script');
869                         script.type = 'text/javascript';
870                         script.async = true;
871                         script.src = NETDATA.jQuery;
872
873                         // script.onabort = onError;
874                         script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
875                         if(typeof callback == "function")
876                                 script.onload = callback;
877
878                         var s = document.getElementsByTagName('script')[0];
879                         s.parentNode.insertBefore(script, s);
880                 }
881                 else if(typeof callback == "function")
882                         callback();
883         }
884
885         NETDATA.ColorLuminance = function(hex, lum) {
886                 // validate hex string
887                 hex = String(hex).replace(/[^0-9a-f]/gi, '');
888                 if (hex.length < 6)
889                         hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
890
891                 lum = lum || 0;
892
893                 // convert to decimal and change luminosity
894                 var rgb = "#", c, i;
895                 for (i = 0; i < 3; i++) {
896                         c = parseInt(hex.substr(i*2,2), 16);
897                         c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
898                         rgb += ("00"+c).substr(c.length);
899                 }
900
901                 return rgb;
902         }
903
904         NETDATA.guid = function() {
905                 function s4() {
906                         return Math.floor((1 + Math.random()) * 0x10000)
907                                         .toString(16)
908                                         .substring(1);
909                         }
910
911                         return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
912         }
913
914         // user function to signal us the DOM has been
915         // updated.
916         NETDATA.updatedDom = function() {
917                 NETDATA.options.updated_dom = 1;
918         }
919
920         // ----------------------------------------------------------------------------------------------------------------
921
922         NETDATA.chartRefresher = function(index) {
923                 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
924
925                 if(NETDATA.options.updated_dom) {
926                         // the dom has been updated
927                         // get the dom parts again
928                         NETDATA.getDomCharts(function() {
929                                 NETDATA.chartRefresher(0);
930                         });
931
932                         return;
933                 }
934
935                 var target = NETDATA.options.targets.get(index);
936                 if(target == null) {
937                         if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
938                                 NETDATA.options.auto_refresher_fast_weight = 0;
939
940                                 setTimeout(function() {
941                                         NETDATA.chartRefresher(0);
942                                 }, NETDATA.options.current.idle_between_loops);
943                         }
944                 else {
945                         var state = NETDATA.chartState(target);
946
947                         if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
948                                 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
949
950                                 state.autoRefresh(function() {
951                                         NETDATA.chartRefresher(++index);
952                                 }, false);
953                         }
954                         else {
955                                 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
956                                 NETDATA.options.auto_refresher_fast_weight = 0;
957
958                                 setTimeout(function() {
959                                         state.autoRefresh(function() {
960                                                 NETDATA.chartRefresher(++index);
961                                         }, false);
962                                 }, NETDATA.options.current.idle_between_charts);
963                         }
964                 }
965         }
966
967         NETDATA.getDomCharts = function(callback) {
968                 NETDATA.options.updated_dom = 0;
969
970                 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
971
972                 if(NETDATA.options.debug.main_loop)
973                         console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
974
975                 // we need to re-size all the charts quickly
976                 // before making any external calls
977                 $.each(NETDATA.options.targets, function(i, target) {
978                         // the initialization will take care of sizing
979                         // and the "loading..." message
980                         var state = NETDATA.chartState(target);
981                 });
982
983                 if(typeof callback == 'function') callback();
984         }
985
986         // this is the main function - where everything starts
987         NETDATA.init = function() {
988                 // this should be called only once
989
990                 NETDATA.options.page_is_visible = 1;
991
992                 $(window).blur(function() {
993                         NETDATA.options.page_is_visible = 0;
994                         if(NETDATA.options.debug.focus) console.log('Lost Focus!');
995                 });
996
997                 $(window).focus(function() {
998                         NETDATA.options.page_is_visible = 1;
999                         if(NETDATA.options.debug.focus) console.log('Focus restored!');
1000                 });
1001
1002                 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
1003                         NETDATA.options.page_is_visible = 0;
1004                         if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1005                 }
1006
1007                 NETDATA.getDomCharts(function() {
1008                         NETDATA.chartRefresher(0);
1009                 });
1010         }
1011
1012         // ----------------------------------------------------------------------------------------------------------------
1013
1014         //var chart = function() {
1015         //}
1016
1017         //chart.prototype.color = function() {
1018         //      return 'red';
1019         //}
1020
1021         //var c = new chart();
1022         //c.color();
1023
1024         // ----------------------------------------------------------------------------------------------------------------
1025         // peity
1026
1027         NETDATA.peityInitialize = function(callback) {
1028                 if(typeof netdataStopPeity == 'undefined') {
1029                         $.getScript(NETDATA.peity_js)
1030                                 .done(function() {
1031                                         NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1032                                 })
1033                                 .fail(function() {
1034                                         NETDATA.error(100, NETDATA.peity_js);
1035                                 })
1036                                 .always(function() {
1037                                         if(typeof callback == "function")
1038                                                 callback();
1039                                 })
1040                 }
1041                 else {
1042                         NETDATA.chartLibraries.peity.enabled = false;
1043                         if(typeof callback == "function")
1044                                 callback();
1045                 }
1046         };
1047
1048         NETDATA.peityChartUpdate = function(element, data) {
1049                 var peity = $(data.state.peity_element);
1050                 peity.html(data.result);
1051                 // peity.change() does not accept options
1052                 // to pass width and height
1053                 //peity.change();
1054                 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1055         }
1056
1057         NETDATA.peityChartCreate = function(element, data) {
1058                 element.innerHTML = '<div id="peity-' + data.state.uuid + '">' + data.result + '</div>';
1059                 data.state.peity_element = document.getElementById('peity-' + data.state.uuid);
1060                 var peity = $(data.state.peity_element);
1061
1062                 peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1063         }
1064
1065         // ----------------------------------------------------------------------------------------------------------------
1066         // sparkline
1067
1068         NETDATA.sparklineInitialize = function(callback) {
1069                 if(typeof netdataStopSparkline == 'undefined') {
1070                         $.getScript(NETDATA.sparkline_js)
1071                                 .done(function() {
1072                                         NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1073                                 })
1074                                 .fail(function() {
1075                                         NETDATA.error(100, NETDATA.sparkline_js);
1076                                 })
1077                                 .always(function() {
1078                                         if(typeof callback == "function")
1079                                                 callback();
1080                                 })
1081                 }
1082                 else {
1083                         NETDATA.chartLibraries.sparkline.enabled = false;
1084                         if(typeof callback == "function") 
1085                                 callback();
1086                 }
1087         };
1088
1089         NETDATA.sparklineChartUpdate = function(element, data) {
1090                 data.state.sparkline_options.width = data.state.calculated_width;
1091                 data.state.sparkline_options.height = data.state.calculated_height;
1092
1093                 spark = $(data.state.sparkline_element);
1094                 spark.sparkline(data.result, data.state.sparkline_options);
1095         }
1096
1097         NETDATA.sparklineChartCreate = function(element, data) {
1098                 var self = $(element);
1099                 var type = self.data('sparkline-type') || 'line';
1100                 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1101                 var fillColor = self.data('sparkline-fillcolor') || (data.state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
1102                 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1103                 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1104                 var composite = self.data('sparkline-composite') || undefined;
1105                 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1106                 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1107                 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1108                 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1109                 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1110                 var spotColor = self.data('sparkline-spotcolor') || undefined;
1111                 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1112                 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1113                 var spotRadius = self.data('sparkline-spotradius') || undefined;
1114                 var valueSpots = self.data('sparkline-valuespots') || undefined;
1115                 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1116                 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1117                 var lineWidth = self.data('sparkline-linewidth') || undefined;
1118                 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1119                 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1120                 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1121                 var xvalues = self.data('sparkline-xvalues') || undefined;
1122                 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1123                 var xvalues = self.data('sparkline-xvalues') || undefined;
1124                 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1125                 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1126                 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1127                 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1128                 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1129                 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1130                 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1131                 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1132                 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1133                 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1134                 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1135                 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + data.state.chart.units;
1136                 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1137                 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1138                 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1139                 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1140                 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1141                 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1142                 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1143                 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1144                 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1145
1146                 data.state.sparkline_options = {
1147                         type: type,
1148                         lineColor: lineColor,
1149                         fillColor: fillColor,
1150                         chartRangeMin: chartRangeMin,
1151                         chartRangeMax: chartRangeMax,
1152                         composite: composite,
1153                         enableTagOptions: enableTagOptions,
1154                         tagOptionPrefix: tagOptionPrefix,
1155                         tagValuesAttribute: tagValuesAttribute,
1156                         disableHiddenCheck: disableHiddenCheck,
1157                         defaultPixelsPerValue: defaultPixelsPerValue,
1158                         spotColor: spotColor,
1159                         minSpotColor: minSpotColor,
1160                         maxSpotColor: maxSpotColor,
1161                         spotRadius: spotRadius,
1162                         valueSpots: valueSpots,
1163                         highlightSpotColor: highlightSpotColor,
1164                         highlightLineColor: highlightLineColor,
1165                         lineWidth: lineWidth,
1166                         normalRangeMin: normalRangeMin,
1167                         normalRangeMax: normalRangeMax,
1168                         drawNormalOnTop: drawNormalOnTop,
1169                         xvalues: xvalues,
1170                         chartRangeClip: chartRangeClip,
1171                         chartRangeMinX: chartRangeMinX,
1172                         chartRangeMaxX: chartRangeMaxX,
1173                         disableInteraction: disableInteraction,
1174                         disableTooltips: disableTooltips,
1175                         disableHighlight: disableHighlight,
1176                         highlightLighten: highlightLighten,
1177                         highlightColor: highlightColor,
1178                         tooltipContainer: tooltipContainer,
1179                         tooltipClassname: tooltipClassname,
1180                         tooltipChartTitle: data.state.chart.title,
1181                         tooltipFormat: tooltipFormat,
1182                         tooltipPrefix: tooltipPrefix,
1183                         tooltipSuffix: tooltipSuffix,
1184                         tooltipSkipNull: tooltipSkipNull,
1185                         tooltipValueLookups: tooltipValueLookups,
1186                         tooltipFormatFieldlist: tooltipFormatFieldlist,
1187                         tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1188                         numberFormatter: numberFormatter,
1189                         numberDigitGroupSep: numberDigitGroupSep,
1190                         numberDecimalMark: numberDecimalMark,
1191                         numberDigitGroupCount: numberDigitGroupCount,
1192                         animatedZooms: animatedZooms,
1193                         width: data.state.calculated_width,
1194                         height: data.state.calculated_height
1195                 };
1196
1197                 element.innerHTML = '<div id="sparkline-' + data.state.uuid + '" style="display: inline-block; position: relative;"></div>';
1198                 data.state.sparkline_element = document.getElementById('sparkline-' + data.state.uuid);
1199
1200                 spark = $(data.state.sparkline_element);
1201                 spark.sparkline(data.result, data.state.sparkline_options);
1202         };
1203
1204         // ----------------------------------------------------------------------------------------------------------------
1205         // dygraph
1206
1207         NETDATA.dygraph = {
1208                 state: null,
1209                 sync: false,
1210                 dont_sync_before: 0,
1211                 slaves: []
1212         };
1213
1214         NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1215                 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1216
1217                 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1218                 //      state.log('sync: I am not the sync master.');
1219                 //      return;
1220                 //}
1221                 // state.debug = true;
1222
1223                 var t = state.mode.after_ms + row * state.mode.view_update_every;
1224                 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT'));
1225
1226                 now = new Date().getTime();
1227                 if(now < NETDATA.dygraph.dont_sync_before) {
1228                         if(state.debug) st.log('sync: cannot sync yet.');
1229                         return;
1230                 }
1231
1232                 // since we are the sync master, we should not call state.setSelection()
1233                 // dygraphs is taking care of visualizing our selection.
1234                 state.selected = true;
1235
1236                 var dygraph = state.dygraph_instance;
1237
1238                 if(!NETDATA.dygraph.sync) {
1239                         if(state.debug) st.log('sync: setting up...');
1240                         $.each(NETDATA.options.targets, function(i, target) {
1241                                 var st = NETDATA.chartState(target);
1242                                 if(st == state) {
1243                                         if(state.debug) st.log('sync: not adding me to sync');
1244                                 }
1245                                 else {
1246                                         if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1247                                                 NETDATA.dygraph.slaves.push(st);
1248                                                 if(state.debug) st.log('sync: added slave to sync');
1249                                         }
1250                                 }
1251                         });
1252                         NETDATA.dygraph.sync = true;
1253                 }
1254
1255                 $.each(NETDATA.dygraph.slaves, function(i, st) {
1256                         if(st == state) {
1257                                 if(state.debug) st.log('sync: ignoring me from set selection');
1258                         }
1259                         else {
1260                                 if(state.debug) st.log('sync: showing master selection');
1261                                 st.setSelection(t);
1262                         }
1263                 });
1264         }
1265
1266         NETDATA.dygraph.syncStop = function(state) {
1267                 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1268
1269                 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1270                 //      state.log('sync: I am not the sync master.');
1271                 //      return;
1272                 //}
1273
1274                 if(NETDATA.dygraph.sync) {
1275                         if(state.debug) st.log('sync: cleaning up...');
1276                         $.each(NETDATA.dygraph.slaves, function(i, st) {
1277                                 if(st == state) {
1278                                         if(state.debug) st.log('sync: not adding me to sync stop');
1279                                 }
1280                                 else {
1281                                         if(state.debug) st.log('sync: removed slave from sync');
1282                                         st.clearSelection();
1283                                 }
1284                         });
1285
1286                         NETDATA.dygraph.slaves = [];
1287                         NETDATA.dygraph.sync = false;
1288                 }
1289
1290                 // since we are the sync master, we should not call state.clearSelection()
1291                 // dygraphs is taking care of visualizing our selection.
1292                 state.selected = false;
1293         }
1294
1295         NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1296                 if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1297
1298                 state.resetChart();
1299                 if(NETDATA.globalPanAndZoom.clearMaster());
1300         }
1301
1302         NETDATA.dygraph.zoomOrPan = function(element, dygraph, after, before) {
1303                 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(element, dygraph, after:' + after + ', before: ' + before + ')');
1304
1305                 state = NETDATA.chartState(element);
1306                 state.updateChartPanOrZoom(after, before);
1307                 return;
1308         }
1309
1310         NETDATA.dygraphSetSelection = function(state, t) {
1311                 if(typeof state.dygraph_instance != 'undefined') {
1312                         var r = state.calculateRowForTime(t);
1313                         if(r != -1) {
1314                                 state.dygraph_instance.setSelection(r);
1315                                 return true;
1316                         }
1317                         else {
1318                                 state.dygraph_instance.clearSelection();
1319                                 return false;
1320                         }
1321                 }
1322         }
1323
1324         NETDATA.dygraphClearSelection = function(state, t) {
1325                 if(typeof state.dygraph_instance != 'undefined') {
1326                         state.dygraph_instance.clearSelection();
1327                 }
1328                 return true;
1329         }
1330
1331         NETDATA.dygraphInitialize = function(callback) {
1332                 if(typeof netdataStopDygraph == 'undefined') {
1333                         $.getScript(NETDATA.dygraph_js)
1334                                 .done(function() {
1335                                         NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1336                                 })
1337                                 .fail(function() {
1338                                         NETDATA.error(100, NETDATA.dygraph_js);
1339                                 })
1340                                 .always(function() {
1341                                         if(typeof callback == "function")
1342                                                 callback();
1343                                 })
1344                 }
1345                 else {
1346                         NETDATA.chartLibraries.dygraph.enabled = false;
1347                         if(typeof callback == "function")
1348                                 callback();
1349                 }
1350         };
1351
1352         NETDATA.dygraphChartUpdate = function(element, data) {
1353                 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate()');
1354
1355                 var dygraph = data.state.dygraph_instance;
1356
1357                 if(data.state.mode.name == 'pan') {
1358                         if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() loose update');
1359                         dygraph.updateOptions({
1360                                 file: data.result.data,
1361                                 labels: data.result.labels,
1362                                 labelsDivWidth: data.state.calculated_width - 70
1363                         });
1364                 }
1365                 else {
1366                         if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() strict update');
1367                         dygraph.updateOptions({
1368                                 file: data.result.data,
1369                                 labels: data.result.labels,
1370                                 labelsDivWidth: data.state.calculated_width - 70,
1371                                 dateWindow: null,
1372                         valueRange: null
1373                         });
1374                 }
1375         };
1376
1377         NETDATA.dygraphChartCreate = function(element, data) {
1378                 if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartCreate()');
1379
1380                 var self = $(element);
1381                 var title = self.data('dygraph-title') || data.state.chart.title;
1382                 var titleHeight = self.data('dygraph-titleheight') || 20;
1383                 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1384                 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1385                 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
1386                 var stackedGraph = self.data('dygraph-stackedgraph') || (data.state.chart.chart_type == 'stacked')?true:false;
1387                 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1388                 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
1389                 var fillGraph = self.data('dygraph-fillgraph') || (data.state.chart.chart_type == 'area')?true:false;
1390                 var drawPoints = self.data('dygraph-drawpoints') || false;
1391                 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1392                 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
1393                 var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
1394                 var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
1395                 var legend = self.data('dygraph-legend') || 'onmouseover';
1396                 var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
1397                 var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
1398                 var axisLineColor = self.data('dygraph-axislinecolor') || '#EEE';
1399                 var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
1400                 var sigFigs = self.data('dygraph-sigfigs') || null;
1401                 var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
1402                 var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
1403                 var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
1404                 var drawAxis = self.data('dygraph-drawaxis') || true;
1405                 var strokeWidth = self.data('dygraph-strokewidth') || 1.0;
1406                 var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
1407                 var colors = self.data('dygraph-colors') || NETDATA.colors;
1408                 var pointSize = self.data('dygraph-pointsize') || 1;
1409                 var stepPlot = self.data('dygraph-stepplot') || false;
1410                 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
1411                 var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (data.state.chart.chart_type == 'stacked')?1.0:0.0;
1412                 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1413                 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1414                 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || { strokeWidth: 1.5 };
1415                 var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (data.state.chart.chart_type == 'stacked')?0.7:0.5;
1416                 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1417                 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1418                 var showRoller = self.data('dygraph-showroller') || false;
1419                 var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
1420                 var rightGap = self.data('dygraph-rightgap') || 5;
1421                 var drawGrid = self.data('dygraph-drawgrid') || true;
1422                 var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
1423                 var drawYGrid = self.data('dygraph-drawygrid') || undefined;
1424                 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1425                 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1426
1427                 data.state.dygraph_options = {
1428                         title: title,
1429                         titleHeight: titleHeight,
1430                         ylabel: data.state.chart.units,
1431                         yLabelWidth: yLabelWidth,
1432                         connectSeparatedPoints: connectSeparatedPoints,
1433                         drawPoints: drawPoints,
1434                         fillGraph: fillGraph,
1435                         stackedGraph: stackedGraph,
1436                         stackedGraphNaNFill: stackedGraphNaNFill,
1437                         drawGrid: drawGrid,
1438                         drawXGrid: drawXGrid,
1439                         drawYGrid: drawYGrid,
1440                         gridLinePattern: gridLinePattern,
1441                         gridLineWidth: gridLineWidth,
1442                         gridLineColor: gridLineColor,
1443                         axisLineColor: axisLineColor,
1444                         axisLineWidth: axisLineWidth,
1445                         drawAxis: drawAxis,
1446                         hideOverlayOnMouseOut: hideOverlayOnMouseOut,
1447                         labelsDiv: labelsDiv,
1448                         labelsDivStyles: labelsDivStyles,
1449                         labelsDivWidth: labelsDivWidth,
1450                         labelsSeparateLines: labelsSeparateLines,
1451                         labelsShowZeroValues: labelsShowZeroValues,
1452                         labelsKMB: false,
1453                         labelsKMG2: false,
1454                         legend: legend,
1455                         showLabelsOnHighlight: showLabelsOnHighlight,
1456                         maxNumberWidth: maxNumberWidth,
1457                         sigFigs: sigFigs,
1458                         digitsAfterDecimal: digitsAfterDecimal,
1459                         axisLabelFontSize: axisLabelFontSize,
1460                         colors: colors,
1461                         strokeWidth: strokeWidth,
1462                         drawGapEdgePoints: drawGapEdgePoints,
1463                         pointSize: pointSize,
1464                         stepPlot: stepPlot,
1465                         strokeBorderColor: strokeBorderColor,
1466                         strokeBorderWidth: strokeBorderWidth,
1467                         strokePattern: strokePattern,
1468                         highlightCircleSize: highlightCircleSize,
1469                         highlightSeriesOpts: highlightSeriesOpts,
1470                         highlightSeriesBackgroundAlpha: highlightSeriesBackgroundAlpha,
1471                         pointClickCallback: pointClickCallback,
1472                         showRangeSelector: showRangeSelector,
1473                         showRoller: showRoller,
1474                         valueFormatter: valueFormatter,
1475                         rightGap: rightGap,
1476                         labels: data.result.labels,
1477                         axes: {
1478                                 x: {
1479                                         pixelsPerLabel: 50,
1480                                         ticker: Dygraph.dateTicker,
1481                                         axisLabelFormatter: function (d, gran) {
1482                                                 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1483                                         },
1484                                         valueFormatter :function (ms) {
1485                                                 var d = new Date(ms);
1486                                                 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1487                                         }
1488                                 },
1489                                 y: {
1490                                         pixelsPerLabel: 15
1491                                 }
1492                         },
1493                         drawCallback: function(dygraph, is_initial) {
1494                                 if(data.state.mode.name != 'auto') {
1495                                         if(NETDATA.options.debug.dygraph) data.state.log('dygraphDrawCallback()');
1496
1497                                         var x_range = dygraph.xAxisRange();
1498                                         var after = Math.round(x_range[0]);
1499                                         var before = Math.round(x_range[1]);
1500
1501                                         NETDATA.dygraph.zoomOrPan(element, this, after, before);
1502                                 }
1503                         },
1504                         zoomCallback: function(minDate, maxDate, yRanges) {
1505                                 if(NETDATA.options.debug.dygraph) data.state.log('dygraphZoomCallback()');
1506                                 NETDATA.dygraph.syncStop(data.state);
1507                                 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1508                                 NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1509                         },
1510                         highlightCallback: function(event, x, points, row, seriesName) {
1511                                 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphHighlightCallback()');
1512                                 data.state.pauseChart();
1513                                 NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1514                         },
1515                         unhighlightCallback: function(event) {
1516                                 if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphUnhighlightCallback()');
1517                                 data.state.unpauseChart();
1518                                 NETDATA.dygraph.syncStop(data.state);
1519                         },
1520                         interactionModel : {
1521                                 mousedown: function(event, dygraph, context) {
1522                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousedown()');
1523                                         NETDATA.dygraph.syncStop(data.state);
1524
1525                                         if(NETDATA.options.debug.dygraph) data.state.log('dygraphMouseDown()');
1526
1527                                         // Right-click should not initiate a zoom.
1528                                         if(event.button && event.button == 2) return;
1529
1530                                         context.initializeMouseDown(event, dygraph, context);
1531                                         
1532                                         if(event.button && event.button == 1) {
1533                                                 if (event.altKey || event.shiftKey) {
1534                                                         data.state.setMode('pan');
1535                                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1536                                                         Dygraph.startPan(event, dygraph, context);
1537                                                 }
1538                                                 else {
1539                                                         data.state.setMode('zoom');
1540                                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1541                                                         Dygraph.startZoom(event, dygraph, context);
1542                                                 }
1543                                         }
1544                                         else {
1545                                                 if (event.altKey || event.shiftKey) {
1546                                                         data.state.setMode('zoom');
1547                                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1548                                                         Dygraph.startZoom(event, dygraph, context);
1549                                                 }
1550                                                 else {
1551                                                         data.state.setMode('pan');
1552                                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1553                                                         Dygraph.startPan(event, dygraph, context);
1554                                                 }
1555                                         }
1556                                 },
1557                                 mousemove: function(event, dygraph, context) {
1558                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousemove()');
1559
1560                                         if(context.isPanning) {
1561                                                 NETDATA.dygraph.syncStop(data.state);
1562                                                 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1563                                                 data.state.setMode('pan');
1564                                                 Dygraph.movePan(event, dygraph, context);
1565                                         }
1566                                         else if(context.isZooming) {
1567                                                 NETDATA.dygraph.syncStop(data.state);
1568                                                 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1569                                                 data.state.setMode('zoom');
1570                                                 Dygraph.moveZoom(event, dygraph, context);
1571                                         }
1572                                 },
1573                                 mouseup: function(event, dygraph, context) {
1574                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mouseup()');
1575
1576                                         if (context.isPanning) {
1577                                                 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1578                                                 Dygraph.endPan(event, dygraph, context);
1579                                         }
1580                                         else if (context.isZooming) {
1581                                                 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1582                                                 Dygraph.endZoom(event, dygraph, context);
1583                                         }
1584                                 },
1585                                 click: function(event, dygraph, context) {
1586                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.click()');
1587                                         Dygraph.cancelEvent(event);
1588                                 },
1589                                 dblclick: function(event, dygraph, context) {
1590                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.dblclick()');
1591                                         NETDATA.dygraph.syncStop(data.state);
1592                                         NETDATA.dygraph.resetChart(data.state, dygraph, context);
1593                                 },
1594                                 mousewheel: function(event, dygraph, context) {
1595                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousewheel()');
1596
1597                                         NETDATA.dygraph.syncStop(data.state);
1598                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1599
1600                                         if(event.altKey || event.shiftKey) {
1601                                                 // http://dygraphs.com/gallery/interaction-api.js
1602                                                 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1603                                                 var percentage = normal / 25;
1604
1605                                                 var before_old = data.state.mode.before_ms;
1606                                                 var after_old = data.state.mode.after_ms;
1607                                                 var range_old = before_old - after_old;
1608
1609                                                 var range = range_old * ( 1 - percentage );
1610                                                 var dt = Math.round((range_old - range) / 2);
1611
1612                                                 var before = before_old - dt;
1613                                                 var after  = after_old  + dt;
1614
1615                                                 if(NETDATA.options.debug.dygraph) data.state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
1616
1617                                                 data.state.setMode('zoom');
1618                                                 NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1619                                         }                                       
1620                                 },
1621                                 touchstart: function(event, dygraph, context) {
1622                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchstart()');
1623                                         NETDATA.dygraph.syncStop(data.state);
1624                                         NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1625                                         Dygraph.Interaction.startTouch(event, dygraph, context);
1626                                         context.touchDirections = { x: true, y: false };
1627                                         data.state.setMode('zoom');
1628                                 },
1629                                 touchmove: function(event, dygraph, context) {
1630                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchmove()');
1631                                         //Dygraph.cancelEvent(event);
1632                                         NETDATA.dygraph.syncStop(data.state);
1633                                         Dygraph.Interaction.moveTouch(event, dygraph, context);
1634                                 },
1635                                 touchend: function(event, dygraph, context) {
1636                                         if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchend()');
1637                                         Dygraph.Interaction.endTouch(event, dygraph, context);
1638                                 }
1639                         }
1640                 };
1641
1642                 self.html('<div id="dygraph-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>');
1643
1644                 data.state.dygraph_instance = new Dygraph(document.getElementById('dygraph-' + data.state.uuid),
1645                         data.result.data, data.state.dygraph_options);
1646         };
1647
1648         // ----------------------------------------------------------------------------------------------------------------
1649         // morris
1650
1651         NETDATA.morrisInitialize = function(callback) {
1652                 if(typeof netdataStopMorris == 'undefined') {
1653
1654                         // morris requires raphael
1655                         if(!NETDATA.chartLibraries.raphael.initialized) {
1656                                 if(NETDATA.chartLibraries.raphael.enabled) {
1657                                         NETDATA.raphaelInitialize(function() {
1658                                                 NETDATA.morrisInitialize(callback);
1659                                         });
1660                                 }
1661                                 else {
1662                                         NETDATA.chartLibraries.morris.enabled = false;
1663                                         if(typeof callback == "function")
1664                                                 callback();
1665                                 }
1666                         }
1667                         else {
1668                                 var fileref = document.createElement("link");
1669                                 fileref.setAttribute("rel", "stylesheet");
1670                                 fileref.setAttribute("type", "text/css");
1671                                 fileref.setAttribute("href", NETDATA.morris_css);
1672
1673                                 if (typeof fileref != "undefined")
1674                                         document.getElementsByTagName("head")[0].appendChild(fileref);
1675
1676                                 $.getScript(NETDATA.morris_js)
1677                                         .done(function() {
1678                                                 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1679                                         })
1680                                         .fail(function() {
1681                                                 NETDATA.error(100, NETDATA.morris_js);
1682                                         })
1683                                         .always(function() {
1684                                                 if(typeof callback == "function")
1685                                                         callback();
1686                                         })
1687                         }
1688                 }
1689                 else {
1690                         NETDATA.chartLibraries.morris.enabled = false;
1691                         if(typeof callback == "function")
1692                                 callback();
1693                 }
1694         };
1695
1696         NETDATA.morrisChartUpdate = function(element, data) {
1697                 data.state.morris_instance.setData(data.result.data);
1698         };
1699
1700         NETDATA.morrisChartCreate = function(element, data) {
1701
1702                 element.innerHTML = '<div id="morris-' + data.state.uuid + '" style="width: ' + data.state.calculated_width + 'px; height: ' + data.state.calculated_height + 'px;"></div>';
1703
1704                 var options = {
1705                                 element: 'morris-' + data.state.uuid,
1706                                 data: data.result.data,
1707                                 xkey: 'time',
1708                                 ykeys: data.dimension_names,
1709                                 labels: data.dimension_names,
1710                                 lineWidth: 2,
1711                                 pointSize: 2,
1712                                 smooth: true,
1713                                 hideHover: 'auto',
1714                                 parseTime: true,
1715                                 continuousLine: false,
1716                                 behaveLikeLine: false
1717                 };
1718
1719                 var morris;
1720                 if(data.state.chart.chart_type == 'line')
1721                         morris = new Morris.Line(options);
1722
1723                 else if(data.state.chart.chart_type == 'area') {
1724                         options.behaveLikeLine = true;
1725                         morris = new Morris.Area(options);
1726                 }
1727                 else // stacked
1728                         morris = new Morris.Area(options);
1729
1730                 data.state.morris_instance = morris;
1731                 data.state.morris_options = options;
1732         };
1733
1734         // ----------------------------------------------------------------------------------------------------------------
1735         // raphael
1736
1737         NETDATA.raphaelInitialize = function(callback) {
1738                 if(typeof netdataStopRaphael == 'undefined') {
1739                         $.getScript(NETDATA.raphael_js)
1740                                 .done(function() {
1741                                         NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
1742                                 })
1743                                 .fail(function() {
1744                                         NETDATA.error(100, NETDATA.raphael_js);
1745                                 })
1746                                 .always(function() {
1747                                         if(typeof callback == "function")
1748                                                 callback();
1749                                 })
1750                 }
1751                 else {
1752                         NETDATA.chartLibraries.raphael.enabled = false;
1753                         if(typeof callback == "function")
1754                                 callback();
1755                 }
1756         };
1757
1758         NETDATA.raphaelChartUpdate = function(element, data) {
1759                 var self = $(element);
1760
1761                 self.raphael(data, {
1762                         width: data.state.calculated_width,
1763                         height: data.state.calculated_height
1764                 })
1765         };
1766
1767         NETDATA.raphaelChartCreate = function(element, data) {
1768                 var self = $(element);
1769
1770                 self.raphael(data, {
1771                         width: data.state.calculated_width,
1772                         height: data.state.calculated_height
1773                 })
1774         };
1775
1776         // ----------------------------------------------------------------------------------------------------------------
1777         // google charts
1778
1779         NETDATA.googleInitialize = function(callback) {
1780                 if(typeof netdataStopGoogleCharts == 'undefined') {
1781                         $.getScript(NETDATA.google_js)
1782                                 .done(function() {
1783                                         NETDATA.registerChartLibrary('google', NETDATA.google_js);
1784
1785                                         google.load('visualization', '1.1', {
1786                                                 'packages': ['corechart', 'controls'],
1787                                                 'callback': callback
1788                                         });
1789                                 })
1790                                 .fail(function() {
1791                                         NETDATA.error(100, NETDATA.google_js);
1792                                         if(typeof callback == "function")
1793                                                 callback();
1794                                 })
1795                 }
1796                 else {
1797                         NETDATA.chartLibraries.google.enabled = false;
1798                         if(typeof callback == "function")
1799                                 callback();
1800                 }
1801         };
1802
1803         NETDATA.googleChartUpdate = function(element, data) {
1804                 var datatable = new google.visualization.DataTable(data.result);
1805                 data.state.google_instance.draw(datatable, data.state.google_options);
1806         };
1807
1808         NETDATA.googleChartCreate = function(element, data) {
1809                 var datatable = new google.visualization.DataTable(data.result);
1810
1811                 var options = {
1812                         // do not set width, height - the chart resizes itself
1813                         //width: data.state.calculated_width,
1814                         //height: data.state.calculated_height,
1815                         lineWidth: 1,
1816                         title: data.state.chart.title,
1817                         fontSize: 11,
1818                         hAxis: {
1819                         //      title: "Time of Day",
1820                         //      format:'HH:mm:ss',
1821                                 viewWindowMode: 'maximized',
1822                                 slantedText: false,
1823                                 format:'HH:mm:ss',
1824                                 textStyle: {
1825                                         fontSize: 9
1826                                 },
1827                                 gridlines: {
1828                                         color: '#EEE'
1829                                 }
1830                         },
1831                         vAxis: {
1832                                 title: data.state.chart.units,
1833                                 viewWindowMode: 'pretty',
1834                                 minValue: -0.1,
1835                                 maxValue: 0.1,
1836                                 direction: 1,
1837                                 textStyle: {
1838                                         fontSize: 9
1839                                 },
1840                                 gridlines: {
1841                                         color: '#EEE'
1842                                 }
1843                         },
1844                         chartArea: {
1845                                 width: '65%',
1846                                 height: '80%'
1847                         },
1848                         focusTarget: 'category',
1849                         annotation: {
1850                                 '1': {
1851                                         style: 'line'
1852                                 }
1853                         },
1854                         pointsVisible: 0,
1855                         titlePosition: 'out',
1856                         titleTextStyle: {
1857                                 fontSize: 11
1858                         },
1859                         tooltip: {
1860                                 isHtml: false,
1861                                 ignoreBounds: true,
1862                                 textStyle: {
1863                                         fontSize: 9
1864                                 }
1865                         },
1866                         curveType: 'function',
1867                         areaOpacity: 0.3,
1868                         isStacked: false
1869                 };
1870
1871                 element.innerHTML = '<div id="google-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>';
1872
1873                 var gchart;
1874                 switch(data.state.chart.chart_type) {
1875                         case "area":
1876                                 options.vAxis.viewWindowMode = 'maximized';
1877                                 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1878                                 break;
1879
1880                         case "stacked":
1881                                 options.isStacked = true;
1882                                 options.areaOpacity = 0.85;
1883                                 options.vAxis.viewWindowMode = 'maximized';
1884                                 options.vAxis.minValue = null;
1885                                 options.vAxis.maxValue = null;
1886                                 gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1887                                 break;
1888
1889                         default:
1890                         case "line":
1891                                 options.lineWidth = 2;
1892                                 gchart = new google.visualization.LineChart(document.getElementById('google-' + data.state.uuid));
1893                                 break;
1894                 }
1895
1896                 gchart.draw(datatable, options);
1897
1898                 data.state.google_instance = gchart;
1899                 data.state.google_options = options;
1900         };
1901
1902         // ----------------------------------------------------------------------------------------------------------------
1903         // Charts Libraries Registration
1904
1905         NETDATA.chartLibraries = {
1906                 "dygraph": {
1907                         initialize: NETDATA.dygraphInitialize,
1908                         create: NETDATA.dygraphChartCreate,
1909                         update: NETDATA.dygraphChartUpdate,
1910                         setSelection: NETDATA.dygraphSetSelection,
1911                         clearSelection:  NETDATA.dygraphClearSelection,
1912                         initialized: false,
1913                         enabled: true,
1914                         format: 'json',
1915                         options: 'ms|flip',
1916                         jsonWrapper: true,
1917                         pixels_per_point: 2,
1918                         detects_dimensions_on_update: false
1919                 },
1920                 "sparkline": {
1921                         initialize: NETDATA.sparklineInitialize,
1922                         create: NETDATA.sparklineChartCreate,
1923                         update: NETDATA.sparklineChartUpdate,
1924                         setSelection: null,
1925                         clearSelection: null,
1926                         initialized: false,
1927                         enabled: true,
1928                         format: 'array',
1929                         options: 'flip|abs',
1930                         jsonWrapper: true,
1931                         pixels_per_point: 2,
1932                         detects_dimensions_on_update: false
1933                 },
1934                 "peity": {
1935                         initialize: NETDATA.peityInitialize,
1936                         create: NETDATA.peityChartCreate,
1937                         update: NETDATA.peityChartUpdate,
1938                         setSelection: null,
1939                         clearSelection: null,
1940                         initialized: false,
1941                         enabled: true,
1942                         format: 'ssvcomma',
1943                         options: 'null2zero|flip|abs',
1944                         jsonWrapper: true,
1945                         pixels_per_point: 2,
1946                         detects_dimensions_on_update: false
1947                 },
1948                 "morris": {
1949                         initialize: NETDATA.morrisInitialize,
1950                         create: NETDATA.morrisChartCreate,
1951                         update: NETDATA.morrisChartUpdate,
1952                         setSelection: null,
1953                         clearSelection: null,
1954                         initialized: false,
1955                         enabled: true,
1956                         format: 'json',
1957                         options: 'objectrows|ms',
1958                         jsonWrapper: true,
1959                         pixels_per_point: 10,
1960                         detects_dimensions_on_update: false
1961                 },
1962                 "google": {
1963                         initialize: NETDATA.googleInitialize,
1964                         create: NETDATA.googleChartCreate,
1965                         update: NETDATA.googleChartUpdate,
1966                         setSelection: null,
1967                         clearSelection: null,
1968                         initialized: false,
1969                         enabled: true,
1970                         format: 'datatable',
1971                         options: '',
1972                         jsonWrapper: true,
1973                         pixels_per_point: 2,
1974                         detects_dimensions_on_update: true
1975                 },
1976                 "raphael": {
1977                         initialize: NETDATA.raphaelInitialize,
1978                         create: NETDATA.raphaelChartCreate,
1979                         update: NETDATA.raphaelChartUpdate,
1980                         setSelection: null,
1981                         clearSelection: null,
1982                         initialized: false,
1983                         enabled: true,
1984                         format: 'json',
1985                         options: '',
1986                         jsonWrapper: true,
1987                         pixels_per_point: 1,
1988                         detects_dimensions_on_update: false
1989                 }
1990         };
1991
1992         NETDATA.registerChartLibrary = function(library, url) {
1993                 console.log("registering chart library: " + library);
1994
1995                 NETDATA.chartLibraries[library].url = url;
1996                 NETDATA.chartLibraries[library].initialized = true;
1997                 NETDATA.chartLibraries[library].enabled = true;
1998
1999                 // console.log(NETDATA.chartLibraries);
2000         }
2001
2002         // ----------------------------------------------------------------------------------------------------------------
2003         // load all libraries and initialize
2004
2005         NETDATA.errorReset();
2006
2007         NETDATA._loadjQuery(function() {
2008                 $.getScript(NETDATA.serverDefault + 'lib/visible.js').then(function() {
2009                         NETDATA.init();
2010                 })
2011         });
2012
2013 })(window);