]> arthur.barton.de Git - netdata.git/blobdiff - web/dashboard.js
Merge pull request #2021 from ktsaou/master
[netdata.git] / web / dashboard.js
index 72960f63d2354ab19355a20cbd0bfde08cd969e7..b34accdecb0948a45a1769631e1f2d5a02681b8a 100644 (file)
@@ -1,38 +1,57 @@
+// ----------------------------------------------------------------------------
 // You can set the following variables before loading this script:
-//
-// var netdataNoDygraphs = true;        // do not use dygraph
-// var netdataNoSparklines = true;      // do not use sparkline
-// var netdataNoPeitys = true;          // do not use peity
-// var netdataNoGoogleCharts = true;    // do not use google
-// var netdataNoMorris = true;          // do not use morris
-// var netdataNoEasyPieChart = true;    // do not use easy pie chart
-// var netdataNoGauge = true;           // do not use gauge.js
-// var netdataNoD3 = true;              // do not use D3
-// var netdataNoC3 = true;              // do not use C3
-// var netdataNoBootstrap = true;       // do not load bootstrap
-// var netdataDontStart = true;         // do not start the thread to process the charts
-// var netdataErrorCallback = null;     // Callback function that will be invoked upon error
-// var netdataRegistry = true;          // Update the registry (default disabled)
-// var netdataRegistryCallback = null;  // Callback function that will be invoked with one param,
-//                                         the URLs from the registry
-// var netdataShowHelp = false;         // enable/disable help (default enabled)
-// var netdataShowAlarms = true;        // enable/disable alarms checks and notifications (default disabled)
-//
-// var netdataRegistryAfterMs = 1500    // the time to consult to registry on startup
-//
-// var netdataCallback = null;          // a function to call when netdata is ready
-//                                      // netdata will be running while this is called (call NETDATA.pause to stop it)
-// var netdataPrepCallback = null;      // a callback to be called before netdata does anything else
-//
-// You can also set the default netdata server, using the following.
-// When this variable is not set, we assume the page is hosted on your
-// netdata server already.
-// var netdataServer = "http://yourhost:19999"; // set your NetData server
 
+/*global netdataNoDygraphs           *//* boolean,  disable dygraph charts
+ *                                                  (default: false) */
+/*global netdataNoSparklines         *//* boolean,  disable sparkline charts
+ *                                                  (default: false) */
+/*global netdataNoPeitys             *//* boolean,  disable peity charts
+ *                                                  (default: false) */
+/*global netdataNoGoogleCharts       *//* boolean,  disable google charts
+ *                                                  (default: false) */
+/*global netdataNoMorris             *//* boolean,  disable morris charts
+ *                                                  (default: false) */
+/*global netdataNoEasyPieChart       *//* boolean,  disable easypiechart charts
+ *                                                  (default: false) */
+/*global netdataNoGauge              *//* boolean,  disable gauge.js charts
+ *                                                  (default: false) */
+/*global netdataNoD3                 *//* boolean,  disable d3 charts
+ *                                                  (default: false) */
+/*global netdataNoC3                 *//* boolean,  disable c3 charts
+ *                                                  (default: false) */
+/*global netdataNoBootstrap          *//* boolean,  disable bootstrap - disables help too
+ *                                                  (default: false) */
+/*global netdataDontStart            *//* boolean,  do not start the thread to process the charts
+ *                                                  (default: false) */
+/*global netdataErrorCallback        *//* function, callback to be called when the dashboard encounters an error
+ *                                                  (default: null) */
+/*global netdataRegistry:true        *//* boolean,  use the netdata registry
+ *                                                  (default: false) */
+/*global netdataNoRegistry           *//* boolean,  included only for compatibility with existing custom dashboard
+ *                                                  (obsolete - do not use this any more) */
+/*global netdataRegistryCallback     *//* function, callback that will be invoked with one param: the URLs from the registry
+ *                                                  (default: null) */
+/*global netdataShowHelp:true        *//* boolean,  disable charts help
+ *                                                  (default: true) */
+/*global netdataShowAlarms:true      *//* boolean,  enable alarms checks and notifications
+ *                                                  (default: false) */
+/*global netdataRegistryAfterMs:true *//* ms,       delay registry use at started
+ *                                                  (default: 1500) */
+/*global netdataCallback             *//* function, callback to be called when netdata is ready to start
+ *                                                  (default: null)
+ *                                                  netdata will be running while this is called
+ *                                                  (call NETDATA.pause to stop it) */
+/*global netdataPrepCallback         *//* function, callback to be called before netdata does anything else
+ *                                                  (default: null) */
+/*global netdataServer               *//* string,   the URL of the netdata server to use
+ *                                                  (default: the URL the page is hosted at) */
+
+// ----------------------------------------------------------------------------
 // global namespace
+
 var NETDATA = window.NETDATA || {};
 
-(function(window, document, undefined) {
+(function(window, document) {
     // ------------------------------------------------------------------------
     // compatibility fixes
 
@@ -111,7 +130,7 @@ var NETDATA = window.NETDATA || {};
     NETDATA.peity_js            = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js';
     NETDATA.sparkline_js        = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js';
     NETDATA.easypiechart_js     = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js';
-    NETDATA.gauge_js            = NETDATA.serverDefault + 'lib/gauge-d5260c3.min.js';
+    NETDATA.gauge_js            = NETDATA.serverDefault + 'lib/gauge-1.3.2.min.js';
     NETDATA.dygraph_js          = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js';
     NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js';
     NETDATA.raphael_js          = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js';
@@ -125,7 +144,7 @@ var NETDATA = window.NETDATA || {};
     NETDATA.themes = {
         white: {
             bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161226-1',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2',
             background: '#FFFFFF',
             foreground: '#000000',
             grid: '#F0F0F0',
@@ -141,8 +160,8 @@ var NETDATA = window.NETDATA || {};
             gauge_gradient: false
         },
         slate: {
-            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161218-2',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161226-1',
+            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2',
             background: '#272b30',
             foreground: '#C8C8C8',
             grid: '#283236',
@@ -194,14 +213,34 @@ var NETDATA = window.NETDATA || {};
 
     if(typeof netdataRegistry === 'undefined') {
         // backward compatibility
-        if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false)
-            netdataRegistry = true;
-        else
-            netdataRegistry = false;
+        netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false);
     }
     if(netdataRegistry === false && typeof netdataRegistryCallback === 'function')
         netdataRegistry = true;
 
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // detect if this is probably a slow device
+
+    var isSlowDeviceResult = undefined;
+    var isSlowDevice = function() {
+        if(isSlowDeviceResult !== undefined)
+            return isSlowDeviceResult;
+
+        try {
+            var ua = navigator.userAgent.toLowerCase();
+
+            var iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream;
+            var android = /android/.test(ua) && !window.MSStream;
+            isSlowDeviceResult = (iOS === true || android === true);
+        }
+        catch (e) {
+            isSlowDeviceResult = false;
+        }
+
+        return isSlowDeviceResult;
+    };
+
     // ----------------------------------------------------------------------------------------------------------------
     // the defaults for all charts
 
@@ -217,7 +256,7 @@ var NETDATA = window.NETDATA || {};
         before: 0,                      // panning
         after: -600,                    // panning
         pixels_per_point: 1,            // the detail of the chart
-        fill_luminance: 0.8             // luminance of colors in solit areas
+        fill_luminance: 0.8             // luminance of colors in solid areas
     };
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -236,12 +275,12 @@ var NETDATA = window.NETDATA || {};
                                         // new elements we have to check.
 
         auto_refresher_fast_weight: 0,  // this is the current time in ms, spent
-                                        // rendering charts continiously.
+                                        // rendering charts continuously.
                                         // used with .current.fast_render_timeframe
 
         page_is_visible: true,          // when true, this page is visible
 
-        auto_refresher_stop_until: 0,   // timestamp in ms - used internaly, to stop the
+        auto_refresher_stop_until: 0,   // timestamp in ms - used internally, to stop the
                                         // auto-refresher for some time (when a chart is
                                         // performing pan or zoom, we need to stop refreshing
                                         // all other charts, to have the maximum speed for
@@ -255,7 +294,7 @@ var NETDATA = window.NETDATA || {};
         // the current profile
         // we may have many...
         current: {
-            pixels_per_point: 1,        // the minimum pixels per point for all charts
+            pixels_per_point: isSlowDevice()?5:1, // the minimum pixels per point for all charts
                                         // increase this to speed javascript up
                                         // each chart library has its own limit too
                                         // the max of this and the chart library is used
@@ -265,7 +304,7 @@ var NETDATA = window.NETDATA || {};
 
             idle_between_charts: 100,   // ms - how much time to wait between chart updates
 
-            fast_render_timeframe: 200, // ms - render continously until this time of continious
+            fast_render_timeframe: 200, // ms - render continuously until this time of continuous
                                         // rendering has been reached
                                         // this setting is used to make it render e.g. 10
                                         // charts at once, sleep idle_between_charts time
@@ -279,8 +318,8 @@ var NETDATA = window.NETDATA || {};
             idle_lost_focus: 500,       // ms - when the window does not have focus, check
                                         // if focus has been regained, every this time
 
-            global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
-                                        // autorefreshing of charts is paused for this amount
+            global_pan_sync_time: 1000, // ms - when you pan or zoom a chart, the background
+                                        // auto-refreshing of charts is paused for this amount
                                         // of time
 
             sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
@@ -297,11 +336,11 @@ var NETDATA = window.NETDATA || {};
 
             update_only_visible: true,  // enable or disable visibility management
 
-            parallel_refresher: true,   // enable parallel refresh of charts
+            parallel_refresher: (isSlowDevice() === false), // enable parallel refresh of charts
 
             concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts
 
-            destroy_on_hide: false,     // destroy charts when they are not visible
+            destroy_on_hide: (isSlowDevice() === true), // destroy charts when they are not visible
 
             show_help: netdataShowHelp, // when enabled the charts will show some help
             show_help_delay_show_ms: 500,
@@ -314,7 +353,7 @@ var NETDATA = window.NETDATA || {};
 
             double_click_speed: 500,    // ms - time between clicks / taps to detect double click/tap
 
-            smooth_plot: true,          // enable smooth plot, where possible
+            smooth_plot: (isSlowDevice() === false), // enable smooth plot, where possible
 
             charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
 
@@ -333,7 +372,7 @@ var NETDATA = window.NETDATA || {};
 
             retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server
 
-            setOptionCallback: function() { }
+            setOptionCallback: function() { }
         },
 
         debug: {
@@ -342,7 +381,7 @@ var NETDATA = window.NETDATA || {};
             focus:              false,
             visibility:         false,
             chart_data_url:     false,
-            chart_errors:       false, // FIXME
+            chart_errors:       false, // FIXME: remember to set it to false before merging
             chart_timing:       false,
             chart_calls:        false,
             libraries:          false,
@@ -366,6 +405,28 @@ var NETDATA = window.NETDATA || {};
         callback: {} // only used for resetting back to defaults
     };
 
+    NETDATA.localStorageTested = -1;
+    NETDATA.localStorageTest = function() {
+        if(NETDATA.localStorageTested !== -1)
+            return NETDATA.localStorageTested;
+
+        if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+            var test = 'test';
+            try {
+                localStorage.setItem(test, test);
+                localStorage.removeItem(test);
+                NETDATA.localStorageTested = true;
+            }
+            catch (e) {
+                NETDATA.localStorageTested = false;
+            }
+        }
+        else
+            NETDATA.localStorageTested = false;
+
+        return NETDATA.localStorageTested;
+    };
+
     NETDATA.localStorageGet = function(key, def, callback) {
         var ret = def;
 
@@ -374,7 +435,7 @@ var NETDATA = window.NETDATA || {};
             NETDATA.localStorage.callback[key.toString()] = callback;
         }
 
-        if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+        if(NETDATA.localStorageTest() === true) {
             try {
                 // console.log('localStorage: loading "' + key.toString() + '"');
                 ret = localStorage.getItem(key.toString());
@@ -416,7 +477,7 @@ var NETDATA = window.NETDATA || {};
             NETDATA.localStorage.callback[key.toString()] = callback;
         }
 
-        if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+        if(NETDATA.localStorageTest() === true) {
             // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
             try {
                 localStorage.setItem(key.toString(), JSON.stringify(value));
@@ -431,7 +492,11 @@ var NETDATA = window.NETDATA || {};
     };
 
     NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
-        for(var i in obj) {
+        var keys = Object.keys(obj);
+        var len = keys.length;
+        while(len--) {
+            var i = keys[len];
+
             if(typeof obj[i] === 'object') {
                 //console.log('object ' + prefix + '.' + i.toString());
                 NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
@@ -478,7 +543,10 @@ var NETDATA = window.NETDATA || {};
     NETDATA.setOption('stop_updates_when_focus_is_lost', true);
 
     NETDATA.resetOptions = function() {
-        for(var i in NETDATA.localStorage.default) {
+        var keys = Object.keys(NETDATA.localStorage.default);
+        var len = keys.length;
+        while(len--) {
+            var i = keys[len];
             var a = i.split('.');
 
             if(a[0] === 'options') {
@@ -494,16 +562,20 @@ var NETDATA = window.NETDATA || {};
                 }
             }
         }
-    }
+    };
 
     // ----------------------------------------------------------------------------------------------------------------
 
     if(NETDATA.options.debug.main_loop === true)
         console.log('welcome to NETDATA');
 
+    NETDATA.onresizeCallback = null;
     NETDATA.onresize = function() {
         NETDATA.options.last_resized = Date.now();
         NETDATA.onscroll();
+
+        if(typeof NETDATA.onresizeCallback === 'function')
+            NETDATA.onresizeCallback();
     };
 
     NETDATA.onscroll_updater_count = 0;
@@ -711,7 +783,7 @@ var NETDATA = window.NETDATA || {};
             // find the common min
             var m = min;
             for(var i in t)
-                if(t[i] < m) m = t[i];
+                if(t.hasOwnProperty(i) && t[i] < m) m = t[i];
 
             //state.log('commonMin ' + state.__commonMin + ' updated: ' + m);
             this.latest[name] = m;
@@ -766,7 +838,7 @@ var NETDATA = window.NETDATA || {};
             // find the common max
             var m = max;
             for(var i in t)
-                if(t[i] > m) m = t[i];
+                if(t.hasOwnProperty(i) && t[i] > m) m = t[i];
 
             //state.log('commonMax ' + state.__commonMax + ' updated: ' + m);
             this.latest[name] = m;
@@ -784,6 +856,13 @@ var NETDATA = window.NETDATA || {};
     // Every time we download a chart definition, we save it here with .add()
     // Then we try to get it back with .get(). If that fails, we download it.
 
+    NETDATA.fixHost = function(host) {
+        while(host.slice(-1) === '/')
+            host = host.substring(0, host.length - 1);
+
+        return host;
+    };
+
     NETDATA.chartRegistry = {
         charts: {},
 
@@ -817,8 +896,7 @@ var NETDATA = window.NETDATA || {};
         },
 
         downloadAll: function(host, callback) {
-            while(host.slice(-1) === '/')
-                host = host.substring(0, host.length - 1);
+            host = NETDATA.fixHost(host);
 
             var self = this;
 
@@ -836,13 +914,13 @@ var NETDATA = window.NETDATA || {};
                 else NETDATA.error(406, host + '/api/v1/charts');
 
                 if(typeof callback === 'function')
-                    callback(data);
+                    return callback(data);
             })
             .fail(function() {
                 NETDATA.error(405, host + '/api/v1/charts');
 
                 if(typeof callback === 'function')
-                    callback(null);
+                    return callback(null);
             });
         }
     };
@@ -859,8 +937,8 @@ var NETDATA = window.NETDATA || {};
                                 // every time a chart is panned or zoomed
                                 // we set the timestamp here
                                 // then we use it as a sequence number
-                                // to find if other charts are syncronized
-                                // to this timerange
+                                // to find if other charts are synchronized
+                                // to this time-range
 
         master: null,           // the master chart (state), to which all others
                                 // are synchronized
@@ -910,14 +988,12 @@ var NETDATA = window.NETDATA || {};
         // is the given state the master of the global
         // pan and zoom sync?
         isMaster: function(state) {
-            if(this.master === state) return true;
-            return false;
+            return (this.master === state);
         },
 
         // are we currently have a global pan and zoom sync?
         isActive: function() {
-            if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
-            return false;
+            return (this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0);
         },
 
         // check if a chart, other than the master
@@ -929,10 +1005,7 @@ var NETDATA = window.NETDATA || {};
             //if(state.needsRecreation())
             //  return true;
 
-            if(state.tm.pan_and_zoom_seq === this.seq)
-                return false;
-
-            return true;
+            return (state.tm.pan_and_zoom_seq !== this.seq);
         }
     };
 
@@ -942,18 +1015,14 @@ var NETDATA = window.NETDATA || {};
     // FIXME
     // move color assignment to dimensions, here
 
-    dimensionStatus = function(parent, label, name_div, value_div, color) {
+    var dimensionStatus = function(parent, label, name_div, value_div, color) {
         this.enabled = false;
         this.parent = parent;
         this.label = label;
         this.name_div = null;
         this.value_div = null;
         this.color = NETDATA.themes.current.foreground;
-
-        if(parent.unselected_count === 0)
-            this.selected = true;
-        else
-            this.selected = false;
+        this.selected = (parent.unselected_count === 0);
 
         this.setOptions(name_div, value_div, color);
     };
@@ -967,7 +1036,7 @@ var NETDATA = window.NETDATA || {};
     dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
         this.color = color;
 
-        if(this.name_div != name_div) {
+        if(this.name_div !== name_div) {
             this.name_div = name_div;
             this.name_div.title = this.label;
             this.name_div.style.color = this.color;
@@ -977,7 +1046,7 @@ var NETDATA = window.NETDATA || {};
                 this.name_div.className = 'netdata-legend-name selected';
         }
 
-        if(this.value_div != value_div) {
+        if(this.value_div !== value_div) {
             this.value_div = value_div;
             this.value_div.title = this.label;
             this.value_div.style.color = this.color;
@@ -1058,7 +1127,7 @@ var NETDATA = window.NETDATA || {};
 
     // ----------------------------------------------------------------------------------------------------------------
 
-    dimensionsVisibility = function(state) {
+    var dimensionsVisibility = function(state) {
         this.state = state;
         this.len = 0;
         this.dimensions = {};
@@ -1082,30 +1151,38 @@ var NETDATA = window.NETDATA || {};
     };
 
     dimensionsVisibility.prototype.invalidateAll = function() {
-        for(var d in this.dimensions)
-            this.dimensions[d].invalidate();
+        var keys = Object.keys(this.dimensions);
+        var len = keys.length;
+        while(len--)
+            this.dimensions[keys[len]].invalidate();
     };
 
     dimensionsVisibility.prototype.selectAll = function() {
-        for(var d in this.dimensions)
-            this.dimensions[d].select();
+        var keys = Object.keys(this.dimensions);
+        var len = keys.length;
+        while(len--)
+            this.dimensions[keys[len]].select();
     };
 
     dimensionsVisibility.prototype.countSelected = function() {
-        var i = 0;
-        for(var d in this.dimensions)
-            if(this.dimensions[d].isSelected()) i++;
+        var selected = 0;
+        var keys = Object.keys(this.dimensions);
+        var len = keys.length;
+        while(len--)
+            if(this.dimensions[keys[len]].isSelected()) selected++;
 
-        return i;
+        return selected;
     };
 
     dimensionsVisibility.prototype.selectNone = function() {
-        for(var d in this.dimensions)
-            this.dimensions[d].unselect();
+        var keys = Object.keys(this.dimensions);
+        var len = keys.length;
+        while(len--)
+            this.dimensions[keys[len]].unselect();
     };
 
     dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
-        var ret = new Array();
+        var ret = [];
         this.selected_count = 0;
         this.unselected_count = 0;
 
@@ -1159,7 +1236,7 @@ var NETDATA = window.NETDATA || {};
     // ----------------------------------------------------------------------------------------------------------------
     // Our state object, where all per-chart values are stored
 
-    chartState = function(element) {
+    var chartState = function(element) {
         var self = $(element);
         this.element = element;
 
@@ -1196,6 +1273,7 @@ var NETDATA = window.NETDATA || {};
         // the user given dimensions of the element
         this.width = self.data('width') || NETDATA.chartDefaults.width;
         this.height = self.data('height') || NETDATA.chartDefaults.height;
+        this.height_original = this.height;
 
         if(this.settings_id !== null) {
             this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
@@ -1256,8 +1334,7 @@ var NETDATA = window.NETDATA || {};
             title_date: null,
             title_time: null,
             title_units: null,
-            nano: null,
-            nano_options: null,
+            perfect_scroller: null, // the container to apply perfect scroller to
             series: null
         };
 
@@ -1270,7 +1347,6 @@ var NETDATA = window.NETDATA || {};
         this.override_options = self.data('override-options') || null;  // override options to pass to netdata
 
         this.running = false;                       // boolean - true when the chart is being refreshed now
-        this.validated = false;                     // boolean - has the chart been validated?
         this.enabled = true;                        // boolean - is the chart enabled for refresh?
         this.paused = false;                        // boolean - is the chart paused for any reason?
         this.selected = false;                      // boolean - is the chart shown a selection?
@@ -1285,13 +1361,9 @@ var NETDATA = window.NETDATA || {};
         this.view_before = 0;
 
         this.value_decimal_detail = -1;
-        {
-            var d = self.data('decimal-digits');
-            if(typeof d === 'number') {
-                this.value_decimal_detail = 1;
-                while(d-- > 0)
-                    this.value_decimal_detail *= 10;
-            }
+        var d = self.data('decimal-digits');
+        if(typeof d === 'number') {
+            this.value_decimal_detail = d;
         }
 
         this.auto = {
@@ -1343,8 +1415,9 @@ var NETDATA = window.NETDATA || {};
         // find the element that needs to be updated
         var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
 
-        if(refresh_dt_element_name !== null)
+        if(refresh_dt_element_name !== null) {
             this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
+        }
         else
             this.refresh_dt_element = null;
 
@@ -1365,7 +1438,7 @@ var NETDATA = window.NETDATA || {};
             that.element.innerHTML = '';
 
             that.element_message = document.createElement('div');
-            that.element_message.className = ' netdata-message hidden';
+            that.element_message.className = 'netdata-message icon hidden';
             that.element.appendChild(that.element_message);
 
             that.element_chart = document.createElement('div');
@@ -1395,9 +1468,9 @@ var NETDATA = window.NETDATA || {};
 
             if(typeof(that.library.aspect_ratio) === 'undefined') {
                 if(typeof(that.height) === 'string')
-                    $(that.element).css('height', that.height);
+                    that.element.style.height = that.height;
                 else if(typeof(that.height) === 'number')
-                    $(that.element).css('height', that.height + 'px');
+                    that.element.style.height = that.height.toString() + 'px';
             }
             else {
                 var w = that.element.offsetWidth;
@@ -1407,7 +1480,7 @@ var NETDATA = window.NETDATA || {};
                     that.tm.last_resized = 0;
                 }
                 else
-                    $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
+                    that.element.style.height = (w * that.library.aspect_ratio / 100).toString() + 'px';
             }
 
             if(NETDATA.chartDefaults.min_width !== null)
@@ -1465,8 +1538,11 @@ var NETDATA = window.NETDATA || {};
         };
 
         var maxMessageFontSize = function() {
+            var screenHeight = screen.height;
+            var el = that.element;
+
             // normally we want a font size, as tall as the element
-            var h = that.element_message.clientHeight;
+            var h = el.clientHeight;
 
             // but give it some air, 20% let's say, or 5 pixels min
             var lost = Math.max(h * 0.2, 5);
@@ -1477,7 +1553,7 @@ var NETDATA = window.NETDATA || {};
 
             // but check the width too
             // it should fit 10 characters in it
-            var w = that.element_message.clientWidth / 10;
+            var w = el.clientWidth / 10;
             if(h > w) {
                 paddingTop += (h - w) / 2;
                 h = w;
@@ -1485,9 +1561,9 @@ var NETDATA = window.NETDATA || {};
 
             // and don't make it too huge
             // 5% of the screen size is good
-            if(h > screen.height / 20) {
-                paddingTop += (h - (screen.height / 20)) / 2;
-                h = screen.height / 20;
+            if(h > screenHeight / 20) {
+                paddingTop += (h - (screenHeight / 20)) / 2;
+                h = screenHeight / 20;
             }
 
             // set it
@@ -1495,25 +1571,17 @@ var NETDATA = window.NETDATA || {};
             that.element_message.style.paddingTop = paddingTop.toString() + 'px';
         };
 
-        var showMessage = function(msg) {
-            that.element_message.className = 'netdata-message';
-            that.element_message.innerHTML = msg;
-            that.element_message.style.fontSize = 'x-small';
-            that.element_message.style.paddingTop = '0px';
-            that.___messageHidden___ = undefined;
-        };
-
         var showMessageIcon = function(icon) {
             that.element_message.innerHTML = icon;
-            that.element_message.className = 'netdata-message icon';
             maxMessageFontSize();
+            $(that.element_message).removeClass('hidden');
             that.___messageHidden___ = undefined;
         };
 
         var hideMessage = function() {
             if(typeof that.___messageHidden___ === 'undefined') {
                 that.___messageHidden___ = true;
-                that.element_message.className = 'netdata-message hidden';
+                $(that.element_message).addClass('hidden');
             }
         };
 
@@ -1540,10 +1608,7 @@ var NETDATA = window.NETDATA || {};
         };
 
         var isHidden = function() {
-            if(typeof that.___chartIsHidden___ !== 'undefined')
-                return true;
-
-            return false;
+            return (typeof that.___chartIsHidden___ !== 'undefined');
         };
 
         // hide the chart, when it is not visible - called from isVisible()
@@ -1595,10 +1660,7 @@ var NETDATA = window.NETDATA || {};
         };
 
         var canBeRendered = function() {
-            if(isHidden() === true || that.isVisible(true) === false)
-                return false;
-
-            return true;
+            return (isHidden() === false && that.isVisible(true) === true);
         };
 
         // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
@@ -1671,8 +1733,8 @@ var NETDATA = window.NETDATA || {};
                 else if(typeof that.library.resize === 'function') {
                     that.library.resize(that);
 
-                    if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
-                        $(that.element_legend_childs.nano).nanoScroller();
+                    if(that.element_legend_childs.perfect_scroller !== null)
+                        Ps.update(that.element_legend_childs.perfect_scroller);
 
                     maxMessageFontSize();
                 }
@@ -1730,23 +1792,53 @@ var NETDATA = window.NETDATA || {};
             this.event_resize.chart_last_h = this.element.clientHeight;
 
             var now = Date.now();
-            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
+            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed && this.element_legend_childs.perfect_scroller !== null) {
                 // double click / double tap event
 
+                // console.dir(this.element_legend_childs.content);
+                // console.dir(this.element_legend_childs.perfect_scroller);
+
                 // the optimal height of the chart
                 // showing the entire legend
                 var optimal = this.event_resize.chart_last_h
-                        + this.element_legend_childs.content.scrollHeight
-                        - this.element_legend_childs.content.clientHeight;
+                        + this.element_legend_childs.perfect_scroller.scrollHeight
+                        - this.element_legend_childs.perfect_scroller.clientHeight;
 
                 // if we are not optimal, be optimal
-                if(this.event_resize.chart_last_h != optimal)
+                if(this.event_resize.chart_last_h !== optimal) {
+                    // this.log('resize to optimal, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
                     resizeChartToHeight(optimal.toString() + 'px');
+                }
 
-                // else if we do not have the original height
-                // reset to the original height
-                else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
+                // else if the current height is not the original/saved height
+                // reset to the original/saved height
+                else if(this.event_resize.chart_last_h !== this.event_resize.chart_original_h) {
+                    // this.log('resize to original, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
                     resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
+                }
+
+                // else if the current height is not the internal default height
+                // reset to the internal default height
+                else if((this.event_resize.chart_last_h.toString() + 'px') !== this.height_original) {
+                    // this.log('resize to internal default, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
+                    resizeChartToHeight(this.height_original.toString());
+                }
+
+                // else if the current height is not the firstchild's clientheight
+                // resize to it
+                else if(typeof this.element_legend_childs.perfect_scroller.firstChild !== 'undefined') {
+                    var parent_rect = this.element.getBoundingClientRect();
+                    var content_rect = this.element_legend_childs.perfect_scroller.firstElementChild.getBoundingClientRect();
+                    var wanted = content_rect.top - parent_rect.top + this.element_legend_childs.perfect_scroller.firstChild.clientHeight + 18; // 15 = toolbox + 3 space
+
+                    // console.log(parent_rect);
+                    // console.log(content_rect);
+                    // console.log(wanted);
+
+                    // this.log('resize to firstChild, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString() + 'px, firstChild = ' + wanted.toString() + 'px' );
+                    if(this.event_resize.chart_last_h !== wanted)
+                        resizeChartToHeight(wanted.toString() + 'px');
+                }
             }
             else {
                 this.event_resize.last = now;
@@ -1780,6 +1872,8 @@ var NETDATA = window.NETDATA || {};
                 this.element_legend_childs.resize_handler.onmouseup =
                 this.element_legend_childs.resize_handler.ontouchend =
                     function(e) {
+                        void(e);
+
                         // remove all the hooks
                         document.onmouseup =
                         document.onmousemove =
@@ -1854,17 +1948,11 @@ var NETDATA = window.NETDATA || {};
             if(NETDATA.options.current.sync_selection === false)
                 return false;
 
-            if(NETDATA.globalSelectionSync.dont_sync_before > Date.now())
-                return false;
-
-            return true;
+            return (NETDATA.globalSelectionSync.dont_sync_before <= Date.now());
         };
 
         this.globalSelectionSyncIsMaster = function() {
-            if(NETDATA.globalSelectionSync.state === this)
-                return true;
-            else
-                return false;
+            return (NETDATA.globalSelectionSync.state === this);
         };
 
         // this chart is the master of the global selection sync
@@ -1895,7 +1983,7 @@ var NETDATA = window.NETDATA || {};
             var targets = NETDATA.options.targets;
             var len = targets.length;
             while(len--) {
-                st = targets[len];
+                var st = targets[len];
 
                 if(st === this) {
                     if(this.debug === true)
@@ -1914,14 +2002,11 @@ var NETDATA = window.NETDATA || {};
 
         // can the chart participate to the global selection sync as a slave?
         this.globalSelectionSyncIsEligible = function() {
-            if(this.enabled === true
+            return (this.enabled === true
                 && this.library !== null
                 && typeof this.library.setSelection === 'function'
                 && this.isVisible() === true
-                && this.chart_created === true)
-                return true;
-
-            return false;
+                && this.chart_created === true);
         };
 
         // this chart becomes a slave of the global selection sync
@@ -1933,12 +2018,8 @@ var NETDATA = window.NETDATA || {};
         // sync all the visible charts to the given time
         // this is to be called from the chart libraries
         this.globalSelectionSync = function(t) {
-            if(this.globalSelectionSyncAbility() === false) {
-                if(this.debug === true)
-                    this.log('sync: cannot sync (yet?).');
-
+            if(this.globalSelectionSyncAbility() === false)
                 return;
-            }
 
             if(this.globalSelectionSyncIsMaster() === false) {
                 if(this.debug === true)
@@ -1946,12 +2027,8 @@ var NETDATA = window.NETDATA || {};
 
                 this.globalSelectionSyncBeMaster();
 
-                if(this.globalSelectionSyncAbility() === false) {
-                    if(this.debug === true)
-                        this.log('sync: cannot sync (yet?).');
-
+                if(this.globalSelectionSyncAbility() === false)
                     return;
-                }
             }
 
             NETDATA.globalSelectionSync.last_t = t;
@@ -1988,13 +2065,10 @@ var NETDATA = window.NETDATA || {};
         };
 
         this.setSelection = function(t) {
-            if(typeof this.library.setSelection === 'function') {
-                if(this.library.setSelection(this, t) === true)
-                    this.selected = true;
-                else
-                    this.selected = false;
-            }
-            else this.selected = true;
+            if(typeof this.library.setSelection === 'function')
+                this.selected = (this.library.setSelection(this, t) === true);
+            else
+                this.selected = true;
 
             if(this.selected === true && this.debug === true)
                 this.log('selection set to ' + t.toString());
@@ -2004,13 +2078,10 @@ var NETDATA = window.NETDATA || {};
 
         this.clearSelection = function() {
             if(this.selected === true) {
-                if(typeof this.library.clearSelection === 'function') {
-                    if(this.library.clearSelection(this) === true)
-                        this.selected = false;
-                    else
-                        this.selected = true;
-                }
-                else this.selected = false;
+                if(typeof this.library.clearSelection === 'function')
+                    this.selected = (this.library.clearSelection(this) !== true);
+                else
+                    this.selected = false;
 
                 if(this.selected === false && this.debug === true)
                     this.log('selection cleared');
@@ -2023,9 +2094,7 @@ var NETDATA = window.NETDATA || {};
 
         // find if a timestamp (ms) is shown in the current chart
         this.timeIsVisible = function(t) {
-            if(t >= this.data_after && t <= this.data_before)
-                return true;
-            return false;
+            return (t >= this.data_after && t <= this.data_before);
         };
 
         this.calculateRowForTime = function(t) {
@@ -2176,19 +2245,69 @@ var NETDATA = window.NETDATA || {};
             return ret;
         };
 
+        var __legendFormatValueChartDecimalsLastMin = undefined;
+        var __legendFormatValueChartDecimalsLastMax = undefined;
+        var __legendFormatValueChartDecimals = -1;
+        this.legendFormatValueDecimalsFromMinMax = function(min, max) {
+            if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax)
+                return;
+
+            __legendFormatValueChartDecimalsLastMin = min;
+            __legendFormatValueChartDecimalsLastMax = max;
+
+            if(this.data !== null && this.data.min === this.data.max)
+                __legendFormatValueChartDecimals = -1;
+
+            else if(this.value_decimal_detail !== -1)
+                __legendFormatValueChartDecimals = this.value_decimal_detail;
+
+            else {
+                var delta;
+
+                if (min === max)
+                    delta = Math.abs(min);
+                else
+                    delta = Math.abs(max - min);
+
+                if (delta > 1000)     __legendFormatValueChartDecimals = 0;
+                else if (delta > 10)  __legendFormatValueChartDecimals = 1;
+                else if (delta > 1)   __legendFormatValueChartDecimals = 2;
+                else if (delta > 0.1) __legendFormatValueChartDecimals = 2;
+                else                  __legendFormatValueChartDecimals = 4;
+            }
+        };
+
         this.legendFormatValue = function(value) {
-            if(value === null || value === 'undefined') return '-';
-            if(typeof value !== 'number') return value;
-
-            if(this.value_decimal_detail !== -1)
-                return (Math.round(value * this.value_decimal_detail) / this.value_decimal_detail).toLocaleString();
-
-            var abs = Math.abs(value);
-            if(abs >= 1000) return (Math.round(value)).toLocaleString();
-            if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
-            if(abs >= 1   ) return (Math.round(value * 100) / 100).toLocaleString();
-            if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
-            return (Math.round(value * 10000) / 10000).toLocaleString();
+            if(typeof value !== 'number') return '-';
+
+            var dmin, dmax;
+
+            if(__legendFormatValueChartDecimals < 0) {
+                dmin = 0;
+                var abs = value;
+                if(abs > 1000)      dmax = 0;
+                else if(abs > 10 )  dmax = 1;
+                else if(abs > 1)    dmax = 2;
+                else if(abs > 0.1)  dmax = 2;
+                else                dmax = 4;
+            }
+            else {
+                dmin = dmax = __legendFormatValueChartDecimals;
+            }
+
+            if(this.value_decimal_detail !== -1) {
+                dmin = dmax = this.value_decimal_detail;
+            }
+
+            return value.toLocaleString(undefined, {
+                // style: 'decimal',
+                // minimumIntegerDigits: 1,
+                // minimumSignificantDigits: 1,
+                // maximumSignificantDigits: 1,
+                useGrouping: true,
+                minimumFractionDigits: dmin,
+                maximumFractionDigits: dmax
+            });
         };
 
         this.legendSetLabelValue = function(label, value) {
@@ -2258,33 +2377,44 @@ var NETDATA = window.NETDATA || {};
             }
         };
 
+        this.legendSetDateLast = {
+            ms: 0,
+            date: undefined,
+            time: undefined
+        };
+
         this.legendSetDate = function(ms) {
             if(typeof ms !== 'number') {
                 this.legendShowUndefined();
                 return;
             }
 
-            var d = new Date(ms);
+            if(this.legendSetDateLast.ms !== ms) {
+                var d = new Date(ms);
+                this.legendSetDateLast.ms = ms;
+                this.legendSetDateLast.date = d.toLocaleDateString();
+                this.legendSetDateLast.time = d.toLocaleTimeString();
+            }
 
-            if(this.element_legend_childs.title_date)
-                this.__legendSetDateString(d.toLocaleDateString());
+            if(this.element_legend_childs.title_date !== null)
+                this.__legendSetDateString(this.legendSetDateLast.date);
 
-            if(this.element_legend_childs.title_time)
-                this.__legendSetTimeString(d.toLocaleTimeString());
+            if(this.element_legend_childs.title_time !== null)
+                this.__legendSetTimeString(this.legendSetDateLast.time);
 
-            if(this.element_legend_childs.title_units)
+            if(this.element_legend_childs.title_units !== null)
                 this.__legendSetUnitsString(this.units)
         };
 
         this.legendShowUndefined = function() {
-            if(this.element_legend_childs.title_date)
+            if(this.element_legend_childs.title_date !== null)
                 this.__legendSetDateString(' ');
 
-            if(this.element_legend_childs.title_time)
+            if(this.element_legend_childs.title_time !== null)
                 this.__legendSetTimeString(this.chart.name);
 
-            if(this.element_legend_childs.title_units)
-                this.__legendSetUnitsString(' ')
+            if(this.element_legend_childs.title_units !== null)
+                this.__legendSetUnitsString(' ');
 
             if(this.data && this.element_legend_childs.series !== null) {
                 var labels = this.data.dimension_names;
@@ -2292,8 +2422,7 @@ var NETDATA = window.NETDATA || {};
                 while(i--) {
                     var label = labels[i];
 
-                    if(typeof label === 'undefined') continue;
-                    if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
+                    if(typeof label === 'undefined' || typeof this.element_legend_childs.series[label] === 'undefined') continue;
                     this.legendSetLabelValue(label, null);
                 }
             }
@@ -2366,8 +2495,8 @@ var NETDATA = window.NETDATA || {};
         this.chartColors = function() {
             if(this.colors !== null) return this.colors;
 
-            this.colors = new Array();
-            this.colors_available = new Array();
+            this.colors = [];
+            this.colors_available = [];
 
             // add the standard colors
             var len = NETDATA.themes.current.colors.length;
@@ -2400,7 +2529,7 @@ var NETDATA = window.NETDATA || {};
         };
 
         this.legendUpdateDOM = function() {
-            var needed = false;
+            var needed = false, dim, keys, len, i;
 
             // check that the legend DOM is up to date for the downloaded dimensions
             if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
@@ -2444,9 +2573,12 @@ var NETDATA = window.NETDATA || {};
             if(this.colors === null) {
                 // this is the first time we update the chart
                 // let's assign colors to all dimensions
-                if(this.library.track_colors() === true)
-                    for(var dim in this.chart.dimensions)
-                        this._chartDimensionColor(this.chart.dimensions[dim].name);
+                if(this.library.track_colors() === true) {
+                    keys = Object.keys(this.chart.dimensions);
+                    len = keys.length;
+                    for(i = 0; i < len ;i++)
+                        this._chartDimensionColor(this.chart.dimensions[keys[i]].name);
+                }
             }
             // we will re-generate the colors for the chart
             // based on the selected dimensions
@@ -2489,7 +2621,7 @@ var NETDATA = window.NETDATA || {};
                     + state.chart.chart_type
                     + '" style="background-color: '
                     + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
-                    + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
+                    + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>';
 
                 var text = document.createTextNode(' ' + name);
                 label.name.appendChild(text);
@@ -2517,18 +2649,7 @@ var NETDATA = window.NETDATA || {};
                     title_date: document.createElement('span'),
                     title_time: document.createElement('span'),
                     title_units: document.createElement('span'),
-                    nano: document.createElement('div'),
-                    nano_options: {
-                        paneClass: 'netdata-legend-series-pane',
-                        sliderClass: 'netdata-legend-series-slider',
-                        contentClass: 'netdata-legend-series-content',
-                        enabledClass: '__enabled',
-                        flashedClass: '__flashed',
-                        activeClass: '__active',
-                        tabIndex: -1,
-                        alwaysVisible: true,
-                        sliderMinHeight: 10
-                    },
+                    perfect_scroller: document.createElement('div'),
                     series: {}
                 };
 
@@ -2536,7 +2657,7 @@ var NETDATA = window.NETDATA || {};
 
                 if(this.library.toolboxPanAndZoom !== null) {
 
-                    function get_pan_and_zoom_step(event) {
+                    var get_pan_and_zoom_step = function(event) {
                         if (event.ctrlKey)
                             return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
 
@@ -2548,7 +2669,7 @@ var NETDATA = window.NETDATA || {};
 
                         else
                             return NETDATA.options.current.pan_and_zoom_factor;
-                    }
+                    };
 
                     this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
                     this.element.appendChild(this.element_legend_childs.toolbox);
@@ -2713,24 +2834,27 @@ var NETDATA = window.NETDATA || {};
 
                 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
                 this.element_legend.appendChild(this.element_legend_childs.title_date);
+                this.__last_shown_legend_date = undefined;
 
                 this.element_legend.appendChild(document.createElement('br'));
 
                 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
                 this.element_legend.appendChild(this.element_legend_childs.title_time);
+                this.__last_shown_legend_time = undefined;
 
                 this.element_legend.appendChild(document.createElement('br'));
 
                 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
                 this.element_legend.appendChild(this.element_legend_childs.title_units);
+                this.__last_shown_legend_units = undefined;
 
                 this.element_legend.appendChild(document.createElement('br'));
 
-                this.element_legend_childs.nano.className = 'netdata-legend-series';
-                this.element_legend.appendChild(this.element_legend_childs.nano);
+                this.element_legend_childs.perfect_scroller.className = 'netdata-legend-series';
+                this.element_legend.appendChild(this.element_legend_childs.perfect_scroller);
 
                 content.className = 'netdata-legend-series-content';
-                this.element_legend_childs.nano.appendChild(content);
+                this.element_legend_childs.perfect_scroller.appendChild(content);
 
                 if(NETDATA.options.current.show_help === true)
                     $(content).popover({
@@ -2741,7 +2865,7 @@ var NETDATA = window.NETDATA || {};
                     placement: 'bottom',
                     title: 'Chart Legend',
                     delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                    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>'
+                    content: 'You can click or tap on the values or the labels to select dimensions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
                 });
             }
             else {
@@ -2758,8 +2882,7 @@ var NETDATA = window.NETDATA || {};
                     title_date: null,
                     title_time: null,
                     title_units: null,
-                    nano: null,
-                    nano_options: null,
+                    perfect_scroller: null,
                     series: {}
                 };
             }
@@ -2769,13 +2892,15 @@ var NETDATA = window.NETDATA || {};
                 if(this.debug === true)
                     this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
 
-                for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
+                for(i = 0, len = this.data.dimension_names.length; i < len ;i++) {
                     genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
                 }
             }
             else {
-                var tmp = new Array();
-                for(var dim in this.chart.dimensions) {
+                var tmp = [];
+                keys = Object.keys(this.chart.dimensions);
+                for(i = 0, len = keys.length; i < len ;i++) {
+                    dim = keys[i];
                     tmp.push(this.chart.dimensions[dim].name);
                     genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
                 }
@@ -2794,8 +2919,22 @@ var NETDATA = window.NETDATA || {};
             this.element_legend_childs.hidden = document.createElement('div');
             el.appendChild(this.element_legend_childs.hidden);
 
-            if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
-                $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
+            if(this.element_legend_childs.perfect_scroller !== null) {
+                Ps.initialize(this.element_legend_childs.perfect_scroller, {
+                    wheelSpeed: 0.2,
+                    wheelPropagation: true,
+                    swipePropagation: true,
+                    minScrollbarLength: null,
+                    maxScrollbarLength: null,
+                    useBothWheelAxes: false,
+                    suppressScrollX: true,
+                    suppressScrollY: false,
+                    scrollXMarginOffset: 0,
+                    scrollYMarginOffset: 0,
+                    theme: 'default'
+                });
+                Ps.update(this.element_legend_childs.perfect_scroller);
+            }
 
             this.legendShowLatestValues();
         };
@@ -2963,12 +3102,12 @@ var NETDATA = window.NETDATA || {};
 
             if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
                 if(this.view_after < this.data_after) {
-                    // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
+                    // console.log('adjusting view_after from ' + this.view_after + ' to ' + this.data_after);
                     this.view_after = this.data_after;
                 }
 
                 if(this.view_before > this.data_before) {
-                    // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
+                    // console.log('adjusting view_before from ' + this.view_before + ' to ' + this.data_before);
                     this.view_before = this.data_before;
                 }
             }
@@ -3000,7 +3139,8 @@ var NETDATA = window.NETDATA || {};
                 if(this.debug === true)
                     this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
 
-                this.chart_created = false;
+                init();
+                return;
             }
 
             // check and update the legend
@@ -3057,8 +3197,10 @@ var NETDATA = window.NETDATA || {};
                 if(this.debug === true)
                     this.log('I am already updating...');
 
-                if(typeof callback === 'function') callback();
-                return false;
+                if(typeof callback === 'function')
+                    return callback();
+
+                return;
             }
 
             // due to late initialization of charts and libraries
@@ -3067,29 +3209,37 @@ var NETDATA = window.NETDATA || {};
                 if(this.debug === true)
                     this.log('I am not enabled');
 
-                if(typeof callback === 'function') callback();
-                return false;
+                if(typeof callback === 'function')
+                    return callback();
+
+                return;
             }
 
             if(canBeRendered() === false) {
-                if(typeof callback === 'function') callback();
-                return false;
-            }
+                if(typeof callback === 'function')
+                    return callback();
 
-            if(this.chart === null) {
-                this.getChart(function() { that.updateChart(callback); });
-                return false;
+                return;
             }
 
+            if(this.chart === null)
+                return this.getChart(function() {
+                    return that.updateChart(callback);
+                });
+
             if(this.library.initialized === false) {
                 if(this.library.enabled === true) {
-                    this.library.initialize(function() { that.updateChart(callback); });
-                    return false;
+                    return this.library.initialize(function () {
+                        return that.updateChart(callback);
+                    });
                 }
                 else {
                     error('chart library "' + this.library_name + '" is not available.');
-                    if(typeof callback === 'function') callback();
-                    return false;
+
+                    if(typeof callback === 'function')
+                        return callback();
+
+                    return;
                 }
             }
 
@@ -3147,10 +3297,10 @@ var NETDATA = window.NETDATA || {};
 
                 NETDATA.statistics.refreshes_active--;
                 that._updating = false;
-                if(typeof callback === 'function') callback();
-            });
 
-            return true;
+                if(typeof callback === 'function')
+                    return callback();
+            });
         };
 
         this.isVisible = function(nocache) {
@@ -3314,12 +3464,12 @@ var NETDATA = window.NETDATA || {};
                     state.running = false;
 
                     if(typeof callback !== 'undefined')
-                        callback();
+                        return callback();
                 });
             }
             else {
                 if(typeof callback !== 'undefined')
-                    callback();
+                    return callback();
             }
         };
 
@@ -3342,7 +3492,9 @@ var NETDATA = window.NETDATA || {};
             this.chart = NETDATA.chartRegistry.get(this.host, this.id);
             if(this.chart) {
                 this._defaultsFromDownloadedChart(this.chart);
-                if(typeof callback === 'function') callback();
+
+                if(typeof callback === 'function')
+                    return callback();
             }
             else {
                 this.chart_url = "/api/v1/chart?chart=" + this.id;
@@ -3366,7 +3518,8 @@ var NETDATA = window.NETDATA || {};
                     error('chart not found on url "' + that.chart_url + '"');
                 })
                 .always(function() {
-                    if(typeof callback === 'function') callback();
+                    if(typeof callback === 'function')
+                        return callback();
                 });
             }
         };
@@ -3427,7 +3580,7 @@ var NETDATA = window.NETDATA || {};
             script.src = NETDATA.jQuery;
 
             // script.onabort = onError;
-            script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
+            script.onerror = function() { NETDATA.error(101, NETDATA.jQuery); };
             if(typeof callback === "function")
                 script.onload = callback;
 
@@ -3435,7 +3588,7 @@ var NETDATA = window.NETDATA || {};
             s.parentNode.insertBefore(script, s);
         }
         else if(typeof callback === "function")
-            callback();
+            return callback();
     };
 
     NETDATA._loadCSS = function(filename) {
@@ -3512,10 +3665,12 @@ var NETDATA = window.NETDATA || {};
     };
 
     NETDATA.pause = function(callback) {
-        if(NETDATA.options.pause === true)
-            callback();
-        else
-            NETDATA.options.pauseCallback = callback;
+        if(typeof callback === 'function') {
+            if (NETDATA.options.pause === true)
+                return callback();
+            else
+                NETDATA.options.pauseCallback = callback;
+        }
     };
 
     NETDATA.unpause = function() {
@@ -3526,10 +3681,10 @@ var NETDATA = window.NETDATA || {};
 
     // ----------------------------------------------------------------------------------------------------------------
 
-    // this is purely sequencial charts refresher
+    // this is purely sequential charts refresher
     // it is meant to be autonomous
     NETDATA.chartRefresherNoParallel = function(index) {
-        if(NETDATA.options.debug.mail_loop === true)
+        if(NETDATA.options.debug.main_loop === true)
             console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
 
         if(NETDATA.options.updated_dom === true) {
@@ -3572,41 +3727,11 @@ var NETDATA = window.NETDATA || {};
         }
     };
 
-    // this is part of the parallel refresher
-    // its cause is to refresh sequencially all the charts
-    // that depend on chart library initialization
-    // it will call the parallel refresher back
-    // as soon as it sees a chart that its chart library
-    // is initialized
-    NETDATA.chartRefresher_uninitialized = function() {
-        if(NETDATA.options.updated_dom === true) {
-            // the dom has been updated
-            // get the dom parts again
-            NETDATA.parseDom(NETDATA.chartRefresher);
-            return;
-        }
-
-        if(NETDATA.options.sequencial.length === 0)
-            NETDATA.chartRefresher();
-        else {
-            var state = NETDATA.options.sequencial.pop();
-            if(state.library.initialized === true)
-                NETDATA.chartRefresher();
-            else
-                state.autoRefresh(NETDATA.chartRefresher_uninitialized);
-        }
-    };
-
     NETDATA.chartRefresherWaitTime = function() {
         return NETDATA.options.current.idle_parallel_loops;
     };
 
     // the default refresher
-    // it will create 2 sets of charts:
-    // - the ones that can be refreshed in parallel
-    // - the ones that depend on something else
-    // the first set will be executed in parallel
-    // the second will be given to NETDATA.chartRefresher_uninitialized()
     NETDATA.chartRefresher = function() {
         // console.log('auto-refresher...');
 
@@ -3639,7 +3764,7 @@ var NETDATA = window.NETDATA || {};
             return;
         }
 
-        var parallel = new Array();
+        var parallel = [];
         var targets = NETDATA.options.targets;
         var len = targets.length;
         var state;
@@ -3686,7 +3811,7 @@ var NETDATA = window.NETDATA || {};
         if(NETDATA.options.debug.main_loop === true)
             console.log('DOM updated - there are ' + targets.length + ' charts on page.');
 
-        NETDATA.options.targets = new Array();
+        NETDATA.options.targets = [];
         var len = targets.length;
         while(len--) {
             // the initialization will take care of sizing
@@ -3694,7 +3819,8 @@ var NETDATA = window.NETDATA || {};
             NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
         }
 
-        if(typeof callback === 'function') callback();
+        if(typeof callback === 'function')
+            return callback();
     };
 
     // this is the main function - where everything starts
@@ -3731,12 +3857,14 @@ var NETDATA = window.NETDATA || {};
         $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
 
         // bootstrap modal switching
-        $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
-        $('.modal').on('shown.bs.modal', NETDATA.onscroll);
+        var $modal = $('.modal');
+        $modal.on('hidden.bs.modal', NETDATA.onscroll);
+        $modal.on('shown.bs.modal', NETDATA.onscroll);
 
         // bootstrap collapse switching
-        $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
-        $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
+        var $collapse = $('.collapse');
+        $collapse.on('hidden.bs.collapse', NETDATA.onscroll);
+        $collapse.on('shown.bs.collapse', NETDATA.onscroll);
 
         NETDATA.parseDom(NETDATA.chartRefresher);
 
@@ -3770,13 +3898,13 @@ var NETDATA = window.NETDATA || {};
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.peity.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -3832,13 +3960,13 @@ var NETDATA = window.NETDATA || {};
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.sparkline.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -3996,7 +4124,7 @@ var NETDATA = window.NETDATA || {};
         return true;
     };
 
-    NETDATA.dygraphClearSelection = function(state, t) {
+    NETDATA.dygraphClearSelection = function(state) {
         if(typeof state.dygraph_instance !== 'undefined') {
             state.dygraph_instance.clearSelection();
         }
@@ -4019,7 +4147,7 @@ var NETDATA = window.NETDATA || {};
         })
         .always(function() {
             if(typeof callback === "function")
-                callback();
+                return callback();
         });
     };
 
@@ -4042,13 +4170,13 @@ var NETDATA = window.NETDATA || {};
                 if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
                     NETDATA.dygraphSmoothInitialize(callback);
                 else if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.dygraph.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -4143,125 +4271,205 @@ var NETDATA = window.NETDATA || {};
 
         var self = $(state.element);
 
-        var chart_type = state.chart.chart_type;
+        var chart_type = self.data('dygraph-type') || state.chart.chart_type;
         if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area';
-        chart_type = self.data('dygraph-type') || chart_type;
 
-        var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false;
-        smooth = self.data('dygraph-smooth') || smooth;
+        var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state) === true)?3:4;
 
-        if(NETDATA.dygraph.smooth === false)
-            smooth = false;
-
-        var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
-        var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
+        var smooth = (NETDATA.dygraph.smooth === true)
+            ?(self.data('dygraph-smooth') || (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false))
+            :false;
 
         state.dygraph_options = {
-            colors: self.data('dygraph-colors') || state.chartColors(),
+            colors:                 self.data('dygraph-colors') || state.chartColors(),
 
             // leave a few pixels empty on the right of the chart
-            rightGap: self.data('dygraph-rightgap') || 5,
-            showRangeSelector: self.data('dygraph-showrangeselector') || false,
-            showRoller: self.data('dygraph-showroller') || false,
-
-            title: self.data('dygraph-title') || state.title,
-            titleHeight: self.data('dygraph-titleheight') || 19,
-
-            legend: self.data('dygraph-legend') || 'always', // we need this to get selection events
-            labels: data.result.labels,
-            labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
-            labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' },
-            labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
-            labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
-            labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
-            labelsKMB: false,
-            labelsKMG2: false,
-            showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
-            hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
-
-            includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
-            xRangePad: self.data('dygraph-xrangepad') || 0,
-            yRangePad: self.data('dygraph-yrangepad') || 1,
-
-            valueRange: self.data('dygraph-valuerange') || [ null, null ],
-
-            ylabel: state.units,
-            yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
-
-            // the function to plot the chart
-            plotter: null,
-
-            // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
-            strokeWidth: self.data('dygraph-strokewidth') || strokeWidth,
-            strokePattern: self.data('dygraph-strokepattern') || undefined,
-
-            // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
-            // i.e. there is a missing point on either side of it. This also controls the size of those dots.
-            drawPoints: self.data('dygraph-drawpoints') || false,
-
-            // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
-            drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
-
-            connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
-            pointSize: self.data('dygraph-pointsize') || 1,
-
-            // enabling this makes the chart with little square lines
-            stepPlot: self.data('dygraph-stepplot') || false,
-
-            // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
-            strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background,
-            strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0,
-
-            fillGraph: self.data('dygraph-fillgraph') || ((chart_type === 'area' || chart_type === 'stacked')?true:false),
-            fillAlpha: self.data('dygraph-fillalpha') || ((chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area),
-            stackedGraph: self.data('dygraph-stackedgraph') || ((chart_type === 'stacked')?true:false),
-            stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
-
-            drawAxis: self.data('dygraph-drawaxis') || true,
-            axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
-            axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
-            axisLineWidth: self.data('dygraph-axislinewidth') || 1.0,
-
-            drawGrid: self.data('dygraph-drawgrid') || true,
-            gridLinePattern: self.data('dygraph-gridlinepattern') || null,
-            gridLineWidth: self.data('dygraph-gridlinewidth') || 1.0,
-            gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
-
-            maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
-            sigFigs: self.data('dygraph-sigfigs') || null,
-            digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
-            valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
-
-            highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize,
-            highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
-            highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
-
-            pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
-            visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
+            rightGap:               self.data('dygraph-rightgap')
+                                    || 5,
+
+            showRangeSelector:      self.data('dygraph-showrangeselector')
+                                    || false,
+
+            showRoller:             self.data('dygraph-showroller')
+                                    || false,
+
+            title:                  self.data('dygraph-title')
+                                    || state.title,
+
+            titleHeight:            self.data('dygraph-titleheight')
+                                    || 19,
+
+            legend:                 self.data('dygraph-legend')
+                                    || 'always', // we need this to get selection events
+
+            labels:                 data.result.labels,
+
+            labelsDiv:              self.data('dygraph-labelsdiv')
+                                    || state.element_legend_childs.hidden,
+
+            labelsDivStyles:        self.data('dygraph-labelsdivstyles')
+                                    || { 'fontSize':'1px' },
+
+            labelsDivWidth:         self.data('dygraph-labelsdivwidth')
+                                    || state.chartWidth() - 70,
+
+            labelsSeparateLines:    self.data('dygraph-labelsseparatelines')
+                                    || true,
+
+            labelsShowZeroValues:   self.data('dygraph-labelsshowzerovalues')
+                                    || true,
+
+            labelsKMB:              false,
+            labelsKMG2:             false,
+
+            showLabelsOnHighlight:  self.data('dygraph-showlabelsonhighlight')
+                                    || true,
+
+            hideOverlayOnMouseOut:  self.data('dygraph-hideoverlayonmouseout')
+                                    || true,
+
+            includeZero:            self.data('dygraph-includezero')
+                                    || (chart_type === 'stacked'),
+
+            xRangePad:              self.data('dygraph-xrangepad')
+                                    || 0,
+
+            yRangePad:              self.data('dygraph-yrangepad')
+                                    || 1,
+
+            valueRange:             self.data('dygraph-valuerange')
+                                    || [ null, null ],
+
+            ylabel:                 state.units,
+
+            yLabelWidth:            self.data('dygraph-ylabelwidth')
+                                    || 12,
+
+                                    // the function to plot the chart
+            plotter:                null,
+
+                                    // The width of the lines connecting data points.
+                                    // This can be used to increase the contrast or some graphs.
+            strokeWidth:            self.data('dygraph-strokewidth')
+                                    || ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7)),
+
+            strokePattern:          self.data('dygraph-strokepattern')
+                                    || undefined,
+
+                                    // The size of the dot to draw on each point in pixels (see drawPoints).
+                                    // A dot is always drawn when a point is "isolated",
+                                    // i.e. there is a missing point on either side of it.
+                                    // This also controls the size of those dots.
+            drawPoints:             self.data('dygraph-drawpoints')
+                                    || false,
+
+                                    // Draw points at the edges of gaps in the data.
+                                    // This improves visibility of small data segments or other data irregularities.
+            drawGapEdgePoints:      self.data('dygraph-drawgapedgepoints')
+                                    || true,
+
+            connectSeparatedPoints: self.data('dygraph-connectseparatedpoints')
+                                    || false,
+
+            pointSize:              self.data('dygraph-pointsize')
+                                    || 1,
+
+                                    // enabling this makes the chart with little square lines
+            stepPlot:               self.data('dygraph-stepplot')
+                                    || false,
+
+                                    // Draw a border around graph lines to make crossing lines more easily
+                                    // distinguishable. Useful for graphs with many lines.
+            strokeBorderColor:      self.data('dygraph-strokebordercolor')
+                                    || NETDATA.themes.current.background,
+
+            strokeBorderWidth:      self.data('dygraph-strokeborderwidth')
+                                    || (chart_type === 'stacked')?0.0:0.0,
+
+            fillGraph:              self.data('dygraph-fillgraph')
+                                    || (chart_type === 'area' || chart_type === 'stacked'),
+
+            fillAlpha:              self.data('dygraph-fillalpha')
+                                    || ((chart_type === 'stacked')
+                                        ?NETDATA.options.current.color_fill_opacity_stacked
+                                        :NETDATA.options.current.color_fill_opacity_area),
+
+            stackedGraph:           self.data('dygraph-stackedgraph')
+                                    || (chart_type === 'stacked'),
+
+            stackedGraphNaNFill:    self.data('dygraph-stackedgraphnanfill')
+                                    || 'none',
+
+            drawAxis:               self.data('dygraph-drawaxis')
+                                    || true,
+
+            axisLabelFontSize:      self.data('dygraph-axislabelfontsize')
+                                    || 10,
+
+            axisLineColor:          self.data('dygraph-axislinecolor')
+                                    || NETDATA.themes.current.axis,
+
+            axisLineWidth:          self.data('dygraph-axislinewidth')
+                                    || 1.0,
+
+            drawGrid:               self.data('dygraph-drawgrid')
+                                    || true,
+
+            gridLinePattern:        self.data('dygraph-gridlinepattern')
+                                    || null,
+
+            gridLineWidth:          self.data('dygraph-gridlinewidth')
+                                    || 1.0,
+
+            gridLineColor:          self.data('dygraph-gridlinecolor')
+                                    || NETDATA.themes.current.grid,
+
+            maxNumberWidth:         self.data('dygraph-maxnumberwidth')
+                                    || 8,
+
+            sigFigs:                self.data('dygraph-sigfigs')
+                                    || null,
+
+            digitsAfterDecimal:     self.data('dygraph-digitsafterdecimal')
+                                    || 2,
+
+            valueFormatter:         self.data('dygraph-valueformatter')
+                                    || undefined,
+
+            highlightCircleSize:    self.data('dygraph-highlightcirclesize')
+                                    || highlightCircleSize,
+
+            highlightSeriesOpts:    self.data('dygraph-highlightseriesopts')
+                                    || null, // TOO SLOW: { strokeWidth: 1.5 },
+
+            highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha')
+                                    || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
+
+            pointClickCallback:     self.data('dygraph-pointclickcallback')
+                                    || undefined,
+
+            visibility:             state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
+
             axes: {
                 x: {
                     pixelsPerLabel: 50,
                     ticker: Dygraph.dateTicker,
                     axisLabelFormatter: function (d, gran) {
+                        void(gran);
                         return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
-                    },
-                    valueFormatter: function (ms) {
-                        //var d = new Date(ms);
-                        //return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
-
-                        // no need to return anything here
-                        return ' ';
-
                     }
                 },
                 y: {
                     pixelsPerLabel: 15,
-                    valueFormatter: function (x) {
-                        // we format legends with the state object
-                        // no need to do anything here
-                        // return (Math.round(x*100) / 100).toLocaleString();
-                        // return state.legendFormatValue(x);
-                        return x;
+                    axisLabelFormatter: function (y) {
+
+                        // unfortunately, we have to call this every single time
+                        state.legendFormatValueDecimalsFromMinMax(
+                            this.axes_[0].extremeRange[0],
+                            this.axes_[0].extremeRange[1]
+                        );
+
+                        return state.legendFormatValue(y);
                     }
                 }
             },
@@ -4279,6 +4487,8 @@ var NETDATA = window.NETDATA || {};
                         var series = data.series[i];
                         if(series.isVisible === true)
                             state.legendSetLabelValue(series.label, series.y);
+                        else
+                            state.legendSetLabelValue(series.label, null);
                     }
                 }
 
@@ -4300,6 +4510,8 @@ var NETDATA = window.NETDATA || {};
                 }
             },
             zoomCallback: function(minDate, maxDate, yRanges) {
+                void(yRanges);
+
                 if(NETDATA.options.debug.dygraph === true)
                     state.log('dygraphZoomCallback()');
 
@@ -4313,6 +4525,8 @@ var NETDATA = window.NETDATA || {};
                 state.updateChartPanOrZoom(minDate, maxDate);
             },
             highlightCallback: function(event, x, points, row, seriesName) {
+                void(seriesName);
+
                 if(NETDATA.options.debug.dygraph === true || state.debug === true)
                     state.log('dygraphHighlightCallback()');
 
@@ -4322,7 +4536,7 @@ var NETDATA = window.NETDATA || {};
                 // the time it thinks is selected is wrong
                 // here we calculate the time t based on the row number selected
                 // which is ok
-                var t = state.data_after + row * state.data_update_every;
+                // var t = state.data_after + row * state.data_update_every;
                 // 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);
 
                 state.globalSelectionSync(x);
@@ -4332,6 +4546,8 @@ var NETDATA = window.NETDATA || {};
                 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
             },
             unhighlightCallback: function(event) {
+                void(event);
+
                 if(NETDATA.options.debug.dygraph === true || state.debug === true)
                     state.log('dygraphUnhighlightCallback()');
 
@@ -4415,17 +4631,26 @@ var NETDATA = window.NETDATA || {};
                     }
                 },
                 click: function(event, dygraph, context) {
+                    void(dygraph);
+                    void(context);
+
                     if(NETDATA.options.debug.dygraph === true || state.debug === true)
                         state.log('interactionModel.click()');
 
                     event.preventDefault();
                 },
                 dblclick: function(event, dygraph, context) {
+                    void(event);
+                    void(dygraph);
+                    void(context);
+
                     if(NETDATA.options.debug.dygraph === true || state.debug === true)
                         state.log('interactionModel.dblclick()');
                     NETDATA.resetAllCharts(state);
                 },
                 wheel: function(event, dygraph, context) {
+                    void(context);
+
                     if(NETDATA.options.debug.dygraph === true || state.debug === true)
                         state.log('interactionModel.wheel()');
 
@@ -4437,7 +4662,7 @@ var NETDATA = window.NETDATA || {};
                         var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
                         var yar0 = g.yAxisRange(0);
 
-                        // This is calculating the pixel of the higest value. (Top pixel)
+                        // This is calculating the pixel of the highest value. (Top pixel)
                         var yOffset = g.toDomCoords(null, yar0[1])[1];
 
                         // x y w and h are relative to the corner of the drawing area,
@@ -4496,7 +4721,7 @@ var NETDATA = window.NETDATA || {};
 
                         // http://dygraphs.com/gallery/interaction-api.js
                         var normal_def;
-                        if(typeof event.wheelDelta === 'number' && event.wheelDelta != NaN)
+                        if(typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta))
                             // chrome
                             normal_def = event.wheelDelta / 40;
                         else
@@ -4548,7 +4773,7 @@ var NETDATA = window.NETDATA || {};
                     Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
 
                     // we overwrite the touch directions at the end, to overwrite
-                    // the internal default of dygraphs
+                    // the internal default of dygraph
                     context.touchDirections = { x: true, y: false };
 
                     state.dygraph_last_touch_start = Date.now();
@@ -4577,7 +4802,7 @@ var NETDATA = window.NETDATA || {};
 
                     // if it didn't move, it is a selection
                     if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
-                        // internal api of dygraphs
+                        // internal api of dygraph
                         var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
                         var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct);
                         if(NETDATA.dygraphSetSelection(state, t) === true)
@@ -4633,7 +4858,7 @@ var NETDATA = window.NETDATA || {};
             state.__commonMax = self.data('common-max') || null;
         }
         else {
-            state.log('incompatible version of dygraphs detected');
+            state.log('incompatible version of Dygraph detected');
             state.__commonMin = null;
             state.__commonMax = null;
         }
@@ -4657,7 +4882,7 @@ var NETDATA = window.NETDATA || {};
                 else {
                     NETDATA.chartLibraries.morris.enabled = false;
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 }
             }
             else {
@@ -4678,14 +4903,14 @@ var NETDATA = window.NETDATA || {};
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 });
             }
         }
         else {
             NETDATA.chartLibraries.morris.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -4744,13 +4969,13 @@ var NETDATA = window.NETDATA || {};
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.raphael.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -4788,7 +5013,7 @@ var NETDATA = window.NETDATA || {};
                 else {
                     NETDATA.chartLibraries.c3.enabled = false;
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 }
             }
             else {
@@ -4809,14 +5034,14 @@ var NETDATA = window.NETDATA || {};
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 });
             }
         }
         else {
             NETDATA.chartLibraries.c3.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -4908,21 +5133,27 @@ var NETDATA = window.NETDATA || {};
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.d3.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
     NETDATA.d3ChartUpdate = function(state, data) {
+        void(state);
+        void(data);
+
         return false;
     };
 
     NETDATA.d3ChartCreate = function(state, data) {
+        void(state);
+        void(data);
+
         return false;
     };
 
@@ -4948,13 +5179,13 @@ var NETDATA = window.NETDATA || {};
                 NETDATA.chartLibraries.google.enabled = false;
                 NETDATA.error(100, NETDATA.google_js);
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.google.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -5105,22 +5336,23 @@ var NETDATA = window.NETDATA || {};
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.easypiechart.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
     NETDATA.easypiechartClearSelection = function(state) {
         if(typeof state.easyPieChartEvent !== 'undefined') {
-            if(state.easyPieChartEvent.timer !== null)
+            if(state.easyPieChartEvent.timer !== undefined) {
                 clearTimeout(state.easyPieChartEvent.timer);
+            }
 
-            state.easyPieChartEvent.timer = null;
+            state.easyPieChartEvent.timer = undefined;
         }
 
         if(state.isAutoRefreshable() === true && state.data !== null) {
@@ -5145,7 +5377,7 @@ var NETDATA = window.NETDATA || {};
 
         if(typeof state.easyPieChartEvent === 'undefined') {
             state.easyPieChartEvent = {
-                timer: null,
+                timer: undefined,
                 value: 0,
                 pcent: 0
             };
@@ -5160,11 +5392,11 @@ var NETDATA = window.NETDATA || {};
         state.easyPieChartEvent.pcent = pcent;
         state.easyPieChartLabel.innerText = state.legendFormatValue(value);
 
-        if(state.easyPieChartEvent.timer === null) {
+        if(state.easyPieChartEvent.timer === undefined) {
             state.easyPieChart_instance.disableAnimation();
 
             state.easyPieChartEvent.timer = setTimeout(function() {
-                state.easyPieChartEvent.timer = null;
+                state.easyPieChartEvent.timer = undefined;
                 state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
             }, NETDATA.options.current.charts_selection_animation_delay);
         }
@@ -5317,13 +5549,13 @@ var NETDATA = window.NETDATA || {};
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.gauge.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
@@ -5351,7 +5583,7 @@ var NETDATA = window.NETDATA || {};
             min = max;
             max = t;
         }
-        else if(min == max)
+        else if(min === max)
             max = min + 1;
 
         // gauge.js has an issue if the needle
@@ -5364,9 +5596,10 @@ var NETDATA = window.NETDATA || {};
         // is always between min and max
         var pcent = (value - min) * 100 / (max - min);
 
-        // these should never happen
-        if(pcent < 0) pcent = 0;
-        if(pcent > 100) pcent = 100;
+        // bug fix for gauge.js 1.3.1
+        // if the value is the absolute min or max, the chart is broken
+        if(pcent < 0.001) pcent = 0.001;
+        if(pcent > 99.999) pcent = 99.999;
 
         state.gauge_instance.set(pcent);
         // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
@@ -5393,10 +5626,11 @@ var NETDATA = window.NETDATA || {};
 
     NETDATA.gaugeClearSelection = function(state) {
         if(typeof state.gaugeEvent !== 'undefined') {
-            if(state.gaugeEvent.timer !== null)
+            if(state.gaugeEvent.timer !== undefined) {
                 clearTimeout(state.gaugeEvent.timer);
+            }
 
-            state.gaugeEvent.timer = null;
+            state.gaugeEvent.timer = undefined;
         }
 
         if(state.isAutoRefreshable() === true && state.data !== null) {
@@ -5422,7 +5656,7 @@ var NETDATA = window.NETDATA || {};
 
         if(typeof state.gaugeEvent === 'undefined') {
             state.gaugeEvent = {
-                timer: null,
+                timer: undefined,
                 value: 0,
                 min: 0,
                 max: 0
@@ -5442,11 +5676,11 @@ var NETDATA = window.NETDATA || {};
         state.gaugeEvent.max = max;
         NETDATA.gaugeSetLabels(state, value, min, max);
 
-        if(state.gaugeEvent.timer === null) {
+        if(state.gaugeEvent.timer === undefined) {
             NETDATA.gaugeAnimation(state, false);
 
             state.gaugeEvent.timer = setTimeout(function() {
-                state.gaugeEvent.timer = null;
+                state.gaugeEvent.timer = undefined;
                 NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
             }, NETDATA.options.current.charts_selection_animation_delay);
         }
@@ -5526,19 +5760,22 @@ var NETDATA = window.NETDATA || {};
 
         var options = {
             lines: 12,                  // The number of lines to draw
-            angle: 0.15,                // The length of each line
-            lineWidth: 0.44,            // 0.44 The line thickness
+            angle: 0.15,                // The span of the gauge arc
+            lineWidth: 0.50,            // The line thickness
+            radiusScale: 0.85,          // Relative radius
             pointer: {
                 length: 0.8,            // 0.9 The radius of the inner circle
                 strokeWidth: 0.035,     // The rotation offset
                 color: pointerColor     // Fill color
             },
+            limitMax: true,             // If false, the max value of the gauge will be updated if value surpass max
+            limitMin: true,             // If true, the min value of the gauge will be fixed unless you set it manually
             colorStart: startColor,     // Colors
             colorStop: stopColor,       // just experiment with them
             strokeColor: strokeColor,   // to see which ones work best for you
-            limitMax: true,
-            generateGradient: (generateGradient === true)?true:false,
-            gradientType: 0
+            generateGradient: (generateGradient === true),
+            gradientType: 0,
+            highDpiSupport: true        // High resolution support
         };
 
         if (generateGradient.constructor === Array) {
@@ -5548,13 +5785,13 @@ var NETDATA = window.NETDATA || {};
             // data-gauge-gradient-percent-color-50="#999900"
             // data-gauge-gradient-percent-color-100="#000000"
 
-            options.percentColors = new Array();
+            options.percentColors = [];
             var len = generateGradient.length;
             while(len--) {
                 var pcent = generateGradient[len];
-                var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
+                var color = self.attr('data-gauge-gradient-percent-color-' + pcent.toString()) || false;
                 if(color !== false) {
-                    var a = new Array();
+                    var a = [];
                     a[0] = pcent / 100;
                     a[1] = color;
                     options.percentColors.unshift(a);
@@ -5564,6 +5801,7 @@ var NETDATA = window.NETDATA || {};
                 delete options.percentColors;
         }
         else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
+            //noinspection PointlessArithmeticExpressionJS
             options.percentColors = [
                 [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
                 [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
@@ -5665,31 +5903,21 @@ var NETDATA = window.NETDATA || {};
             toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'json'; },
-            options: function(state) { return 'ms|flip'; },
+            format: function(state) { void(state); return 'json'; },
+            options: function(state) { void(state); return 'ms|flip'; },
             legend: function(state) {
-                if(this.isSparkline(state) === false)
-                    return 'right-side';
-                else
-                    return null;
+                return (this.isSparkline(state) === false)?'right-side':null;
             },
-            autoresize: function(state) { return true; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return true; },
+            autoresize: function(state) { void(state); return true; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return true; },
             pixels_per_point: function(state) {
-                if(this.isSparkline(state) === false)
-                    return 3;
-                else
-                    return 2;
+                return (this.isSparkline(state) === false)?3:2;
             },
-
             isSparkline: function(state) {
                 if(typeof state.dygraph_sparkline === 'undefined') {
                     var t = $(state.element).data('dygraph-theme');
-                    if(t === 'sparkline')
-                        state.dygraph_sparkline = true;
-                    else
-                        state.dygraph_sparkline = false;
+                    state.dygraph_sparkline = (t === 'sparkline');
                 }
                 return state.dygraph_sparkline;
             }
@@ -5699,126 +5927,126 @@ var NETDATA = window.NETDATA || {};
             create: NETDATA.sparklineChartCreate,
             update: NETDATA.sparklineChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'array'; },
-            options: function(state) { return 'flip|abs'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 3; }
+            format: function(state) { void(state); return 'array'; },
+            options: function(state) { void(state); return 'flip|abs'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 3; }
         },
         "peity": {
             initialize: NETDATA.peityInitialize,
             create: NETDATA.peityChartCreate,
             update: NETDATA.peityChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'ssvcomma'; },
-            options: function(state) { return 'null2zero|flip|abs'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 3; }
+            format: function(state) { void(state); return 'ssvcomma'; },
+            options: function(state) { void(state); return 'null2zero|flip|abs'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 3; }
         },
         "morris": {
             initialize: NETDATA.morrisInitialize,
             create: NETDATA.morrisChartCreate,
             update: NETDATA.morrisChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'json'; },
-            options: function(state) { return 'objectrows|ms'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 50; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 15; }
+            format: function(state) { void(state); return 'json'; },
+            options: function(state) { void(state); return 'objectrows|ms'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 50; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 15; }
         },
         "google": {
             initialize: NETDATA.googleInitialize,
             create: NETDATA.googleChartCreate,
             update: NETDATA.googleChartUpdate,
             resize: null,
-            setSelection: undefined, //function(state, t) { return true; },
-            clearSelection: undefined, //function(state) { return true; },
+            setSelection: undefined, //function(state, t) { void(state); return true; },
+            clearSelection: undefined, //function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'datatable'; },
-            options: function(state) { return ''; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 300; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 4; }
+            format: function(state) { void(state); return 'datatable'; },
+            options: function(state) { void(state); return ''; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 300; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 4; }
         },
         "raphael": {
             initialize: NETDATA.raphaelInitialize,
             create: NETDATA.raphaelChartCreate,
             update: NETDATA.raphaelChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'json'; },
-            options: function(state) { return ''; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 3; }
+            format: function(state) { void(state); return 'json'; },
+            options: function(state) { void(state); return ''; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 3; }
         },
         "c3": {
             initialize: NETDATA.c3Initialize,
             create: NETDATA.c3ChartCreate,
             update: NETDATA.c3ChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'csvjsonarray'; },
-            options: function(state) { return 'milliseconds'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 15; }
+            format: function(state) { void(state); return 'csvjsonarray'; },
+            options: function(state) { void(state); return 'milliseconds'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 15; }
         },
         "d3": {
             initialize: NETDATA.d3Initialize,
             create: NETDATA.d3ChartCreate,
             update: NETDATA.d3ChartUpdate,
             resize: null,
-            setSelection: undefined, // function(state, t) { return true; },
-            clearSelection: undefined, // function(state) { return true; },
+            setSelection: undefined, // function(state, t) { void(state); return true; },
+            clearSelection: undefined, // function(state) { void(state); return true; },
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'json'; },
-            options: function(state) { return ''; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return false; },
-            pixels_per_point: function(state) { return 3; }
+            format: function(state) { void(state); return 'json'; },
+            options: function(state) { void(state); return ''; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return false; },
+            pixels_per_point: function(state) { void(state); return 3; }
         },
         "easypiechart": {
             initialize: NETDATA.easypiechartInitialize,
@@ -5830,13 +6058,13 @@ var NETDATA = window.NETDATA || {};
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'array'; },
-            options: function(state) { return 'absolute'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return true; },
-            pixels_per_point: function(state) { return 3; },
+            format: function(state) { void(state); return 'array'; },
+            options: function(state) { void(state); return 'absolute'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return true; },
+            pixels_per_point: function(state) { void(state); return 3; },
             aspect_ratio: 100
         },
         "gauge": {
@@ -5849,13 +6077,13 @@ var NETDATA = window.NETDATA || {};
             toolboxPanAndZoom: null,
             initialized: false,
             enabled: true,
-            format: function(state) { return 'array'; },
-            options: function(state) { return 'absolute'; },
-            legend: function(state) { return null; },
-            autoresize: function(state) { return false; },
-            max_updates_to_recreate: function(state) { return 5000; },
-            track_colors: function(state) { return true; },
-            pixels_per_point: function(state) { return 3; },
+            format: function(state) { void(state); return 'array'; },
+            options: function(state) { void(state); return 'absolute'; },
+            legend: function(state) { void(state); return null; },
+            autoresize: function(state) { void(state); return false; },
+            max_updates_to_recreate: function(state) { void(state); return 5000; },
+            track_colors: function(state) { void(state); return true; },
+            pixels_per_point: function(state) { void(state); return 3; },
             aspect_ratio: 70
         }
     };
@@ -5878,18 +6106,15 @@ var NETDATA = window.NETDATA || {};
             async: false,
             isAlreadyLoaded: function() {
                 // check if bootstrap is loaded
-                if(typeof $().emulateTransitionEnd == 'function')
+                if(typeof $().emulateTransitionEnd === 'function')
                     return true;
                 else {
-                    if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
-                        return true;
-                    else
-                        return false;
+                    return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true);
                 }
             }
         },
         {
-            url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js',
+            url: NETDATA.serverDefault + 'lib/perfect-scrollbar-0.6.15.min.js',
             isAlreadyLoaded: function() { return false; }
         }
     ];
@@ -5898,10 +6123,7 @@ var NETDATA = window.NETDATA || {};
         {
             url: NETDATA.themes.current.bootstrap_css,
             isAlreadyLoaded: function() {
-                if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
-                    return true;
-                else
-                    return false;
+                return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true);
             }
         },
         {
@@ -5918,7 +6140,7 @@ var NETDATA = window.NETDATA || {};
     NETDATA.loadRequiredJs = function(index, callback) {
         if(index >= NETDATA.requiredJs.length) {
             if(typeof callback === 'function')
-                callback();
+                return callback();
             return;
         }
 
@@ -5953,7 +6175,7 @@ var NETDATA = window.NETDATA || {};
 
             if(async === false)
                 NETDATA.loadRequiredJs(++index, callback);
-        })
+        });
 
         if(async === true)
             NETDATA.loadRequiredJs(++index, callback);
@@ -5993,7 +6215,7 @@ var NETDATA = window.NETDATA || {};
         last_notification_id: 0,        // the id of the last alarm_log we have raised an alarm for
         first_notification_id: 0,       // the id of the first alarm_log entry for this session
                                         // this is used to prevent CLEAR notifications for past events
-        // notifications_shown: new Array(),
+        // notifications_shown: [],
 
         server: null,                   // the server to connect to for fetching alarms
         current: null,                  // the list of raised alarms - updated in the background
@@ -6007,16 +6229,18 @@ var NETDATA = window.NETDATA || {};
                 return;
             }
 
-            var value = entry.value;
+            var value_string = entry.value_string;
+
             if(NETDATA.alarms.current !== null) {
+                // get the current value_string
                 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
-                if(typeof t !== 'undefined' && entry.status == t.status)
-                    value = t.value;
+                if(typeof t !== 'undefined' && entry.status === t.status && typeof t.value_string !== 'undefined')
+                    value_string = t.value_string;
             }
 
             var name = entry.name.replace(/_/g, ' ');
             var status = entry.status.toLowerCase();
-            var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
+            var title = name + ' = ' + value_string.toString();
             var tag = entry.alarm_id;
             var icon = 'images/seo-performance-128.png';
             var interaction = false;
@@ -6045,8 +6269,12 @@ var NETDATA = window.NETDATA || {};
                         // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
                         return;
                     }
-                    title = name + ' back to normal';
-                    icon = 'images/check-mark-2-128-green.png'
+                    if(entry.no_clear_notification === true) {
+                        // console.log('alarm' + entry.unique_id + ' is CLEAR but has no_clear_notification flag');
+                        return;
+                    }
+                    title = name + ' back to normal (' + value_string.toString() + ')';
+                    icon = 'images/check-mark-2-128-green.png';
                     interaction = false;
                     break;
 
@@ -6062,7 +6290,7 @@ var NETDATA = window.NETDATA || {};
                     if(entry.old_status === 'WARNING')
                         status = 'escalated to ' + entry.status.toLowerCase();
                     
-                    icon = 'images/alert-128-red.png'
+                    icon = 'images/alert-128-red.png';
                     interaction = true;
                     break;
 
@@ -6222,13 +6450,13 @@ var NETDATA = window.NETDATA || {};
                         NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id;
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(415, NETDATA.alarms.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
@@ -6267,23 +6495,21 @@ var NETDATA = window.NETDATA || {};
             })
                 .done(function(data) {
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(416, NETDATA.alarms.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
         init: function() {
-            var host = NETDATA.serverDefault;
-            while(host.slice(-1) === '/')
-                host = host.substring(0, host.length - 1);
-            NETDATA.alarms.server = host;
+            NETDATA.alarms.server = NETDATA.fixHost(NETDATA.serverDefault);
 
-            NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
+            NETDATA.alarms.last_notification_id =
+                NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null);
 
             if(NETDATA.alarms.onclick === null)
                 NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm;
@@ -6306,12 +6532,12 @@ var NETDATA = window.NETDATA || {};
     // Registry of netdata hosts
 
     NETDATA.registry = {
-        server: null,       // the netdata registry server
-        person_guid: null,  // the unique ID of this browser / user
-        machine_guid: null, // the unique ID the netdata server that served dashboard.js
-        hostname: null,     // the hostname of the netdata server that served dashboard.js
-        machines: null,         // the user's other URLs
-        machines_array: null,   // the user's other URLs in an array
+        server: null,         // the netdata registry server
+        person_guid: null,    // the unique ID of this browser / user
+        machine_guid: null,   // the unique ID the netdata server that served dashboard.js
+        hostname: 'unknown',  // the hostname of the netdata server that served dashboard.js
+        machines: null,       // the user's other URLs
+        machines_array: null, // the user's other URLs in an array
         person_urls: null,
 
         parsePersonUrls: function(person_urls) {
@@ -6320,9 +6546,8 @@ var NETDATA = window.NETDATA || {};
 
             if(person_urls) {
                 NETDATA.registry.machines = {};
-                NETDATA.registry.machines_array = new Array();
+                NETDATA.registry.machines_array = [];
 
-                var now = Date.now();
                 var apu = person_urls;
                 var i = apu.length;
                 while(i--) {
@@ -6335,7 +6560,7 @@ var NETDATA = window.NETDATA || {};
                             last_t: apu[i][2],
                             accesses: apu[i][3],
                             name: apu[i][4],
-                            alternate_urls: new Array()
+                            alternate_urls: []
                         };
                         obj.alternate_urls.push(apu[i][1]);
 
@@ -6379,8 +6604,7 @@ var NETDATA = window.NETDATA || {};
         },
 
         hello: function(host, callback) {
-            while(host.slice(-1) === '/')
-                host = host.substring(0, host.length - 1);
+            host = NETDATA.fixHost(host);
 
             // send HELLO to a netdata server:
             // 1. verifies the server is reachable
@@ -6402,13 +6626,13 @@ var NETDATA = window.NETDATA || {};
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(407, host);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
@@ -6445,7 +6669,7 @@ var NETDATA = window.NETDATA || {};
                         }
                         else {
                             if(typeof callback === 'function')
-                                callback(null);
+                                return callback(null);
                         }
                     }
                     else {
@@ -6453,14 +6677,14 @@ var NETDATA = window.NETDATA || {};
                             NETDATA.registry.person_guid = data.person_guid;
 
                         if(typeof callback === 'function')
-                            callback(data.urls);
+                            return callback(data.urls);
                     }
                 })
                 .fail(function() {
                     NETDATA.error(410, NETDATA.registry.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
@@ -6483,13 +6707,13 @@ var NETDATA = window.NETDATA || {};
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(412, NETDATA.registry.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
@@ -6512,13 +6736,13 @@ var NETDATA = window.NETDATA || {};
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(418, NETDATA.registry.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
@@ -6541,13 +6765,13 @@ var NETDATA = window.NETDATA || {};
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(414, NETDATA.registry.server);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         }
     };