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