]> arthur.barton.de Git - netdata.git/blobdiff - web/dashboard.js
fix spelling of "systemd"
[netdata.git] / web / dashboard.js
index 262bac8a6992022676a0002c2691515a5462302b..6fc294204249e0f3cd331a3b1c8f350b1f8cfeb0 100644 (file)
 // netdata server already.
 // var netdataServer = "http://yourhost:19999"; // set your NetData server
 
-//(function(window, document, undefined) {
+// global namespace
+var NETDATA = window.NETDATA || {};
 
+(function(window, document) {
     // ------------------------------------------------------------------------
     // compatibility fixes
 
@@ -53,9 +55,6 @@
         };
     }
 
-    // global namespace
-    var NETDATA = window.NETDATA || {};
-
     NETDATA.name2id = function(s) {
         return s
             .replace(/ /g, '_')
     // default URLs for all the external files we need
     // make them RELATIVE so that the whole thing can also be
     // installed under a web server
-    NETDATA.jQuery              = NETDATA.serverDefault + 'lib/jquery-3.1.1.min.js';
+    NETDATA.jQuery              = NETDATA.serverDefault + 'lib/jquery-2.2.4.min.js';
     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.dygraph_js          = NETDATA.serverDefault + 'lib/dygraph-combined-f6ec7be.js';
-    NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-f6ec7be.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';
     NETDATA.c3_js               = NETDATA.serverDefault + 'lib/c3-0.4.11.min.js';
     NETDATA.c3_css              = NETDATA.serverDefault + 'css/c3-0.4.11.min.css';
-    NETDATA.d3_js               = NETDATA.serverDefault + 'lib/d3-4.2.6.min.js';
+    NETDATA.d3_js               = NETDATA.serverDefault + 'lib/d3-3.5.17.min.js';
     NETDATA.morris_js           = NETDATA.serverDefault + 'lib/morris-0.5.1.min.js';
     NETDATA.morris_css          = NETDATA.serverDefault + 'css/morris-0.5.1.css';
     NETDATA.google_js           = 'https://www.google.com/jsapi';
 
     NETDATA.themes = {
         white: {
-            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161002-1',
+            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2',
             background: '#FFFFFF',
             foreground: '#000000',
-            grid: '#DDDDDD',
-            axis: '#CCCCCC',
+            grid: '#F0F0F0',
+            axis: '#F0F0F0',
             colors: [   '#3366CC', '#DC3912',   '#109618', '#FF9900',   '#990099', '#DD4477',
                         '#3B3EAC', '#66AA00',   '#0099C6', '#B82E2E',   '#AAAA11', '#5574A6',
                         '#994499', '#22AA99',   '#6633CC', '#E67300',   '#316395', '#8B0707',
             gauge_gradient: false
         },
         slate: {
-            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css?v20161002-1',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-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: '#35393e',
-            axis: '#35393e',
+            grid: '#283236',
+            axis: '#283236',
 /*          colors: [   '#55bb33', '#ff2222',   '#0099C6', '#faa11b',   '#adbce0', '#DDDD00',
                         '#4178ba', '#f58122',   '#a5cc39', '#f58667',   '#f5ef89', '#cf93c0',
                         '#a5d18a', '#b8539d',   '#3954a3', '#c8a9cf',   '#c7de8a', '#fad20a',
 
     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;
                                         // rendering the chart that is panned or zoomed).
                                         // Used with .current.global_pan_sync_time
 
-        last_resized: new Date().getTime(), // the timestamp of the last resize request
+        last_resized: Date.now(),       // the timestamp of the last resize request
 
         last_page_scroll: 0,            // the timestamp the last time the page was scrolled
 
             async_on_scroll: false,                 // sync/async onscroll handler
             onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler
 
-            setOptionCallback: function() { ; }
+            retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server
+
+            setOptionCallback: function() { }
         },
 
         debug: {
             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,
     };
 
     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);
     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') {
                 }
             }
         }
-    }
+    };
 
     // ----------------------------------------------------------------------------------------------------------------
 
     if(NETDATA.options.debug.main_loop === true)
         console.log('welcome to NETDATA');
 
+    NETDATA.onresizeCallback = null;
     NETDATA.onresize = function() {
-        NETDATA.options.last_resized = new Date().getTime();
+        NETDATA.options.last_resized = Date.now();
         NETDATA.onscroll();
+
+        if(typeof NETDATA.onresizeCallback === 'function')
+            NETDATA.onresizeCallback();
     };
 
     NETDATA.onscroll_updater_count = 0;
     NETDATA.onscroll_updater = function() {
         NETDATA.onscroll_updater_running = true;
         NETDATA.onscroll_updater_count++;
-        var start = new Date().getTime();
+        var start = Date.now();
 
         var targets = NETDATA.options.targets;
         var len = targets.length;
                 targets[len].isVisible();
         }
 
-        var end = new Date().getTime();
+        var end = Date.now();
         // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms');
 
         if(NETDATA.options.current.async_on_scroll === false) {
     NETDATA.onscroll = function() {
         // console.log('onscroll');
 
-        NETDATA.options.last_page_scroll = new Date().getTime();
+        NETDATA.options.last_page_scroll = Date.now();
         NETDATA.options.auto_refresher_stop_until = 0;
 
         if(NETDATA.options.targets === null) return;
     NETDATA.error = function(code, msg) {
         NETDATA.errorLast.code = code;
         NETDATA.errorLast.message = msg;
-        NETDATA.errorLast.datetime = new Date().getTime();
+        NETDATA.errorLast.datetime = Date.now();
 
         console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
 
         NETDATA.errorLast.datetime = 0;
     };
 
+    // ----------------------------------------------------------------------------------------------------------------
+    // commonMin & commonMax
+
+    NETDATA.commonMin = {
+        keys: {},
+        latest: {},
+
+        get: function(state) {
+            if(typeof state.__commonMin === 'undefined') {
+                // get the commonMin setting
+                var self = $(state.element);
+                state.__commonMin = self.data('common-min') || null;
+            }
+
+            var min = state.data.min;
+            var name = state.__commonMin;
+
+            if(name === null) {
+                // we don't need commonMin
+                //state.log('no need for commonMin');
+                return min;
+            }
+
+            var t = this.keys[name];
+            if(typeof t === 'undefined') {
+                // add our commonMin
+                this.keys[name] = {};
+                t = this.keys[name];
+            }
+
+            var uuid = state.uuid;
+            if(typeof t[uuid] !== 'undefined') {
+                if(t[uuid] === min) {
+                    //state.log('commonMin ' + state.__commonMin + ' not changed: ' + this.latest[name]);
+                    return this.latest[name];
+                }
+                else if(min < this.latest[name]) {
+                    //state.log('commonMin ' + state.__commonMin + ' increased: ' + min);
+                    t[uuid] = min;
+                    this.latest[name] = min;
+                    return min;
+                }
+            }
+
+            // add our min
+            t[uuid] = min;
+
+            // find the common min
+            var m = min;
+            for(var i in t)
+                if(t[i] < m) m = t[i];
+
+            //state.log('commonMin ' + state.__commonMin + ' updated: ' + m);
+            this.latest[name] = m;
+            return m;
+        }
+    };
+
+    NETDATA.commonMax = {
+        keys: {},
+        latest: {},
+
+        get: function(state) {
+            if(typeof state.__commonMax === 'undefined') {
+                // get the commonMax setting
+                var self = $(state.element);
+                state.__commonMax = self.data('common-max') || null;
+            }
+
+            var max = state.data.max;
+            var name = state.__commonMax;
+
+            if(name === null) {
+                // we don't need commonMax
+                //state.log('no need for commonMax');
+                return max;
+            }
+
+            var t = this.keys[name];
+            if(typeof t === 'undefined') {
+                // add our commonMax
+                this.keys[name] = {};
+                t = this.keys[name];
+            }
+
+            var uuid = state.uuid;
+            if(typeof t[uuid] !== 'undefined') {
+                if(t[uuid] === max) {
+                    //state.log('commonMax ' + state.__commonMax + ' not changed: ' + this.latest[name]);
+                    return this.latest[name];
+                }
+                else if(max > this.latest[name]) {
+                    //state.log('commonMax ' + state.__commonMax + ' increased: ' + max);
+                    t[uuid] = max;
+                    this.latest[name] = max;
+                    return max;
+                }
+            }
+
+            // add our max
+            t[uuid] = max;
+
+            // find the common max
+            var m = max;
+            for(var i in t)
+                if(t[i] > m) m = t[i];
+
+            //state.log('commonMax ' + state.__commonMax + ' updated: ' + m);
+            this.latest[name] = m;
+            return m;
+        }
+    };
+
     // ----------------------------------------------------------------------------------------------------------------
     // Chart Registry
 
     // 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: {},
 
         },
 
         downloadAll: function(host, callback) {
-            while(host.slice(-1) === '/')
-                host = host.substring(0, host.length - 1);
+            host = NETDATA.fixHost(host);
 
             var self = this;
 
                 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);
             });
         }
     };
             if(this.master !== null && this.master !== state)
                 this.master.resetChart(true, true);
 
-            var now = new Date().getTime();
+            var now = Date.now();
             this.master = state;
             this.seq = now;
             this.force_after_ms = after;
         this.value_div = null;
         this.color = NETDATA.themes.current.foreground;
 
-        if(parent.selected_count > parent.unselected_count)
+        if(parent.unselected_count === 0)
             this.selected = true;
         else
             this.selected = false;
     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;
                 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;
     };
 
     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) {
         this.selected_count = 0;
         this.unselected_count = 0;
 
-        for(var i = 0, len = array.length; i < len ; i++) {
-            var ds = this.dimensions[array[i]];
+        var len = array.length;
+        while(len--) {
+            var ds = this.dimensions[array[len]];
             if(typeof ds === 'undefined') {
                 // console.log(array[i] + ' is not found');
-                ret.push(false);
-                continue;
+                ret.unshift(false);
             }
-
-            if(ds.isSelected()) {
-                ret.push(true);
+            else if(ds.isSelected()) {
+                ret.unshift(true);
                 this.selected_count++;
             }
             else {
-                ret.push(false);
+                ret.unshift(false);
                 this.unselected_count++;
             }
         }
         // 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) {
         // the chart library requested by the user
         this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
 
+        // how many retries we have made to load chart data from the server
+        this.retries_on_data_failures = 0;
+
         // object - the chart library used
         this.library = null;
 
             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
         };
 
 
         this.title = self.data('title') || null;    // the title of the chart
         this.units = self.data('units') || null;    // the units of the chart dimensions
-        this.append_options = self.data('append-options') || null;  // the units of the chart dimensions
+        this.append_options = self.data('append-options') || null;  // additional options to pass to 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.view_after = 0;
         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;
+        }
+
         this.auto = {
             name: 'auto',
             autorefresh: true,
             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');
 
             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;
                     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)
                 $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
 
-            that.tm.last_dom_created = new Date().getTime();
+            that.tm.last_dom_created = Date.now();
 
             showLoading();
         };
             that.data_before = 0;           // milliseconds - the last timestamp of the data
             that.data_update_every = 0;     // milliseconds - the frequency to update the data
 
-            that.tm.last_initialized = new Date().getTime();
+            that.tm.last_initialized = Date.now();
             createDOM();
 
             that.setMode('auto');
         };
 
         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);
 
             // 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;
 
             // 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
             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');
             }
         };
 
                     showRendering();
                     that.element_chart.style.display = 'none';
                     if(that.element_legend !== null) that.element_legend.style.display = 'none';
-                    that.tm.last_hidden = new Date().getTime();
+                    that.tm.last_hidden = Date.now();
 
                     // de-allocate data
                     // This works, but I not sure there are no corner cases somewhere
                 init();
             }
             else {
-                that.tm.last_unhidden = new Date().getTime();
+                that.tm.last_unhidden = Date.now();
                 that.element_chart.style.display = '';
                 if(that.element_legend !== null) that.element_legend.style.display = '';
                 resizeChart();
                 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();
                 }
 
-                that.tm.last_resized = new Date().getTime();
+                that.tm.last_resized = Date.now();
             }
         };
 
             if(that.settings_id !== null)
                 NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
 
-            var now = new Date().getTime();
+            var now = Date.now();
             NETDATA.options.last_page_scroll = now;
             NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
 
             this.event_resize.chart_last_w = this.element.clientWidth;
             this.event_resize.chart_last_h = this.element.clientHeight;
 
-            var now = new Date().getTime();
-            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
+            var now = Date.now();
+            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;
         var noDataToShow = function() {
             showMessageIcon('<i class="fa fa-warning"></i> empty');
             that.legendUpdateDOM();
-            that.tm.last_autorefreshed = new Date().getTime();
+            that.tm.last_autorefreshed = Date.now();
             // that.data_update_every = 30 * 1000;
             //that.element_chart.style.display = 'none';
             //if(that.element_legend !== null) that.element_legend.style.display = 'none';
             this.current.force_before_ms = null;
             this.current.force_after_ms = null;
 
-            this.tm.last_mode_switch = new Date().getTime();
+            this.tm.last_mode_switch = Date.now();
         };
 
         // ----------------------------------------------------------------------------------------------------------------
                 return;
 
             if(typeof ms === 'number')
-                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
+                NETDATA.globalSelectionSync.dont_sync_before = Date.now() + ms;
             else
-                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
+                NETDATA.globalSelectionSync.dont_sync_before = Date.now() + NETDATA.options.current.sync_selection_delay;
         };
 
         // can we globally apply selection sync?
             if(NETDATA.options.current.sync_selection === false)
                 return false;
 
-            if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
+            if(NETDATA.globalSelectionSync.dont_sync_before > Date.now())
                 return false;
 
             return true;
         };
 
         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
         // 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)
 
                 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;
             if(this.debug === true)
                 this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
 
-            this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
+            this.current.force_update_at = Date.now() + NETDATA.options.current.pan_and_zoom_delay;
             this.current.force_after_ms = after;
             this.current.force_before_ms = before;
             NETDATA.globalPanAndZoom.setMaster(this, after, before);
             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(typeof series === 'undefined') return;
             if(series.value === null && series.user === null) return;
 
+            /*
+            // this slows down firefox and edge significantly
+            // since it requires to use innerHTML(), instead of innerText()
+
             // if the value has not changed, skip DOM update
             //if(series.last === value) return;
 
                     else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
                 }
                 else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+
                 series.last = v;
             }
             else {
-                s = r = value;
+                if(value === null)
+                    s = r = '';
+                else
+                    s = r = value;
+
                 series.last = value;
             }
+            */
+
+            var s = this.legendFormatValue(value);
+
+            // caching: do not update the update to show the same value again
+            if(s === series.last_shown_value) return;
+            series.last_shown_value = s;
+
+            if(series.value !== null) series.value.innerText = s;
+            if(series.user !== null) series.user.innerText = s;
+        };
+
+        this.__legendSetDateString = function(date) {
+            if(date !== this.__last_shown_legend_date) {
+                this.element_legend_childs.title_date.innerText = date;
+                this.__last_shown_legend_date = date;
+            }
+        };
+
+        this.__legendSetTimeString = function(time) {
+            if(time !== this.__last_shown_legend_time) {
+                this.element_legend_childs.title_time.innerText = time;
+                this.__last_shown_legend_time = time;
+            }
+        };
 
-            if(series.value !== null) series.value.innerHTML = s;
-            if(series.user !== null) series.user.innerHTML = r;
+        this.__legendSetUnitsString = function(units) {
+            if(units !== this.__last_shown_legend_units) {
+                this.element_legend_childs.title_units.innerText = units;
+                this.__last_shown_legend_units = units;
+            }
         };
 
         this.legendSetDate = function(ms) {
             var d = new Date(ms);
 
             if(this.element_legend_childs.title_date)
-                this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
+                this.__legendSetDateString(d.toLocaleDateString());
 
             if(this.element_legend_childs.title_time)
-                this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
+                this.__legendSetTimeString(d.toLocaleTimeString());
 
             if(this.element_legend_childs.title_units)
-                this.element_legend_childs.title_units.innerHTML = this.units;
+                this.__legendSetUnitsString(this.units)
         };
 
         this.legendShowUndefined = function() {
             if(this.element_legend_childs.title_date)
-                this.element_legend_childs.title_date.innerHTML = '&nbsp;';
+                this.__legendSetDateString(' ');
 
             if(this.element_legend_childs.title_time)
-                this.element_legend_childs.title_time.innerHTML = this.chart.name;
+                this.__legendSetTimeString(this.chart.name);
 
             if(this.element_legend_childs.title_units)
-                this.element_legend_childs.title_units.innerHTML = '&nbsp;';
+                this.__legendSetUnitsString(' ')
 
             if(this.data && this.element_legend_childs.series !== null) {
                 var labels = this.data.dimension_names;
 
             if(typeof this.colors_assigned[label] === 'undefined') {
                 if(this.colors_available.length === 0) {
-                    for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
-                        this.colors_available.push(NETDATA.themes.current.colors[i]);
+                    var len = NETDATA.themes.current.colors.length;
+                    while(len--)
+                        this.colors_available.unshift(NETDATA.themes.current.colors[len]);
                 }
 
                 this.colors_assigned[label] = this.colors_available.shift();
 
             this.colors = new Array();
             this.colors_available = new Array();
-            var i, len;
 
+            // add the standard colors
+            var len = NETDATA.themes.current.colors.length;
+            while(len--)
+                this.colors_available.unshift(NETDATA.themes.current.colors[len]);
+
+            // add the user supplied colors
             var c = $(this.element).data('colors');
             // this.log('read colors: ' + c);
             if(typeof c !== 'undefined' && c !== null && c.length > 0) {
                     var added = 0;
 
                     while(added < 20) {
-                        for(i = 0, len = c.length; i < len ; i++) {
+                        len = c.length;
+                        while(len--) {
                             added++;
-                            this.colors_available.push(c[i]);
-                            // this.log('adding color: ' + c[i]);
+                            this.colors_available.unshift(c[len]);
+                            // this.log('adding color: ' + c[len]);
                         }
                     }
                 }
             }
 
-            // push all the standard colors too
-            for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
-                this.colors_available.push(NETDATA.themes.current.colors[i]);
-
             return this.colors;
         };
 
         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) {
             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
                 var color = state._chartDimensionColor(name);
 
                 var user_element = null;
-                var user_id = self.data('show-value-of-' + dim + '-at') || null;
+                var user_id = self.data('show-value-of-' + name.toLowerCase() + '-at') || null;
+                if(user_id === null)
+                    user_id = self.data('show-value-of-' + dim.toLowerCase() + '-at') || null;
                 if(user_id !== null) {
                     user_element = document.getElementById(user_id) || null;
-                    if(user_element === null)
+                    if (user_element === null)
                         state.log('Cannot find element with id: ' + user_id);
                 }
 
                     name: document.createElement('span'),
                     value: document.createElement('span'),
                     user: user_element,
-                    last: null
+                    last: null,
+                    last_shown_value: null
                 };
 
                 var label = state.element_legend_childs.series[name];
                     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: {}
                 };
 
 
                 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;
 
 
                         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);
 
                 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({
                     title_date: null,
                     title_time: null,
                     title_units: null,
-                    nano: null,
-                    nano_options: null,
+                    perfect_scroller: null,
                     series: {}
                 };
             }
                 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) {
+                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);
                 }
             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();
         };
             this.data_url += "&format="  + this.library.format();
             this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
             this.data_url += "&group="   + this.method;
-            this.data_url += "&options=" + this.library.options(this);
+
+            if(this.override_options !== null)
+                this.data_url += "&options=" + this.override_options.toString();
+            else
+                this.data_url += "&options=" + this.library.options(this);
+
             this.data_url += '|jsonwrap';
 
             if(NETDATA.options.current.eliminate_zero_dimensions === true)
             this.updates_since_last_unhide++;
             this.updates_since_last_creation++;
 
-            var started = new Date().getTime();
+            var started = Date.now();
 
             // if the result is JSON, find the latest update-every
             this.data_update_every = data.view_update_every * 1000;
                 NETDATA.globalSelectionSync.stop();
 
             // update the performance counters
-            var now = new Date().getTime();
+            var now = Date.now();
             this.tm.last_updated = now;
 
             // don't update last_autorefreshed if this chart is
             NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
 
             if(this.refresh_dt_element !== null)
-                this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
+                this.refresh_dt_element.innerText = this.refresh_dt_ms.toString();
         };
 
         this.updateChart = function(callback) {
                 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
                 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;
                 }
             }
 
                 url: this.data_url,
                 cache: false,
                 async: true,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
             .done(function(data) {
                 that.xhr = undefined;
+                that.retries_on_data_failures = 0;
 
                 if(that.debug === true)
                     that.log('data received. updating chart.');
             .fail(function(msg) {
                 that.xhr = undefined;
 
-                if(msg.statusText !== 'abort')
-                    error('data download failed for url: ' + that.data_url);
+                if(msg.statusText !== 'abort') {
+                    that.retries_on_data_failures++;
+                    if(that.retries_on_data_failures > NETDATA.options.current.retries_on_data_failures) {
+                        // that.log('failed ' + that.retries_on_data_failures.toString() + ' times - giving up');
+                        that.retries_on_data_failures = 0;
+                        error('data download failed for url: ' + that.data_url);
+                    }
+                    else {
+                        that.tm.last_autorefreshed = Date.now();
+                        // that.log('failed ' + that.retries_on_data_failures.toString() + ' times, but I will retry');
+                    }
+                }
             })
             .always(function() {
                 that.xhr = undefined;
 
                 NETDATA.statistics.refreshes_active--;
                 that._updating = false;
-                if(typeof callback === 'function') callback();
-            });
 
-            return true;
+                if(typeof callback === 'function')
+                    return callback();
+            });
         };
 
         this.isVisible = function(nocache) {
             if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
                 return this.___isVisible___;
 
-            this.tm.last_visible_check = new Date().getTime();
+            this.tm.last_visible_check = Date.now();
 
             var wh = window.innerHeight;
             var x = this.element.getBoundingClientRect();
         };
 
         this.canBeAutoRefreshed = function() {
-            var now = new Date().getTime();
+            var now = Date.now();
 
             if(this.running === true) {
                 if(this.debug === true)
                     state.running = false;
 
                     if(typeof callback !== 'undefined')
-                        callback();
+                        return callback();
                 });
             }
             else {
                 if(typeof callback !== 'undefined')
-                    callback();
+                    return callback();
             }
         };
 
             this.chart_url = chart.url;
             this.data_update_every = chart.update_every * 1000;
             this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
-            this.tm.last_info_downloaded = new Date().getTime();
+            this.tm.last_info_downloaded = Date.now();
 
             if(this.title === null)
                 this.title = chart.title;
             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;
                     error('chart not found on url "' + that.chart_url + '"');
                 })
                 .always(function() {
-                    if(typeof callback === 'function') callback();
+                    if(typeof callback === 'function')
+                        return callback();
                 });
             }
         };
             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;
 
             s.parentNode.insertBefore(script, s);
         }
         else if(typeof callback === "function")
-            callback();
+            return callback();
     };
 
     NETDATA._loadCSS = function(filename) {
     };
 
     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() {
     };
 
     NETDATA.parseDom = function(callback) {
-        NETDATA.options.last_page_scroll = new Date().getTime();
+        NETDATA.options.last_page_scroll = Date.now();
         NETDATA.options.updated_dom = false;
 
         var targets = $('div[data-netdata]'); //.filter(':visible');
             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
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.peity.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.sparkline.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
         var self = $(state.element);
         var type = self.data('sparkline-type') || 'line';
         var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
-        var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
+        var fillColor = self.data('sparkline-fillcolor') || ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance));
         var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
         var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
         var composite = self.data('sparkline-composite') || undefined;
         if(minSpotColor === 'disable') minSpotColor='';
         if(maxSpotColor === 'disable') maxSpotColor='';
 
+        // state.log('sparkline type ' + type + ', lineColor: ' + lineColor + ', fillColor: ' + fillColor);
+
         state.sparkline_options = {
             type: type,
             lineColor: lineColor,
         })
         .always(function() {
             if(typeof callback === "function")
-                callback();
+                return callback();
         });
     };
 
                 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();
         }
     };
 
                 state.log('dygraphChartUpdate() forced zoom update');
 
             options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
-            options.valueRange = state.dygraph_options.valueRange;
             options.isZoomedIgnoreProgrammaticZoom = true;
             state.dygraph_force_zoom = false;
         }
         else if(state.current.name !== 'auto') {
             if(NETDATA.options.debug.dygraph === true || state.debug === true)
                 state.log('dygraphChartUpdate() loose update');
-
-            options.valueRange = state.dygraph_options.valueRange;
         }
         else {
             if(NETDATA.options.debug.dygraph === true || state.debug === true)
                 state.log('dygraphChartUpdate() strict update');
 
             options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
-            options.valueRange = state.dygraph_options.valueRange;
             options.isZoomedIgnoreProgrammaticZoom = true;
         }
 
+        options.valueRange = state.dygraph_options.valueRange;
+
+        var oldMax = null, oldMin = null;
+        if(state.__commonMin !== null) {
+            state.data.min = state.dygraph_instance.axes_[0].extremeRange[0];
+            oldMin = options.valueRange[0] = NETDATA.commonMin.get(state);
+        }
+        if(state.__commonMax !== null) {
+            state.data.max = state.dygraph_instance.axes_[0].extremeRange[1];
+            oldMax = options.valueRange[1] = NETDATA.commonMax.get(state);
+        }
+
         if(state.dygraph_smooth_eligible === true) {
             if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter)
                 || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) {
 
         dygraph.updateOptions(options);
 
-        state.dygraph_last_rendered = new Date().getTime();
+        var redraw = false;
+        if(oldMin !== null && oldMin > state.dygraph_instance.axes_[0].extremeRange[0]) {
+            state.data.min = state.dygraph_instance.axes_[0].extremeRange[0];
+            options.valueRange[0] = NETDATA.commonMin.get(state);
+            redraw = true;
+        }
+        if(oldMax !== null && oldMax < state.dygraph_instance.axes_[0].extremeRange[1]) {
+            state.data.max = state.dygraph_instance.axes_[0].extremeRange[1];
+            options.valueRange[1] = NETDATA.commonMax.get(state);
+            redraw = true;
+        }
+
+        if(redraw === true) {
+            // state.log('forcing redraw to adapt to common- min/max');
+            dygraph.updateOptions(options);
+        }
+
+        state.dygraph_last_rendered = Date.now();
         return true;
     };
 
             title: self.data('dygraph-title') || state.title,
             titleHeight: self.data('dygraph-titleheight') || 19,
 
-            legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
+            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' },
 
             includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false),
             xRangePad: self.data('dygraph-xrangepad') || 0,
-            // yRangePad: self.data('dygraph-yrangepad') || 1,
+            yRangePad: self.data('dygraph-yrangepad') || 1,
 
-            valueRange: self.data('dygraph-valuerange') || null,
+            valueRange: self.data('dygraph-valuerange') || [ null, null ],
 
             ylabel: state.units,
             yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
             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,
+            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') || 0.3,
+            axisLineWidth: self.data('dygraph-axislinewidth') || 1.0,
 
             drawGrid: self.data('dygraph-drawgrid') || true,
-            drawXGrid: self.data('dygraph-drawxgrid') || undefined,
-            drawYGrid: self.data('dygraph-drawygrid') || undefined,
             gridLinePattern: self.data('dygraph-gridlinepattern') || null,
-            gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4,
+            gridLineWidth: self.data('dygraph-gridlinewidth') || 1.0,
             gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
 
             maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
                         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();
-                        // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
+                        //var d = new Date(ms);
+                        //return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
+
+                        // no need to return anything here
+                        return ' ';
+
                     }
                 },
                 y: {
                     var i = data.series.length;
                     while(i--) {
                         var series = data.series[i];
-                        if(!series.isVisible) continue;
-                        state.legendSetLabelValue(series.label, series.y);
+                        if(series.isVisible === true)
+                            state.legendSetLabelValue(series.label, series.y);
+                        else
+                            state.legendSetLabelValue(series.label, null);
                     }
                 }
 
                         state.globalSelectionSyncStop();
                         state.globalSelectionSyncDelay();
                         state.setMode('pan');
+                        context.is2DPan = false;
                         Dygraph.movePan(event, dygraph, context);
                     }
                     else if(context.isZooming) {
                         state.log('interactionModel.dblclick()');
                     NETDATA.resetAllCharts(state);
                 },
-                mousewheel: function(event, dygraph, context) {
+                wheel: function(event, dygraph, context) {
                     if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                        state.log('interactionModel.mousewheel()');
+                        state.log('interactionModel.wheel()');
 
                     // Take the offset of a mouse event on the dygraph canvas and
                     // convert it to a pair of percentages from the bottom left.
                         state.globalSelectionSyncDelay();
 
                         // http://dygraphs.com/gallery/interaction-api.js
-                        var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
+                        var normal_def;
+                        if(typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta))
+                            // chrome
+                            normal_def = event.wheelDelta / 40;
+                        else
+                            // firefox
+                            normal_def = event.deltaY * -1.2;
+
+                        var normal = (event.detail) ? event.detail * -1 : normal_def;
                         var percentage = normal / 50;
 
                         if (!(event.offsetX && event.offsetY)){
                         var yPct = percentages[1];
 
                         var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
-
                         var after = new_x_range[0];
                         var before = new_x_range[1];
 
                     // the internal default of dygraphs
                     context.touchDirections = { x: true, y: false };
 
-                    state.dygraph_last_touch_start = new Date().getTime();
+                    state.dygraph_last_touch_start = Date.now();
                     state.dygraph_last_touch_move = 0;
 
                     if(typeof event.touches[0].pageX === 'number')
                     state.dygraph_user_action = true;
                     Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
 
-                    state.dygraph_last_touch_move = new Date().getTime();
+                    state.dygraph_last_touch_move = Date.now();
                 },
                 touchend: function(event, dygraph, context) {
                     if(NETDATA.options.debug.dygraph === true || state.debug === true)
                     }
 
                     // if it was double tap within double click time, reset the charts
-                    var now = new Date().getTime();
+                    var now = Date.now();
                     if(typeof state.dygraph_last_touch_end !== 'undefined') {
                         if(state.dygraph_last_touch_move === 0) {
                             var dt = now - state.dygraph_last_touch_end;
             state.dygraph_options.drawGrid = false;
             state.dygraph_options.drawAxis = false;
             state.dygraph_options.title = undefined;
-            state.dygraph_options.units = undefined;
             state.dygraph_options.ylabel = undefined;
             state.dygraph_options.yLabelWidth = 0;
             state.dygraph_options.labelsDivWidth = 120;
 
         state.dygraph_force_zoom = false;
         state.dygraph_user_action = false;
-        state.dygraph_last_rendered = new Date().getTime();
+        state.dygraph_last_rendered = Date.now();
+
+        if(typeof state.dygraph_instance.axes_[0].extremeRange !== 'undefined') {
+            state.__commonMin = self.data('common-min') || null;
+            state.__commonMax = self.data('common-max') || null;
+        }
+        else {
+            state.log('incompatible version of dygraphs detected');
+            state.__commonMin = null;
+            state.__commonMax = null;
+        }
+
         return true;
     };
 
                 else {
                     NETDATA.chartLibraries.morris.enabled = false;
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 }
             }
             else {
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 });
             }
         }
         else {
             NETDATA.chartLibraries.morris.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.raphael.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
                 else {
                     NETDATA.chartLibraries.c3.enabled = false;
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 }
             }
             else {
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 });
             }
         }
         else {
             NETDATA.chartLibraries.c3.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
             })
             .always(function() {
                 if(typeof callback === "function")
-                    callback();
+                    return callback();
             });
         }
         else {
             NETDATA.chartLibraries.d3.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
                 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();
         }
     };
 
 
     // ----------------------------------------------------------------------------------------------------------------
 
-    NETDATA.percentFromValueMax = function(value, max) {
-        if(value === null) value = 0;
+    NETDATA.easypiechartPercentFromValueMinMax = function(value, min, max) {
+        if(typeof value !== 'number') value = 0;
+        if(typeof min !== 'number') min = 0;
+        if(typeof max !== 'number') max = 0;
+
+        if(min > value) min = value;
         if(max < value) max = value;
 
+        // make sure it is zero based
+        if(min > 0) min = 0;
+        if(max < 0) max = 0;
+
         var pcent = 0;
-        if(max !== 0) {
-            pcent = Math.round(value * 100 / max);
-            if(pcent === 0 && value > 0) pcent = 1;
+        if(value >= 0) {
+            if(max !== 0)
+                pcent = Math.round(value * 100 / max);
+            if(pcent === 0) pcent = 0.1;
+        }
+        else {
+            if(min !== 0)
+                pcent = Math.round(-value * 100 / min);
+            if(pcent === 0) pcent = -0.1;
         }
 
         return pcent;
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.easypiechart.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
             NETDATA.easypiechartChartUpdate(state, state.data);
         }
         else {
-            state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
+            state.easyPieChartLabel.innerText = state.legendFormatValue(null);
             state.easyPieChart_instance.update(0);
         }
         state.easyPieChart_instance.enableAnimation();
         }
 
         var value = state.data.result[state.data.result.length - 1 - slot];
-        var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
-        var pcent = NETDATA.percentFromValueMax(value, max);
+        var min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin;
+        var max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax;
+        var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max);
 
         state.easyPieChartEvent.value = value;
         state.easyPieChartEvent.pcent = pcent;
-        state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
+        state.easyPieChartLabel.innerText = state.legendFormatValue(value);
 
         if(state.easyPieChartEvent.timer === null) {
             state.easyPieChart_instance.disableAnimation();
     };
 
     NETDATA.easypiechartChartUpdate = function(state, data) {
-        var value, max, pcent;
+        var value, min, max, pcent;
 
         if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
             value = null;
-            max = 0;
             pcent = 0;
         }
         else {
             value = data.result[0];
-            max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
-            pcent = NETDATA.percentFromValueMax(value, max);
+            min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin;
+            max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax;
+            pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max);
         }
 
-        state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
+        state.easyPieChartLabel.innerText = state.legendFormatValue(value);
         state.easyPieChart_instance.update(pcent);
         return true;
     };
         var chart = $(state.element_chart);
 
         var value = data.result[0];
+        var min = self.data('easypiechart-min-value') || null;
         var max = self.data('easypiechart-max-value') || null;
         var adjust = self.data('easypiechart-adjust') || null;
 
+        if(min === null) {
+            min = NETDATA.commonMin.get(state);
+            state.easyPieChartMin = null;
+        }
+        else
+            state.easyPieChartMin = min;
+
         if(max === null) {
-            max = data.max;
+            max = NETDATA.commonMax.get(state);
             state.easyPieChartMax = null;
         }
         else
             state.easyPieChartMax = max;
 
-        var pcent = NETDATA.percentFromValueMax(value, max);
+        var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max);
 
         chart.data('data-percent', pcent);
 
         var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
         state.easyPieChartLabel = document.createElement('span');
         state.easyPieChartLabel.className = 'easyPieChartLabel';
-        state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
+        state.easyPieChartLabel.innerText = state.legendFormatValue(value);
         state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
         state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
         state.element_chart.appendChild(state.easyPieChartLabel);
         var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
         state.easyPieChartTitle = document.createElement('span');
         state.easyPieChartTitle.className = 'easyPieChartTitle';
-        state.easyPieChartTitle.innerHTML = state.title;
+        state.easyPieChartTitle.innerText = state.title;
         state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
         state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
         state.easyPieChartTitle.style.top = titletop.toString() + 'px';
         var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
         state.easyPieChartUnits = document.createElement('span');
         state.easyPieChartUnits.className = 'easyPieChartUnits';
-        state.easyPieChartUnits.innerHTML = state.units;
+        state.easyPieChartUnits.innerText = state.units;
         state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
         state.easyPieChartUnits.style.top = unittop.toString() + 'px';
         state.element_chart.appendChild(state.easyPieChartUnits);
 
+        var barColor = self.data('easypiechart-barcolor');
+        if(typeof barColor === 'undefined' || barColor === null)
+            barColor = state.chartColors()[0];
+        else {
+            // <div ... data-easypiechart-barcolor="(function(percent){return(percent < 50 ? '#5cb85c' : percent < 85 ? '#f0ad4e' : '#cb3935');})" ...></div>
+            var tmp = eval(barColor);
+            if(typeof tmp === 'function')
+                barColor = tmp;
+        }
+
         chart.easyPieChart({
-            barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
+            barColor: barColor,
             trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
             scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
             scaleLength: self.data('easypiechart-scalelength') || 5,
             trackWidth: self.data('easypiechart-trackwidth') || undefined,
             size: self.data('easypiechart-size') || size,
             rotate: self.data('easypiechart-rotate') || 0,
-            animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
+            animate: self.data('easypiechart-animate') || {duration: 500, enabled: true},
             easing: self.data('easypiechart-easing') || undefined
         });
 
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.gauge.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
         else if(typeof status === 'number')
             speed = status;
 
-        console.log('gauge speed ' + speed);
+        // console.log('gauge speed ' + speed);
         state.gauge_instance.animationSpeed = speed;
         state.___gaugeOld__.speed = speed;
     };
             min = max;
             max = t;
         }
-        else if(min == max)
+        else if(min === max)
             max = min + 1;
 
         // gauge.js has an issue if the needle
         if(pcent > 100) pcent = 100;
 
         state.gauge_instance.set(pcent);
-        console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
+        // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);
 
         state.___gaugeOld__.value = value;
         state.___gaugeOld__.min = min;
     NETDATA.gaugeSetLabels = function(state, value, min, max) {
         if(state.___gaugeOld__.valueLabel !== value) {
             state.___gaugeOld__.valueLabel = value;
-            state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
+            state.gaugeChartLabel.innerText = state.legendFormatValue(value);
         }
         if(state.___gaugeOld__.minLabel !== min) {
             state.___gaugeOld__.minLabel = min;
-            state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
+            state.gaugeChartMin.innerText = state.legendFormatValue(min);
         }
         if(state.___gaugeOld__.maxLabel !== max) {
             state.___gaugeOld__.maxLabel = max;
-            state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
+            state.gaugeChartMax.innerText = state.legendFormatValue(max);
         }
     };
 
         }
 
         var value = state.data.result[state.data.result.length - 1 - slot];
-        var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
-        var min = 0;
+        var min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin;
+        var max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax;
+
+        // make sure it is zero based
+        if(min > 0) min = 0;
+        if(max < 0) max = 0;
 
         state.gaugeEvent.value = value;
-        state.gaugeEvent.max = max;
         state.gaugeEvent.min = min;
+        state.gaugeEvent.max = max;
         NETDATA.gaugeSetLabels(state, value, min, max);
 
         if(state.gaugeEvent.timer === null) {
         }
         else {
             value = data.result[0];
-            min = 0;
-            max = (state.gaugeMax === null)?data.max:state.gaugeMax;
+            min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin;
+            max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax;
+            if(value < min) min = value;
             if(value > max) max = value;
+
+            // make sure it is zero based
+            if(min > 0) min = 0;
+            if(max < 0) max = 0;
+
             NETDATA.gaugeSetLabels(state, value, min, max);
         }
 
         // var chart = $(state.element_chart);
 
         var value = data.result[0];
+        var min = self.data('gauge-min-value') || null;
         var max = self.data('gauge-max-value') || null;
         var adjust = self.data('gauge-adjust') || null;
         var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer;
         var stopColor = self.data('gauge-stop-color') || void 0;
         var generateGradient = self.data('gauge-generate-gradient') || false;
 
+        if(min === null) {
+            min = NETDATA.commonMin.get(state);
+            state.gaugeMin = null;
+        }
+        else
+            state.gaugeMin = min;
+
         if(max === null) {
-            max = data.max;
+            max = NETDATA.commonMax.get(state);
             state.gaugeMax = null;
         }
         else
             state.gaugeMax = max;
 
+        // make sure it is zero based
+        if(min > 0) min = 0;
+        if(max < 0) max = 0;
+
         var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
         //switch(adjust) {
         //  case 'width': width = height * ratio; break;
         var titletop = 0;
         state.gaugeChartTitle = document.createElement('span');
         state.gaugeChartTitle.className = 'gaugeChartTitle';
-        state.gaugeChartTitle.innerHTML = state.title;
+        state.gaugeChartTitle.innerText = state.title;
         state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
         state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
         state.gaugeChartTitle.style.top = titletop.toString() + 'px';
         var unitfontsize = Math.round(titlefontsize * 0.9);
         state.gaugeChartUnits = document.createElement('span');
         state.gaugeChartUnits.className = 'gaugeChartUnits';
-        state.gaugeChartUnits.innerHTML = state.units;
+        state.gaugeChartUnits.innerText = state.units;
         state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
         state.element_chart.appendChild(state.gaugeChartUnits);
 
 
         state.___gaugeOld__ = {
             value: value,
-            min: 0,
+            min: min,
             max: max,
             valueLabel: null,
             minLabel: null,
         state.gauge_instance.maxValue = 100;
 
         NETDATA.gaugeAnimation(state, animate);
-        NETDATA.gaugeSet(state, value, 0, max);
-        NETDATA.gaugeSetLabels(state, value, 0, max);
+        NETDATA.gaugeSet(state, value, min, max);
+        NETDATA.gaugeSetLabels(state, value, min, max);
         NETDATA.gaugeAnimation(state, true);
         return true;
     };
             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)
             }
         },
         {
-            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; }
         }
     ];
             }
         },
         {
-            url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3',
+            url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.7.0',
             isAlreadyLoaded: function() { return false; }
         },
         {
     NETDATA.loadRequiredJs = function(index, callback) {
         if(index >= NETDATA.requiredJs.length) {
             if(typeof callback === 'function')
-                callback();
+                return callback();
             return;
         }
 
             var value = entry.value;
             if(NETDATA.alarms.current !== null) {
                 var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name];
-                if(typeof t !== 'undefined' && entry.status == t.status)
+                if(typeof t !== 'undefined' && entry.status === t.status)
                     value = t.value;
             }
 
                 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                         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);
                 });
         },
 
                 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .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;
                 NETDATA.registry.machines = {};
                 NETDATA.registry.machines_array = new Array();
 
-                var now = new Date().getTime();
+                var now = Date.now();
                 var apu = person_urls;
                 var i = apu.length;
                 while(i--) {
         },
 
         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
                     url: host + '/api/v1/registry?action=hello',
                     async: true,
                     cache: false,
+                    headers: {
+                        'Cache-Control': 'no-cache, no-store',
+                        'Pragma': 'no-cache'
+                    },
                     xhrFields: { withCredentials: true } // required for the cookie
                 })
                 .done(function(data) {
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(407, host);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
                     url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location),
                     async: true,
                     cache: false,
+                    headers: {
+                        'Cache-Control': 'no-cache, no-store',
+                        'Pragma': 'no-cache'
+                    },
                     xhrFields: { withCredentials: true } // required for the cookie
                 })
                 .done(function(data) {
                         }
                         else {
                             if(typeof callback === 'function')
-                                callback(null);
+                                return callback(null);
                         }
                     }
                     else {
                             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);
                 });
         },
 
                 url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                     }
 
                     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);
                 });
         },
 
                 url: NETDATA.registry.server + '/api/v1/registry?action=search&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&for=' + machine_guid,
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                     }
 
                     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);
                 });
         },
 
                 url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid,
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                     }
 
                     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);
                 });
         }
     };
             }
         });
     });
-
-    // window.NETDATA = NETDATA;
-// })(window, document);
+})(window, document);