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