]> arthur.barton.de Git - netdata.git/blob - web/dashboard.js
Merge pull request #262 from ktsaou/master
[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 netdataNoEasyPieChart = true;    // do not use easy pie chart
9 // var netdataNoGauge = true;                   // do not use gauge.js
10 // var netdataNoD3 = true;                              // do not use D3
11 // var netdataNoC3 = true;                              // do not use C3
12 // var netdataNoBootstrap = true;               // do not load bootstrap
13 // var netdataDontStart = true;                 // do not start the thread to process the charts
14 //
15 // You can also set the default netdata server, using the following.
16 // When this variable is not set, we assume the page is hosted on your
17 // netdata server already.
18 // var netdataServer = "http://yourhost:19999"; // set your NetData server
19
20 //(function(window, document, undefined) {
21         // fix IE issue with console
22         if(!window.console){ window.console = {log: function(){} }; }
23
24         // global namespace
25         var NETDATA = window.NETDATA || {};
26
27         // ----------------------------------------------------------------------------------------------------------------
28         // Detect the netdata server
29
30         // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
31         // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
32         NETDATA._scriptSource = function() {
33                 var script = null;
34
35                 if(typeof document.currentScript !== 'undefined') {
36                         script = document.currentScript;
37                 }
38                 else {
39                         var all_scripts = document.getElementsByTagName('script');
40                         script = all_scripts[all_scripts.length - 1];
41                 }
42
43                 if (typeof script.getAttribute.length !== 'undefined')
44                         script = script.src;
45                 else
46                         script = script.getAttribute('src', -1);
47
48                 return script;
49         };
50
51         if(typeof netdataServer !== 'undefined')
52                 NETDATA.serverDefault = netdataServer;
53         else {
54                 var s = NETDATA._scriptSource();
55                 NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
56         }
57
58         if(NETDATA.serverDefault === null)
59                 NETDATA.serverDefault = '';
60         else if(NETDATA.serverDefault.slice(-1) !== '/')
61                 NETDATA.serverDefault += '/';
62
63         // default URLs for all the external files we need
64         // make them RELATIVE so that the whole thing can also be
65         // installed under a web server
66         NETDATA.jQuery                  = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
67         NETDATA.peity_js                = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
68         NETDATA.sparkline_js            = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
69         NETDATA.easypiechart_js         = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
70         NETDATA.gauge_js                        = NETDATA.serverDefault + 'lib/gauge.min.js';
71         NETDATA.dygraph_js              = NETDATA.serverDefault + 'lib/dygraph-combined.js';
72         NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
73         NETDATA.raphael_js              = NETDATA.serverDefault + 'lib/raphael-min.js';
74         NETDATA.morris_js               = NETDATA.serverDefault + 'lib/morris.min.js';
75         NETDATA.d3_js                           = NETDATA.serverDefault + 'lib/d3.min.js';
76         NETDATA.c3_js                           = NETDATA.serverDefault + 'lib/c3.min.js';
77         NETDATA.c3_css                          = NETDATA.serverDefault + 'css/c3.min.css';
78         NETDATA.morris_css              = NETDATA.serverDefault + 'css/morris.css';
79         NETDATA.google_js               = 'https://www.google.com/jsapi';
80
81         NETDATA.themes = {
82                 default: {
83                         bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
84                         dashboard_css: NETDATA.serverDefault + 'dashboard.css',
85                         background: '#FFFFFF',
86                         foreground: '#000000',
87                         grid: '#DDDDDD',
88                         axis: '#CCCCCC',
89                         colors: [       '#3366CC', '#DC3912',   '#109618', '#FF9900',   '#990099', '#DD4477',
90                                                 '#3B3EAC', '#66AA00',   '#0099C6', '#B82E2E',   '#AAAA11', '#5574A6',
91                                                 '#994499', '#22AA99',   '#6633CC', '#E67300',   '#316395', '#8B0707',
92                                                 '#329262', '#3B3EAC' ],
93                         easypiechart_track: '#f0f0f0',
94                         easypiechart_scale: '#dfe0e0',
95                         gauge_pointer: '#C0C0C0',
96                         gauge_stroke: '#F0F0F0',
97                         gauge_gradient: true
98                 },
99                 slate: {
100                         bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
101                         dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
102                         background: '#272b30',
103                         foreground: '#C8C8C8',
104                         grid: '#373b40',
105                         axis: '#373b40',
106 /*                      colors: [       '#55bb33', '#ff2222',   '#0099C6', '#faa11b',   '#adbce0', '#DDDD00',
107                                                 '#4178ba', '#f58122',   '#a5cc39', '#f58667',   '#f5ef89', '#cf93c0',
108                                                 '#a5d18a', '#b8539d',   '#3954a3', '#c8a9cf',   '#c7de8a', '#fad20a',
109                                                 '#a6a479', '#a66da8' ],
110 */
111                         colors: [       '#66AA00', '#FE3912',   '#3366CC', '#D66300',   '#0099C6', '#DDDD00',
112                                                 '#3B3EAC', '#EE9911',   '#BB44CC', '#C83E3E',   '#990099', '#CC7700',
113                                                 '#22AA99', '#109618',   '#6633CC', '#DD4477',   '#316395', '#8B0707',
114                                                 '#329262', '#3B3EFF' ],
115                         easypiechart_track: '#373b40',
116                         easypiechart_scale: '#373b40',
117                         gauge_pointer: '#474b50',
118                         gauge_stroke: '#373b40',
119                         gauge_gradient: false
120                 }
121         };
122
123         if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
124                 NETDATA.themes.current = NETDATA.themes[netdataTheme];
125         else
126                 NETDATA.themes.current = NETDATA.themes.default;
127
128         NETDATA.colors = NETDATA.themes.current.colors;
129
130         // these are the colors Google Charts are using
131         // we have them here to attempt emulate their look and feel on the other chart libraries
132         // http://there4.io/2012/05/02/google-chart-color-list/
133         //NETDATA.colors                = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
134         //                                              '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
135         //                                              '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
136
137         // an alternative set
138         // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
139         //                         (blue)     (red)      (orange)   (green)    (pink)     (brown)    (purple)   (yellow)   (gray)
140         //NETDATA.colors                = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
141
142         // ----------------------------------------------------------------------------------------------------------------
143         // the defaults for all charts
144
145         // if the user does not specify any of these, the following will be used
146
147         NETDATA.chartDefaults = {
148                 host: NETDATA.serverDefault,    // the server to get data from
149                 width: '100%',                                  // the chart width - can be null
150                 height: '100%',                                 // the chart height - can be null
151                 min_width: null,                                // the chart minimum width - can be null
152                 library: 'dygraph',                             // the graphing library to use
153                 method: 'average',                              // the grouping method
154                 before: 0,                                              // panning
155                 after: -600,                                    // panning
156                 pixels_per_point: 1,                    // the detail of the chart
157                 fill_luminance: 0.8                             // luminance of colors in solit areas
158         };
159
160         // ----------------------------------------------------------------------------------------------------------------
161         // global options
162
163         NETDATA.options = {
164                 pauseCallback: null,                    // a callback when we are really paused
165
166                 pause: false,                                   // when enabled we don't auto-refresh the charts
167
168                 targets: null,                                  // an array of all the state objects that are
169                                                                                 // currently active (independently of their
170                                                                                 // viewport visibility)
171
172                 updated_dom: true,                              // when true, the DOM has been updated with
173                                                                                 // new elements we have to check.
174
175                 auto_refresher_fast_weight: 0,  // this is the current time in ms, spent
176                                                                                 // rendering charts continiously.
177                                                                                 // used with .current.fast_render_timeframe
178
179                 page_is_visible: true,                  // when true, this page is visible
180
181                 auto_refresher_stop_until: 0,   // timestamp in ms - used internaly, to stop the
182                                                                                 // auto-refresher for some time (when a chart is
183                                                                                 // performing pan or zoom, we need to stop refreshing
184                                                                                 // all other charts, to have the maximum speed for
185                                                                                 // rendering the chart that is panned or zoomed).
186                                                                                 // Used with .current.global_pan_sync_time
187
188                 last_resized: new Date().getTime(), // the timestamp of the last resize request
189
190                 crossDomainAjax: false,                 // enable this to request crossDomain AJAX
191
192                 last_page_scroll: 0,                    // the timestamp the last time the page was scrolled
193
194                 // the current profile
195                 // we may have many...
196                 current: {
197                         pixels_per_point: 1,            // the minimum pixels per point for all charts
198                                                                                 // increase this to speed javascript up
199                                                                                 // each chart library has its own limit too
200                                                                                 // the max of this and the chart library is used
201                                                                                 // the final is calculated every time, so a change
202                                                                                 // here will have immediate effect on the next chart
203                                                                                 // update
204
205                         idle_between_charts: 100,       // ms - how much time to wait between chart updates
206
207                         fast_render_timeframe: 200, // ms - render continously until this time of continious
208                                                                                 // rendering has been reached
209                                                                                 // this setting is used to make it render e.g. 10
210                                                                                 // charts at once, sleep idle_between_charts time
211                                                                                 // and continue for another 10 charts.
212
213                         idle_between_loops: 500,        // ms - if all charts have been updated, wait this
214                                                                                 // time before starting again.
215
216                         idle_parallel_loops: 100,       // ms - the time between parallel refresher updates
217
218                         idle_lost_focus: 500,           // ms - when the window does not have focus, check
219                                                                                 // if focus has been regained, every this time
220
221                         global_pan_sync_time: 1000,     // ms - when you pan or zoon a chart, the background
222                                                                                 // autorefreshing of charts is paused for this amount
223                                                                                 // of time
224
225                         sync_selection_delay: 1500,     // ms - when you pan or zoom a chart, wait this amount
226                                                                                 // of time before setting up synchronized selections
227                                                                                 // on hover.
228
229                         sync_selection: true,           // enable or disable selection sync
230
231                         pan_and_zoom_delay: 50,         // when panning or zooming, how ofter to update the chart
232
233                         sync_pan_and_zoom: true,        // enable or disable pan and zoom sync
234
235                         pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
236
237                         update_only_visible: true,      // enable or disable visibility management
238
239                         parallel_refresher: true,       // enable parallel refresh of charts
240
241                         concurrent_refreshes: true,     // when parallel_refresher is enabled, sync also the charts
242
243                         destroy_on_hide: false,         // destroy charts when they are not visible
244
245                         show_help: true,                        // when enabled the charts will show some help
246                         show_help_delay_show_ms: 500,
247                         show_help_delay_hide_ms: 0,
248
249                         eliminate_zero_dimensions: true, // do not show dimensions with just zeros
250
251                         stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
252                         stop_updates_while_resizing: 1000,      // ms - time to stop auto-refreshes while resizing the charts
253
254                         double_click_speed: 500,        // ms - time between clicks / taps to detect double click/tap
255
256                         smooth_plot: true,                      // enable smooth plot, where possible
257
258                         charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
259
260                         color_fill_opacity_line: 1.0,
261                         color_fill_opacity_area: 0.2,
262                         color_fill_opacity_stacked: 0.8,
263
264                         pan_and_zoom_factor: 0.25,              // the increment when panning and zooming with the toolbox
265                         pan_and_zoom_factor_multiplier_control: 2.0,
266                         pan_and_zoom_factor_multiplier_shift: 3.0,
267                         pan_and_zoom_factor_multiplier_alt: 4.0,
268
269                         setOptionCallback: function() { ; }
270                 },
271
272                 debug: {
273                         show_boxes:             false,
274                         main_loop:                      false,
275                         focus:                          false,
276                         visibility:             false,
277                         chart_data_url:         false,
278                         chart_errors:           false, // FIXME
279                         chart_timing:           false,
280                         chart_calls:            false,
281                         libraries:                      false,
282                         dygraph:                        false
283                 }
284         };
285
286
287         // ----------------------------------------------------------------------------------------------------------------
288         // local storage options
289
290         NETDATA.localStorage = {
291                 default: {},
292                 current: {},
293                 callback: {} // only used for resetting back to defaults
294         };
295
296         NETDATA.localStorageGet = function(key, def, callback) {
297                 var ret = def;
298
299                 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
300                         NETDATA.localStorage.default[key.toString()] = def;
301                         NETDATA.localStorage.callback[key.toString()] = callback;
302                 }
303
304                 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
305                         try {
306                                 // console.log('localStorage: loading "' + key.toString() + '"');
307                                 ret = localStorage.getItem(key.toString());
308                                 if(ret === null || ret === 'undefined') {
309                                         // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
310                                         localStorage.setItem(key.toString(), JSON.stringify(def));
311                                         ret = def;
312                                 }
313                                 else {
314                                         // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
315                                         ret = JSON.parse(ret);
316                                         // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
317                                 }
318                         }
319                         catch(error) {
320                                 console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
321                                 ret = def;
322                         }
323                 }
324
325                 if(typeof ret === 'undefined' || ret === 'undefined') {
326                         console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
327                         ret = def;
328                 }
329
330                 NETDATA.localStorage.current[key.toString()] = ret;
331                 return ret;
332         };
333
334         NETDATA.localStorageSet = function(key, value, callback) {
335                 if(typeof value === 'undefined' || value === 'undefined') {
336                         console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
337                 }
338
339                 if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
340                         NETDATA.localStorage.default[key.toString()] = value;
341                         NETDATA.localStorage.current[key.toString()] = value;
342                         NETDATA.localStorage.callback[key.toString()] = callback;
343                 }
344
345                 if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
346                         // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
347                         try {
348                                 localStorage.setItem(key.toString(), JSON.stringify(value));
349                         }
350                         catch(e) {
351                                 console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
352                         }
353                 }
354
355                 NETDATA.localStorage.current[key.toString()] = value;
356                 return value;
357         };
358
359         NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
360                 for(var i in obj) {
361                         if(typeof obj[i] === 'object') {
362                                 //console.log('object ' + prefix + '.' + i.toString());
363                                 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
364                                 continue;
365                         }
366
367                         obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
368                 }
369         };
370
371         NETDATA.setOption = function(key, value) {
372                 if(key.toString() === 'setOptionCallback') {
373                         if(typeof NETDATA.options.current.setOptionCallback === 'function') {
374                                 NETDATA.options.current[key.toString()] = value;
375                                 NETDATA.options.current.setOptionCallback();
376                         }
377                 }
378                 else if(NETDATA.options.current[key.toString()] !== value) {
379                         var name = 'options.' + key.toString();
380
381                         if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
382                                 console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
383
384                         //console.log(NETDATA.localStorage);
385                         //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
386                         //console.log(NETDATA.options);
387                         NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
388
389                         if(typeof NETDATA.options.current.setOptionCallback === 'function')
390                                 NETDATA.options.current.setOptionCallback();
391                 }
392
393                 return true;
394         };
395
396         NETDATA.getOption = function(key) {
397                 return NETDATA.options.current[key.toString()];
398         };
399
400         // read settings from local storage
401         NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
402
403         // always start with this option enabled.
404         NETDATA.setOption('stop_updates_when_focus_is_lost', true);
405
406         NETDATA.resetOptions = function() {
407                 for(var i in NETDATA.localStorage.default) {
408                         var a = i.split('.');
409
410                         if(a[0] === 'options') {
411                                 if(a[1] === 'setOptionCallback') continue;
412                                 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
413                                 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
414
415                                 NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
416                         }
417                         else if(a[0] === 'chart_heights') {
418                                 if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
419                                         NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
420                                 }
421                         }
422                 }
423         }
424
425         // ----------------------------------------------------------------------------------------------------------------
426
427         if(NETDATA.options.debug.main_loop === true)
428                 console.log('welcome to NETDATA');
429
430         NETDATA.onresize = function() {
431                 NETDATA.options.last_resized = new Date().getTime();
432                 NETDATA.onscroll();
433         };
434
435         NETDATA.onscroll = function() {
436                 // console.log('onscroll');
437
438                 NETDATA.options.last_page_scroll = new Date().getTime();
439                 if(NETDATA.options.targets === null) return;
440
441                 // when the user scrolls he sees that we have
442                 // hidden all the not-visible charts
443                 // using this little function we try to switch
444                 // the charts back to visible quickly
445                 var targets = NETDATA.options.targets;
446                 var len = targets.length;
447                 while(len--) targets[len].isVisible();
448         };
449
450         window.onresize = NETDATA.onresize;
451         window.onscroll = NETDATA.onscroll;
452
453         // ----------------------------------------------------------------------------------------------------------------
454         // Error Handling
455
456         NETDATA.errorCodes = {
457                 100: { message: "Cannot load chart library", alert: true },
458                 101: { message: "Cannot load jQuery", alert: true },
459                 402: { message: "Chart library not found", alert: false },
460                 403: { message: "Chart library not enabled/is failed", alert: false },
461                 404: { message: "Chart not found", alert: false }
462         };
463         NETDATA.errorLast = {
464                 code: 0,
465                 message: "",
466                 datetime: 0
467         };
468
469         NETDATA.error = function(code, msg) {
470                 NETDATA.errorLast.code = code;
471                 NETDATA.errorLast.message = msg;
472                 NETDATA.errorLast.datetime = new Date().getTime();
473
474                 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
475
476                 if(NETDATA.errorCodes[code].alert)
477                         alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
478         };
479
480         NETDATA.errorReset = function() {
481                 NETDATA.errorLast.code = 0;
482                 NETDATA.errorLast.message = "You are doing fine!";
483                 NETDATA.errorLast.datetime = 0;
484         };
485
486         // ----------------------------------------------------------------------------------------------------------------
487         // Chart Registry
488
489         // When multiple charts need the same chart, we avoid downloading it
490         // multiple times (and having it in browser memory multiple time)
491         // by using this registry.
492
493         // Every time we download a chart definition, we save it here with .add()
494         // Then we try to get it back with .get(). If that fails, we download it.
495
496         NETDATA.chartRegistry = {
497                 charts: {},
498
499                 fixid: function(id) {
500                         return id.replace(/:/g, "_").replace(/\//g, "_");
501                 },
502
503                 add: function(host, id, data) {
504                         host = this.fixid(host);
505                         id   = this.fixid(id);
506
507                         if(typeof this.charts[host] === 'undefined')
508                                 this.charts[host] = {};
509
510                         //console.log('added ' + host + '/' + id);
511                         this.charts[host][id] = data;
512                 },
513
514                 get: function(host, id) {
515                         host = this.fixid(host);
516                         id   = this.fixid(id);
517
518                         if(typeof this.charts[host] === 'undefined')
519                                 return null;
520
521                         if(typeof this.charts[host][id] === 'undefined')
522                                 return null;
523
524                         //console.log('cached ' + host + '/' + id);
525                         return this.charts[host][id];
526                 },
527
528                 downloadAll: function(host, callback) {
529                         while(host.slice(-1) === '/')
530                                 host = host.substring(0, host.length - 1);
531
532                         var self = this;
533
534                         $.ajax({
535                                 url: host + '/api/v1/charts',
536                                 crossDomain: NETDATA.options.crossDomainAjax,
537                                 async: true,
538                                 cache: false
539                         })
540                         .done(function(data) {
541                                 if(data !== null) {
542                                         var h = NETDATA.chartRegistry.fixid(host);
543                                         //console.log('downloaded all charts from ' + host + ' (' + h + ')');
544                                         self.charts[h] = data.charts;
545                                 }
546                                 if(typeof callback === 'function')
547                                         callback(data);
548                         })
549                         .fail(function() {
550                                 if(typeof callback === 'function')
551                                         callback(null);
552                         });
553                 }
554         };
555
556         // ----------------------------------------------------------------------------------------------------------------
557         // Global Pan and Zoom on charts
558
559         // Using this structure are synchronize all the charts, so that
560         // when you pan or zoom one, all others are automatically refreshed
561         // to the same timespan.
562
563         NETDATA.globalPanAndZoom = {
564                 seq: 0,                                 // timestamp ms
565                                                                 // every time a chart is panned or zoomed
566                                                                 // we set the timestamp here
567                                                                 // then we use it as a sequence number
568                                                                 // to find if other charts are syncronized
569                                                                 // to this timerange
570
571                 master: null,                   // the master chart (state), to which all others
572                                                                 // are synchronized
573
574                 force_before_ms: null,  // the timespan to sync all other charts
575                 force_after_ms: null,
576
577                 // set a new master
578                 setMaster: function(state, after, before) {
579                         if(NETDATA.options.current.sync_pan_and_zoom === false)
580                                 return;
581
582                         if(this.master !== null && this.master !== state)
583                                 this.master.resetChart(true, true);
584
585                         var now = new Date().getTime();
586                         this.master = state;
587                         this.seq = now;
588                         this.force_after_ms = after;
589                         this.force_before_ms = before;
590                         NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
591                 },
592
593                 // clear the master
594                 clearMaster: function() {
595                         if(this.master !== null) {
596                                 var st = this.master;
597                                 this.master = null;
598                                 st.resetChart();
599                         }
600
601                         this.master = null;
602                         this.seq = 0;
603                         this.force_after_ms = null;
604                         this.force_before_ms = null;
605                         NETDATA.options.auto_refresher_stop_until = 0;
606                 },
607
608                 // is the given state the master of the global
609                 // pan and zoom sync?
610                 isMaster: function(state) {
611                         if(this.master === state) return true;
612                         return false;
613                 },
614
615                 // are we currently have a global pan and zoom sync?
616                 isActive: function() {
617                         if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
618                         return false;
619                 },
620
621                 // check if a chart, other than the master
622                 // needs to be refreshed, due to the global pan and zoom
623                 shouldBeAutoRefreshed: function(state) {
624                         if(this.master === null || this.seq === 0)
625                                 return false;
626
627                         //if(state.needsRecreation())
628                         //      return true;
629
630                         if(state.tm.pan_and_zoom_seq === this.seq)
631                                 return false;
632
633                         return true;
634                 }
635         };
636
637         // ----------------------------------------------------------------------------------------------------------------
638         // dimensions selection
639
640         // FIXME
641         // move color assignment to dimensions, here
642
643         dimensionStatus = function(parent, label, name_div, value_div, color) {
644                 this.enabled = false;
645                 this.parent = parent;
646                 this.label = label;
647                 this.name_div = null;
648                 this.value_div = null;
649                 this.color = NETDATA.themes.current.foreground;
650
651                 if(parent.selected_count > parent.unselected_count)
652                         this.selected = true;
653                 else
654                         this.selected = false;
655
656                 this.setOptions(name_div, value_div, color);
657         };
658
659         dimensionStatus.prototype.invalidate = function() {
660                 this.name_div = null;
661                 this.value_div = null;
662                 this.enabled = false;
663         };
664
665         dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
666                 this.color = color;
667
668                 if(this.name_div != name_div) {
669                         this.name_div = name_div;
670                         this.name_div.title = this.label;
671                         this.name_div.style.color = this.color;
672                         if(this.selected === false)
673                                 this.name_div.className = 'netdata-legend-name not-selected';
674                         else
675                                 this.name_div.className = 'netdata-legend-name selected';
676                 }
677
678                 if(this.value_div != value_div) {
679                         this.value_div = value_div;
680                         this.value_div.title = this.label;
681                         this.value_div.style.color = this.color;
682                         if(this.selected === false)
683                                 this.value_div.className = 'netdata-legend-value not-selected';
684                         else
685                                 this.value_div.className = 'netdata-legend-value selected';
686                 }
687
688                 this.enabled = true;
689                 this.setHandler();
690         };
691
692         dimensionStatus.prototype.setHandler = function() {
693                 if(this.enabled === false) return;
694
695                 var ds = this;
696
697                 // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
698                 this.name_div.onclick = this.value_div.onclick = function(e) {
699                         e.preventDefault();
700                         if(ds.isSelected()) {
701                                 // this is selected
702                                 if(e.shiftKey === true || e.ctrlKey === true) {
703                                         // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
704                                         ds.unselect();
705
706                                         if(ds.parent.countSelected() === 0)
707                                                 ds.parent.selectAll();
708                                 }
709                                 else {
710                                         // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
711                                         if(ds.parent.countSelected() === 1) {
712                                                 ds.parent.selectAll();
713                                         }
714                                         else {
715                                                 ds.parent.selectNone();
716                                                 ds.select();
717                                         }
718                                 }
719                         }
720                         else {
721                                 // this is not selected
722                                 if(e.shiftKey === true || e.ctrlKey === true) {
723                                         // control or shift key is pressed -> select this too
724                                         ds.select();
725                                 }
726                                 else {
727                                         // no key is pressed -> select only this
728                                         ds.parent.selectNone();
729                                         ds.select();
730                                 }
731                         }
732
733                         ds.parent.state.redrawChart();
734                 }
735         };
736
737         dimensionStatus.prototype.select = function() {
738                 if(this.enabled === false) return;
739
740                 this.name_div.className = 'netdata-legend-name selected';
741                 this.value_div.className = 'netdata-legend-value selected';
742                 this.selected = true;
743         };
744
745         dimensionStatus.prototype.unselect = function() {
746                 if(this.enabled === false) return;
747
748                 this.name_div.className = 'netdata-legend-name not-selected';
749                 this.value_div.className = 'netdata-legend-value hidden';
750                 this.selected = false;
751         };
752
753         dimensionStatus.prototype.isSelected = function() {
754                 return(this.enabled === true && this.selected === true);
755         };
756
757         // ----------------------------------------------------------------------------------------------------------------
758
759         dimensionsVisibility = function(state) {
760                 this.state = state;
761                 this.len = 0;
762                 this.dimensions = {};
763                 this.selected_count = 0;
764                 this.unselected_count = 0;
765         };
766
767         dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
768                 if(typeof this.dimensions[label] === 'undefined') {
769                         this.len++;
770                         this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
771                 }
772                 else
773                         this.dimensions[label].setOptions(name_div, value_div, color);
774
775                 return this.dimensions[label];
776         };
777
778         dimensionsVisibility.prototype.dimensionGet = function(label) {
779                 return this.dimensions[label];
780         };
781
782         dimensionsVisibility.prototype.invalidateAll = function() {
783                 for(var d in this.dimensions)
784                         this.dimensions[d].invalidate();
785         };
786
787         dimensionsVisibility.prototype.selectAll = function() {
788                 for(var d in this.dimensions)
789                         this.dimensions[d].select();
790         };
791
792         dimensionsVisibility.prototype.countSelected = function() {
793                 var i = 0;
794                 for(var d in this.dimensions)
795                         if(this.dimensions[d].isSelected()) i++;
796
797                 return i;
798         };
799
800         dimensionsVisibility.prototype.selectNone = function() {
801                 for(var d in this.dimensions)
802                         this.dimensions[d].unselect();
803         };
804
805         dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
806                 var ret = new Array();
807                 this.selected_count = 0;
808                 this.unselected_count = 0;
809
810                 for(var i = 0, len = array.length; i < len ; i++) {
811                         var ds = this.dimensions[array[i]];
812                         if(typeof ds === 'undefined') {
813                                 // console.log(array[i] + ' is not found');
814                                 ret.push(false);
815                                 continue;
816                         }
817
818                         if(ds.isSelected()) {
819                                 ret.push(true);
820                                 this.selected_count++;
821                         }
822                         else {
823                                 ret.push(false);
824                                 this.unselected_count++;
825                         }
826                 }
827
828                 if(this.selected_count === 0 && this.unselected_count !== 0) {
829                         this.selectAll();
830                         return this.selected2BooleanArray(array);
831                 }
832
833                 return ret;
834         };
835
836
837         // ----------------------------------------------------------------------------------------------------------------
838         // global selection sync
839
840         NETDATA.globalSelectionSync = {
841                 state: null,
842                 dont_sync_before: 0,
843                 last_t: 0,
844                 slaves: [],
845
846                 stop: function() {
847                         if(this.state !== null)
848                                 this.state.globalSelectionSyncStop();
849                 },
850
851                 delay: function() {
852                         if(this.state !== null) {
853                                 this.state.globalSelectionSyncDelay();
854                         }
855                 }
856         };
857
858         // ----------------------------------------------------------------------------------------------------------------
859         // Our state object, where all per-chart values are stored
860
861         chartState = function(element) {
862                 var self = $(element);
863                 this.element = element;
864
865                 // IMPORTANT:
866                 // all private functions should use 'that', instead of 'this'
867                 var that = this;
868
869                 /* error() - private
870                  * show an error instead of the chart
871                  */
872                 var error = function(msg) {
873                         that.element.innerHTML = that.id + ': ' + msg;
874                         that.enabled = false;
875                         that.current = that.pan;
876                 };
877
878                 // GUID - a unique identifier for the chart
879                 this.uuid = NETDATA.guid();
880
881                 // string - the name of chart
882                 this.id = self.data('netdata');
883
884                 // string - the key for localStorage settings
885                 this.settings_id = self.data('id') || null;
886
887                 // the user given dimensions of the element
888                 this.width = self.data('width') || NETDATA.chartDefaults.width;
889                 this.height = self.data('height') || NETDATA.chartDefaults.height;
890
891                 if(this.settings_id !== null) {
892                         this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
893                                 // this is the callback that will be called
894                                 // if and when the user resets all localStorage variables
895                                 // to their defaults
896
897                                 resizeChartToHeight(height);
898                         });
899                 }
900
901                 // string - the netdata server URL, without any path
902                 this.host = self.data('host') || NETDATA.chartDefaults.host;
903
904                 // make sure the host does not end with /
905                 // all netdata API requests use absolute paths
906                 while(this.host.slice(-1) === '/')
907                         this.host = this.host.substring(0, this.host.length - 1);
908
909                 // string - the grouping method requested by the user
910                 this.method = self.data('method') || NETDATA.chartDefaults.method;
911
912                 // the time-range requested by the user
913                 this.after = self.data('after') || NETDATA.chartDefaults.after;
914                 this.before = self.data('before') || NETDATA.chartDefaults.before;
915
916                 // the pixels per point requested by the user
917                 this.pixels_per_point = self.data('pixels-per-point') || 1;
918                 this.points = self.data('points') || null;
919
920                 // the dimensions requested by the user
921                 this.dimensions = self.data('dimensions') || null;
922
923                 // the chart library requested by the user
924                 this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
925
926                 // object - the chart library used
927                 this.library = null;
928
929                 // color management
930                 this.colors = null;
931                 this.colors_assigned = {};
932                 this.colors_available = null;
933
934                 // the element already created by the user
935                 this.element_message = null;
936
937                 // the element with the chart
938                 this.element_chart = null;
939
940                 // the element with the legend of the chart (if created by us)
941                 this.element_legend = null;
942                 this.element_legend_childs = {
943                         hidden: null,
944                         title_date: null,
945                         title_time: null,
946                         title_units: null,
947                         nano: null,
948                         nano_options: null,
949                         series: null
950                 };
951
952                 this.chart_url = null;                                          // string - the url to download chart info
953                 this.chart = null;                                                      // object - the chart as downloaded from the server
954
955                 this.title = self.data('title') || null;        // the title of the chart
956                 this.units = self.data('units') || null;        // the units of the chart dimensions
957                 this.append_options = self.data('append-options') || null;      // the units of the chart dimensions
958
959                 this.validated = false;                                         // boolean - has the chart been validated?
960                 this.enabled = true;                                            // boolean - is the chart enabled for refresh?
961                 this.paused = false;                                            // boolean - is the chart paused for any reason?
962                 this.selected = false;                                          // boolean - is the chart shown a selection?
963                 this.debug = false;                                                     // boolean - console.log() debug info about this chart
964
965                 this.netdata_first = 0;                                         // milliseconds - the first timestamp in netdata
966                 this.netdata_last = 0;                                          // milliseconds - the last timestamp in netdata
967                 this.requested_after = null;                            // milliseconds - the timestamp of the request after param
968                 this.requested_before = null;                           // milliseconds - the timestamp of the request before param
969                 this.requested_padding = null;
970                 this.view_after = 0;
971                 this.view_before = 0;
972
973                 this.auto = {
974                         name: 'auto',
975                         autorefresh: true,
976                         force_update_at: 0, // the timestamp to force the update at
977                         force_before_ms: null,
978                         force_after_ms: null
979                 };
980                 this.pan = {
981                         name: 'pan',
982                         autorefresh: false,
983                         force_update_at: 0, // the timestamp to force the update at
984                         force_before_ms: null,
985                         force_after_ms: null
986                 };
987                 this.zoom = {
988                         name: 'zoom',
989                         autorefresh: false,
990                         force_update_at: 0, // the timestamp to force the update at
991                         force_before_ms: null,
992                         force_after_ms: null
993                 };
994
995                 // this is a pointer to one of the sub-classes below
996                 // auto, pan, zoom
997                 this.current = this.auto;
998
999                 // check the requested library is available
1000                 // we don't initialize it here - it will be initialized when
1001                 // this chart will be first used
1002                 if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
1003                         NETDATA.error(402, that.library_name);
1004                         error('chart library "' + that.library_name + '" is not found');
1005                         return;
1006                 }
1007                 else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
1008                         NETDATA.error(403, that.library_name);
1009                         error('chart library "' + that.library_name + '" is not enabled');
1010                         return;
1011                 }
1012                 else
1013                         that.library = NETDATA.chartLibraries[that.library_name];
1014
1015                 // milliseconds - the time the last refresh took
1016                 this.refresh_dt_ms = 0;
1017
1018                 // if we need to report the rendering speed
1019                 // find the element that needs to be updated
1020                 var refresh_dt_element_name = self.data('dt-element-name') || null;     // string - the element to print refresh_dt_ms
1021
1022                 if(refresh_dt_element_name !== null)
1023                         this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
1024                 else
1025                         this.refresh_dt_element = null;
1026
1027                 this.dimensions_visibility = new dimensionsVisibility(this);
1028
1029                 this._updating = false;
1030
1031                 // ============================================================================================================
1032                 // PRIVATE FUNCTIONS
1033
1034                 var createDOM = function() {
1035                         if(that.enabled === false) return;
1036
1037                         if(that.element_message !== null) that.element_message.innerHTML = '';
1038                         if(that.element_legend !== null) that.element_legend.innerHTML = '';
1039                         if(that.element_chart !== null) that.element_chart.innerHTML = '';
1040
1041                         that.element.innerHTML = '';
1042
1043                         that.element_message = document.createElement('div');
1044                         that.element_message.className = ' netdata-message hidden';
1045                         that.element.appendChild(that.element_message);
1046
1047                         that.element_chart = document.createElement('div');
1048                         that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
1049                         that.element.appendChild(that.element_chart);
1050
1051                         if(that.hasLegend() === true) {
1052                                 that.element.className = "netdata-container-with-legend";
1053                                 that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
1054
1055                                 that.element_legend = document.createElement('div');
1056                                 that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
1057                                 that.element.appendChild(that.element_legend);
1058                         }
1059                         else {
1060                                 that.element.className = "netdata-container";
1061                                 that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
1062
1063                                 that.element_legend = null;
1064                         }
1065                         that.element_legend_childs.series = null;
1066
1067                         if(typeof(that.width) === 'string')
1068                                 $(that.element).css('width', that.width);
1069                         else if(typeof(that.width) === 'number')
1070                                 $(that.element).css('width', that.width + 'px');
1071
1072                         if(typeof(that.library.aspect_ratio) === 'undefined') {
1073                                 if(typeof(that.height) === 'string')
1074                                         $(that.element).css('height', that.height);
1075                                 else if(typeof(that.height) === 'number')
1076                                         $(that.element).css('height', that.height + 'px');
1077                         }
1078                         else {
1079                                 var w = that.element.offsetWidth;
1080                                 if(w === null || w === 0) {
1081                                         // the div is hidden
1082                                         // this is resize the chart when next viewed
1083                                         that.tm.last_resized = 0;
1084                                 }
1085                                 else
1086                                         $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
1087                         }
1088
1089                         if(NETDATA.chartDefaults.min_width !== null)
1090                                 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1091
1092                         that.tm.last_dom_created = new Date().getTime();
1093
1094                         showLoading();
1095                 };
1096
1097                 /* init() private
1098                  * initialize state variables
1099                  * destroy all (possibly) created state elements
1100                  * create the basic DOM for a chart
1101                  */
1102                 var init = function() {
1103                         if(that.enabled === false) return;
1104
1105                         that.paused = false;
1106                         that.selected = false;
1107
1108                         that.chart_created = false;                     // boolean - is the library.create() been called?
1109                         that.updates_counter = 0;                       // numeric - the number of refreshes made so far
1110                         that.updates_since_last_unhide = 0;     // numeric - the number of refreshes made since the last time the chart was unhidden
1111                         that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
1112
1113                         that.tm = {
1114                                 last_initialized: 0,            // milliseconds - the timestamp it was last initialized
1115                                 last_dom_created: 0,            // milliseconds - the timestamp its DOM was last created
1116                                 last_mode_switch: 0,            // milliseconds - the timestamp it switched modes
1117
1118                                 last_info_downloaded: 0,        // milliseconds - the timestamp we downloaded the chart
1119                                 last_updated: 0,                        // the timestamp the chart last updated with data
1120                                 pan_and_zoom_seq: 0,            // the sequence number of the global synchronization
1121                                                                                         // between chart.
1122                                                                                         // Used with NETDATA.globalPanAndZoom.seq
1123                                 last_visible_check: 0,          // the time we last checked if it is visible
1124                                 last_resized: 0,                        // the time the chart was resized
1125                                 last_hidden: 0,                         // the time the chart was hidden
1126                                 last_unhidden: 0,                       // the time the chart was unhidden
1127                                 last_autorefreshed: 0           // the time the chart was last refreshed
1128                         };
1129
1130                         that.data = null;                               // the last data as downloaded from the netdata server
1131                         that.data_url = 'invalid://';   // string - the last url used to update the chart
1132                         that.data_points = 0;                   // number - the number of points returned from netdata
1133                         that.data_after = 0;                    // milliseconds - the first timestamp of the data
1134                         that.data_before = 0;                   // milliseconds - the last timestamp of the data
1135                         that.data_update_every = 0;             // milliseconds - the frequency to update the data
1136
1137                         that.tm.last_initialized = new Date().getTime();
1138                         createDOM();
1139
1140                         that.setMode('auto');
1141                 };
1142
1143                 var maxMessageFontSize = function() {
1144                         // normally we want a font size, as tall as the element
1145                         var h = that.element_message.clientHeight;
1146
1147                         // but give it some air, 20% let's say, or 5 pixels min
1148                         var lost = Math.max(h * 0.2, 5);
1149                         h -= lost;
1150
1151                         // center the text, verically
1152                         var paddingTop = (lost - 5) / 2;
1153
1154                         // but check the width too
1155                         // it should fit 10 characters in it
1156                         var w = that.element_message.clientWidth / 10;
1157                         if(h > w) {
1158                                 paddingTop += (h - w) / 2;
1159                                 h = w;
1160                         }
1161
1162                         // and don't make it too huge
1163                         // 5% of the screen size is good
1164                         if(h > screen.height / 20) {
1165                                 paddingTop += (h - (screen.height / 20)) / 2;
1166                                 h = screen.height / 20;
1167                         }
1168
1169                         // set it
1170                         that.element_message.style.fontSize = h.toString() + 'px';
1171                         that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1172                 };
1173
1174                 var showMessage = function(msg) {
1175                         that.element_message.className = 'netdata-message';
1176                         that.element_message.innerHTML = msg;
1177                         that.element_message.style.fontSize = 'x-small';
1178                         that.element_message.style.paddingTop = '0px';
1179                         that.___messageHidden___ = undefined;
1180                 };
1181
1182                 var showMessageIcon = function(icon) {
1183                         that.element_message.innerHTML = icon;
1184                         that.element_message.className = 'netdata-message icon';
1185                         maxMessageFontSize();
1186                         that.___messageHidden___ = undefined;
1187                 };
1188
1189                 var hideMessage = function() {
1190                         if(typeof that.___messageHidden___ === 'undefined') {
1191                                 that.___messageHidden___ = true;
1192                                 that.element_message.className = 'netdata-message hidden';
1193                         }
1194                 };
1195
1196                 var showRendering = function() {
1197                         var icon;
1198                         if(that.chart !== null) {
1199                                 if(that.chart.chart_type === 'line')
1200                                         icon = '<i class="fa fa-line-chart"></i>';
1201                                 else
1202                                         icon = '<i class="fa fa-area-chart"></i>';
1203                         }
1204                         else
1205                                 icon = '<i class="fa fa-area-chart"></i>';
1206
1207                         showMessageIcon(icon + ' netdata');
1208                 };
1209
1210                 var showLoading = function() {
1211                         if(that.chart_created === false) {
1212                                 showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1213                                 return true;
1214                         }
1215                         return false;
1216                 };
1217
1218                 var isHidden = function() {
1219                         if(typeof that.___chartIsHidden___ !== 'undefined')
1220                                 return true;
1221
1222                         return false;
1223                 };
1224
1225                 // hide the chart, when it is not visible - called from isVisible()
1226                 var hideChart = function() {
1227                         // hide it, if it is not already hidden
1228                         if(isHidden() === true) return;
1229
1230                         if(that.chart_created === true) {
1231                                 // we should destroy it
1232                                 if(NETDATA.options.current.destroy_on_hide === true) {
1233                                         init();
1234                                 }
1235                                 else {
1236                                         showRendering();
1237                                         that.element_chart.style.display = 'none';
1238                                         if(that.element_legend !== null) that.element_legend.style.display = 'none';
1239                                         that.tm.last_hidden = new Date().getTime();
1240                                 }
1241                         }
1242
1243                         that.___chartIsHidden___ = true;
1244                 };
1245
1246                 // unhide the chart, when it is visible - called from isVisible()
1247                 var unhideChart = function() {
1248                         if(isHidden() === false) return;
1249
1250                         that.___chartIsHidden___ = undefined;
1251                         that.updates_since_last_unhide = 0;
1252
1253                         if(that.chart_created === false) {
1254                                 // we need to re-initialize it, to show our background
1255                                 // logo in bootstrap tabs, until the chart loads
1256                                 init();
1257                         }
1258                         else {
1259                                 that.tm.last_unhidden = new Date().getTime();
1260                                 that.element_chart.style.display = '';
1261                                 if(that.element_legend !== null) that.element_legend.style.display = '';
1262                                 resizeChart();
1263                                 hideMessage();
1264                         }
1265                 };
1266
1267                 var canBeRendered = function() {
1268                         if(isHidden() === true || that.isVisible(true) === false)
1269                                 return false;
1270
1271                         return true;
1272                 };
1273
1274                 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1275                 var callChartLibraryUpdateSafely = function(data) {
1276                         var status;
1277
1278                         if(canBeRendered() === false)
1279                                 return false;
1280
1281                         if(NETDATA.options.debug.chart_errors === true)
1282                                 status = that.library.update(that, data);
1283                         else {
1284                                 try {
1285                                         status = that.library.update(that, data);
1286                                 }
1287                                 catch(err) {
1288                                         status = false;
1289                                 }
1290                         }
1291
1292                         if(status === false) {
1293                                 error('chart failed to be updated as ' + that.library_name);
1294                                 return false;
1295                         }
1296
1297                         return true;
1298                 };
1299
1300                 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
1301                 var callChartLibraryCreateSafely = function(data) {
1302                         var status;
1303
1304                         if(canBeRendered() === false)
1305                                 return false;
1306
1307                         if(NETDATA.options.debug.chart_errors === true)
1308                                 status = that.library.create(that, data);
1309                         else {
1310                                 try {
1311                                         status = that.library.create(that, data);
1312                                 }
1313                                 catch(err) {
1314                                         status = false;
1315                                 }
1316                         }
1317
1318                         if(status === false) {
1319                                 error('chart failed to be created as ' + that.library_name);
1320                                 return false;
1321                         }
1322
1323                         that.chart_created = true;
1324                         that.updates_since_last_creation = 0;
1325                         return true;
1326                 };
1327
1328                 // ----------------------------------------------------------------------------------------------------------------
1329                 // Chart Resize
1330
1331                 // resizeChart() - private
1332                 // to be called just before the chart library to make sure that
1333                 // a properly sized dom is available
1334                 var resizeChart = function() {
1335                         if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1336                                 if(that.chart_created === false) return;
1337
1338                                 if(that.needsRecreation()) {
1339                                         init();
1340                                 }
1341                                 else if(typeof that.library.resize === 'function') {
1342                                         that.library.resize(that);
1343
1344                                         if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1345                                                 $(that.element_legend_childs.nano).nanoScroller();
1346
1347                                         maxMessageFontSize();
1348                                 }
1349
1350                                 that.tm.last_resized = new Date().getTime();
1351                         }
1352                 };
1353
1354                 // this is the actual chart resize algorithm
1355                 // it will:
1356                 // - resize the entire container
1357                 // - update the internal states
1358                 // - resize the chart as the div changes height
1359                 // - update the scrollbar of the legend
1360                 var resizeChartToHeight = function(h) {
1361                         // console.log(h);
1362                         that.element.style.height = h;
1363
1364                         if(that.settings_id !== null)
1365                                 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1366
1367                         var now = new Date().getTime();
1368                         NETDATA.options.last_page_scroll = now;
1369                         NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1370
1371                         // force a resize
1372                         that.tm.last_resized = 0;
1373                         resizeChart();
1374                 };
1375
1376                 this.resizeHandler = function(e) {
1377                         e.preventDefault();
1378
1379                         if(typeof this.event_resize === 'undefined'
1380                                 || this.event_resize.chart_original_w === 'undefined'
1381                                 || this.event_resize.chart_original_h === 'undefined')
1382                                 this.event_resize = {
1383                                         chart_original_w: this.element.clientWidth,
1384                                         chart_original_h: this.element.clientHeight,
1385                                         last: 0
1386                                 };
1387
1388                         if(e.type === 'touchstart') {
1389                                 this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1390                                 this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1391                         }
1392                         else {
1393                                 this.event_resize.mouse_start_x = e.clientX;
1394                                 this.event_resize.mouse_start_y = e.clientY;
1395                         }
1396
1397                         this.event_resize.chart_start_w = this.element.clientWidth;
1398                         this.event_resize.chart_start_h = this.element.clientHeight;
1399                         this.event_resize.chart_last_w = this.element.clientWidth;
1400                         this.event_resize.chart_last_h = this.element.clientHeight;
1401
1402                         var now = new Date().getTime();
1403                         if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1404                                 // double click / double tap event
1405
1406                                 // the optimal height of the chart
1407                                 // showing the entire legend
1408                                 var optimal = this.event_resize.chart_last_h
1409                                                 + this.element_legend_childs.content.scrollHeight
1410                                                 - this.element_legend_childs.content.clientHeight;
1411
1412                                 // if we are not optimal, be optimal
1413                                 if(this.event_resize.chart_last_h != optimal)
1414                                         resizeChartToHeight(optimal.toString() + 'px');
1415
1416                                 // else if we do not have the original height
1417                                 // reset to the original height
1418                                 else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1419                                         resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1420                         }
1421                         else {
1422                                 this.event_resize.last = now;
1423
1424                                 // process movement event
1425                                 document.onmousemove =
1426                                 document.ontouchmove =
1427                                 this.element_legend_childs.resize_handler.onmousemove =
1428                                 this.element_legend_childs.resize_handler.ontouchmove =
1429                                         function(e) {
1430                                                 var y = null;
1431
1432                                                 switch(e.type) {
1433                                                         case 'mousemove': y = e.clientY; break;
1434                                                         case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1435                                                 }
1436
1437                                                 if(y !== null) {
1438                                                         var     newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1439
1440                                                         if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1441                                                                 resizeChartToHeight(newH.toString() + 'px');
1442                                                                 that.event_resize.chart_last_h = newH;
1443                                                         }
1444                                                 }
1445                                         };
1446
1447                                 // process end event
1448                                 document.onmouseup =
1449                                 document.ontouchend =
1450                                 this.element_legend_childs.resize_handler.onmouseup =
1451                                 this.element_legend_childs.resize_handler.ontouchend =
1452                                         function(e) {
1453                                                 // remove all the hooks
1454                                                 document.onmouseup =
1455                                                 document.onmousemove =
1456                                                 document.ontouchmove =
1457                                                 document.ontouchend =
1458                                                 that.element_legend_childs.resize_handler.onmousemove =
1459                                                 that.element_legend_childs.resize_handler.ontouchmove =
1460                                                 that.element_legend_childs.resize_handler.onmouseout =
1461                                                 that.element_legend_childs.resize_handler.onmouseup =
1462                                                 that.element_legend_childs.resize_handler.ontouchend =
1463                                                         null;
1464
1465                                                 // allow auto-refreshes
1466                                                 NETDATA.options.auto_refresher_stop_until = 0;
1467                                         };
1468                         }
1469                 };
1470
1471
1472                 var noDataToShow = function() {
1473                         showMessageIcon('<i class="fa fa-warning"></i> empty');
1474                         that.legendUpdateDOM();
1475                         that.tm.last_autorefreshed = new Date().getTime();
1476                         // that.data_update_every = 30 * 1000;
1477                         //that.element_chart.style.display = 'none';
1478                         //if(that.element_legend !== null) that.element_legend.style.display = 'none';
1479                         //that.___chartIsHidden___ = true;
1480                 };
1481
1482                 // ============================================================================================================
1483                 // PUBLIC FUNCTIONS
1484
1485                 this.error = function(msg) {
1486                         error(msg);
1487                 };
1488
1489                 this.setMode = function(m) {
1490                         if(this.current !== null && this.current.name === m) return;
1491
1492                         if(m === 'auto')
1493                                 this.current = this.auto;
1494                         else if(m === 'pan')
1495                                 this.current = this.pan;
1496                         else if(m === 'zoom')
1497                                 this.current = this.zoom;
1498                         else
1499                                 this.current = this.auto;
1500
1501                         this.current.force_update_at = 0;
1502                         this.current.force_before_ms = null;
1503                         this.current.force_after_ms = null;
1504
1505                         this.tm.last_mode_switch = new Date().getTime();
1506                 };
1507
1508                 // ----------------------------------------------------------------------------------------------------------------
1509                 // global selection sync
1510
1511                 // prevent to global selection sync for some time
1512                 this.globalSelectionSyncDelay = function(ms) {
1513                         if(NETDATA.options.current.sync_selection === false)
1514                                 return;
1515
1516                         if(typeof ms === 'number')
1517                                 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1518                         else
1519                                 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1520                 };
1521
1522                 // can we globally apply selection sync?
1523                 this.globalSelectionSyncAbility = function() {
1524                         if(NETDATA.options.current.sync_selection === false)
1525                                 return false;
1526
1527                         if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1528                                 return false;
1529
1530                         return true;
1531                 };
1532
1533                 this.globalSelectionSyncIsMaster = function() {
1534                         if(NETDATA.globalSelectionSync.state === this)
1535                                 return true;
1536                         else
1537                                 return false;
1538                 };
1539
1540                 // this chart is the master of the global selection sync
1541                 this.globalSelectionSyncBeMaster = function() {
1542                         // am I the master?
1543                         if(this.globalSelectionSyncIsMaster()) {
1544                                 if(this.debug === true)
1545                                         this.log('sync: I am the master already.');
1546
1547                                 return;
1548                         }
1549
1550                         if(NETDATA.globalSelectionSync.state) {
1551                                 if(this.debug === true)
1552                                         this.log('sync: I am not the sync master. Resetting global sync.');
1553
1554                                 this.globalSelectionSyncStop();
1555                         }
1556
1557                         // become the master
1558                         if(this.debug === true)
1559                                 this.log('sync: becoming sync master.');
1560
1561                         this.selected = true;
1562                         NETDATA.globalSelectionSync.state = this;
1563
1564                         // find the all slaves
1565                         var targets = NETDATA.options.targets;
1566                         var len = targets.length;
1567                         while(len--) {
1568                                 st = targets[len];
1569
1570                                 if(st === this) {
1571                                         if(this.debug === true)
1572                                                 st.log('sync: not adding me to sync');
1573                                 }
1574                                 else if(st.globalSelectionSyncIsEligible()) {
1575                                         if(this.debug === true)
1576                                                 st.log('sync: adding to sync as slave');
1577
1578                                         st.globalSelectionSyncBeSlave();
1579                                 }
1580                         }
1581
1582                         // this.globalSelectionSyncDelay(100);
1583                 };
1584
1585                 // can the chart participate to the global selection sync as a slave?
1586                 this.globalSelectionSyncIsEligible = function() {
1587                         if(this.enabled === true
1588                                 && this.library !== null
1589                                 && typeof this.library.setSelection === 'function'
1590                                 && this.isVisible() === true
1591                                 && this.chart_created === true)
1592                                 return true;
1593
1594                         return false;
1595                 };
1596
1597                 // this chart becomes a slave of the global selection sync
1598                 this.globalSelectionSyncBeSlave = function() {
1599                         if(NETDATA.globalSelectionSync.state !== this)
1600                                 NETDATA.globalSelectionSync.slaves.push(this);
1601                 };
1602
1603                 // sync all the visible charts to the given time
1604                 // this is to be called from the chart libraries
1605                 this.globalSelectionSync = function(t) {
1606                         if(this.globalSelectionSyncAbility() === false) {
1607                                 if(this.debug === true)
1608                                         this.log('sync: cannot sync (yet?).');
1609
1610                                 return;
1611                         }
1612
1613                         if(this.globalSelectionSyncIsMaster() === false) {
1614                                 if(this.debug === true)
1615                                         this.log('sync: trying to be sync master.');
1616
1617                                 this.globalSelectionSyncBeMaster();
1618
1619                                 if(this.globalSelectionSyncAbility() === false) {
1620                                         if(this.debug === true)
1621                                                 this.log('sync: cannot sync (yet?).');
1622
1623                                         return;
1624                                 }
1625                         }
1626
1627                         NETDATA.globalSelectionSync.last_t = t;
1628                         $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1629                                 st.setSelection(t);
1630                         });
1631                 };
1632
1633                 // stop syncing all charts to the given time
1634                 this.globalSelectionSyncStop = function() {
1635                         if(NETDATA.globalSelectionSync.slaves.length) {
1636                                 if(this.debug === true)
1637                                         this.log('sync: cleaning up...');
1638
1639                                 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1640                                         if(st === that) {
1641                                                 if(that.debug === true)
1642                                                         st.log('sync: not adding me to sync stop');
1643                                         }
1644                                         else {
1645                                                 if(that.debug === true)
1646                                                         st.log('sync: removed slave from sync');
1647
1648                                                 st.clearSelection();
1649                                         }
1650                                 });
1651
1652                                 NETDATA.globalSelectionSync.last_t = 0;
1653                                 NETDATA.globalSelectionSync.slaves = [];
1654                                 NETDATA.globalSelectionSync.state = null;
1655                         }
1656
1657                         this.clearSelection();
1658                 };
1659
1660                 this.setSelection = function(t) {
1661                         if(typeof this.library.setSelection === 'function') {
1662                                 if(this.library.setSelection(this, t) === true)
1663                                         this.selected = true;
1664                                 else
1665                                         this.selected = false;
1666                         }
1667                         else this.selected = true;
1668
1669                         if(this.selected === true && this.debug === true)
1670                                 this.log('selection set to ' + t.toString());
1671
1672                         return this.selected;
1673                 };
1674
1675                 this.clearSelection = function() {
1676                         if(this.selected === true) {
1677                                 if(typeof this.library.clearSelection === 'function') {
1678                                         if(this.library.clearSelection(this) === true)
1679                                                 this.selected = false;
1680                                         else
1681                                                 this.selected = true;
1682                                 }
1683                                 else this.selected = false;
1684
1685                                 if(this.selected === false && this.debug === true)
1686                                         this.log('selection cleared');
1687
1688                                 this.legendReset();
1689                         }
1690
1691                         return this.selected;
1692                 };
1693
1694                 // find if a timestamp (ms) is shown in the current chart
1695                 this.timeIsVisible = function(t) {
1696                         if(t >= this.data_after && t <= this.data_before)
1697                                 return true;
1698                         return false;
1699                 };
1700
1701                 this.calculateRowForTime = function(t) {
1702                         if(this.timeIsVisible(t) === false) return -1;
1703                         return Math.floor((t - this.data_after) / this.data_update_every);
1704                 };
1705
1706                 // ----------------------------------------------------------------------------------------------------------------
1707
1708                 // console logging
1709                 this.log = function(msg) {
1710                         console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1711                 };
1712
1713                 this.pauseChart = function() {
1714                         if(this.paused === false) {
1715                                 if(this.debug === true)
1716                                         this.log('pauseChart()');
1717
1718                                 this.paused = true;
1719                         }
1720                 };
1721
1722                 this.unpauseChart = function() {
1723                         if(this.paused === true) {
1724                                 if(this.debug === true)
1725                                         this.log('unpauseChart()');
1726
1727                                 this.paused = false;
1728                         }
1729                 };
1730
1731                 this.resetChart = function(dont_clear_master, dont_update) {
1732                         if(this.debug === true)
1733                                 this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
1734
1735                         if(typeof dont_clear_master === 'undefined')
1736                                 dont_clear_master = false;
1737
1738                         if(typeof dont_update === 'undefined')
1739                                 dont_update = false;
1740
1741                         if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
1742                                 if(this.debug === true)
1743                                         this.log('resetChart() diverting to clearMaster().');
1744                                 // this will call us back with master === true
1745                                 NETDATA.globalPanAndZoom.clearMaster();
1746                                 return;
1747                         }
1748
1749                         this.clearSelection();
1750
1751                         this.tm.pan_and_zoom_seq = 0;
1752
1753                         this.setMode('auto');
1754                         this.current.force_update_at = 0;
1755                         this.current.force_before_ms = null;
1756                         this.current.force_after_ms = null;
1757                         this.tm.last_autorefreshed = 0;
1758                         this.paused = false;
1759                         this.selected = false;
1760                         this.enabled = true;
1761                         // this.debug = false;
1762
1763                         // do not update the chart here
1764                         // or the chart will flip-flop when it is the master
1765                         // of a selection sync and another chart becomes
1766                         // the new master
1767
1768                         if(dont_update !== true && this.isVisible() === true) {
1769                                 this.updateChart();
1770                         }
1771                 };
1772
1773                 this.updateChartPanOrZoom = function(after, before) {
1774                         var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
1775                         var ret = true;
1776
1777                         if(this.debug === true)
1778                                 this.log(logme);
1779
1780                         if(before < after) {
1781                                 if(this.debug === true)
1782                                         this.log(logme + 'flipped parameters, rejecting it.');
1783
1784                                 return false;
1785                         }
1786
1787                         if(typeof this.fixed_min_duration === 'undefined')
1788                                 this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
1789
1790                         var min_duration = this.fixed_min_duration;
1791                         var current_duration = Math.round(this.view_before - this.view_after);
1792
1793                         // round the numbers
1794                         after = Math.round(after);
1795                         before = Math.round(before);
1796
1797                         // align them to update_every
1798                         // stretching them further away
1799                         after -= after % this.data_update_every;
1800                         before += this.data_update_every - (before % this.data_update_every);
1801
1802                         // the final wanted duration
1803                         var wanted_duration = before - after;
1804
1805                         // to allow panning, accept just a point below our minimum
1806                         if((current_duration - this.data_update_every) < min_duration)
1807                                 min_duration = current_duration - this.data_update_every;
1808
1809                         // we do it, but we adjust to minimum size and return false
1810                         // when the wanted size is below the current and the minimum
1811                         // and we zoom
1812                         if(wanted_duration < current_duration && wanted_duration < min_duration) {
1813                                 if(this.debug === true)
1814                                         this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
1815
1816                                 min_duration = this.fixed_min_duration;
1817
1818                                 var dt = (min_duration - wanted_duration) / 2;
1819                                 before += dt;
1820                                 after -= dt;
1821                                 wanted_duration = before - after;
1822                                 ret = false;
1823                         }
1824
1825                         var tolerance = this.data_update_every * 2;
1826                         var movement = Math.abs(before - this.view_before);
1827
1828                         if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
1829                                 if(this.debug === true)
1830                                         this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
1831                                 return false;
1832                         }
1833
1834                         if(this.current.name === 'auto') {
1835                                 this.log(logme + 'caller called me with mode: ' + this.current.name);
1836                                 this.setMode('pan');
1837                         }
1838
1839                         if(this.debug === true)
1840                                 this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
1841
1842                         this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1843                         this.current.force_after_ms = after;
1844                         this.current.force_before_ms = before;
1845                         NETDATA.globalPanAndZoom.setMaster(this, after, before);
1846                         return ret;
1847                 };
1848
1849                 this.legendFormatValue = function(value) {
1850                         if(value === null || value === 'undefined') return '-';
1851                         if(typeof value !== 'number') return value;
1852
1853                         var abs = Math.abs(value);
1854                         if(abs >= 1000) return (Math.round(value)).toLocaleString();
1855                         if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
1856                         if(abs >= 1   ) return (Math.round(value * 100) / 100).toLocaleString();
1857                         if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
1858                         return (Math.round(value * 10000) / 10000).toLocaleString();
1859                 };
1860
1861                 this.legendSetLabelValue = function(label, value) {
1862                         var series = this.element_legend_childs.series[label];
1863                         if(typeof series === 'undefined') return;
1864                         if(series.value === null && series.user === null) return;
1865
1866                         // if the value has not changed, skip DOM update
1867                         //if(series.last === value) return;
1868
1869                         var s, r;
1870                         if(typeof value === 'number') {
1871                                 var v = Math.abs(value);
1872                                 s = r = this.legendFormatValue(value);
1873
1874                                 if(typeof series.last === 'number') {
1875                                         if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1876                                         else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1877                                         else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1878                                 }
1879                                 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1880                                 series.last = v;
1881                         }
1882                         else {
1883                                 s = r = value;
1884                                 series.last = value;
1885                         }
1886
1887                         if(series.value !== null) series.value.innerHTML = s;
1888                         if(series.user !== null) series.user.innerHTML = r;
1889                 };
1890
1891                 this.legendSetDate = function(ms) {
1892                         if(typeof ms !== 'number') {
1893                                 this.legendShowUndefined();
1894                                 return;
1895                         }
1896
1897                         var d = new Date(ms);
1898
1899                         if(this.element_legend_childs.title_date)
1900                                 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
1901
1902                         if(this.element_legend_childs.title_time)
1903                                 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
1904
1905                         if(this.element_legend_childs.title_units)
1906                                 this.element_legend_childs.title_units.innerHTML = this.units;
1907                 };
1908
1909                 this.legendShowUndefined = function() {
1910                         if(this.element_legend_childs.title_date)
1911                                 this.element_legend_childs.title_date.innerHTML = '&nbsp;';
1912
1913                         if(this.element_legend_childs.title_time)
1914                                 this.element_legend_childs.title_time.innerHTML = this.chart.name;
1915
1916                         if(this.element_legend_childs.title_units)
1917                                 this.element_legend_childs.title_units.innerHTML = '&nbsp;';
1918
1919                         if(this.data && this.element_legend_childs.series !== null) {
1920                                 var labels = this.data.dimension_names;
1921                                 var i = labels.length;
1922                                 while(i--) {
1923                                         var label = labels[i];
1924
1925                                         if(typeof label === 'undefined') continue;
1926                                         if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1927                                         this.legendSetLabelValue(label, null);
1928                                 }
1929                         }
1930                 };
1931
1932                 this.legendShowLatestValues = function() {
1933                         if(this.chart === null) return;
1934                         if(this.selected) return;
1935
1936                         if(this.data === null || this.element_legend_childs.series === null) {
1937                                 this.legendShowUndefined();
1938                                 return;
1939                         }
1940
1941                         var show_undefined = true;
1942                         if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
1943                                 show_undefined = false;
1944
1945                         if(show_undefined) {
1946                                 this.legendShowUndefined();
1947                                 return;
1948                         }
1949
1950                         this.legendSetDate(this.view_before);
1951
1952                         var labels = this.data.dimension_names;
1953                         var i = labels.length;
1954                         while(i--) {
1955                                 var label = labels[i];
1956
1957                                 if(typeof label === 'undefined') continue;
1958                                 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1959
1960                                 if(show_undefined)
1961                                         this.legendSetLabelValue(label, null);
1962                                 else
1963                                         this.legendSetLabelValue(label, this.data.view_latest_values[i]);
1964                         }
1965                 };
1966
1967                 this.legendReset = function() {
1968                         this.legendShowLatestValues();
1969                 };
1970
1971                 // this should be called just ONCE per dimension per chart
1972                 this._chartDimensionColor = function(label) {
1973                         if(this.colors === null) this.chartColors();
1974
1975                         if(typeof this.colors_assigned[label] === 'undefined') {
1976                                 if(this.colors_available.length === 0) {
1977                                         for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
1978                                                 this.colors_available.push(NETDATA.themes.current.colors[i]);
1979                                 }
1980
1981                                 this.colors_assigned[label] = this.colors_available.shift();
1982
1983                                 if(this.debug === true)
1984                                         this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
1985                         }
1986                         else {
1987                                 if(this.debug === true)
1988                                         this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
1989                         }
1990
1991                         this.colors.push(this.colors_assigned[label]);
1992                         return this.colors_assigned[label];
1993                 };
1994
1995                 this.chartColors = function() {
1996                         if(this.colors !== null) return this.colors;
1997
1998                         this.colors = new Array();
1999                         this.colors_available = new Array();
2000                         var i, len;
2001
2002                         var c = $(this.element).data('colors');
2003                         // this.log('read colors: ' + c);
2004                         if(typeof c !== 'undefined' && c !== null && c.length > 0) {
2005                                 if(typeof c !== 'string') {
2006                                         this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
2007                                 }
2008                                 else {
2009                                         c = c.split(' ');
2010                                         var added = 0;
2011
2012                                         while(added < 20) {
2013                                                 for(i = 0, len = c.length; i < len ; i++) {
2014                                                         added++;
2015                                                         this.colors_available.push(c[i]);
2016                                                         // this.log('adding color: ' + c[i]);
2017                                                 }
2018                                         }
2019                                 }
2020                         }
2021
2022                         // push all the standard colors too
2023                         for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
2024                                 this.colors_available.push(NETDATA.themes.current.colors[i]);
2025
2026                         return this.colors;
2027                 };
2028
2029                 this.legendUpdateDOM = function() {
2030                         var needed = false;
2031
2032                         // check that the legend DOM is up to date for the downloaded dimensions
2033                         if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
2034                                 // this.log('the legend does not have any series - requesting legend update');
2035                                 needed = true;
2036                         }
2037                         else if(this.data === null) {
2038                                 // this.log('the chart does not have any data - requesting legend update');
2039                                 needed = true;
2040                         }
2041                         else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
2042                                 needed = true;
2043                         }
2044                         else {
2045                                 var labels = this.data.dimension_names.toString();
2046                                 if(labels !== this.element_legend_childs.series.labels_key) {
2047                                         needed = true;
2048
2049                                         if(this.debug === true)
2050                                                 this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
2051                                 }
2052                         }
2053
2054                         if(needed === false) {
2055                                 // make sure colors available
2056                                 this.chartColors();
2057
2058                                 // do we have to update the current values?
2059                                 // we do this, only when the visible chart is current
2060                                 if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
2061                                         if(this.debug === true)
2062                                                 this.log('chart is in latest position... updating values on legend...');
2063
2064                                         //var labels = this.data.dimension_names;
2065                                         //var i = labels.length;
2066                                         //while(i--)
2067                                         //      this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
2068                                 }
2069                                 return;
2070                         }
2071                         if(this.colors === null) {
2072                                 // this is the first time we update the chart
2073                                 // let's assign colors to all dimensions
2074                                 if(this.library.track_colors() === true)
2075                                         for(var dim in this.chart.dimensions)
2076                                                 this._chartDimensionColor(this.chart.dimensions[dim].name);
2077                         }
2078                         // we will re-generate the colors for the chart
2079                         // based on the selected dimensions
2080                         this.colors = null;
2081
2082                         if(this.debug === true)
2083                                 this.log('updating Legend DOM');
2084
2085                         // mark all dimensions as invalid
2086                         this.dimensions_visibility.invalidateAll();
2087
2088                         var genLabel = function(state, parent, name, count) {
2089                                 var color = state._chartDimensionColor(name);
2090
2091                                 var user_element = null;
2092                                 var user_id = self.data('show-value-of-' + name + '-at') || null;
2093                                 if(user_id !== null) {
2094                                         user_element = document.getElementById(user_id) || null;
2095                                         if(user_element === null)
2096                                                 state.log('Cannot find element with id: ' + user_id);
2097                                 }
2098
2099                                 state.element_legend_childs.series[name] = {
2100                                         name: document.createElement('span'),
2101                                         value: document.createElement('span'),
2102                                         user: user_element,
2103                                         last: null
2104                                 };
2105
2106                                 var label = state.element_legend_childs.series[name];
2107
2108                                 // create the dimension visibility tracking for this label
2109                                 state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
2110
2111                                 var rgb = NETDATA.colorHex2Rgb(color);
2112                                 label.name.innerHTML = '<table class="netdata-legend-name-table-'
2113                                         + state.chart.chart_type
2114                                         + '" style="background-color: '
2115                                         + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
2116                                         + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
2117
2118                                 var text = document.createTextNode(' ' + name);
2119                                 label.name.appendChild(text);
2120
2121                                 if(count > 0)
2122                                         parent.appendChild(document.createElement('br'));
2123
2124                                 parent.appendChild(label.name);
2125                                 parent.appendChild(label.value);
2126                         };
2127
2128                         var content = document.createElement('div');
2129
2130                         if(this.hasLegend()) {
2131                                 this.element_legend_childs = {
2132                                         content: content,
2133                                         resize_handler: document.createElement('div'),
2134                                         toolbox: document.createElement('div'),
2135                                         toolbox_left: document.createElement('div'),
2136                                         toolbox_right: document.createElement('div'),
2137                                         toolbox_reset: document.createElement('div'),
2138                                         toolbox_zoomin: document.createElement('div'),
2139                                         toolbox_zoomout: document.createElement('div'),
2140                                         toolbox_volume: document.createElement('div'),
2141                                         title_date: document.createElement('span'),
2142                                         title_time: document.createElement('span'),
2143                                         title_units: document.createElement('span'),
2144                                         nano: document.createElement('div'),
2145                                         nano_options: {
2146                                                 paneClass: 'netdata-legend-series-pane',
2147                                                 sliderClass: 'netdata-legend-series-slider',
2148                                                 contentClass: 'netdata-legend-series-content',
2149                                                 enabledClass: '__enabled',
2150                                                 flashedClass: '__flashed',
2151                                                 activeClass: '__active',
2152                                                 tabIndex: -1,
2153                                                 alwaysVisible: true,
2154                                                 sliderMinHeight: 10
2155                                         },
2156                                         series: {}
2157                                 };
2158
2159                                 this.element_legend.innerHTML = '';
2160
2161                                 if(this.library.toolboxPanAndZoom !== null) {
2162
2163                                         function get_pan_and_zoom_step(event) {
2164                                                 if (event.ctrlKey)
2165                                                         return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
2166
2167                                                 else if (event.shiftKey)
2168                                                         return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
2169
2170                                                 else if (event.altKey)
2171                                                         return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
2172
2173                                                 else
2174                                                         return NETDATA.options.current.pan_and_zoom_factor;
2175                                         }
2176
2177                                         this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
2178                                         this.element.appendChild(this.element_legend_childs.toolbox);
2179
2180                                         this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
2181                                         this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
2182                                         this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
2183                                         this.element_legend_childs.toolbox_left.onclick = function(e) {
2184                                                 e.preventDefault();
2185
2186                                                 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2187                                                 var before = that.view_before - step;
2188                                                 var after = that.view_after - step;
2189                                                 if(after >= that.netdata_first)
2190                                                         that.library.toolboxPanAndZoom(that, after, before);
2191                                         };
2192                                         if(NETDATA.options.current.show_help === true)
2193                                                 $(this.element_legend_childs.toolbox_left).popover({
2194                                                 container: "body",
2195                                                 animation: false,
2196                                                 html: true,
2197                                                 trigger: 'hover',
2198                                                 placement: 'bottom',
2199                                                 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2200                                                 title: 'Pan Left',
2201                                                 content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2202                                         });
2203
2204
2205                                         this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
2206                                         this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
2207                                         this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
2208                                         this.element_legend_childs.toolbox_reset.onclick = function(e) {
2209                                                 e.preventDefault();
2210                                                 NETDATA.resetAllCharts(that);
2211                                         };
2212                                         if(NETDATA.options.current.show_help === true)
2213                                                 $(this.element_legend_childs.toolbox_reset).popover({
2214                                                 container: "body",
2215                                                 animation: false,
2216                                                 html: true,
2217                                                 trigger: 'hover',
2218                                                 placement: 'bottom',
2219                                                 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2220                                                 title: 'Chart Reset',
2221                                                 content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2222                                         });
2223                                         
2224                                         this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
2225                                         this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
2226                                         this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
2227                                         this.element_legend_childs.toolbox_right.onclick = function(e) {
2228                                                 e.preventDefault();
2229                                                 var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
2230                                                 var before = that.view_before + step;
2231                                                 var after = that.view_after + step;
2232                                                 if(before <= that.netdata_last)
2233                                                         that.library.toolboxPanAndZoom(that, after, before);
2234                                         };
2235                                         if(NETDATA.options.current.show_help === true)
2236                                                 $(this.element_legend_childs.toolbox_right).popover({
2237                                                 container: "body",
2238                                                 animation: false,
2239                                                 html: true,
2240                                                 trigger: 'hover',
2241                                                 placement: 'bottom',
2242                                                 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2243                                                 title: 'Pan Right',
2244                                                 content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
2245                                         });
2246
2247                                         
2248                                         this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
2249                                         this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
2250                                         this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
2251                                         this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
2252                                                 e.preventDefault();
2253                                                 var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
2254                                                 var before = that.view_before - dt;
2255                                                 var after = that.view_after + dt;
2256                                                 that.library.toolboxPanAndZoom(that, after, before);
2257                                         };
2258                                         if(NETDATA.options.current.show_help === true)
2259                                                 $(this.element_legend_childs.toolbox_zoomin).popover({
2260                                                 container: "body",
2261                                                 animation: false,
2262                                                 html: true,
2263                                                 trigger: 'hover',
2264                                                 placement: 'bottom',
2265                                                 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2266                                                 title: 'Chart Zoom In',
2267                                                 content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2268                                         });
2269                                         
2270                                         this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
2271                                         this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
2272                                         this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
2273                                         this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
2274                                                 e.preventDefault();
2275                                                 var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
2276                                                 var before = that.view_before + dt;
2277                                                 var after = that.view_after - dt;
2278
2279                                                 that.library.toolboxPanAndZoom(that, after, before);
2280                                         };
2281                                         if(NETDATA.options.current.show_help === true)
2282                                                 $(this.element_legend_childs.toolbox_zoomout).popover({
2283                                                 container: "body",
2284                                                 animation: false,
2285                                                 html: true,
2286                                                 trigger: 'hover',
2287                                                 placement: 'bottom',
2288                                                 delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2289                                                 title: 'Chart Zoom Out',
2290                                                 content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
2291                                         });
2292                                         
2293                                         //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
2294                                         //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
2295                                         //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
2296                                         //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
2297                                         //this.element_legend_childs.toolbox_volume.onclick = function(e) {
2298                                                 //e.preventDefault();
2299                                                 //alert('clicked toolbox_volume on ' + that.id);
2300                                         //}
2301                                 }
2302                                 else {
2303                                         this.element_legend_childs.toolbox = null;
2304                                         this.element_legend_childs.toolbox_left = null;
2305                                         this.element_legend_childs.toolbox_reset = null;
2306                                         this.element_legend_childs.toolbox_right = null;
2307                                         this.element_legend_childs.toolbox_zoomin = null;
2308                                         this.element_legend_childs.toolbox_zoomout = null;
2309                                         this.element_legend_childs.toolbox_volume = null;
2310                                 }
2311                                 
2312                                 this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
2313                                 this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
2314                                 this.element.appendChild(this.element_legend_childs.resize_handler);
2315                                 if(NETDATA.options.current.show_help === true)
2316                                         $(this.element_legend_childs.resize_handler).popover({
2317                                         container: "body",
2318                                         animation: false,
2319                                         html: true,
2320                                         trigger: 'hover',
2321                                         placement: 'bottom',
2322                                         delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2323                                         title: 'Chart Resize',
2324                                         content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
2325                                 });
2326
2327                                 // mousedown event
2328                                 this.element_legend_childs.resize_handler.onmousedown =
2329                                         function(e) {
2330                                                 that.resizeHandler(e);
2331                                         };
2332
2333                                 // touchstart event
2334                                 this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
2335                                         that.resizeHandler(e);
2336                                 }, false);
2337
2338                                 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
2339                                 this.element_legend.appendChild(this.element_legend_childs.title_date);
2340
2341                                 this.element_legend.appendChild(document.createElement('br'));
2342
2343                                 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
2344                                 this.element_legend.appendChild(this.element_legend_childs.title_time);
2345
2346                                 this.element_legend.appendChild(document.createElement('br'));
2347
2348                                 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
2349                                 this.element_legend.appendChild(this.element_legend_childs.title_units);
2350
2351                                 this.element_legend.appendChild(document.createElement('br'));
2352
2353                                 this.element_legend_childs.nano.className = 'netdata-legend-series';
2354                                 this.element_legend.appendChild(this.element_legend_childs.nano);
2355
2356                                 content.className = 'netdata-legend-series-content';
2357                                 this.element_legend_childs.nano.appendChild(content);
2358
2359                                 if(NETDATA.options.current.show_help === true)
2360                                         $(content).popover({
2361                                         container: "body",
2362                                         animation: false,
2363                                         html: true,
2364                                         trigger: 'hover',
2365                                         placement: 'bottom',
2366                                         title: 'Chart Legend',
2367                                         delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
2368                                         content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
2369                                 });
2370                         }
2371                         else {
2372                                 this.element_legend_childs = {
2373                                         content: content,
2374                                         resize_handler: null,
2375                                         toolbox: null,
2376                                         toolbox_left: null,
2377                                         toolbox_right: null,
2378                                         toolbox_reset: null,
2379                                         toolbox_zoomin: null,
2380                                         toolbox_zoomout: null,
2381                                         toolbox_volume: null,
2382                                         title_date: null,
2383                                         title_time: null,
2384                                         title_units: null,
2385                                         nano: null,
2386                                         nano_options: null,
2387                                         series: {}
2388                                 };
2389                         }
2390
2391                         if(this.data) {
2392                                 this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
2393                                 if(this.debug === true)
2394                                         this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
2395
2396                                 for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
2397                                         genLabel(this, content, this.data.dimension_names[i], i);
2398                                 }
2399                         }
2400                         else {
2401                                 var tmp = new Array();
2402                                 for(var dim in this.chart.dimensions) {
2403                                         tmp.push(this.chart.dimensions[dim].name);
2404                                         genLabel(this, content, this.chart.dimensions[dim].name, i);
2405                                 }
2406                                 this.element_legend_childs.series.labels_key = tmp.toString();
2407                                 if(this.debug === true)
2408                                         this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
2409                         }
2410
2411                         // create a hidden div to be used for hidding
2412                         // the original legend of the chart library
2413                         var el = document.createElement('div');
2414                         if(this.element_legend !== null)
2415                                 this.element_legend.appendChild(el);
2416                         el.style.display = 'none';
2417
2418                         this.element_legend_childs.hidden = document.createElement('div');
2419                         el.appendChild(this.element_legend_childs.hidden);
2420
2421                         if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2422                                 $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2423
2424                         this.legendShowLatestValues();
2425                 };
2426
2427                 this.hasLegend = function() {
2428                         if(typeof this.___hasLegendCache___ !== 'undefined')
2429                                 return this.___hasLegendCache___;
2430
2431                         var leg = false;
2432                         if(this.library && this.library.legend(this) === 'right-side') {
2433                                 var legend = $(this.element).data('legend') || 'yes';
2434                                 if(legend === 'yes') leg = true;
2435                         }
2436
2437                         this.___hasLegendCache___ = leg;
2438                         return leg;
2439                 };
2440
2441                 this.legendWidth = function() {
2442                         return (this.hasLegend())?140:0;
2443                 };
2444
2445                 this.legendHeight = function() {
2446                         return $(this.element).height();
2447                 };
2448
2449                 this.chartWidth = function() {
2450                         return $(this.element).width() - this.legendWidth();
2451                 };
2452
2453                 this.chartHeight = function() {
2454                         return $(this.element).height();
2455                 };
2456
2457                 this.chartPixelsPerPoint = function() {
2458                         // force an options provided detail
2459                         var px = this.pixels_per_point;
2460
2461                         if(this.library && px < this.library.pixels_per_point(this))
2462                                 px = this.library.pixels_per_point(this);
2463
2464                         if(px < NETDATA.options.current.pixels_per_point)
2465                                 px = NETDATA.options.current.pixels_per_point;
2466
2467                         return px;
2468                 };
2469
2470                 this.needsRecreation = function() {
2471                         return (
2472                                         this.chart_created === true
2473                                         && this.library
2474                                         && this.library.autoresize() === false
2475                                         && this.tm.last_resized < NETDATA.options.last_resized
2476                                 );
2477                 };
2478
2479                 this.chartURL = function() {
2480                         var after, before, points_multiplier = 1;
2481                         if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
2482                                 this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2483
2484                                 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2485                                 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2486                                 this.view_after = after * 1000;
2487                                 this.view_before = before * 1000;
2488
2489                                 this.requested_padding = null;
2490                                 points_multiplier = 1;
2491                         }
2492                         else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
2493                                 this.tm.pan_and_zoom_seq = 0;
2494
2495                                 before = Math.round(this.current.force_before_ms / 1000);
2496                                 after  = Math.round(this.current.force_after_ms / 1000);
2497                                 this.view_after = after * 1000;
2498                                 this.view_before = before * 1000;
2499
2500                                 if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
2501                                         this.requested_padding = Math.round((before - after) / 2);
2502                                         after -= this.requested_padding;
2503                                         before += this.requested_padding;
2504                                         this.requested_padding *= 1000;
2505                                         points_multiplier = 2;
2506                                 }
2507
2508                                 this.current.force_before_ms = null;
2509                                 this.current.force_after_ms = null;
2510                         }
2511                         else {
2512                                 this.tm.pan_and_zoom_seq = 0;
2513
2514                                 before = this.before;
2515                                 after  = this.after;
2516                                 this.view_after = after * 1000;
2517                                 this.view_before = before * 1000;
2518
2519                                 this.requested_padding = null;
2520                                 points_multiplier = 1;
2521                         }
2522
2523                         this.requested_after = after * 1000;
2524                         this.requested_before = before * 1000;
2525
2526                         this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2527
2528                         // build the data URL
2529                         this.data_url = this.host + this.chart.data_url;
2530                         this.data_url += "&format="  + this.library.format();
2531                         this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
2532                         this.data_url += "&group="   + this.method;
2533                         this.data_url += "&options=" + this.library.options(this);
2534                         this.data_url += '|jsonwrap';
2535
2536                         if(NETDATA.options.current.eliminate_zero_dimensions === true)
2537                                 this.data_url += '|nonzero';
2538
2539                         if(this.append_options !== null)
2540                                 this.data_url += '|' + this.append_options.toString();
2541
2542                         if(after)
2543                                 this.data_url += "&after="  + after.toString();
2544
2545                         if(before)
2546                                 this.data_url += "&before=" + before.toString();
2547
2548                         if(this.dimensions)
2549                                 this.data_url += "&dimensions=" + this.dimensions;
2550
2551                         if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2552                                 this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2553                 };
2554
2555                 this.redrawChart = function() {
2556                         if(this.data !== null)
2557                                 this.updateChartWithData(this.data);
2558                 };
2559
2560                 this.updateChartWithData = function(data) {
2561                         if(this.debug === true)
2562                                 this.log('updateChartWithData() called.');
2563
2564                         this._updating = false;
2565
2566                         // this may force the chart to be re-created
2567                         resizeChart();
2568
2569                         this.data = data;
2570                         this.updates_counter++;
2571                         this.updates_since_last_unhide++;
2572                         this.updates_since_last_creation++;
2573
2574                         var started = new Date().getTime();
2575
2576                         // if the result is JSON, find the latest update-every
2577                         this.data_update_every = data.view_update_every * 1000;
2578                         this.data_after = data.after * 1000;
2579                         this.data_before = data.before * 1000;
2580                         this.netdata_first = data.first_entry * 1000;
2581                         this.netdata_last = data.last_entry * 1000;
2582                         this.data_points = data.points;
2583                         data.state = this;
2584
2585                         if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
2586                                 if(this.view_after < this.data_after) {
2587                                         // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
2588                                         this.view_after = this.data_after;
2589                                 }
2590
2591                                 if(this.view_before > this.data_before) {
2592                                         // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
2593                                         this.view_before = this.data_before;
2594                                 }
2595                         }
2596                         else {
2597                                 this.view_after = this.data_after;
2598                                 this.view_before = this.data_before;
2599                         }
2600
2601                         if(this.debug === true) {
2602                                 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2603
2604                                 if(this.current.force_after_ms)
2605                                         this.log('STATUS: forced    : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2606                                 else
2607                                         this.log('STATUS: forced    : unset');
2608
2609                                 this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2610                                 this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2611                                 this.log('STATUS: rendered  : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
2612                                 this.log('STATUS: points    : ' + (this.data_points).toString());
2613                         }
2614
2615                         if(this.data_points === 0) {
2616                                 noDataToShow();
2617                                 return;
2618                         }
2619
2620                         if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2621                                 if(this.debug === true)
2622                                         this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2623
2624                                 this.chart_created = false;
2625                         }
2626
2627                         // check and update the legend
2628                         this.legendUpdateDOM();
2629
2630                         if(this.chart_created === true
2631                                 && typeof this.library.update === 'function') {
2632
2633                                 if(this.debug === true)
2634                                         this.log('updating chart...');
2635
2636                                 if(callChartLibraryUpdateSafely(data) === false)
2637                                         return;
2638                         }
2639                         else {
2640                                 if(this.debug === true)
2641                                         this.log('creating chart...');
2642
2643                                 if(callChartLibraryCreateSafely(data) === false)
2644                                         return;
2645                         }
2646                         hideMessage();
2647                         this.legendShowLatestValues();
2648                         if(this.selected === true)
2649                                 NETDATA.globalSelectionSync.stop();
2650
2651                         // update the performance counters
2652                         var now = new Date().getTime();
2653                         this.tm.last_updated = now;
2654
2655                         // don't update last_autorefreshed if this chart is
2656                         // forced to be updated with global PanAndZoom
2657                         if(NETDATA.globalPanAndZoom.isActive())
2658                                 this.tm.last_autorefreshed = 0;
2659                         else {
2660                                 if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes)
2661                                         this.tm.last_autorefreshed = Math.round(now / this.data_update_every) * this.data_update_every;
2662                                 else
2663                                         this.tm.last_autorefreshed = now;
2664                         }
2665
2666                         this.refresh_dt_ms = now - started;
2667                         NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2668
2669                         if(this.refresh_dt_element !== null)
2670                                 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2671                 };
2672
2673                 this.updateChart = function(callback) {
2674                         if(this.debug === true)
2675                                 this.log('updateChart() called.');
2676
2677                         if(this._updating === true) {
2678                                 if(this.debug === true)
2679                                         this.log('I am already updating...');
2680
2681                                 if(typeof callback === 'function') callback();
2682                                 return false;
2683                         }
2684
2685                         // due to late initialization of charts and libraries
2686                         // we need to check this too
2687                         if(this.enabled === false) {
2688                                 if(this.debug === true)
2689                                         this.log('I am not enabled');
2690
2691                                 if(typeof callback === 'function') callback();
2692                                 return false;
2693                         }
2694
2695                         if(canBeRendered() === false) {
2696                                 if(typeof callback === 'function') callback();
2697                                 return false;
2698                         }
2699
2700                         if(this.chart === null) {
2701                                 this.getChart(function() { that.updateChart(callback); });
2702                                 return false;
2703                         }
2704
2705                         if(this.library.initialized === false) {
2706                                 if(this.library.enabled === true) {
2707                                         this.library.initialize(function() { that.updateChart(callback); });
2708                                         return false;
2709                                 }
2710                                 else {
2711                                         error('chart library "' + this.library_name + '" is not available.');
2712                                         if(typeof callback === 'function') callback();
2713                                         return false;
2714                                 }
2715                         }
2716
2717                         this.clearSelection();
2718                         this.chartURL();
2719
2720                         if(this.debug === true)
2721                                 this.log('updating from ' + this.data_url);
2722
2723                         this._updating = true;
2724
2725                         this.xhr = $.ajax( {
2726                                 url: this.data_url,
2727                                 crossDomain: NETDATA.options.crossDomainAjax,
2728                                 cache: false,
2729                                 async: true
2730                         })
2731                         .success(function(data) {
2732                                 if(that.debug === true)
2733                                         that.log('data received. updating chart.');
2734
2735                                 that.updateChartWithData(data);
2736                         })
2737                         .fail(function() {
2738                                 error('data download failed for url: ' + that.data_url);
2739                         })
2740                         .always(function() {
2741                                 that._updating = false;
2742                                 if(typeof callback === 'function') callback();
2743                         });
2744
2745                         return true;
2746                 };
2747
2748                 this.isVisible = function(nocache) {
2749                         if(typeof nocache === 'undefined')
2750                                 nocache = false;
2751
2752                         // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2753
2754                         // caching - we do not evaluate the charts visibility
2755                         // if the page has not been scrolled since the last check
2756                         if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
2757                                 return this.___isVisible___;
2758
2759                         this.tm.last_visible_check = new Date().getTime();
2760
2761                         var wh = window.innerHeight;
2762                         var x = this.element.getBoundingClientRect();
2763                         var ret = 0;
2764                         var tolerance = 0;
2765
2766                         if(x.width === 0 || x.height === 0) {
2767                                 hideChart();
2768                                 this.___isVisible___ = false;
2769                                 return this.___isVisible___;
2770                         }
2771
2772                         if(x.top < 0 && -x.top > x.height) {
2773                                 // the chart is entirely above
2774                                 ret = -x.top - x.height;
2775                         }
2776                         else if(x.top > wh) {
2777                                 // the chart is entirely below
2778                                 ret = x.top - wh;
2779                         }
2780
2781                         if(ret > tolerance) {
2782                                 // the chart is too far
2783
2784                                 hideChart();
2785                                 this.___isVisible___ = false;
2786                                 return this.___isVisible___;
2787                         }
2788                         else {
2789                                 // the chart is inside or very close
2790
2791                                 unhideChart();
2792                                 this.___isVisible___ = true;
2793                                 return this.___isVisible___;
2794                         }
2795                 };
2796
2797                 this.isAutoRefreshed = function() {
2798                         return (this.current.autorefresh);
2799                 };
2800
2801                 this.canBeAutoRefreshed = function() {
2802                         var now = new Date().getTime();
2803
2804                         if(this.enabled === false) {
2805                                 if(this.debug === true)
2806                                         this.log('I am not enabled');
2807
2808                                 return false;
2809                         }
2810
2811                         if(this.library === null || this.library.enabled === false) {
2812                                 error('charting library "' + this.library_name + '" is not available');
2813                                 if(this.debug === true)
2814                                         this.log('My chart library ' + this.library_name + ' is not available');
2815
2816                                 return false;
2817                         }
2818
2819                         if(this.isVisible() === false) {
2820                                 if(NETDATA.options.debug.visibility === true || this.debug === true)
2821                                         this.log('I am not visible');
2822
2823                                 return false;
2824                         }
2825
2826                         if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
2827                                 if(this.debug === true)
2828                                         this.log('timed force update detected - allowing this update');
2829
2830                                 this.current.force_update_at = 0;
2831                                 return true;
2832                         }
2833
2834                         if(this.isAutoRefreshed() === true) {
2835                                 // allow the first update, even if the page is not visible
2836                                 if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
2837                                         if(NETDATA.options.debug.focus === true || this.debug === true)
2838                                                 this.log('canBeAutoRefreshed(): page does not have focus');
2839
2840                                         return false;
2841                                 }
2842
2843                                 if(this.needsRecreation() === true) {
2844                                         if(this.debug === true)
2845                                                 this.log('canBeAutoRefreshed(): needs re-creation.');
2846
2847                                         return true;
2848                                 }
2849
2850                                 // options valid only for autoRefresh()
2851                                 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
2852                                         if(NETDATA.globalPanAndZoom.isActive()) {
2853                                                 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
2854                                                         if(this.debug === true)
2855                                                                 this.log('canBeAutoRefreshed(): global panning: I need an update.');
2856
2857                                                         return true;
2858                                                 }
2859                                                 else {
2860                                                         if(this.debug === true)
2861                                                                 this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
2862
2863                                                         return false;
2864                                                 }
2865                                         }
2866
2867                                         if(this.selected === true) {
2868                                                 if(this.debug === true)
2869                                                         this.log('canBeAutoRefreshed(): I have a selection in place.');
2870
2871                                                 return false;
2872                                         }
2873
2874                                         if(this.paused === true) {
2875                                                 if(this.debug === true)
2876                                                         this.log('canBeAutoRefreshed(): I am paused.');
2877
2878                                                 return false;
2879                                         }
2880
2881                                         if(now - this.tm.last_autorefreshed >= this.data_update_every) {
2882                                                 if(this.debug === true)
2883                                                         this.log('canBeAutoRefreshed(): It is time to update me.');
2884
2885                                                 return true;
2886                                         }
2887                                 }
2888                         }
2889
2890                         return false;
2891                 };
2892
2893                 this.autoRefresh = function(callback) {
2894                         if(this.canBeAutoRefreshed() === true) {
2895                                 this.updateChart(callback);
2896                         }
2897                         else {
2898                                 if(typeof callback !== 'undefined')
2899                                         callback();
2900                         }
2901                 };
2902
2903                 this._defaultsFromDownloadedChart = function(chart) {
2904                         this.chart = chart;
2905                         this.chart_url = chart.url;
2906                         this.data_update_every = chart.update_every * 1000;
2907                         this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2908                         this.tm.last_info_downloaded = new Date().getTime();
2909
2910                         if(this.title === null)
2911                                 this.title = chart.title;
2912
2913                         if(this.units === null)
2914                                 this.units = chart.units;
2915                 };
2916
2917                 // fetch the chart description from the netdata server
2918                 this.getChart = function(callback) {
2919                         this.chart = NETDATA.chartRegistry.get(this.host, this.id);
2920                         if(this.chart) {
2921                                 this._defaultsFromDownloadedChart(this.chart);
2922                                 if(typeof callback === 'function') callback();
2923                         }
2924                         else {
2925                                 this.chart_url = "/api/v1/chart?chart=" + this.id;
2926
2927                                 if(this.debug === true)
2928                                         this.log('downloading ' + this.chart_url);
2929
2930                                 $.ajax( {
2931                                         url:  this.host + this.chart_url,
2932                                         crossDomain: NETDATA.options.crossDomainAjax,
2933                                         cache: false,
2934                                         async: true
2935                                 })
2936                                 .done(function(chart) {
2937                                         chart.url = that.chart_url;
2938                                         that._defaultsFromDownloadedChart(chart);
2939                                         NETDATA.chartRegistry.add(that.host, that.id, chart);
2940                                 })
2941                                 .fail(function() {
2942                                         NETDATA.error(404, that.chart_url);
2943                                         error('chart not found on url "' + that.chart_url + '"');
2944                                 })
2945                                 .always(function() {
2946                                         if(typeof callback === 'function') callback();
2947                                 });
2948                         }
2949                 };
2950
2951                 // ============================================================================================================
2952                 // INITIALIZATION
2953
2954                 init();
2955         };
2956
2957         NETDATA.resetAllCharts = function(state) {
2958                 // first clear the global selection sync
2959                 // to make sure no chart is in selected state
2960                 state.globalSelectionSyncStop();
2961
2962                 // there are 2 possibilities here
2963                 // a. state is the global Pan and Zoom master
2964                 // b. state is not the global Pan and Zoom master
2965                 var master = true;
2966                 if(NETDATA.globalPanAndZoom.isMaster(state) === false)
2967                         master = false;
2968
2969                 // clear the global Pan and Zoom
2970                 // this will also refresh the master
2971                 // and unblock any charts currently mirroring the master
2972                 NETDATA.globalPanAndZoom.clearMaster();
2973
2974                 // if we were not the master, reset our status too
2975                 // this is required because most probably the mouse
2976                 // is over this chart, blocking it from auto-refreshing
2977                 if(master === false && (state.paused === true || state.selected === true))
2978                         state.resetChart();
2979         };
2980
2981         // get or create a chart state, given a DOM element
2982         NETDATA.chartState = function(element) {
2983                 var state = $(element).data('netdata-state-object') || null;
2984                 if(state === null) {
2985                         state = new chartState(element);
2986                         $(element).data('netdata-state-object', state);
2987                 }
2988                 return state;
2989         };
2990
2991         // ----------------------------------------------------------------------------------------------------------------
2992         // Library functions
2993
2994         // Load a script without jquery
2995         // This is used to load jquery - after it is loaded, we use jquery
2996         NETDATA._loadjQuery = function(callback) {
2997                 if(typeof jQuery === 'undefined') {
2998                         if(NETDATA.options.debug.main_loop === true)
2999                                 console.log('loading ' + NETDATA.jQuery);
3000
3001                         var script = document.createElement('script');
3002                         script.type = 'text/javascript';
3003                         script.async = true;
3004                         script.src = NETDATA.jQuery;
3005
3006                         // script.onabort = onError;
3007                         script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
3008                         if(typeof callback === "function")
3009                                 script.onload = callback;
3010
3011                         var s = document.getElementsByTagName('script')[0];
3012                         s.parentNode.insertBefore(script, s);
3013                 }
3014                 else if(typeof callback === "function")
3015                         callback();
3016         };
3017
3018         NETDATA._loadCSS = function(filename) {
3019                 // don't use jQuery here
3020                 // styles are loaded before jQuery
3021                 // to eliminate showing an unstyled page to the user
3022
3023                 var fileref = document.createElement("link");
3024                 fileref.setAttribute("rel", "stylesheet");
3025                 fileref.setAttribute("type", "text/css");
3026                 fileref.setAttribute("href", filename);
3027
3028                 if (typeof fileref !== 'undefined')
3029                         document.getElementsByTagName("head")[0].appendChild(fileref);
3030         };
3031
3032         NETDATA.colorHex2Rgb = function(hex) {
3033                 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
3034                 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3035                         hex = hex.replace(shorthandRegex, function(m, r, g, b) {
3036                         return r + r + g + g + b + b;
3037                 });
3038
3039                 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
3040                 return result ? {
3041                         r: parseInt(result[1], 16),
3042                         g: parseInt(result[2], 16),
3043                         b: parseInt(result[3], 16)
3044                 } : null;
3045         };
3046
3047         NETDATA.colorLuminance = function(hex, lum) {
3048                 // validate hex string
3049                 hex = String(hex).replace(/[^0-9a-f]/gi, '');
3050                 if (hex.length < 6)
3051                         hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3052
3053                 lum = lum || 0;
3054
3055                 // convert to decimal and change luminosity
3056                 var rgb = "#", c, i;
3057                 for (i = 0; i < 3; i++) {
3058                         c = parseInt(hex.substr(i*2,2), 16);
3059                         c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
3060                         rgb += ("00"+c).substr(c.length);
3061                 }
3062
3063                 return rgb;
3064         };
3065
3066         NETDATA.guid = function() {
3067                 function s4() {
3068                         return Math.floor((1 + Math.random()) * 0x10000)
3069                                         .toString(16)
3070                                         .substring(1);
3071                         }
3072
3073                         return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3074         };
3075
3076         NETDATA.zeropad = function(x) {
3077                 if(x > -10 && x < 10) return '0' + x.toString();
3078                 else return x.toString();
3079         };
3080
3081         // user function to signal us the DOM has been
3082         // updated.
3083         NETDATA.updatedDom = function() {
3084                 NETDATA.options.updated_dom = true;
3085         };
3086
3087         NETDATA.ready = function(callback) {
3088                 NETDATA.options.pauseCallback = callback;
3089         };
3090
3091         NETDATA.pause = function(callback) {
3092                 if(NETDATA.options.pause === true)
3093                         callback();
3094                 else
3095                         NETDATA.options.pauseCallback = callback;
3096         };
3097
3098         NETDATA.unpause = function() {
3099                 NETDATA.options.pauseCallback = null;
3100                 NETDATA.options.updated_dom = true;
3101                 NETDATA.options.pause = false;
3102         };
3103
3104         // ----------------------------------------------------------------------------------------------------------------
3105
3106         // this is purely sequencial charts refresher
3107         // it is meant to be autonomous
3108         NETDATA.chartRefresherNoParallel = function(index) {
3109                 if(NETDATA.options.debug.mail_loop === true)
3110                         console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
3111
3112                 if(NETDATA.options.updated_dom === true) {
3113                         // the dom has been updated
3114                         // get the dom parts again
3115                         NETDATA.parseDom(NETDATA.chartRefresher);
3116                         return;
3117                 }
3118                 if(index >= NETDATA.options.targets.length) {
3119                         if(NETDATA.options.debug.main_loop === true)
3120                                 console.log('waiting to restart main loop...');
3121
3122                         NETDATA.options.auto_refresher_fast_weight = 0;
3123
3124                         setTimeout(function() {
3125                                 NETDATA.chartRefresher();
3126                         }, NETDATA.options.current.idle_between_loops);
3127                 }
3128                 else {
3129                         var state = NETDATA.options.targets[index];
3130
3131                         if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
3132                                 if(NETDATA.options.debug.main_loop === true)
3133                                         console.log('fast rendering...');
3134
3135                                 state.autoRefresh(function() {
3136                                         NETDATA.chartRefresherNoParallel(++index);
3137                                 });
3138                         }
3139                         else {
3140                                 if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
3141                                 NETDATA.options.auto_refresher_fast_weight = 0;
3142
3143                                 setTimeout(function() {
3144                                         state.autoRefresh(function() {
3145                                                 NETDATA.chartRefresherNoParallel(++index);
3146                                         });
3147                                 }, NETDATA.options.current.idle_between_charts);
3148                         }
3149                 }
3150         };
3151
3152         // this is part of the parallel refresher
3153         // its cause is to refresh sequencially all the charts
3154         // that depend on chart library initialization
3155         // it will call the parallel refresher back
3156         // as soon as it sees a chart that its chart library
3157         // is initialized
3158         NETDATA.chartRefresher_uninitialized = function() {
3159                 if(NETDATA.options.updated_dom === true) {
3160                         // the dom has been updated
3161                         // get the dom parts again
3162                         NETDATA.parseDom(NETDATA.chartRefresher);
3163                         return;
3164                 }
3165
3166                 if(NETDATA.options.sequencial.length === 0)
3167                         NETDATA.chartRefresher();
3168                 else {
3169                         var state = NETDATA.options.sequencial.pop();
3170                         if(state.library.initialized === true)
3171                                 NETDATA.chartRefresher();
3172                         else
3173                                 state.autoRefresh(NETDATA.chartRefresher_uninitialized);
3174                 }
3175         };
3176
3177         NETDATA.chartRefresherWaitTime = function() {
3178                 return NETDATA.options.current.idle_parallel_loops;
3179         };
3180
3181         // the default refresher
3182         // it will create 2 sets of charts:
3183         // - the ones that can be refreshed in parallel
3184         // - the ones that depend on something else
3185         // the first set will be executed in parallel
3186         // the second will be given to NETDATA.chartRefresher_uninitialized()
3187         NETDATA.chartRefresher = function() {
3188                 if(NETDATA.options.pause === true) {
3189                         // console.log('auto-refresher is paused');
3190                         setTimeout(NETDATA.chartRefresher,
3191                                 NETDATA.chartRefresherWaitTime());
3192                         return;
3193                 }
3194
3195                 if(typeof NETDATA.options.pauseCallback === 'function') {
3196                         // console.log('auto-refresher is calling pauseCallback');
3197                         NETDATA.options.pause = true;
3198                         NETDATA.options.pauseCallback();
3199                         NETDATA.chartRefresher();
3200                         return;
3201                 }
3202
3203                 if(NETDATA.options.current.parallel_refresher === false) {
3204                         NETDATA.chartRefresherNoParallel(0);
3205                         return;
3206                 }
3207
3208                 if(NETDATA.options.updated_dom === true) {
3209                         // the dom has been updated
3210                         // get the dom parts again
3211                         NETDATA.parseDom(NETDATA.chartRefresher);
3212                         return;
3213                 }
3214
3215                 var parallel = new Array();
3216                 var targets = NETDATA.options.targets;
3217                 var len = targets.length;
3218                 while(len--) {
3219                         if(targets[len].isVisible() === false)
3220                                 continue;
3221
3222                         var state = targets[len];
3223                         if(state.library.initialized === false) {
3224                                 if(state.library.enabled === true) {
3225                                         state.library.initialize(NETDATA.chartRefresher);
3226                                         return;
3227                                 }
3228                                 else {
3229                                         state.error('chart library "' + state.library_name + '" is not enabled.');
3230                                 }
3231                         }
3232
3233                         parallel.unshift(state);
3234                 }
3235
3236                 if(parallel.length > 0) {
3237                         var parallel_jobs = parallel.length;
3238
3239                         // this will execute the jobs in parallel
3240                         $(parallel).each(function() {
3241                                 this.autoRefresh(function() {
3242                                         parallel_jobs--;
3243
3244                                         if(parallel_jobs === 0) {
3245                                                 setTimeout(NETDATA.chartRefresher,
3246                                                         NETDATA.chartRefresherWaitTime());
3247                                         }
3248                                 });
3249                         })
3250                 }
3251                 else {
3252                         setTimeout(NETDATA.chartRefresher,
3253                                 NETDATA.chartRefresherWaitTime());
3254                 }
3255         };
3256
3257         NETDATA.parseDom = function(callback) {
3258                 NETDATA.options.last_page_scroll = new Date().getTime();
3259                 NETDATA.options.updated_dom = false;
3260
3261                 var targets = $('div[data-netdata]'); //.filter(':visible');
3262
3263                 if(NETDATA.options.debug.main_loop === true)
3264                         console.log('DOM updated - there are ' + targets.length + ' charts on page.');
3265
3266                 NETDATA.options.targets = new Array();
3267                 var len = targets.length;
3268                 while(len--) {
3269                         // the initialization will take care of sizing
3270                         // and the "loading..." message
3271                         NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
3272                 }
3273
3274                 if(typeof callback === 'function') callback();
3275         };
3276
3277         // this is the main function - where everything starts
3278         NETDATA.start = function() {
3279                 // this should be called only once
3280
3281                 NETDATA.options.page_is_visible = true;
3282
3283                 $(window).blur(function() {
3284                         if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3285                                 NETDATA.options.page_is_visible = false;
3286                                 if(NETDATA.options.debug.focus === true)
3287                                         console.log('Lost Focus!');
3288                         }
3289                 });
3290
3291                 $(window).focus(function() {
3292                         if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3293                                 NETDATA.options.page_is_visible = true;
3294                                 if(NETDATA.options.debug.focus === true)
3295                                         console.log('Focus restored!');
3296                         }
3297                 });
3298
3299                 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
3300                         if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
3301                                 NETDATA.options.page_is_visible = false;
3302                                 if(NETDATA.options.debug.focus === true)
3303                                         console.log('Document has no focus!');
3304                         }
3305                 }
3306
3307                 // bootstrap tab switching
3308                 $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
3309
3310                 // bootstrap modal switching
3311                 $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
3312                 $('.modal').on('shown.bs.modal', NETDATA.onscroll);
3313
3314                 NETDATA.parseDom(NETDATA.chartRefresher);
3315         };
3316
3317         // ----------------------------------------------------------------------------------------------------------------
3318         // peity
3319
3320         NETDATA.peityInitialize = function(callback) {
3321                 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
3322                         $.ajax({
3323                                 url: NETDATA.peity_js,
3324                                 cache: true,
3325                                 dataType: "script"
3326                         })
3327                         .done(function() {
3328                                 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
3329                         })
3330                         .fail(function() {
3331                                 NETDATA.chartLibraries.peity.enabled = false;
3332                                 NETDATA.error(100, NETDATA.peity_js);
3333                         })
3334                         .always(function() {
3335                                 if(typeof callback === "function")
3336                                         callback();
3337                         });
3338                 }
3339                 else {
3340                         NETDATA.chartLibraries.peity.enabled = false;
3341                         if(typeof callback === "function")
3342                                 callback();
3343                 }
3344         };
3345
3346         NETDATA.peityChartUpdate = function(state, data) {
3347                 state.peity_instance.innerHTML = data.result;
3348
3349                 if(state.peity_options.stroke !== state.chartColors()[0]) {
3350                         state.peity_options.stroke = state.chartColors()[0];
3351                         if(state.chart.chart_type === 'line')
3352                                 state.peity_options.fill = NETDATA.themes.current.background;
3353                         else
3354                                 state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
3355                 }
3356
3357                 $(state.peity_instance).peity('line', state.peity_options);
3358                 return true;
3359         };
3360
3361         NETDATA.peityChartCreate = function(state, data) {
3362                 state.peity_instance = document.createElement('div');
3363                 state.element_chart.appendChild(state.peity_instance);
3364
3365                 var self = $(state.element);
3366                 state.peity_options = {
3367                         stroke: NETDATA.themes.current.foreground,
3368                         strokeWidth: self.data('peity-strokewidth') || 1,
3369                         width: state.chartWidth(),
3370                         height: state.chartHeight(),
3371                         fill: NETDATA.themes.current.foreground
3372                 };
3373
3374                 NETDATA.peityChartUpdate(state, data);
3375                 return true;
3376         };
3377
3378         // ----------------------------------------------------------------------------------------------------------------
3379         // sparkline
3380
3381         NETDATA.sparklineInitialize = function(callback) {
3382                 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
3383                         $.ajax({
3384                                 url: NETDATA.sparkline_js,
3385                                 cache: true,
3386                                 dataType: "script"
3387                         })
3388                         .done(function() {
3389                                 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
3390                         })
3391                         .fail(function() {
3392                                 NETDATA.chartLibraries.sparkline.enabled = false;
3393                                 NETDATA.error(100, NETDATA.sparkline_js);
3394                         })
3395                         .always(function() {
3396                                 if(typeof callback === "function")
3397                                         callback();
3398                         });
3399                 }
3400                 else {
3401                         NETDATA.chartLibraries.sparkline.enabled = false;
3402                         if(typeof callback === "function")
3403                                 callback();
3404                 }
3405         };
3406
3407         NETDATA.sparklineChartUpdate = function(state, data) {
3408                 state.sparkline_options.width = state.chartWidth();
3409                 state.sparkline_options.height = state.chartHeight();
3410
3411                 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3412                 return true;
3413         };
3414
3415         NETDATA.sparklineChartCreate = function(state, data) {
3416                 var self = $(state.element);
3417                 var type = self.data('sparkline-type') || 'line';
3418                 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
3419                 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
3420                 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
3421                 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
3422                 var composite = self.data('sparkline-composite') || undefined;
3423                 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
3424                 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
3425                 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
3426                 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
3427                 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
3428                 var spotColor = self.data('sparkline-spotcolor') || undefined;
3429                 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
3430                 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
3431                 var spotRadius = self.data('sparkline-spotradius') || undefined;
3432                 var valueSpots = self.data('sparkline-valuespots') || undefined;
3433                 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
3434                 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
3435                 var lineWidth = self.data('sparkline-linewidth') || undefined;
3436                 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
3437                 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
3438                 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
3439                 var xvalues = self.data('sparkline-xvalues') || undefined;
3440                 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
3441                 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
3442                 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
3443                 var disableInteraction = self.data('sparkline-disableinteraction') || false;
3444                 var disableTooltips = self.data('sparkline-disabletooltips') || false;
3445                 var disableHighlight = self.data('sparkline-disablehighlight') || false;
3446                 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
3447                 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
3448                 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
3449                 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
3450                 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
3451                 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
3452                 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
3453                 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
3454                 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
3455                 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
3456                 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
3457                 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
3458                 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
3459                 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
3460                 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
3461                 var animatedZooms = self.data('sparkline-animatedzooms') || false;
3462
3463                 state.sparkline_options = {
3464                         type: type,
3465                         lineColor: lineColor,
3466                         fillColor: fillColor,
3467                         chartRangeMin: chartRangeMin,
3468                         chartRangeMax: chartRangeMax,
3469                         composite: composite,
3470                         enableTagOptions: enableTagOptions,
3471                         tagOptionPrefix: tagOptionPrefix,
3472                         tagValuesAttribute: tagValuesAttribute,
3473                         disableHiddenCheck: disableHiddenCheck,
3474                         defaultPixelsPerValue: defaultPixelsPerValue,
3475                         spotColor: spotColor,
3476                         minSpotColor: minSpotColor,
3477                         maxSpotColor: maxSpotColor,
3478                         spotRadius: spotRadius,
3479                         valueSpots: valueSpots,
3480                         highlightSpotColor: highlightSpotColor,
3481                         highlightLineColor: highlightLineColor,
3482                         lineWidth: lineWidth,
3483                         normalRangeMin: normalRangeMin,
3484                         normalRangeMax: normalRangeMax,
3485                         drawNormalOnTop: drawNormalOnTop,
3486                         xvalues: xvalues,
3487                         chartRangeClip: chartRangeClip,
3488                         chartRangeMinX: chartRangeMinX,
3489                         chartRangeMaxX: chartRangeMaxX,
3490                         disableInteraction: disableInteraction,
3491                         disableTooltips: disableTooltips,
3492                         disableHighlight: disableHighlight,
3493                         highlightLighten: highlightLighten,
3494                         highlightColor: highlightColor,
3495                         tooltipContainer: tooltipContainer,
3496                         tooltipClassname: tooltipClassname,
3497                         tooltipChartTitle: state.title,
3498                         tooltipFormat: tooltipFormat,
3499                         tooltipPrefix: tooltipPrefix,
3500                         tooltipSuffix: tooltipSuffix,
3501                         tooltipSkipNull: tooltipSkipNull,
3502                         tooltipValueLookups: tooltipValueLookups,
3503                         tooltipFormatFieldlist: tooltipFormatFieldlist,
3504                         tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
3505                         numberFormatter: numberFormatter,
3506                         numberDigitGroupSep: numberDigitGroupSep,
3507                         numberDecimalMark: numberDecimalMark,
3508                         numberDigitGroupCount: numberDigitGroupCount,
3509                         animatedZooms: animatedZooms,
3510                         width: state.chartWidth(),
3511                         height: state.chartHeight()
3512                 };
3513
3514                 $(state.element_chart).sparkline(data.result, state.sparkline_options);
3515                 return true;
3516         };
3517
3518         // ----------------------------------------------------------------------------------------------------------------
3519         // dygraph
3520
3521         NETDATA.dygraph = {
3522                 smooth: false
3523         };
3524
3525         NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
3526                 if(after < state.netdata_first)
3527                         after = state.netdata_first;
3528
3529                 if(before > state.netdata_last)
3530                         before = state.netdata_last;
3531
3532                 state.setMode('zoom');
3533                 state.globalSelectionSyncStop();
3534                 state.globalSelectionSyncDelay();
3535                 state.dygraph_user_action = true;
3536                 state.dygraph_force_zoom = true;
3537                 state.updateChartPanOrZoom(after, before);
3538                 NETDATA.globalPanAndZoom.setMaster(state, after, before);
3539         };
3540
3541         NETDATA.dygraphSetSelection = function(state, t) {
3542                 if(typeof state.dygraph_instance !== 'undefined') {
3543                         var r = state.calculateRowForTime(t);
3544                         if(r !== -1)
3545                                 state.dygraph_instance.setSelection(r);
3546                         else {
3547                                 state.dygraph_instance.clearSelection();
3548                                 state.legendShowUndefined();
3549                         }
3550                 }
3551
3552                 return true;
3553         };
3554
3555         NETDATA.dygraphClearSelection = function(state, t) {
3556                 if(typeof state.dygraph_instance !== 'undefined') {
3557                         state.dygraph_instance.clearSelection();
3558                 }
3559                 return true;
3560         };
3561
3562         NETDATA.dygraphSmoothInitialize = function(callback) {
3563                 $.ajax({
3564                         url: NETDATA.dygraph_smooth_js,
3565                         cache: true,
3566                         dataType: "script"
3567                 })
3568                 .done(function() {
3569                         NETDATA.dygraph.smooth = true;
3570                         smoothPlotter.smoothing = 0.3;
3571                 })
3572                 .fail(function() {
3573                         NETDATA.dygraph.smooth = false;
3574                 })
3575                 .always(function() {
3576                         if(typeof callback === "function")
3577                                 callback();
3578                 });
3579         };
3580
3581         NETDATA.dygraphInitialize = function(callback) {
3582                 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
3583                         $.ajax({
3584                                 url: NETDATA.dygraph_js,
3585                                 cache: true,
3586                                 dataType: "script"
3587                         })
3588                         .done(function() {
3589                                 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
3590                         })
3591                         .fail(function() {
3592                                 NETDATA.chartLibraries.dygraph.enabled = false;
3593                                 NETDATA.error(100, NETDATA.dygraph_js);
3594                         })
3595                         .always(function() {
3596                                 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
3597                                         NETDATA.dygraphSmoothInitialize(callback);
3598                                 else if(typeof callback === "function")
3599                                         callback();
3600                         });
3601                 }
3602                 else {
3603                         NETDATA.chartLibraries.dygraph.enabled = false;
3604                         if(typeof callback === "function")
3605                                 callback();
3606                 }
3607         };
3608
3609         NETDATA.dygraphChartUpdate = function(state, data) {
3610                 var dygraph = state.dygraph_instance;
3611
3612                 if(typeof dygraph === 'undefined')
3613                         return NETDATA.dygraphChartCreate(state, data);
3614
3615                 // when the chart is not visible, and hidden
3616                 // if there is a window resize, dygraph detects
3617                 // its element size as 0x0.
3618                 // this will make it re-appear properly
3619
3620                 if(state.tm.last_unhidden > state.dygraph_last_rendered)
3621                         dygraph.resize();
3622
3623                 var options = {
3624                                 file: data.result.data,
3625                                 colors: state.chartColors(),
3626                                 labels: data.result.labels,
3627                                 labelsDivWidth: state.chartWidth() - 70,
3628                                 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3629                 };
3630
3631                 if(state.dygraph_force_zoom === true) {
3632                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3633                                 state.log('dygraphChartUpdate() forced zoom update');
3634
3635                         options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3636                         options.valueRange = null;
3637                         options.isZoomedIgnoreProgrammaticZoom = true;
3638                         state.dygraph_force_zoom = false;
3639                 }
3640                 else if(state.current.name !== 'auto') {
3641                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3642                                 state.log('dygraphChartUpdate() loose update');
3643                 }
3644                 else {
3645                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3646                                 state.log('dygraphChartUpdate() strict update');
3647
3648                         options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3649                         options.valueRange = null;
3650                         options.isZoomedIgnoreProgrammaticZoom = true;
3651                 }
3652
3653                 if(state.dygraph_smooth_eligible === true) {
3654                         if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
3655                                 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
3656                                 NETDATA.dygraphChartCreate(state, data);
3657                                 return;
3658                         }
3659                 }
3660
3661                 dygraph.updateOptions(options);
3662
3663                 state.dygraph_last_rendered = new Date().getTime();
3664                 return true;
3665         };
3666
3667         NETDATA.dygraphChartCreate = function(state, data) {
3668                 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3669                         state.log('dygraphChartCreate()');
3670
3671                 var self = $(state.element);
3672
3673                 var chart_type = state.chart.chart_type;
3674                 if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
3675                 chart_type = self.data('dygraph-type') || chart_type;
3676
3677                 var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
3678                 smooth = self.data('dygraph-smooth') || smooth;
3679
3680                 if(NETDATA.dygraph.smooth === false)
3681                         smooth = false;
3682
3683                 var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
3684                 var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
3685
3686                 state.dygraph_options = {
3687                         colors: self.data('dygraph-colors') || state.chartColors(),
3688
3689                         // leave a few pixels empty on the right of the chart
3690                         rightGap: self.data('dygraph-rightgap') || 5,
3691                         showRangeSelector: self.data('dygraph-showrangeselector') || false,
3692                         showRoller: self.data('dygraph-showroller') || false,
3693
3694                         title: self.data('dygraph-title') || state.title,
3695                         titleHeight: self.data('dygraph-titleheight') || 19,
3696
3697                         legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
3698                         labels: data.result.labels,
3699                         labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
3700                         labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
3701                         labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
3702                         labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
3703                         labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
3704                         labelsKMB: false,
3705                         labelsKMG2: false,
3706                         showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
3707                         hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
3708
3709                         ylabel: state.units,
3710                         yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
3711
3712                         // the function to plot the chart
3713                         plotter: null,
3714
3715                         // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
3716                         strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
3717                         strokePattern: self.data('dygraph-strokepattern') || undefined,
3718
3719                         // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
3720                         // i.e. there is a missing point on either side of it. This also controls the size of those dots.
3721                         drawPoints: self.data('dygraph-drawpoints') || false,
3722
3723                         // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
3724                         drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
3725
3726                         connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
3727                         pointSize: self.data('dygraph-pointsize') || 1,
3728
3729                         // enabling this makes the chart with little square lines
3730                         stepPlot: self.data('dygraph-stepplot') || false,
3731
3732                         // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
3733                         strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
3734                         strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
3735
3736                         fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false,
3737                         fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area,
3738                         stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false,
3739                         stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
3740
3741                         drawAxis: self.data('dygraph-drawaxis') || true,
3742                         axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
3743                         axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
3744                         axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
3745
3746                         drawGrid: self.data('dygraph-drawgrid') || true,
3747                         drawXGrid: self.data('dygraph-drawxgrid') || undefined,
3748                         drawYGrid: self.data('dygraph-drawygrid') || undefined,
3749                         gridLinePattern: self.data('dygraph-gridlinepattern') || null,
3750                         gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
3751                         gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
3752
3753                         maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
3754                         sigFigs: self.data('dygraph-sigfigs') || null,
3755                         digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
3756                         valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
3757
3758                         highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
3759                         highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
3760                         highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
3761
3762                         pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
3763                         visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
3764                         axes: {
3765                                 x: {
3766                                         pixelsPerLabel: 50,
3767                                         ticker: Dygraph.dateTicker,
3768                                         axisLabelFormatter: function (d, gran) {
3769                                                 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3770                                         },
3771                                         valueFormatter: function (ms) {
3772                                                 var d = new Date(ms);
3773                                                 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
3774                                                 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
3775                                         }
3776                                 },
3777                                 y: {
3778                                         pixelsPerLabel: 15,
3779                                         valueFormatter: function (x) {
3780                                                 // we format legends with the state object
3781                                                 // no need to do anything here
3782                                                 // return (Math.round(x*100) / 100).toLocaleString();
3783                                                 // return state.legendFormatValue(x);
3784                                                 return x;
3785                                         }
3786                                 }
3787                         },
3788                         legendFormatter: function(data) {
3789                                 var elements = state.element_legend_childs;
3790
3791                                 // if the hidden div is not there
3792                                 // we are not managing the legend
3793                                 if(elements.hidden === null) return;
3794
3795                                 if (typeof data.x !== 'undefined') {
3796                                         state.legendSetDate(data.x);
3797                                         var i = data.series.length;
3798                                         while(i--) {
3799                                                 var series = data.series[i];
3800                                                 if(!series.isVisible) continue;
3801                                                 state.legendSetLabelValue(series.label, series.y);
3802                                         }
3803                                 }
3804
3805                                 return '';
3806                         },
3807                         drawCallback: function(dygraph, is_initial) {
3808                                 if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
3809                                         state.dygraph_user_action = false;
3810
3811                                         var x_range = dygraph.xAxisRange();
3812                                         var after = Math.round(x_range[0]);
3813                                         var before = Math.round(x_range[1]);
3814
3815                                         if(NETDATA.options.debug.dygraph === true)
3816                                                 state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
3817
3818                                         if(before <= state.netdata_last && after >= state.netdata_first)
3819                                                 state.updateChartPanOrZoom(after, before);
3820                                 }
3821                         },
3822                         zoomCallback: function(minDate, maxDate, yRanges) {
3823                                 if(NETDATA.options.debug.dygraph === true)
3824                                         state.log('dygraphZoomCallback()');
3825
3826                                 state.globalSelectionSyncStop();
3827                                 state.globalSelectionSyncDelay();
3828                                 state.setMode('zoom');
3829
3830                                 // refresh it to the greatest possible zoom level
3831                                 state.dygraph_user_action = true;
3832                                 state.dygraph_force_zoom = true;
3833                                 state.updateChartPanOrZoom(minDate, maxDate);
3834                         },
3835                         highlightCallback: function(event, x, points, row, seriesName) {
3836                                 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3837                                         state.log('dygraphHighlightCallback()');
3838
3839                                 state.pauseChart();
3840
3841                                 // there is a bug in dygraph when the chart is zoomed enough
3842                                 // the time it thinks is selected is wrong
3843                                 // here we calculate the time t based on the row number selected
3844                                 // which is ok
3845                                 var t = state.data_after + row * state.data_update_every;
3846                                 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
3847
3848                                 state.globalSelectionSync(x);
3849
3850                                 // fix legend zIndex using the internal structures of dygraph legend module
3851                                 // this works, but it is a hack!
3852                                 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
3853                         },
3854                         unhighlightCallback: function(event) {
3855                                 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3856                                         state.log('dygraphUnhighlightCallback()');
3857
3858                                 state.unpauseChart();
3859                                 state.globalSelectionSyncStop();
3860                         },
3861                         interactionModel : {
3862                                 mousedown: function(event, dygraph, context) {
3863                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3864                                                 state.log('interactionModel.mousedown()');
3865
3866                                         state.dygraph_user_action = true;
3867                                         state.globalSelectionSyncStop();
3868
3869                                         if(NETDATA.options.debug.dygraph === true)
3870                                                 state.log('dygraphMouseDown()');
3871
3872                                         // Right-click should not initiate a zoom.
3873                                         if(event.button && event.button === 2) return;
3874
3875                                         context.initializeMouseDown(event, dygraph, context);
3876
3877                                         if(event.button && event.button === 1) {
3878                                                 if (event.altKey || event.shiftKey) {
3879                                                         state.setMode('pan');
3880                                                         state.globalSelectionSyncDelay();
3881                                                         Dygraph.startPan(event, dygraph, context);
3882                                                 }
3883                                                 else {
3884                                                         state.setMode('zoom');
3885                                                         state.globalSelectionSyncDelay();
3886                                                         Dygraph.startZoom(event, dygraph, context);
3887                                                 }
3888                                         }
3889                                         else {
3890                                                 if (event.altKey || event.shiftKey) {
3891                                                         state.setMode('zoom');
3892                                                         state.globalSelectionSyncDelay();
3893                                                         Dygraph.startZoom(event, dygraph, context);
3894                                                 }
3895                                                 else {
3896                                                         state.setMode('pan');
3897                                                         state.globalSelectionSyncDelay();
3898                                                         Dygraph.startPan(event, dygraph, context);
3899                                                 }
3900                                         }
3901                                 },
3902                                 mousemove: function(event, dygraph, context) {
3903                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3904                                                 state.log('interactionModel.mousemove()');
3905
3906                                         if(context.isPanning) {
3907                                                 state.dygraph_user_action = true;
3908                                                 state.globalSelectionSyncStop();
3909                                                 state.globalSelectionSyncDelay();
3910                                                 state.setMode('pan');
3911                                                 Dygraph.movePan(event, dygraph, context);
3912                                         }
3913                                         else if(context.isZooming) {
3914                                                 state.dygraph_user_action = true;
3915                                                 state.globalSelectionSyncStop();
3916                                                 state.globalSelectionSyncDelay();
3917                                                 state.setMode('zoom');
3918                                                 Dygraph.moveZoom(event, dygraph, context);
3919                                         }
3920                                 },
3921                                 mouseup: function(event, dygraph, context) {
3922                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3923                                                 state.log('interactionModel.mouseup()');
3924
3925                                         if (context.isPanning) {
3926                                                 state.dygraph_user_action = true;
3927                                                 state.globalSelectionSyncDelay();
3928                                                 Dygraph.endPan(event, dygraph, context);
3929                                         }
3930                                         else if (context.isZooming) {
3931                                                 state.dygraph_user_action = true;
3932                                                 state.globalSelectionSyncDelay();
3933                                                 Dygraph.endZoom(event, dygraph, context);
3934                                         }
3935                                 },
3936                                 click: function(event, dygraph, context) {
3937                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3938                                                 state.log('interactionModel.click()');
3939
3940                                         event.preventDefault();
3941                                 },
3942                                 dblclick: function(event, dygraph, context) {
3943                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3944                                                 state.log('interactionModel.dblclick()');
3945                                         NETDATA.resetAllCharts(state);
3946                                 },
3947                                 mousewheel: function(event, dygraph, context) {
3948                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
3949                                                 state.log('interactionModel.mousewheel()');
3950
3951                                         // Take the offset of a mouse event on the dygraph canvas and
3952                                         // convert it to a pair of percentages from the bottom left.
3953                                         // (Not top left, bottom is where the lower value is.)
3954                                         function offsetToPercentage(g, offsetX, offsetY) {
3955                                                 // This is calculating the pixel offset of the leftmost date.
3956                                                 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
3957                                                 var yar0 = g.yAxisRange(0);
3958
3959                                                 // This is calculating the pixel of the higest value. (Top pixel)
3960                                                 var yOffset = g.toDomCoords(null, yar0[1])[1];
3961
3962                                                 // x y w and h are relative to the corner of the drawing area,
3963                                                 // so that the upper corner of the drawing area is (0, 0).
3964                                                 var x = offsetX - xOffset;
3965                                                 var y = offsetY - yOffset;
3966
3967                                                 // This is computing the rightmost pixel, effectively defining the
3968                                                 // width.
3969                                                 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
3970
3971                                                 // This is computing the lowest pixel, effectively defining the height.
3972                                                 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
3973
3974                                                 // Percentage from the left.
3975                                                 var xPct = w === 0 ? 0 : (x / w);
3976                                                 // Percentage from the top.
3977                                                 var yPct = h === 0 ? 0 : (y / h);
3978
3979                                                 // The (1-) part below changes it from "% distance down from the top"
3980                                                 // to "% distance up from the bottom".
3981                                                 return [xPct, (1-yPct)];
3982                                         }
3983
3984                                         // Adjusts [x, y] toward each other by zoomInPercentage%
3985                                         // Split it so the left/bottom axis gets xBias/yBias of that change and
3986                                         // tight/top gets (1-xBias)/(1-yBias) of that change.
3987                                         //
3988                                         // If a bias is missing it splits it down the middle.
3989                                         function zoomRange(g, zoomInPercentage, xBias, yBias) {
3990                                                 xBias = xBias || 0.5;
3991                                                 yBias = yBias || 0.5;
3992
3993                                                 function adjustAxis(axis, zoomInPercentage, bias) {
3994                                                         var delta = axis[1] - axis[0];
3995                                                         var increment = delta * zoomInPercentage;
3996                                                         var foo = [increment * bias, increment * (1-bias)];
3997
3998                                                         return [ axis[0] + foo[0], axis[1] - foo[1] ];
3999                                                 }
4000
4001                                                 var yAxes = g.yAxisRanges();
4002                                                 var newYAxes = [];
4003                                                 for (var i = 0; i < yAxes.length; i++) {
4004                                                         newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
4005                                                 }
4006
4007                                                 return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
4008                                         }
4009
4010                                         if(event.altKey || event.shiftKey) {
4011                                                 state.dygraph_user_action = true;
4012
4013                                                 state.globalSelectionSyncStop();
4014                                                 state.globalSelectionSyncDelay();
4015
4016                                                 // http://dygraphs.com/gallery/interaction-api.js
4017                                                 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
4018                                                 var percentage = normal / 50;
4019
4020                                                 if (!(event.offsetX && event.offsetY)){
4021                                                         event.offsetX = event.layerX - event.target.offsetLeft;
4022                                                         event.offsetY = event.layerY - event.target.offsetTop;
4023                                                 }
4024
4025                                                 var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
4026                                                 var xPct = percentages[0];
4027                                                 var yPct = percentages[1];
4028
4029                                                 var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
4030
4031                                                 var after = new_x_range[0];
4032                                                 var before = new_x_range[1];
4033
4034                                                 var first = state.netdata_first + state.data_update_every;
4035                                                 var last = state.netdata_last + state.data_update_every;
4036
4037                                                 if(before > last) {
4038                                                         after -= (before - last);
4039                                                         before = last;
4040                                                 }
4041                                                 if(after < first) {
4042                                                         after = first;
4043                                                 }
4044
4045                                                 state.setMode('zoom');
4046                                                 if(state.updateChartPanOrZoom(after, before) === true)
4047                                                         dygraph.updateOptions({ dateWindow: [ after, before ] });
4048
4049                                                 event.preventDefault();
4050                                         }
4051                                 },
4052                                 touchstart: function(event, dygraph, context) {
4053                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
4054                                                 state.log('interactionModel.touchstart()');
4055
4056                                         state.dygraph_user_action = true;
4057                                         state.setMode('zoom');
4058                                         state.pauseChart();
4059
4060                                         Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
4061
4062                                         // we overwrite the touch directions at the end, to overwrite
4063                                         // the internal default of dygraphs
4064                                         context.touchDirections = { x: true, y: false };
4065
4066                                         state.dygraph_last_touch_start = new Date().getTime();
4067                                         state.dygraph_last_touch_move = 0;
4068
4069                                         if(typeof event.touches[0].pageX === 'number')
4070                                                 state.dygraph_last_touch_page_x = event.touches[0].pageX;
4071                                         else
4072                                                 state.dygraph_last_touch_page_x = 0;
4073                                 },
4074                                 touchmove: function(event, dygraph, context) {
4075                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
4076                                                 state.log('interactionModel.touchmove()');
4077
4078                                         state.dygraph_user_action = true;
4079                                         Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
4080
4081                                         state.dygraph_last_touch_move = new Date().getTime();
4082                                 },
4083                                 touchend: function(event, dygraph, context) {
4084                                         if(NETDATA.options.debug.dygraph === true || state.debug === true)
4085                                                 state.log('interactionModel.touchend()');
4086
4087                                         state.dygraph_user_action = true;
4088                                         Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
4089
4090                                         // if it didn't move, it is a selection
4091                                         if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
4092                                                 // internal api of dygraphs
4093                                                 var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
4094                                                 var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
4095                                                 if(NETDATA.dygraphSetSelection(state, t) === true)
4096                                                         state.globalSelectionSync(t);
4097                                         }
4098
4099                                         // if it was double tap within double click time, reset the charts
4100                                         var now = new Date().getTime();
4101                                         if(typeof state.dygraph_last_touch_end !== 'undefined') {
4102                                                 if(state.dygraph_last_touch_move === 0) {
4103                                                         var dt = now - state.dygraph_last_touch_end;
4104                                                         if(dt <= NETDATA.options.current.double_click_speed)
4105                                                                 NETDATA.resetAllCharts(state);
4106                                                 }
4107                                         }
4108
4109                                         // remember the timestamp of the last touch end
4110                                         state.dygraph_last_touch_end = now;
4111                                 }
4112                         }
4113                 };
4114
4115                 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
4116                         state.dygraph_options.drawGrid = false;
4117                         state.dygraph_options.drawAxis = false;
4118                         state.dygraph_options.title = undefined;
4119                         state.dygraph_options.units = undefined;
4120                         state.dygraph_options.ylabel = undefined;
4121                         state.dygraph_options.yLabelWidth = 0;
4122                         state.dygraph_options.labelsDivWidth = 120;
4123                         state.dygraph_options.labelsDivStyles.width = '120px';
4124                         state.dygraph_options.labelsSeparateLines = true;
4125                         state.dygraph_options.rightGap = 0;
4126                 }
4127
4128                 if(smooth === true) {
4129                         state.dygraph_smooth_eligible = true;
4130
4131                         if(NETDATA.options.current.smooth_plot === true)
4132                                 state.dygraph_options.plotter = smoothPlotter;
4133                 }
4134                 else state.dygraph_smooth_eligible = false;
4135
4136                 state.dygraph_instance = new Dygraph(state.element_chart,
4137                         data.result.data, state.dygraph_options);
4138
4139                 state.dygraph_force_zoom = false;
4140                 state.dygraph_user_action = false;
4141                 state.dygraph_last_rendered = new Date().getTime();
4142                 return true;
4143         };
4144
4145         // ----------------------------------------------------------------------------------------------------------------
4146         // morris
4147
4148         NETDATA.morrisInitialize = function(callback) {
4149                 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
4150
4151                         // morris requires raphael
4152                         if(!NETDATA.chartLibraries.raphael.initialized) {
4153                                 if(NETDATA.chartLibraries.raphael.enabled) {
4154                                         NETDATA.raphaelInitialize(function() {
4155                                                 NETDATA.morrisInitialize(callback);
4156                                         });
4157                                 }
4158                                 else {
4159                                         NETDATA.chartLibraries.morris.enabled = false;
4160                                         if(typeof callback === "function")
4161                                                 callback();
4162                                 }
4163                         }
4164                         else {
4165                                 NETDATA._loadCSS(NETDATA.morris_css);
4166
4167                                 $.ajax({
4168                                         url: NETDATA.morris_js,
4169                                         cache: true,
4170                                         dataType: "script"
4171                                 })
4172                                 .done(function() {
4173                                         NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
4174                                 })
4175                                 .fail(function() {
4176                                         NETDATA.chartLibraries.morris.enabled = false;
4177                                         NETDATA.error(100, NETDATA.morris_js);
4178                                 })
4179                                 .always(function() {
4180                                         if(typeof callback === "function")
4181                                                 callback();
4182                                 });
4183                         }
4184                 }
4185                 else {
4186                         NETDATA.chartLibraries.morris.enabled = false;
4187                         if(typeof callback === "function")
4188                                 callback();
4189                 }
4190         };
4191
4192         NETDATA.morrisChartUpdate = function(state, data) {
4193                 state.morris_instance.setData(data.result.data);
4194                 return true;
4195         };
4196
4197         NETDATA.morrisChartCreate = function(state, data) {
4198
4199                 state.morris_options = {
4200                                 element: state.element_chart.id,
4201                                 data: data.result.data,
4202                                 xkey: 'time',
4203                                 ykeys: data.dimension_names,
4204                                 labels: data.dimension_names,
4205                                 lineWidth: 2,
4206                                 pointSize: 3,
4207                                 smooth: true,
4208                                 hideHover: 'auto',
4209                                 parseTime: true,
4210                                 continuousLine: false,
4211                                 behaveLikeLine: false
4212                 };
4213
4214                 if(state.chart.chart_type === 'line')
4215                         state.morris_instance = new Morris.Line(state.morris_options);
4216
4217                 else if(state.chart.chart_type === 'area') {
4218                         state.morris_options.behaveLikeLine = true;
4219                         state.morris_instance = new Morris.Area(state.morris_options);
4220                 }
4221                 else // stacked
4222                         state.morris_instance = new Morris.Area(state.morris_options);
4223
4224                 return true;
4225         };
4226
4227         // ----------------------------------------------------------------------------------------------------------------
4228         // raphael
4229
4230         NETDATA.raphaelInitialize = function(callback) {
4231                 if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
4232                         $.ajax({
4233                                 url: NETDATA.raphael_js,
4234                                 cache: true,
4235                                 dataType: "script"
4236                         })
4237                         .done(function() {
4238                                 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
4239                         })
4240                         .fail(function() {
4241                                 NETDATA.chartLibraries.raphael.enabled = false;
4242                                 NETDATA.error(100, NETDATA.raphael_js);
4243                         })
4244                         .always(function() {
4245                                 if(typeof callback === "function")
4246                                         callback();
4247                         });
4248                 }
4249                 else {
4250                         NETDATA.chartLibraries.raphael.enabled = false;
4251                         if(typeof callback === "function")
4252                                 callback();
4253                 }
4254         };
4255
4256         NETDATA.raphaelChartUpdate = function(state, data) {
4257                 $(state.element_chart).raphael(data.result, {
4258                         width: state.chartWidth(),
4259                         height: state.chartHeight()
4260                 });
4261
4262                 return false;
4263         };
4264
4265         NETDATA.raphaelChartCreate = function(state, data) {
4266                 $(state.element_chart).raphael(data.result, {
4267                         width: state.chartWidth(),
4268                         height: state.chartHeight()
4269                 });
4270
4271                 return false;
4272         };
4273
4274         // ----------------------------------------------------------------------------------------------------------------
4275         // C3
4276
4277         NETDATA.c3Initialize = function(callback) {
4278                 if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
4279
4280                         // C3 requires D3
4281                         if(!NETDATA.chartLibraries.d3.initialized) {
4282                                 if(NETDATA.chartLibraries.d3.enabled) {
4283                                         NETDATA.d3Initialize(function() {
4284                                                 NETDATA.c3Initialize(callback);
4285                                         });
4286                                 }
4287                                 else {
4288                                         NETDATA.chartLibraries.c3.enabled = false;
4289                                         if(typeof callback === "function")
4290                                                 callback();
4291                                 }
4292                         }
4293                         else {
4294                                 NETDATA._loadCSS(NETDATA.c3_css);
4295
4296                                 $.ajax({
4297                                         url: NETDATA.c3_js,
4298                                         cache: true,
4299                                         dataType: "script"
4300                                 })
4301                                 .done(function() {
4302                                         NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
4303                                 })
4304                                 .fail(function() {
4305                                         NETDATA.chartLibraries.c3.enabled = false;
4306                                         NETDATA.error(100, NETDATA.c3_js);
4307                                 })
4308                                 .always(function() {
4309                                         if(typeof callback === "function")
4310                                                 callback();
4311                                 });
4312                         }
4313                 }
4314                 else {
4315                         NETDATA.chartLibraries.c3.enabled = false;
4316                         if(typeof callback === "function")
4317                                 callback();
4318                 }
4319         };
4320
4321         NETDATA.c3ChartUpdate = function(state, data) {
4322                 state.c3_instance.destroy();
4323                 return NETDATA.c3ChartCreate(state, data);
4324
4325                 //state.c3_instance.load({
4326                 //      rows: data.result,
4327                 //      unload: true
4328                 //});
4329
4330                 //return true;
4331         };
4332
4333         NETDATA.c3ChartCreate = function(state, data) {
4334
4335                 state.element_chart.id = 'c3-' + state.uuid;
4336                 // console.log('id = ' + state.element_chart.id);
4337
4338                 state.c3_instance = c3.generate({
4339                         bindto: '#' + state.element_chart.id,
4340                         size: {
4341                                 width: state.chartWidth(),
4342                                 height: state.chartHeight()
4343                         },
4344                         color: {
4345                                 pattern: state.chartColors()
4346                         },
4347                         data: {
4348                                 x: 'time',
4349                                 rows: data.result,
4350                                 type: (state.chart.chart_type === 'line')?'spline':'area-spline'
4351                         },
4352                         axis: {
4353                                 x: {
4354                                         type: 'timeseries',
4355                                         tick: {
4356                                                 format: function(x) {
4357                                                         return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
4358                                                 }
4359                                         }
4360                                 }
4361                         },
4362                         grid: {
4363                                 x: {
4364                                         show: true
4365                                 },
4366                                 y: {
4367                                         show: true
4368                                 }
4369                         },
4370                         point: {
4371                                 show: false
4372                         },
4373                         line: {
4374                                 connectNull: false
4375                         },
4376                         transition: {
4377                                 duration: 0
4378                         },
4379                         interaction: {
4380                                 enabled: true
4381                         }
4382                 });
4383
4384                 // console.log(state.c3_instance);
4385
4386                 return true;
4387         };
4388
4389         // ----------------------------------------------------------------------------------------------------------------
4390         // D3
4391
4392         NETDATA.d3Initialize = function(callback) {
4393                 if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
4394                         $.ajax({
4395                                 url: NETDATA.d3_js,
4396                                 cache: true,
4397                                 dataType: "script"
4398                         })
4399                         .done(function() {
4400                                 NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
4401                         })
4402                         .fail(function() {
4403                                 NETDATA.chartLibraries.d3.enabled = false;
4404                                 NETDATA.error(100, NETDATA.d3_js);
4405                         })
4406                         .always(function() {
4407                                 if(typeof callback === "function")
4408                                         callback();
4409                         });
4410                 }
4411                 else {
4412                         NETDATA.chartLibraries.d3.enabled = false;
4413                         if(typeof callback === "function")
4414                                 callback();
4415                 }
4416         };
4417
4418         NETDATA.d3ChartUpdate = function(state, data) {
4419                 return false;
4420         };
4421
4422         NETDATA.d3ChartCreate = function(state, data) {
4423                 return false;
4424         };
4425
4426         // ----------------------------------------------------------------------------------------------------------------
4427         // google charts
4428
4429         NETDATA.googleInitialize = function(callback) {
4430                 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
4431                         $.ajax({
4432                                 url: NETDATA.google_js,
4433                                 cache: true,
4434                                 dataType: "script"
4435                         })
4436                         .done(function() {
4437                                 NETDATA.registerChartLibrary('google', NETDATA.google_js);
4438                                 google.load('visualization', '1.1', {
4439                                         'packages': ['corechart', 'controls'],
4440                                         'callback': callback
4441                                 });
4442                         })
4443                         .fail(function() {
4444                                 NETDATA.chartLibraries.google.enabled = false;
4445                                 NETDATA.error(100, NETDATA.google_js);
4446                                 if(typeof callback === "function")
4447                                         callback();
4448                         });
4449                 }
4450                 else {
4451                         NETDATA.chartLibraries.google.enabled = false;
4452                         if(typeof callback === "function")
4453                                 callback();
4454                 }
4455         };
4456
4457         NETDATA.googleChartUpdate = function(state, data) {
4458                 var datatable = new google.visualization.DataTable(data.result);
4459                 state.google_instance.draw(datatable, state.google_options);
4460                 return true;
4461         };
4462
4463         NETDATA.googleChartCreate = function(state, data) {
4464                 var datatable = new google.visualization.DataTable(data.result);
4465
4466                 state.google_options = {
4467                         colors: state.chartColors(),
4468
4469                         // do not set width, height - the chart resizes itself
4470                         //width: state.chartWidth(),
4471                         //height: state.chartHeight(),
4472                         lineWidth: 1,
4473                         title: state.title,
4474                         fontSize: 11,
4475                         hAxis: {
4476                         //      title: "Time of Day",
4477                         //      format:'HH:mm:ss',
4478                                 viewWindowMode: 'maximized',
4479                                 slantedText: false,
4480                                 format:'HH:mm:ss',
4481                                 textStyle: {
4482                                         fontSize: 9
4483                                 },
4484                                 gridlines: {
4485                                         color: '#EEE'
4486                                 }
4487                         },
4488                         vAxis: {
4489                                 title: state.units,
4490                                 viewWindowMode: 'pretty',
4491                                 minValue: -0.1,
4492                                 maxValue: 0.1,
4493                                 direction: 1,
4494                                 textStyle: {
4495                                         fontSize: 9
4496                                 },
4497                                 gridlines: {
4498                                         color: '#EEE'
4499                                 }
4500                         },
4501                         chartArea: {
4502                                 width: '65%',
4503                                 height: '80%'
4504                         },
4505                         focusTarget: 'category',
4506                         annotation: {
4507                                 '1': {
4508                                         style: 'line'
4509                                 }
4510                         },
4511                         pointsVisible: 0,
4512                         titlePosition: 'out',
4513                         titleTextStyle: {
4514                                 fontSize: 11
4515                         },
4516                         tooltip: {
4517                                 isHtml: false,
4518                                 ignoreBounds: true,
4519                                 textStyle: {
4520                                         fontSize: 9
4521                                 }
4522                         },
4523                         curveType: 'function',
4524                         areaOpacity: 0.3,
4525                         isStacked: false
4526                 };
4527
4528                 switch(state.chart.chart_type) {
4529                         case "area":
4530                                 state.google_options.vAxis.viewWindowMode = 'maximized';
4531                                 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
4532                                 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4533                                 break;
4534
4535                         case "stacked":
4536                                 state.google_options.isStacked = true;
4537                                 state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
4538                                 state.google_options.vAxis.viewWindowMode = 'maximized';
4539                                 state.google_options.vAxis.minValue = null;
4540                                 state.google_options.vAxis.maxValue = null;
4541                                 state.google_instance = new google.visualization.AreaChart(state.element_chart);
4542                                 break;
4543
4544                         default:
4545                         case "line":
4546                                 state.google_options.lineWidth = 2;
4547                                 state.google_instance = new google.visualization.LineChart(state.element_chart);
4548                                 break;
4549                 }
4550
4551                 state.google_instance.draw(datatable, state.google_options);
4552                 return true;
4553         };
4554
4555         // ----------------------------------------------------------------------------------------------------------------
4556
4557         NETDATA.percentFromValueMax = function(value, max) {
4558                 if(value === null) value = 0;
4559                 if(max < value) max = value;
4560
4561                 var pcent = 0;
4562                 if(max !== 0) {
4563                         pcent = Math.round(value * 100 / max);
4564                         if(pcent === 0 && value > 0) pcent = 1;
4565                 }
4566
4567                 return pcent;
4568         };
4569
4570         // ----------------------------------------------------------------------------------------------------------------
4571         // easy-pie-chart
4572
4573         NETDATA.easypiechartInitialize = function(callback) {
4574                 if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
4575                         $.ajax({
4576                                 url: NETDATA.easypiechart_js,
4577                                 cache: true,
4578                                 dataType: "script"
4579                         })
4580                                 .done(function() {
4581                                         NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
4582                                 })
4583                                 .fail(function() {
4584                                         NETDATA.chartLibraries.easypiechart.enabled = false;
4585                                         NETDATA.error(100, NETDATA.easypiechart_js);
4586                                 })
4587                                 .always(function() {
4588                                         if(typeof callback === "function")
4589                                                 callback();
4590                                 })
4591                 }
4592                 else {
4593                         NETDATA.chartLibraries.easypiechart.enabled = false;
4594                         if(typeof callback === "function")
4595                                 callback();
4596                 }
4597         };
4598
4599         NETDATA.easypiechartClearSelection = function(state) {
4600                 if(typeof state.easyPieChartEvent !== 'undefined') {
4601                         if(state.easyPieChartEvent.timer !== null)
4602                                 clearTimeout(state.easyPieChartEvent.timer);
4603
4604                         state.easyPieChartEvent.timer = null;
4605                 }
4606
4607                 if(state.isAutoRefreshed() === true && state.data !== null) {
4608                         NETDATA.easypiechartChartUpdate(state, state.data);
4609                 }
4610                 else {
4611                         state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
4612                         state.easyPieChart_instance.update(0);
4613                 }
4614                 state.easyPieChart_instance.enableAnimation();
4615
4616                 return true;
4617         };
4618
4619         NETDATA.easypiechartSetSelection = function(state, t) {
4620                 if(state.timeIsVisible(t) !== true)
4621                         return NETDATA.easypiechartClearSelection(state);
4622
4623                 var slot = state.calculateRowForTime(t);
4624                 if(slot < 0 || slot >= state.data.result.length)
4625                         return NETDATA.easypiechartClearSelection(state);
4626
4627                 if(typeof state.easyPieChartEvent === 'undefined') {
4628                         state.easyPieChartEvent = {
4629                                 timer: null,
4630                                 value: 0,
4631                                 pcent: 0
4632                         };
4633                 }
4634
4635                 var value = state.data.result[state.data.result.length - 1 - slot];
4636                 var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
4637                 var pcent = NETDATA.percentFromValueMax(value, max);
4638
4639                 state.easyPieChartEvent.value = value;
4640                 state.easyPieChartEvent.pcent = pcent;
4641                 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4642
4643                 if(state.easyPieChartEvent.timer === null) {
4644                         state.easyPieChart_instance.disableAnimation();
4645
4646                         state.easyPieChartEvent.timer = setTimeout(function() {
4647                                 state.easyPieChartEvent.timer = null;
4648                                 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
4649                         }, NETDATA.options.current.charts_selection_animation_delay);
4650                 }
4651
4652                 return true;
4653         };
4654
4655         NETDATA.easypiechartChartUpdate = function(state, data) {
4656                 var value, max, pcent;
4657
4658                 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshed() === false) {
4659                         value = null;
4660                         max = 0;
4661                         pcent = 0;
4662                 }
4663                 else {
4664                         value = data.result[0];
4665                         max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
4666                         pcent = NETDATA.percentFromValueMax(value, max);
4667                 }
4668
4669                 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4670                 state.easyPieChart_instance.update(pcent);
4671                 return true;
4672         };
4673
4674         NETDATA.easypiechartChartCreate = function(state, data) {
4675                 var self = $(state.element);
4676                 var chart = $(state.element_chart);
4677
4678                 var value = data.result[0];
4679                 var max = self.data('easypiechart-max-value') || null;
4680                 var adjust = self.data('easypiechart-adjust') || null;
4681
4682                 if(max === null) {
4683                         max = data.max;
4684                         state.easyPieChartMax = null;
4685                 }
4686                 else
4687                         state.easyPieChartMax = max;
4688
4689                 var pcent = NETDATA.percentFromValueMax(value, max);
4690
4691                 chart.data('data-percent', pcent);
4692
4693                 var size;
4694                 switch(adjust) {
4695                         case 'width': size = state.chartHeight(); break;
4696                         case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
4697                         case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
4698                         case 'height':
4699                         default: size = state.chartWidth(); break;
4700                 }
4701                 state.element.style.width = size + 'px';
4702                 state.element.style.height = size + 'px';
4703
4704                 var stroke = Math.floor(size / 22);
4705                 if(stroke < 3) stroke = 2;
4706
4707                 var valuefontsize = Math.floor((size * 2 / 3) / 5);
4708                 var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
4709                 state.easyPieChartLabel = document.createElement('span');
4710                 state.easyPieChartLabel.className = 'easyPieChartLabel';
4711                 state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
4712                 state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
4713                 state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
4714                 state.element_chart.appendChild(state.easyPieChartLabel);
4715
4716                 var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
4717                 var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
4718                 state.easyPieChartTitle = document.createElement('span');
4719                 state.easyPieChartTitle.className = 'easyPieChartTitle';
4720                 state.easyPieChartTitle.innerHTML = state.title;
4721                 state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
4722                 state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
4723                 state.easyPieChartTitle.style.top = titletop.toString() + 'px';
4724                 state.element_chart.appendChild(state.easyPieChartTitle);
4725
4726                 var unitfontsize = Math.round(titlefontsize * 0.9);
4727                 var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
4728                 state.easyPieChartUnits = document.createElement('span');
4729                 state.easyPieChartUnits.className = 'easyPieChartUnits';
4730                 state.easyPieChartUnits.innerHTML = state.units;
4731                 state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
4732                 state.easyPieChartUnits.style.top = unittop.toString() + 'px';
4733                 state.element_chart.appendChild(state.easyPieChartUnits);
4734
4735                 chart.easyPieChart({
4736                         barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
4737                         trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
4738                         scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
4739                         scaleLength: self.data('easypiechart-scalelength') || 5,
4740                         lineCap: self.data('easypiechart-linecap') || 'round',
4741                         lineWidth: self.data('easypiechart-linewidth') || stroke,
4742                         trackWidth: self.data('easypiechart-trackwidth') || undefined,
4743                         size: self.data('easypiechart-size') || size,
4744                         rotate: self.data('easypiechart-rotate') || 0,
4745                         animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
4746                         easing: self.data('easypiechart-easing') || undefined
4747                 });
4748
4749                 // when we just re-create the chart
4750                 // do not animate the first update
4751                 var animate = true;
4752                 if(typeof state.easyPieChart_instance !== 'undefined')
4753                         animate = false;
4754
4755                 state.easyPieChart_instance = chart.data('easyPieChart');
4756                 if(animate === false) state.easyPieChart_instance.disableAnimation();
4757                 state.easyPieChart_instance.update(pcent);
4758                 if(animate === false) state.easyPieChart_instance.enableAnimation();
4759                 return true;
4760         };
4761
4762         // ----------------------------------------------------------------------------------------------------------------
4763         // gauge.js
4764
4765         NETDATA.gaugeInitialize = function(callback) {
4766                 if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
4767                         $.ajax({
4768                                 url: NETDATA.gauge_js,
4769                                 cache: true,
4770                                 dataType: "script"
4771                         })
4772                                 .done(function() {
4773                                         NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
4774                                 })
4775                                 .fail(function() {
4776                                         NETDATA.chartLibraries.gauge.enabled = false;
4777                                         NETDATA.error(100, NETDATA.gauge_js);
4778                                 })
4779                                 .always(function() {
4780                                         if(typeof callback === "function")
4781                                                 callback();
4782                                 })
4783                 }
4784                 else {
4785                         NETDATA.chartLibraries.gauge.enabled = false;
4786                         if(typeof callback === "function")
4787                                 callback();
4788                 }
4789         };
4790
4791         NETDATA.gaugeAnimation = function(state, status) {
4792                 var speed = 32;
4793
4794                 if(typeof status === 'boolean' && status === false)
4795                         speed = 1000000000;
4796                 else if(typeof status === 'number')
4797                         speed = status;
4798
4799                 state.gauge_instance.animationSpeed = speed;
4800                 state.___gaugeOld__.speed = speed;
4801         };
4802
4803         NETDATA.gaugeSet = function(state, value, min, max) {
4804                 if(typeof value !== 'number') value = 0;
4805                 if(typeof min !== 'number') min = 0;
4806                 if(typeof max !== 'number') max = 0;
4807                 if(value > max) max = value;
4808                 if(value < min) min = value;
4809                 if(min > max) {
4810                         var t = min;
4811                         min = max;
4812                         max = t;
4813                 }
4814                 else if(min == max)
4815                         max = min + 1;
4816
4817                 // gauge.js has an issue if the needle
4818                 // is smaller than min or larger than max
4819                 // when we set the new values
4820                 // the needle will go crazy
4821
4822                 // to prevent it, we always feed it
4823                 // with a percentage, so that the needle
4824                 // is always between min and max
4825                 var pcent = (value - min) * 100 / (max - min);
4826
4827                 // these should never happen
4828                 if(pcent < 0) pcent = 0;
4829                 if(pcent > 100) pcent = 100;
4830
4831                 state.gauge_instance.set(pcent);
4832
4833                 state.___gaugeOld__.value = value;
4834                 state.___gaugeOld__.min = min;
4835                 state.___gaugeOld__.max = max;
4836         };
4837
4838         NETDATA.gaugeSetLabels = function(state, value, min, max) {
4839                 if(state.___gaugeOld__.valueLabel !== value) {
4840                         state.___gaugeOld__.valueLabel = value;
4841                         state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
4842                 }
4843                 if(state.___gaugeOld__.minLabel !== min) {
4844                         state.___gaugeOld__.minLabel = min;
4845                         state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
4846                 }
4847                 if(state.___gaugeOld__.maxLabel !== max) {
4848                         state.___gaugeOld__.maxLabel = max;
4849                         state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
4850                 }
4851         };
4852
4853         NETDATA.gaugeClearSelection = function(state) {
4854                 if(typeof state.gaugeEvent !== 'undefined') {
4855                         if(state.gaugeEvent.timer !== null)
4856                                 clearTimeout(state.gaugeEvent.timer);
4857
4858                         state.gaugeEvent.timer = null;
4859                 }
4860
4861                 if(state.isAutoRefreshed() === true && state.data !== null) {
4862                         NETDATA.gaugeChartUpdate(state, state.data);
4863                 }
4864                 else {
4865                         NETDATA.gaugeAnimation(state, false);
4866                         NETDATA.gaugeSet(state, null, null, null);
4867                         NETDATA.gaugeSetLabels(state, null, null, null);
4868                 }
4869
4870                 NETDATA.gaugeAnimation(state, true);
4871                 return true;
4872         };
4873
4874         NETDATA.gaugeSetSelection = function(state, t) {
4875                 if(state.timeIsVisible(t) !== true)
4876                         return NETDATA.gaugeClearSelection(state);
4877
4878                 var slot = state.calculateRowForTime(t);
4879                 if(slot < 0 || slot >= state.data.result.length)
4880                         return NETDATA.gaugeClearSelection(state);
4881
4882                 if(typeof state.gaugeEvent === 'undefined') {
4883                         state.gaugeEvent = {
4884                                 timer: null,
4885                                 value: 0,
4886                                 min: 0,
4887                                 max: 0
4888                         };
4889                 }
4890
4891                 var value = state.data.result[state.data.result.length - 1 - slot];
4892                 var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
4893                 var min = 0;
4894
4895                 state.gaugeEvent.value = value;
4896                 state.gaugeEvent.max = max;
4897                 state.gaugeEvent.min = min;
4898                 NETDATA.gaugeSetLabels(state, value, min, max);
4899
4900                 if(state.gaugeEvent.timer === null) {
4901                         NETDATA.gaugeAnimation(state, false);
4902
4903                         state.gaugeEvent.timer = setTimeout(function() {
4904                                 state.gaugeEvent.timer = null;
4905                                 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
4906                         }, NETDATA.options.current.charts_selection_animation_delay);
4907                 }
4908
4909                 return true;
4910         };
4911
4912         NETDATA.gaugeChartUpdate = function(state, data) {
4913                 var value, min, max;
4914
4915                 if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshed() === false) {
4916                         value = 0;
4917                         min = 0;
4918                         max = 1;
4919                         NETDATA.gaugeSetLabels(state, null, null, null);
4920                 }
4921                 else {
4922                         value = data.result[0];
4923                         min = 0;
4924                         max = (state.gaugeMax === null)?data.max:state.gaugeMax;
4925                         if(value > max) max = value;
4926                         NETDATA.gaugeSetLabels(state, value, min, max);
4927                 }
4928
4929                 NETDATA.gaugeSet(state, value, min, max);
4930                 return true;
4931         };
4932
4933         NETDATA.gaugeChartCreate = function(state, data) {
4934                 var self = $(state.element);
4935                 // var chart = $(state.element_chart);
4936
4937                 var value = data.result[0];
4938                 var max = self.data('gauge-max-value') || null;
4939                 var adjust = self.data('gauge-adjust') || null;
4940                 var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
4941                 var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
4942                 var startColor = self.data('gauge-start-color') || state.chartColors()[0];
4943                 var stopColor = self.data('gauge-stop-color') || void 0;
4944                 var generateGradient = self.data('gauge-generate-gradient') || false;
4945
4946                 if(max === null) {
4947                         max = data.max;
4948                         state.gaugeMax = null;
4949                 }
4950                 else
4951                         state.gaugeMax = max;
4952
4953                 var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
4954                 //switch(adjust) {
4955                 //      case 'width': width = height * ratio; break;
4956                 //      case 'height':
4957                 //      default: height = width / ratio; break;
4958                 //}
4959                 //state.element.style.width = width.toString() + 'px';
4960                 //state.element.style.height = height.toString() + 'px';
4961
4962                 var lum_d = 0.05;
4963
4964                 var options = {
4965                         lines: 12,                                      // The number of lines to draw
4966                         angle: 0.15,                            // The length of each line
4967                         lineWidth: 0.44,                        // 0.44 The line thickness
4968                         pointer: {
4969                                 length: 0.8,                    // 0.9 The radius of the inner circle
4970                                 strokeWidth: 0.035,             // The rotation offset
4971                                 color: pointerColor             // Fill color
4972                         },
4973                         colorStart: startColor,         // Colors
4974                         colorStop: stopColor,           // just experiment with them
4975                         strokeColor: strokeColor,       // to see which ones work best for you
4976                         limitMax: true,
4977                         generateGradient: generateGradient,
4978                         gradientType: 0
4979                 };
4980
4981                 if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
4982                         options.percentColors = [
4983                                 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
4984                                 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
4985                                 [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
4986                                 [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
4987                                 [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
4988                                 [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
4989                                 [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
4990                                 [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
4991                                 [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
4992                                 [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
4993                                 [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
4994                 }
4995
4996                 state.gauge_canvas = document.createElement('canvas');
4997                 state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
4998                 state.gauge_canvas.className = 'gaugeChart';
4999                 state.gauge_canvas.width  = width;
5000                 state.gauge_canvas.height = height;
5001                 state.element_chart.appendChild(state.gauge_canvas);
5002
5003                 var valuefontsize = Math.floor(height / 6);
5004                 var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
5005                 state.gaugeChartLabel = document.createElement('span');
5006                 state.gaugeChartLabel.className = 'gaugeChartLabel';
5007                 state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
5008                 state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
5009                 state.element_chart.appendChild(state.gaugeChartLabel);
5010
5011                 var titlefontsize = Math.round(valuefontsize / 2);
5012                 var titletop = 0;
5013                 state.gaugeChartTitle = document.createElement('span');
5014                 state.gaugeChartTitle.className = 'gaugeChartTitle';
5015                 state.gaugeChartTitle.innerHTML = state.title;
5016                 state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
5017                 state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
5018                 state.gaugeChartTitle.style.top = titletop.toString() + 'px';
5019                 state.element_chart.appendChild(state.gaugeChartTitle);
5020
5021                 var unitfontsize = Math.round(titlefontsize * 0.9);
5022                 state.gaugeChartUnits = document.createElement('span');
5023                 state.gaugeChartUnits.className = 'gaugeChartUnits';
5024                 state.gaugeChartUnits.innerHTML = state.units;
5025                 state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
5026                 state.element_chart.appendChild(state.gaugeChartUnits);
5027
5028                 state.gaugeChartMin = document.createElement('span');
5029                 state.gaugeChartMin.className = 'gaugeChartMin';
5030                 state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5031                 state.element_chart.appendChild(state.gaugeChartMin);
5032
5033                 state.gaugeChartMax = document.createElement('span');
5034                 state.gaugeChartMax.className = 'gaugeChartMax';
5035                 state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
5036                 state.element_chart.appendChild(state.gaugeChartMax);
5037
5038                 // when we just re-create the chart
5039                 // do not animate the first update
5040                 var animate = true;
5041                 if(typeof state.gauge_instance !== 'undefined')
5042                         animate = false;
5043
5044                 state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
5045
5046                 state.___gaugeOld__ = {
5047                         value: value,
5048                         min: 0,
5049                         max: max,
5050                         valueLabel: null,
5051                         minLabel: null,
5052                         maxLabel: null
5053                 };
5054
5055                 // we will always feed a percentage
5056                 state.gauge_instance.minValue = 0;
5057                 state.gauge_instance.maxValue = 100;
5058
5059                 NETDATA.gaugeAnimation(state, animate);
5060                 NETDATA.gaugeSet(state, value, 0, max);
5061                 NETDATA.gaugeSetLabels(state, value, 0, max);
5062                 NETDATA.gaugeAnimation(state, true);
5063                 return true;
5064         };
5065
5066         // ----------------------------------------------------------------------------------------------------------------
5067         // Charts Libraries Registration
5068
5069         NETDATA.chartLibraries = {
5070                 "dygraph": {
5071                         initialize: NETDATA.dygraphInitialize,
5072                         create: NETDATA.dygraphChartCreate,
5073                         update: NETDATA.dygraphChartUpdate,
5074                         resize: function(state) {
5075                                 if(typeof state.dygraph_instance.resize === 'function')
5076                                         state.dygraph_instance.resize();
5077                         },
5078                         setSelection: NETDATA.dygraphSetSelection,
5079                         clearSelection:  NETDATA.dygraphClearSelection,
5080                         toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
5081                         initialized: false,
5082                         enabled: true,
5083                         format: function(state) { return 'json'; },
5084                         options: function(state) { return 'ms|flip'; },
5085                         legend: function(state) {
5086                                 if(this.isSparkline(state) === false)
5087                                         return 'right-side';
5088                                 else
5089                                         return null;
5090                         },
5091                         autoresize: function(state) { return true; },
5092                         max_updates_to_recreate: function(state) { return 5000; },
5093                         track_colors: function(state) { return true; },
5094                         pixels_per_point: function(state) {
5095                                 if(this.isSparkline(state) === false)
5096                                         return 3;
5097                                 else
5098                                         return 2;
5099                         },
5100
5101                         isSparkline: function(state) {
5102                                 if(typeof state.dygraph_sparkline === 'undefined') {
5103                                         var t = $(state.element).data('dygraph-theme');
5104                                         if(t === 'sparkline')
5105                                                 state.dygraph_sparkline = true;
5106                                         else
5107                                                 state.dygraph_sparkline = false;
5108                                 }
5109                                 return state.dygraph_sparkline;
5110                         }
5111                 },
5112                 "sparkline": {
5113                         initialize: NETDATA.sparklineInitialize,
5114                         create: NETDATA.sparklineChartCreate,
5115                         update: NETDATA.sparklineChartUpdate,
5116                         resize: null,
5117                         setSelection: undefined, // function(state, t) { return true; },
5118                         clearSelection: undefined, // function(state) { return true; },
5119                         toolboxPanAndZoom: null,
5120                         initialized: false,
5121                         enabled: true,
5122                         format: function(state) { return 'array'; },
5123                         options: function(state) { return 'flip|abs'; },
5124                         legend: function(state) { return null; },
5125                         autoresize: function(state) { return false; },
5126                         max_updates_to_recreate: function(state) { return 5000; },
5127                         track_colors: function(state) { return false; },
5128                         pixels_per_point: function(state) { return 3; }
5129                 },
5130                 "peity": {
5131                         initialize: NETDATA.peityInitialize,
5132                         create: NETDATA.peityChartCreate,
5133                         update: NETDATA.peityChartUpdate,
5134                         resize: null,
5135                         setSelection: undefined, // function(state, t) { return true; },
5136                         clearSelection: undefined, // function(state) { return true; },
5137                         toolboxPanAndZoom: null,
5138                         initialized: false,
5139                         enabled: true,
5140                         format: function(state) { return 'ssvcomma'; },
5141                         options: function(state) { return 'null2zero|flip|abs'; },
5142                         legend: function(state) { return null; },
5143                         autoresize: function(state) { return false; },
5144                         max_updates_to_recreate: function(state) { return 5000; },
5145                         track_colors: function(state) { return false; },
5146                         pixels_per_point: function(state) { return 3; }
5147                 },
5148                 "morris": {
5149                         initialize: NETDATA.morrisInitialize,
5150                         create: NETDATA.morrisChartCreate,
5151                         update: NETDATA.morrisChartUpdate,
5152                         resize: null,
5153                         setSelection: undefined, // function(state, t) { return true; },
5154                         clearSelection: undefined, // function(state) { return true; },
5155                         toolboxPanAndZoom: null,
5156                         initialized: false,
5157                         enabled: true,
5158                         format: function(state) { return 'json'; },
5159                         options: function(state) { return 'objectrows|ms'; },
5160                         legend: function(state) { return null; },
5161                         autoresize: function(state) { return false; },
5162                         max_updates_to_recreate: function(state) { return 50; },
5163                         track_colors: function(state) { return false; },
5164                         pixels_per_point: function(state) { return 15; }
5165                 },
5166                 "google": {
5167                         initialize: NETDATA.googleInitialize,
5168                         create: NETDATA.googleChartCreate,
5169                         update: NETDATA.googleChartUpdate,
5170                         resize: null,
5171                         setSelection: undefined, //function(state, t) { return true; },
5172                         clearSelection: undefined, //function(state) { return true; },
5173                         toolboxPanAndZoom: null,
5174                         initialized: false,
5175                         enabled: true,
5176                         format: function(state) { return 'datatable'; },
5177                         options: function(state) { return ''; },
5178                         legend: function(state) { return null; },
5179                         autoresize: function(state) { return false; },
5180                         max_updates_to_recreate: function(state) { return 300; },
5181                         track_colors: function(state) { return false; },
5182                         pixels_per_point: function(state) { return 4; }
5183                 },
5184                 "raphael": {
5185                         initialize: NETDATA.raphaelInitialize,
5186                         create: NETDATA.raphaelChartCreate,
5187                         update: NETDATA.raphaelChartUpdate,
5188                         resize: null,
5189                         setSelection: undefined, // function(state, t) { return true; },
5190                         clearSelection: undefined, // function(state) { return true; },
5191                         toolboxPanAndZoom: null,
5192                         initialized: false,
5193                         enabled: true,
5194                         format: function(state) { return 'json'; },
5195                         options: function(state) { return ''; },
5196                         legend: function(state) { return null; },
5197                         autoresize: function(state) { return false; },
5198                         max_updates_to_recreate: function(state) { return 5000; },
5199                         track_colors: function(state) { return false; },
5200                         pixels_per_point: function(state) { return 3; }
5201                 },
5202                 "c3": {
5203                         initialize: NETDATA.c3Initialize,
5204                         create: NETDATA.c3ChartCreate,
5205                         update: NETDATA.c3ChartUpdate,
5206                         resize: null,
5207                         setSelection: undefined, // function(state, t) { return true; },
5208                         clearSelection: undefined, // function(state) { return true; },
5209                         toolboxPanAndZoom: null,
5210                         initialized: false,
5211                         enabled: true,
5212                         format: function(state) { return 'csvjsonarray'; },
5213                         options: function(state) { return 'milliseconds'; },
5214                         legend: function(state) { return null; },
5215                         autoresize: function(state) { return false; },
5216                         max_updates_to_recreate: function(state) { return 5000; },
5217                         track_colors: function(state) { return false; },
5218                         pixels_per_point: function(state) { return 15; }
5219                 },
5220                 "d3": {
5221                         initialize: NETDATA.d3Initialize,
5222                         create: NETDATA.d3ChartCreate,
5223                         update: NETDATA.d3ChartUpdate,
5224                         resize: null,
5225                         setSelection: undefined, // function(state, t) { return true; },
5226                         clearSelection: undefined, // function(state) { return true; },
5227                         toolboxPanAndZoom: null,
5228                         initialized: false,
5229                         enabled: true,
5230                         format: function(state) { return 'json'; },
5231                         options: function(state) { return ''; },
5232                         legend: function(state) { return null; },
5233                         autoresize: function(state) { return false; },
5234                         max_updates_to_recreate: function(state) { return 5000; },
5235                         track_colors: function(state) { return false; },
5236                         pixels_per_point: function(state) { return 3; }
5237                 },
5238                 "easypiechart": {
5239                         initialize: NETDATA.easypiechartInitialize,
5240                         create: NETDATA.easypiechartChartCreate,
5241                         update: NETDATA.easypiechartChartUpdate,
5242                         resize: null,
5243                         setSelection: NETDATA.easypiechartSetSelection,
5244                         clearSelection: NETDATA.easypiechartClearSelection,
5245                         toolboxPanAndZoom: null,
5246                         initialized: false,
5247                         enabled: true,
5248                         format: function(state) { return 'array'; },
5249                         options: function(state) { return 'absolute'; },
5250                         legend: function(state) { return null; },
5251                         autoresize: function(state) { return false; },
5252                         max_updates_to_recreate: function(state) { return 5000; },
5253                         track_colors: function(state) { return true; },
5254                         pixels_per_point: function(state) { return 3; },
5255                         aspect_ratio: 100
5256                 },
5257                 "gauge": {
5258                         initialize: NETDATA.gaugeInitialize,
5259                         create: NETDATA.gaugeChartCreate,
5260                         update: NETDATA.gaugeChartUpdate,
5261                         resize: null,
5262                         setSelection: NETDATA.gaugeSetSelection,
5263                         clearSelection: NETDATA.gaugeClearSelection,
5264                         toolboxPanAndZoom: null,
5265                         initialized: false,
5266                         enabled: true,
5267                         format: function(state) { return 'array'; },
5268                         options: function(state) { return 'absolute'; },
5269                         legend: function(state) { return null; },
5270                         autoresize: function(state) { return false; },
5271                         max_updates_to_recreate: function(state) { return 5000; },
5272                         track_colors: function(state) { return true; },
5273                         pixels_per_point: function(state) { return 3; },
5274                         aspect_ratio: 70
5275                 }
5276         };
5277
5278         NETDATA.registerChartLibrary = function(library, url) {
5279                 if(NETDATA.options.debug.libraries === true)
5280                         console.log("registering chart library: " + library);
5281
5282                 NETDATA.chartLibraries[library].url = url;
5283                 NETDATA.chartLibraries[library].initialized = true;
5284                 NETDATA.chartLibraries[library].enabled = true;
5285         };
5286
5287         // ----------------------------------------------------------------------------------------------------------------
5288         // Start up
5289
5290         NETDATA.requiredJs = [
5291                 {
5292                         url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
5293                         isAlreadyLoaded: function() {
5294                                 if(typeof $().emulateTransitionEnd == 'function')
5295                                         return true;
5296                                 else {
5297                                         if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5298                                                 return true;
5299                                         else
5300                                                 return false;
5301                                 }
5302                         }
5303                 },
5304                 {
5305                         url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
5306                         isAlreadyLoaded: function() { return false; }
5307                 },
5308                 {
5309                         url: NETDATA.serverDefault + 'lib/bootstrap-toggle.min.js',
5310                         isAlreadyLoaded: function() { return false; }
5311                 }
5312         ];
5313
5314         NETDATA.requiredCSS = [
5315                 {
5316                         url: NETDATA.themes.current.bootstrap_css,
5317                         isAlreadyLoaded: function() {
5318                                 if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
5319                                         return true;
5320                                 else
5321                                         return false;
5322                         }
5323                 },
5324                 {
5325                         url: NETDATA.serverDefault + 'css/font-awesome.min.css',
5326                         isAlreadyLoaded: function() { return false; }
5327                 },
5328                 {
5329                         url: NETDATA.themes.current.dashboard_css,
5330                         isAlreadyLoaded: function() { return false; }
5331                 },
5332                 {
5333                         url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
5334                         isAlreadyLoaded: function() { return false; }
5335                 }
5336         ];
5337
5338         NETDATA.loadRequiredJs = function(index, callback) {
5339                 if(index >= NETDATA.requiredJs.length)  {
5340                         if(typeof callback === 'function')
5341                                 callback();
5342                         return;
5343                 }
5344
5345                 if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
5346                         NETDATA.loadRequiredJs(++index, callback);
5347                         return;
5348                 }
5349
5350                 if(NETDATA.options.debug.main_loop === true)
5351                         console.log('loading ' + NETDATA.requiredJs[index].url);
5352
5353                 $.ajax({
5354                         url: NETDATA.requiredJs[index].url,
5355                         cache: true,
5356                         dataType: "script"
5357                 })
5358                 .success(function() {
5359                         if(NETDATA.options.debug.main_loop === true)
5360                                 console.log('loaded ' + NETDATA.requiredJs[index].url);
5361
5362                         NETDATA.loadRequiredJs(++index, callback);
5363                 })
5364                 .fail(function() {
5365                         alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
5366                 })
5367         };
5368
5369         NETDATA.loadRequiredCSS = function(index) {
5370                 if(index >= NETDATA.requiredCSS.length)
5371                         return;
5372
5373                 if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
5374                         NETDATA.loadRequiredCSS(++index);
5375                         return;
5376                 }
5377
5378                 if(NETDATA.options.debug.main_loop === true)
5379                         console.log('loading ' + NETDATA.requiredCSS[index].url);
5380
5381                 NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
5382                 NETDATA.loadRequiredCSS(++index);
5383         };
5384
5385         NETDATA.errorReset();
5386         NETDATA.loadRequiredCSS(0);
5387
5388         NETDATA._loadjQuery(function() {
5389                 NETDATA.loadRequiredJs(0, function() {
5390                         if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
5391                                 if(NETDATA.options.debug.main_loop === true)
5392                                         console.log('starting chart refresh thread');
5393
5394                                 NETDATA.start();
5395                         }
5396                 });
5397         });
5398
5399         // window.NETDATA = NETDATA;
5400 // })(window, document);