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