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