]> arthur.barton.de Git - netdata.git/blobdiff - web/dashboard.js
Merge pull request #1665 from ktsaou/master
[netdata.git] / web / dashboard.js
index c4f3a93e47f16a82872969ba9f0ca5081bb22b78..346a8e9c10d0e3b5e42eb7364c6146c461a7d4df 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, '_')
     NETDATA.themes = {
         white: {
             bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161226-1',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2',
             background: '#FFFFFF',
             foreground: '#000000',
             grid: '#F0F0F0',
             gauge_gradient: false
         },
         slate: {
-            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161218-2',
-            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161226-1',
+            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2',
             background: '#272b30',
             foreground: '#C8C8C8',
             grid: '#283236',
 
     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;
 
             retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server
 
-            setOptionCallback: function() { }
+            setOptionCallback: function() { }
         },
 
         debug: {
             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 = Date.now();
         NETDATA.onscroll();
+
+        if(typeof NETDATA.onresizeCallback === 'function')
+            NETDATA.onresizeCallback();
     };
 
     NETDATA.onscroll_updater_count = 0;
     // 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);
             });
         }
     };
     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) {
         // 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) {
             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.view_before = 0;
 
         this.value_decimal_detail = -1;
-        {
-            var d = self.data('decimal-digits');
-            if(typeof d === 'number') {
-                this.value_decimal_detail = 1;
-                while(d-- > 0)
-                    this.value_decimal_detail *= 10;
-            }
+        var d = self.data('decimal-digits');
+        if(typeof d === 'number') {
+            this.value_decimal_detail = 1;
+            while(d-- > 0)
+                this.value_decimal_detail *= 10;
         }
 
         this.auto = {
             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)
         };
 
         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');
             }
         };
 
                 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();
                 }
             this.event_resize.chart_last_h = this.element.clientHeight;
 
             var now = Date.now();
-            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
+            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed && this.element_legend_childs.perfect_scroller !== null) {
                 // double click / double tap event
 
+                // console.dir(this.element_legend_childs.content);
+                // console.dir(this.element_legend_childs.perfect_scroller);
+
                 // the optimal height of the chart
                 // showing the entire legend
                 var optimal = this.event_resize.chart_last_h
-                        + this.element_legend_childs.content.scrollHeight
-                        - this.element_legend_childs.content.clientHeight;
+                        + this.element_legend_childs.perfect_scroller.scrollHeight
+                        - this.element_legend_childs.perfect_scroller.clientHeight;
 
                 // if we are not optimal, be optimal
-                if(this.event_resize.chart_last_h != optimal)
+                if(this.event_resize.chart_last_h !== optimal) {
+                    // this.log('resize to optimal, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
                     resizeChartToHeight(optimal.toString() + 'px');
+                }
 
-                // else if we do not have the original height
-                // reset to the original height
-                else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
+                // else if the current height is not the original/saved height
+                // reset to the original/saved height
+                else if(this.event_resize.chart_last_h !== this.event_resize.chart_original_h) {
+                    // this.log('resize to original, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
                     resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
+                }
+
+                // else if the current height is not the internal default height
+                // reset to the internal default height
+                else if((this.event_resize.chart_last_h.toString() + 'px') !== this.height_original) {
+                    // this.log('resize to internal default, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString());
+                    resizeChartToHeight(this.height_original.toString());
+                }
+
+                // else if the current height is not the firstchild's clientheight
+                // resize to it
+                else if(typeof this.element_legend_childs.perfect_scroller.firstChild !== 'undefined') {
+                    var parent_rect = this.element.getBoundingClientRect();
+                    var content_rect = this.element_legend_childs.perfect_scroller.firstElementChild.getBoundingClientRect();
+                    var wanted = content_rect.top - parent_rect.top + this.element_legend_childs.perfect_scroller.firstChild.clientHeight + 18; // 15 = toolbox + 3 space
+
+                    // console.log(parent_rect);
+                    // console.log(content_rect);
+                    // console.log(wanted);
+
+                    // this.log('resize to firstChild, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString() + 'px, firstChild = ' + wanted.toString() + 'px' );
+                    if(this.event_resize.chart_last_h !== wanted)
+                        resizeChartToHeight(wanted.toString() + 'px');
+                }
             }
             else {
                 this.event_resize.last = now;
         };
 
         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;
         };
 
         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
                     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();
         };
                 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;
                 }
             }
 
 
                 NETDATA.statistics.refreshes_active--;
                 that._updating = false;
-                if(typeof callback === 'function') callback();
-            });
 
-            return true;
+                if(typeof callback === 'function')
+                    return callback();
+            });
         };
 
         this.isVisible = function(nocache) {
                     state.running = false;
 
                     if(typeof callback !== 'undefined')
-                        callback();
+                        return callback();
                 });
             }
             else {
                 if(typeof callback !== 'undefined')
-                    callback();
+                    return callback();
             }
         };
 
             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.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();
         }
     };
 
         })
         .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();
         }
     };
 
                         var series = data.series[i];
                         if(series.isVisible === true)
                             state.legendSetLabelValue(series.label, series.y);
+                        else
+                            state.legendSetLabelValue(series.label, null);
                     }
                 }
 
 
                         // http://dygraphs.com/gallery/interaction-api.js
                         var normal_def;
-                        if(typeof event.wheelDelta === 'number' && event.wheelDelta != NaN)
+                        if(typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta))
                             // chrome
                             normal_def = event.wheelDelta / 40;
                         else
                 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();
         }
     };
 
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.easypiechart.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
                 })
                 .always(function() {
                     if(typeof callback === "function")
-                        callback();
+                        return callback();
                 })
         }
         else {
             NETDATA.chartLibraries.gauge.enabled = false;
             if(typeof callback === "function")
-                callback();
+                return callback();
         }
     };
 
             min = max;
             max = t;
         }
-        else if(min == max)
+        else if(min === max)
             max = min + 1;
 
         // gauge.js has an issue if the needle
             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; }
         }
     ];
     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;
             }
 
             var name = entry.name.replace(/_/g, ' ');
             var status = entry.status.toLowerCase();
-            var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
+            var title = name + ' = ' + entry.value_string.toString();
             var tag = entry.alarm_id;
             var icon = 'images/seo-performance-128.png';
             var interaction = false;
                         // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
                         return;
                     }
-                    title = name + ' back to normal';
+                    if(entry.no_clear_notification === true) {
+                        // console.log('alarm' + entry.unique_id + ' is CLEAR but has no_clear_notification flag');
+                        return;
+                    }
+                    title = name + ' back to normal (' + entry.value_string.toString() + ')';
                     icon = 'images/check-mark-2-128-green.png'
                     interaction = false;
                     break;
                         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);
                 });
         },
 
             })
                 .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;
         },
 
         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
                     }
 
                     if(typeof callback === 'function')
-                        callback(data);
+                        return callback(data);
                 })
                 .fail(function() {
                     NETDATA.error(407, host);
 
                     if(typeof callback === 'function')
-                        callback(null);
+                        return callback(null);
                 });
         },
 
                         }
                         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);
                 });
         },
 
                     }
 
                     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);
                 });
         },
 
                     }
 
                     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);
                 });
         },
 
                     }
 
                     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);