]> arthur.barton.de Git - netdata.git/blobdiff - web/dashboard.js
Merge pull request #299 from fredericopissarra/master
[netdata.git] / web / dashboard.js
old mode 100755 (executable)
new mode 100644 (file)
index aa114a3..a2089d4
@@ -11,6 +11,7 @@
 // var netdataNoC3 = true;                             // do not use C3
 // var netdataNoBootstrap = true;              // do not load bootstrap
 // var netdataDontStart = true;                        // do not start the thread to process the charts
+// var netdataErrorCallback = null;            // Callback function that will be invoked upon error
 //
 // You can also set the default netdata server, using the following.
 // When this variable is not set, we assume the page is hosted on your
@@ -29,8 +30,8 @@
 
        // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
        // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
-       NETDATA._scriptSource = function(scripts) {
-               var script = null, base = null;
+       NETDATA._scriptSource = function() {
+               var script = null;
 
                if(typeof document.currentScript !== 'undefined') {
                        script = document.currentScript;
                else
                        script = script.getAttribute('src', -1);
 
-               var link = document.createElement('a');
-               link.setAttribute('href', script);
-
-               if(!link.protocol || !link.hostname) return null;
-
-               base = link.protocol;
-               if(base) base += "//";
-               base += link.hostname;
-
-               if(link.port) base += ":" + link.port;
-               base += "/";
-
-               return base;
+               return script;
        };
 
        if(typeof netdataServer !== 'undefined')
                NETDATA.serverDefault = netdataServer;
-       else
-               NETDATA.serverDefault = NETDATA._scriptSource();
+       else {
+               var s = NETDATA._scriptSource();
+               NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
+       }
 
        if(NETDATA.serverDefault === null)
                NETDATA.serverDefault = '';
        NETDATA.c3_js                           = NETDATA.serverDefault + 'lib/c3.min.js';
        NETDATA.c3_css                          = NETDATA.serverDefault + 'css/c3.min.css';
        NETDATA.morris_css              = NETDATA.serverDefault + 'css/morris.css';
-       NETDATA.dashboard_css           = NETDATA.serverDefault + 'dashboard.css';
        NETDATA.google_js               = 'https://www.google.com/jsapi';
 
+       NETDATA.themes = {
+               default: {
+                       bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
+                       dashboard_css: NETDATA.serverDefault + 'dashboard.css',
+                       background: '#FFFFFF',
+                       foreground: '#000000',
+                       grid: '#DDDDDD',
+                       axis: '#CCCCCC',
+                       colors: [       '#3366CC', '#DC3912',   '#109618', '#FF9900',   '#990099', '#DD4477',
+                                               '#3B3EAC', '#66AA00',   '#0099C6', '#B82E2E',   '#AAAA11', '#5574A6',
+                                               '#994499', '#22AA99',   '#6633CC', '#E67300',   '#316395', '#8B0707',
+                                               '#329262', '#3B3EAC' ],
+                       easypiechart_track: '#f0f0f0',
+                       easypiechart_scale: '#dfe0e0',
+                       gauge_pointer: '#C0C0C0',
+                       gauge_stroke: '#F0F0F0',
+                       gauge_gradient: true
+               },
+               slate: {
+                       bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
+                       dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
+                       background: '#272b30',
+                       foreground: '#C8C8C8',
+                       grid: '#373b40',
+                       axis: '#373b40',
+/*                     colors: [       '#55bb33', '#ff2222',   '#0099C6', '#faa11b',   '#adbce0', '#DDDD00',
+                                               '#4178ba', '#f58122',   '#a5cc39', '#f58667',   '#f5ef89', '#cf93c0',
+                                               '#a5d18a', '#b8539d',   '#3954a3', '#c8a9cf',   '#c7de8a', '#fad20a',
+                                               '#a6a479', '#a66da8' ],
+*/
+                       colors: [       '#66AA00', '#FE3912',   '#3366CC', '#D66300',   '#0099C6', '#DDDD00',
+                                               '#3B3EAC', '#EE9911',   '#BB44CC', '#C83E3E',   '#990099', '#CC7700',
+                                               '#22AA99', '#109618',   '#6633CC', '#DD4477',   '#316395', '#8B0707',
+                                               '#329262', '#3B3EFF' ],
+                       easypiechart_track: '#373b40',
+                       easypiechart_scale: '#373b40',
+                       gauge_pointer: '#474b50',
+                       gauge_stroke: '#373b40',
+                       gauge_gradient: false
+               }
+       };
+
+       if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
+               NETDATA.themes.current = NETDATA.themes[netdataTheme];
+       else
+               NETDATA.themes.current = NETDATA.themes.default;
+
+       NETDATA.colors = NETDATA.themes.current.colors;
+
        // these are the colors Google Charts are using
        // we have them here to attempt emulate their look and feel on the other chart libraries
        // http://there4.io/2012/05/02/google-chart-color-list/
        //                                              '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
        //                                              '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
 
-       NETDATA.colors          = [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', '#3B3EAC',
-                                                       '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', '#994499', '#22AA99',
-                                                       '#6633CC', '#E67300', '#316395', '#8B0707', '#329262', '#3B3EAC' ];
        // an alternative set
        // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
        //                         (blue)     (red)      (orange)   (green)    (pink)     (brown)    (purple)   (yellow)   (gray)
                after: -600,                                    // panning
                pixels_per_point: 1,                    // the detail of the chart
                fill_luminance: 0.8                             // luminance of colors in solit areas
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // global options
 
                        destroy_on_hide: false,         // destroy charts when they are not visible
 
+                       show_help: true,                        // when enabled the charts will show some help
+                       show_help_delay_show_ms: 500,
+                       show_help_delay_hide_ms: 0,
+
                        eliminate_zero_dimensions: true, // do not show dimensions with just zeros
 
                        stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
 
                        smooth_plot: true,                      // enable smooth plot, where possible
 
+                       charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
+
                        color_fill_opacity_line: 1.0,
                        color_fill_opacity_area: 0.2,
                        color_fill_opacity_stacked: 0.8,
 
+                       pan_and_zoom_factor: 0.25,              // the increment when panning and zooming with the toolbox
+                       pan_and_zoom_factor_multiplier_control: 2.0,
+                       pan_and_zoom_factor_multiplier_shift: 3.0,
+                       pan_and_zoom_factor_multiplier_alt: 4.0,
+
                        setOptionCallback: function() { ; }
                },
 
                        libraries:                      false,
                        dygraph:                        false
                }
-       }
+       };
 
 
        // ----------------------------------------------------------------------------------------------------------------
 
                NETDATA.localStorage.current[key.toString()] = ret;
                return ret;
-       }
+       };
 
        NETDATA.localStorageSet = function(key, value, callback) {
                if(typeof value === 'undefined' || value === 'undefined') {
 
                NETDATA.localStorage.current[key.toString()] = value;
                return value;
-       }
+       };
 
        NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
                for(var i in obj) {
 
                        obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
                }
-       }
+       };
 
        NETDATA.setOption = function(key, value) {
                if(key.toString() === 'setOptionCallback') {
                }
 
                return true;
-       }
+       };
 
        NETDATA.getOption = function(key) {
                return NETDATA.options.current[key.toString()];
-       }
+       };
 
        // read settings from local storage
        NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
                101: { message: "Cannot load jQuery", alert: true },
                402: { message: "Chart library not found", alert: false },
                403: { message: "Chart library not enabled/is failed", alert: false },
-               404: { message: "Chart not found", alert: false }
+               404: { message: "Chart not found", alert: false },
+               405: { message: "Cannot download charts index from server", alert: true },
+               406: { message: "Invalid charts index downloaded from server", alert: true }
        };
        NETDATA.errorLast = {
                code: 0,
 
                console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
 
-               if(NETDATA.errorCodes[code].alert)
+               var ret = true;
+               if(typeof netdataErrorCallback === 'function') {
+                  ret = netdataErrorCallback('system', code, msg);
+               }
+
+               if(ret && NETDATA.errorCodes[code].alert)
                        alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
-       }
+       };
 
        NETDATA.errorReset = function() {
                NETDATA.errorLast.code = 0;
                                cache: false
                        })
                        .done(function(data) {
-                               var h = NETDATA.chartRegistry.fixid(host);
-                               //console.log('downloaded all charts from ' + host + ' (' + h + ')');
-                               self.charts[h] = data.charts;
+                               if(data !== null) {
+                                       var h = NETDATA.chartRegistry.fixid(host);
+                                       self.charts[h] = data.charts;
+                               }
+                               else NETDATA.error(406, host + '/api/v1/charts');
+
                                if(typeof callback === 'function')
                                        callback(data);
                        })
                        .fail(function() {
+                               NETDATA.error(405, host + '/api/v1/charts');
+
                                if(typeof callback === 'function')
                                        callback(null);
                        });
                master: null,                   // the master chart (state), to which all others
                                                                // are synchronized
 
-               force_before_ms: null,  // the timespan to sync all other charts 
+               force_before_ms: null,  // the timespan to sync all other charts
                force_after_ms: null,
 
                // set a new master
 
                        return true;
                }
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // dimensions selection
                this.label = label;
                this.name_div = null;
                this.value_div = null;
-               this.color = '#000';
+               this.color = NETDATA.themes.current.foreground;
 
-               if(parent.selected === parent.unselected)
+               if(parent.selected_count > parent.unselected_count)
                        this.selected = true;
                else
                        this.selected = false;
 
                this.setOptions(name_div, value_div, color);
-       }
+       };
 
        dimensionStatus.prototype.invalidate = function() {
                this.name_div = null;
                this.value_div = null;
                this.enabled = false;
-       }
+       };
 
        dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
                this.color = color;
 
                this.enabled = true;
                this.setHandler();
-       }
+       };
 
        dimensionStatus.prototype.setHandler = function() {
                if(this.enabled === false) return;
 
                        ds.parent.state.redrawChart();
                }
-       }
+       };
 
        dimensionStatus.prototype.select = function() {
                if(this.enabled === false) return;
                this.name_div.className = 'netdata-legend-name selected';
                this.value_div.className = 'netdata-legend-value selected';
                this.selected = true;
-       }
+       };
 
        dimensionStatus.prototype.unselect = function() {
                if(this.enabled === false) return;
                this.name_div.className = 'netdata-legend-name not-selected';
                this.value_div.className = 'netdata-legend-value hidden';
                this.selected = false;
-       }
+       };
 
        dimensionStatus.prototype.isSelected = function() {
                return(this.enabled === true && this.selected === true);
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
 
                this.dimensions = {};
                this.selected_count = 0;
                this.unselected_count = 0;
-       }
+       };
 
        dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
                if(typeof this.dimensions[label] === 'undefined') {
                        this.dimensions[label].setOptions(name_div, value_div, color);
 
                return this.dimensions[label];
-       }
+       };
 
        dimensionsVisibility.prototype.dimensionGet = function(label) {
                return this.dimensions[label];
-       }
+       };
 
        dimensionsVisibility.prototype.invalidateAll = function() {
                for(var d in this.dimensions)
                        this.dimensions[d].invalidate();
-       }
+       };
 
        dimensionsVisibility.prototype.selectAll = function() {
                for(var d in this.dimensions)
                        this.dimensions[d].select();
-       }
+       };
 
        dimensionsVisibility.prototype.countSelected = function() {
                var i = 0;
                        if(this.dimensions[d].isSelected()) i++;
 
                return i;
-       }
+       };
 
        dimensionsVisibility.prototype.selectNone = function() {
                for(var d in this.dimensions)
                        this.dimensions[d].unselect();
-       }
+       };
 
        dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
                var ret = new Array();
                }
 
                return ret;
-       }
+       };
 
 
        // ----------------------------------------------------------------------------------------------------------------
                 * show an error instead of the chart
                 */
                var error = function(msg) {
-                       that.element.innerHTML = that.id + ': ' + msg;
-                       that.enabled = false;
-                       that.current = that.pan;
-               }
+                       var ret = true;
+
+                       if(typeof netdataErrorCallback === 'function') {
+                               ret = netdataErrorCallback('chart', that.id, msg);
+                       }
+
+                       if(ret) {
+                               that.element.innerHTML = that.id + ': ' + msg;
+                               that.enabled = false;
+                               that.current = that.pan;
+                       }
+               };
 
                // GUID - a unique identifier for the chart
                this.uuid = NETDATA.guid();
 
                this.dimensions_visibility = new dimensionsVisibility(this);
 
+               this._updating = false;
+
                // ============================================================================================================
                // PRIVATE FUNCTIONS
 
                        that.tm.last_dom_created = new Date().getTime();
 
                        showLoading();
-               }
+               };
 
                /* init() private
-                * initialize state viariables
+                * initialize state variables
                 * destroy all (possibly) created state elements
                 * create the basic DOM for a chart
                 */
                                last_hidden: 0,                         // the time the chart was hidden
                                last_unhidden: 0,                       // the time the chart was unhidden
                                last_autorefreshed: 0           // the time the chart was last refreshed
-                       },
+                       };
 
                        that.data = null;                               // the last data as downloaded from the netdata server
                        that.data_url = 'invalid://';   // string - the last url used to update the chart
                        createDOM();
 
                        that.setMode('auto');
-               }
+               };
 
                var maxMessageFontSize = function() {
                        // normally we want a font size, as tall as the element
                        // set it
                        that.element_message.style.fontSize = h.toString() + 'px';
                        that.element_message.style.paddingTop = paddingTop.toString() + 'px';
-               }
+               };
 
                var showMessage = function(msg) {
                        that.element_message.className = 'netdata-message';
                        that.element_message.innerHTML = msg;
-                       this.element_message.style.fontSize = 'x-small';
+                       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.___messageHidden___ = undefined;
-               }
+               };
 
                var hideMessage = function() {
                        if(typeof that.___messageHidden___ === 'undefined') {
                                that.___messageHidden___ = true;
                                that.element_message.className = 'netdata-message hidden';
                        }
-               }
+               };
 
                var showRendering = function() {
                        var icon;
                                icon = '<i class="fa fa-area-chart"></i>';
 
                        showMessageIcon(icon + ' netdata');
-               }
+               };
 
                var showLoading = function() {
                        if(that.chart_created === false) {
                                return true;
                        }
                        return false;
-               }
+               };
 
                var isHidden = function() {
                        if(typeof that.___chartIsHidden___ !== 'undefined')
                                return true;
 
                        return false;
-               }
+               };
 
                // hide the chart, when it is not visible - called from isVisible()
                var hideChart = function() {
                                        that.tm.last_hidden = new Date().getTime();
                                }
                        }
-                       
+
                        that.___chartIsHidden___ = true;
-               }
+               };
 
                // unhide the chart, when it is visible - called from isVisible()
                var unhideChart = function() {
                                resizeChart();
                                hideMessage();
                        }
-               }
+               };
 
                var canBeRendered = function() {
                        if(isHidden() === true || that.isVisible(true) === false)
                                return false;
 
                        return true;
-               }
+               };
 
                // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
                var callChartLibraryUpdateSafely = function(data) {
                        }
 
                        return true;
-               }
+               };
 
                // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
                var callChartLibraryCreateSafely = function(data) {
                        that.chart_created = true;
                        that.updates_since_last_creation = 0;
                        return true;
-               }
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
                // Chart Resize
 
                                that.tm.last_resized = new Date().getTime();
                        }
-               }
+               };
 
                // this is the actual chart resize algorithm
                // it will:
                                        };
 
                                // process end event
-                               document.onmouseup = 
-                               document.ontouchend = 
+                               document.onmouseup =
+                               document.ontouchend =
                                this.element_legend_childs.resize_handler.onmouseup =
                                this.element_legend_childs.resize_handler.ontouchend =
                                        function(e) {
                                                NETDATA.options.auto_refresher_stop_until = 0;
                                        };
                        }
-               }
+               };
 
 
                var noDataToShow = function() {
                        //that.element_chart.style.display = 'none';
                        //if(that.element_legend !== null) that.element_legend.style.display = 'none';
                        //that.___chartIsHidden___ = true;
-               }
+               };
 
                // ============================================================================================================
                // PUBLIC FUNCTIONS
 
                this.error = function(msg) {
                        error(msg);
-               }
+               };
 
                this.setMode = function(m) {
                        if(this.current !== null && this.current.name === m) return;
                        this.current.force_after_ms = null;
 
                        this.tm.last_mode_switch = new Date().getTime();
-               }
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
                // global selection sync
                                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
                        else
                                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
-               }
+               };
 
                // can we globally apply selection sync?
                this.globalSelectionSyncAbility = function() {
                                return false;
 
                        return true;
-               }
+               };
 
                this.globalSelectionSyncIsMaster = function() {
                        if(NETDATA.globalSelectionSync.state === this)
                                return true;
                        else
                                return false;
-               }
+               };
 
                // this chart is the master of the global selection sync
                this.globalSelectionSyncBeMaster = function() {
                        }
 
                        // this.globalSelectionSyncDelay(100);
-               }
+               };
 
                // can the chart participate to the global selection sync as a slave?
                this.globalSelectionSyncIsEligible = function() {
                                return true;
 
                        return false;
-               }
+               };
 
                // this chart becomes a slave of the global selection sync
                this.globalSelectionSyncBeSlave = function() {
                        if(NETDATA.globalSelectionSync.state !== this)
                                NETDATA.globalSelectionSync.slaves.push(this);
-               }
+               };
 
                // sync all the visible charts to the given time
                // this is to be called from the chart libraries
                        $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
                                st.setSelection(t);
                        });
-               }
+               };
 
                // stop syncing all charts to the given time
                this.globalSelectionSyncStop = function() {
                        }
 
                        this.clearSelection();
-               }
+               };
 
                this.setSelection = function(t) {
                        if(typeof this.library.setSelection === 'function') {
                                this.log('selection set to ' + t.toString());
 
                        return this.selected;
-               }
+               };
 
                this.clearSelection = function() {
                        if(this.selected === true) {
                        }
 
                        return this.selected;
-               }
+               };
 
                // find if a timestamp (ms) is shown in the current chart
                this.timeIsVisible = function(t) {
                        if(t >= this.data_after && t <= this.data_before)
                                return true;
                        return false;
-               },
+               };
 
                this.calculateRowForTime = function(t) {
                        if(this.timeIsVisible(t) === false) return -1;
                        return Math.floor((t - this.data_after) / this.data_update_every);
-               }
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
 
                // console logging
                this.log = function(msg) {
                        console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
-               }
+               };
 
                this.pauseChart = function() {
                        if(this.paused === false) {
 
                                this.paused = true;
                        }
-               }
+               };
 
                this.unpauseChart = function() {
                        if(this.paused === true) {
 
                                this.paused = false;
                        }
-               }
+               };
 
                this.resetChart = function(dont_clear_master, dont_update) {
                        if(this.debug === true)
                        if(dont_update !== true && this.isVisible() === true) {
                                this.updateChart();
                        }
-               }
+               };
 
                this.updateChartPanOrZoom = function(after, before) {
                        var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
                                this.log(logme);
 
                        if(before < after) {
-                               this.log(logme + 'flipped parameters, rejecting it.');
+                               if(this.debug === true)
+                                       this.log(logme + 'flipped parameters, rejecting it.');
+
                                return false;
                        }
 
                        if(typeof this.fixed_min_duration === 'undefined')
                                this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
-                       
+
                        var min_duration = this.fixed_min_duration;
                        var current_duration = Math.round(this.view_before - this.view_after);
 
 
                        // the final wanted duration
                        var wanted_duration = before - after;
-                       
+
                        // to allow panning, accept just a point below our minimum
                        if((current_duration - this.data_update_every) < min_duration)
                                min_duration = current_duration - this.data_update_every;
                        // we do it, but we adjust to minimum size and return false
                        // when the wanted size is below the current and the minimum
                        // and we zoom
-                       if(this.current === this.zoom && wanted_duration < current_duration && wanted_duration < min_duration) {
+                       if(wanted_duration < current_duration && wanted_duration < min_duration) {
                                if(this.debug === true)
                                        this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
 
                                var dt = (min_duration - wanted_duration) / 2;
                                before += dt;
                                after -= dt;
+                               wanted_duration = before - after;
                                ret = false;
                        }
 
                        var tolerance = this.data_update_every * 2;
                        var movement = Math.abs(before - this.view_before);
 
-                       if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) {
+                       if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
                                if(this.debug === true)
-                                       this.log(logme + 'REJECTING UPDATE: current duration: ' + (current_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
+                                       this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
                                return false;
                        }
 
                        }
 
                        if(this.debug === true)
-                               this.log(logme + 'ACCEPTING UPDATE: current duration: ' + (current_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.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_after_ms = after;
                        this.current.force_before_ms = before;
                        NETDATA.globalPanAndZoom.setMaster(this, after, before);
                        return ret;
-               }
+               };
 
                this.legendFormatValue = function(value) {
                        if(value === null || value === 'undefined') return '-';
                        if(abs >= 1   ) return (Math.round(value * 100) / 100).toLocaleString();
                        if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
                        return (Math.round(value * 10000) / 10000).toLocaleString();
-               }
+               };
 
                this.legendSetLabelValue = function(label, value) {
                        var series = this.element_legend_childs.series[label];
 
                        if(series.value !== null) series.value.innerHTML = s;
                        if(series.user !== null) series.user.innerHTML = r;
-               }
+               };
 
                this.legendSetDate = function(ms) {
                        if(typeof ms !== 'number') {
 
                        if(this.element_legend_childs.title_units)
                                this.element_legend_childs.title_units.innerHTML = this.units;
-               }
+               };
 
                this.legendShowUndefined = function() {
                        if(this.element_legend_childs.title_date)
                                        this.legendSetLabelValue(label, null);
                                }
                        }
-               }
+               };
 
                this.legendShowLatestValues = function() {
                        if(this.chart === null) return;
                        }
 
                        var show_undefined = true;
-                       if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every)
+                       if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
                                show_undefined = false;
 
                        if(show_undefined) {
                                return;
                        }
 
-                       this.legendSetDate(this.data.before * 1000);
+                       this.legendSetDate(this.view_before);
 
                        var labels = this.data.dimension_names;
                        var i = labels.length;
                                else
                                        this.legendSetLabelValue(label, this.data.view_latest_values[i]);
                        }
-               }
+               };
 
                this.legendReset = function() {
                        this.legendShowLatestValues();
-               }
+               };
 
                // this should be called just ONCE per dimension per chart
                this._chartDimensionColor = function(label) {
 
                        if(typeof this.colors_assigned[label] === 'undefined') {
                                if(this.colors_available.length === 0) {
-                                       for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
-                                               this.colors_available.push(NETDATA.colors[i]);
+                                       for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
+                                               this.colors_available.push(NETDATA.themes.current.colors[i]);
                                }
 
                                this.colors_assigned[label] = this.colors_available.shift();
 
                        this.colors.push(this.colors_assigned[label]);
                        return this.colors_assigned[label];
-               }
+               };
 
                this.chartColors = function() {
                        if(this.colors !== null) return this.colors;
 
                        this.colors = new Array();
                        this.colors_available = new Array();
-                       // this.colors_assigned = {};
+                       var i, len;
 
                        var c = $(this.element).data('colors');
-                       if(typeof c !== 'undefined' && c !== null) {
+                       // this.log('read colors: ' + c);
+                       if(typeof c !== 'undefined' && c !== null && c.length > 0) {
                                if(typeof c !== 'string') {
                                        this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
                                }
                                else {
                                        c = c.split(' ');
-                                       for(var i = 0, len = c.length; i < len ; i++)
-                                               this.colors_available.push(c[i]);
+                                       var added = 0;
+
+                                       while(added < 20) {
+                                               for(i = 0, len = c.length; i < len ; i++) {
+                                                       added++;
+                                                       this.colors_available.push(c[i]);
+                                                       // this.log('adding color: ' + c[i]);
+                                               }
+                                       }
                                }
                        }
 
                        // push all the standard colors too
-                       for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
-                               this.colors_available.push(NETDATA.colors[i]);
+                       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;
 
                                // do we have to update the current values?
                                // we do this, only when the visible chart is current
-                               if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) {
+                               if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
                                        if(this.debug === true)
-                                               this.log('chart in running... updating values on legend...');
+                                               this.log('chart is in latest position... updating values on legend...');
 
                                        //var labels = this.data.dimension_names;
                                        //var i = labels.length;
                                                this._chartDimensionColor(this.chart.dimensions[dim].name);
                        }
                        // we will re-generate the colors for the chart
+                       // based on the selected dimensions
                        this.colors = null;
 
                        if(this.debug === true)
                                if(user_id !== null) {
                                        user_element = document.getElementById(user_id) || null;
                                        if(user_element === null)
-                                               me.log('Cannot find element with id: ' + user_id);
+                                               state.log('Cannot find element with id: ' + user_id);
                                }
 
                                state.element_legend_childs.series[name] = {
                                this.element_legend_childs = {
                                        content: content,
                                        resize_handler: document.createElement('div'),
+                                       toolbox: document.createElement('div'),
+                                       toolbox_left: document.createElement('div'),
+                                       toolbox_right: document.createElement('div'),
+                                       toolbox_reset: document.createElement('div'),
+                                       toolbox_zoomin: document.createElement('div'),
+                                       toolbox_zoomout: document.createElement('div'),
+                                       toolbox_volume: document.createElement('div'),
                                        title_date: document.createElement('span'),
                                        title_time: document.createElement('span'),
                                        title_units: document.createElement('span'),
 
                                this.element_legend.innerHTML = '';
 
+                               if(this.library.toolboxPanAndZoom !== null) {
+
+                                       function get_pan_and_zoom_step(event) {
+                                               if (event.ctrlKey)
+                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
+
+                                               else if (event.shiftKey)
+                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
+
+                                               else if (event.altKey)
+                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
+
+                                               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_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
+                                       this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
+                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
+                                       this.element_legend_childs.toolbox_left.onclick = function(e) {
+                                               e.preventDefault();
+
+                                               var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+                                               var before = that.view_before - step;
+                                               var after = that.view_after - step;
+                                               if(after >= that.netdata_first)
+                                                       that.library.toolboxPanAndZoom(that, after, before);
+                                       };
+                                       if(NETDATA.options.current.show_help === true)
+                                               $(this.element_legend_childs.toolbox_left).popover({
+                                               container: "body",
+                                               animation: false,
+                                               html: true,
+                                               trigger: 'hover',
+                                               placement: 'bottom',
+                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                               title: 'Pan Left',
+                                               content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                                       });
+
+
+                                       this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
+                                       this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
+                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
+                                       this.element_legend_childs.toolbox_reset.onclick = function(e) {
+                                               e.preventDefault();
+                                               NETDATA.resetAllCharts(that);
+                                       };
+                                       if(NETDATA.options.current.show_help === true)
+                                               $(this.element_legend_childs.toolbox_reset).popover({
+                                               container: "body",
+                                               animation: false,
+                                               html: true,
+                                               trigger: 'hover',
+                                               placement: 'bottom',
+                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                               title: 'Chart Reset',
+                                               content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                                       });
+                                       
+                                       this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
+                                       this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
+                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
+                                       this.element_legend_childs.toolbox_right.onclick = function(e) {
+                                               e.preventDefault();
+                                               var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+                                               var before = that.view_before + step;
+                                               var after = that.view_after + step;
+                                               if(before <= that.netdata_last)
+                                                       that.library.toolboxPanAndZoom(that, after, before);
+                                       };
+                                       if(NETDATA.options.current.show_help === true)
+                                               $(this.element_legend_childs.toolbox_right).popover({
+                                               container: "body",
+                                               animation: false,
+                                               html: true,
+                                               trigger: 'hover',
+                                               placement: 'bottom',
+                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                               title: 'Pan Right',
+                                               content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                                       });
+
+                                       
+                                       this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
+                                       this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
+                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
+                                       this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
+                                               e.preventDefault();
+                                               var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
+                                               var before = that.view_before - dt;
+                                               var after = that.view_after + dt;
+                                               that.library.toolboxPanAndZoom(that, after, before);
+                                       };
+                                       if(NETDATA.options.current.show_help === true)
+                                               $(this.element_legend_childs.toolbox_zoomin).popover({
+                                               container: "body",
+                                               animation: false,
+                                               html: true,
+                                               trigger: 'hover',
+                                               placement: 'bottom',
+                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                               title: 'Chart Zoom In',
+                                               content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
+                                       });
+                                       
+                                       this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
+                                       this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
+                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
+                                       this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
+                                               e.preventDefault();
+                                               var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
+                                               var before = that.view_before + dt;
+                                               var after = that.view_after - dt;
+
+                                               that.library.toolboxPanAndZoom(that, after, before);
+                                       };
+                                       if(NETDATA.options.current.show_help === true)
+                                               $(this.element_legend_childs.toolbox_zoomout).popover({
+                                               container: "body",
+                                               animation: false,
+                                               html: true,
+                                               trigger: 'hover',
+                                               placement: 'bottom',
+                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                               title: 'Chart Zoom Out',
+                                               content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
+                                       });
+                                       
+                                       //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
+                                       //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
+                                       //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
+                                       //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
+                                       //this.element_legend_childs.toolbox_volume.onclick = function(e) {
+                                               //e.preventDefault();
+                                               //alert('clicked toolbox_volume on ' + that.id);
+                                       //}
+                               }
+                               else {
+                                       this.element_legend_childs.toolbox = null;
+                                       this.element_legend_childs.toolbox_left = null;
+                                       this.element_legend_childs.toolbox_reset = null;
+                                       this.element_legend_childs.toolbox_right = null;
+                                       this.element_legend_childs.toolbox_zoomin = null;
+                                       this.element_legend_childs.toolbox_zoomout = null;
+                                       this.element_legend_childs.toolbox_volume = null;
+                               }
+                               
                                this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
                                this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
                                this.element.appendChild(this.element_legend_childs.resize_handler);
+                               if(NETDATA.options.current.show_help === true)
+                                       $(this.element_legend_childs.resize_handler).popover({
+                                       container: "body",
+                                       animation: false,
+                                       html: true,
+                                       trigger: 'hover',
+                                       placement: 'bottom',
+                                       delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                       title: 'Chart Resize',
+                                       content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
+                               });
 
                                // mousedown event
                                this.element_legend_childs.resize_handler.onmousedown =
 
                                content.className = 'netdata-legend-series-content';
                                this.element_legend_childs.nano.appendChild(content);
+
+                               if(NETDATA.options.current.show_help === true)
+                                       $(content).popover({
+                                       container: "body",
+                                       animation: false,
+                                       html: true,
+                                       trigger: 'hover',
+                                       placement: 'bottom',
+                                       title: 'Chart Legend',
+                                       delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                                       content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
+                               });
                        }
                        else {
                                this.element_legend_childs = {
                                        content: content,
                                        resize_handler: null,
+                                       toolbox: null,
+                                       toolbox_left: null,
+                                       toolbox_right: null,
+                                       toolbox_reset: null,
+                                       toolbox_zoomin: null,
+                                       toolbox_zoomout: null,
+                                       toolbox_volume: null,
                                        title_date: null,
                                        title_time: null,
                                        title_units: null,
                                $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
 
                        this.legendShowLatestValues();
-               }
+               };
 
                this.hasLegend = function() {
                        if(typeof this.___hasLegendCache___ !== 'undefined')
 
                        this.___hasLegendCache___ = leg;
                        return leg;
-               }
+               };
 
                this.legendWidth = function() {
                        return (this.hasLegend())?140:0;
-               }
+               };
 
                this.legendHeight = function() {
                        return $(this.element).height();
-               }
+               };
 
                this.chartWidth = function() {
                        return $(this.element).width() - this.legendWidth();
-               }
+               };
 
                this.chartHeight = function() {
                        return $(this.element).height();
-               }
+               };
 
                this.chartPixelsPerPoint = function() {
                        // force an options provided detail
                                px = NETDATA.options.current.pixels_per_point;
 
                        return px;
-               }
+               };
 
                this.needsRecreation = function() {
                        return (
                                        && this.library.autoresize() === false
                                        && this.tm.last_resized < NETDATA.options.last_resized
                                );
-               }
+               };
 
                this.chartURL = function() {
                        var after, before, points_multiplier = 1;
                        this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
 
                        // build the data URL
-                       this.data_url = this.chart.data_url;
+                       this.data_url = this.host + this.chart.data_url;
                        this.data_url += "&format="  + this.library.format();
                        this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
                        this.data_url += "&group="   + this.method;
 
                        if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
                                this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
-               }
+               };
 
                this.redrawChart = function() {
                        if(this.data !== null)
                                this.updateChartWithData(this.data);
-               }
+               };
 
                this.updateChartWithData = function(data) {
                        if(this.debug === true)
                                this.log('updateChartWithData() called.');
 
+                       this._updating = false;
+
                        // this may force the chart to be re-created
                        resizeChart();
 
 
                        if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) {
                                if(this.view_after < this.data_after) {
-                                       console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
+                                       // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after);
                                        this.view_after = this.data_after;
                                }
-                               
+
                                if(this.view_before > this.data_before) {
-                                       console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
+                                       // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before);
                                        this.view_before = this.data_before;
                                }
                        }
 
                        if(this.refresh_dt_element !== null)
                                this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
-               }
+               };
 
                this.updateChart = function(callback) {
                        if(this.debug === true)
                                this.log('updateChart() called.');
 
+                       if(this._updating === true) {
+                               if(this.debug === true)
+                                       this.log('I am already updating...');
+
+                               if(typeof callback === 'function') callback();
+                               return false;
+                       }
+
                        // due to late initialization of charts and libraries
                        // we need to check this too
                        if(this.enabled === false) {
                        if(this.debug === true)
                                this.log('updating from ' + this.data_url);
 
+                       this._updating = true;
+
                        this.xhr = $.ajax( {
                                url: this.data_url,
                                crossDomain: NETDATA.options.crossDomainAjax,
                                error('data download failed for url: ' + that.data_url);
                        })
                        .always(function() {
+                               that._updating = false;
                                if(typeof callback === 'function') callback();
                        });
 
                        return true;
-               }
+               };
 
                this.isVisible = function(nocache) {
                        if(typeof nocache === 'undefined')
                                this.___isVisible___ = true;
                                return this.___isVisible___;
                        }
-               }
+               };
 
                this.isAutoRefreshed = function() {
                        return (this.current.autorefresh);
-               }
+               };
 
                this.canBeAutoRefreshed = function() {
-                       now = new Date().getTime();
+                       var now = new Date().getTime();
 
                        if(this.enabled === false) {
                                if(this.debug === true)
                        }
 
                        return false;
-               }
+               };
 
                this.autoRefresh = function(callback) {
                        if(this.canBeAutoRefreshed() === true) {
                                if(typeof callback !== 'undefined')
                                        callback();
                        }
-               }
+               };
 
                this._defaultsFromDownloadedChart = function(chart) {
                        this.chart = chart;
 
                        if(this.units === null)
                                this.units = chart.units;
-               }
+               };
 
                // fetch the chart description from the netdata server
                this.getChart = function(callback) {
                                if(typeof callback === 'function') callback();
                        }
                        else {
-                               this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
+                               this.chart_url = "/api/v1/chart?chart=" + this.id;
 
                                if(this.debug === true)
                                        this.log('downloading ' + this.chart_url);
 
                                $.ajax( {
-                                       url:  this.chart_url,
+                                       url:  this.host + this.chart_url,
                                        crossDomain: NETDATA.options.crossDomainAjax,
                                        cache: false,
                                        async: true
                                })
                                .done(function(chart) {
                                        chart.url = that.chart_url;
-                                       chart.data_url = (that.host + chart.data_url);
                                        that._defaultsFromDownloadedChart(chart);
                                        NETDATA.chartRegistry.add(that.host, that.id, chart);
                                })
                                        if(typeof callback === 'function') callback();
                                });
                        }
-               }
+               };
 
                // ============================================================================================================
                // INITIALIZATION
 
                init();
-       }
+       };
 
        NETDATA.resetAllCharts = function(state) {
                // first clear the global selection sync
 
                // if we were not the master, reset our status too
                // this is required because most probably the mouse
-               // is over this chart, blocking it from autorefreshing
+               // is over this chart, blocking it from auto-refreshing
                if(master === false && (state.paused === true || state.selected === true))
                        state.resetChart();
-       }
+       };
 
        // get or create a chart state, given a DOM element
        NETDATA.chartState = function(element) {
                        $(element).data('netdata-state-object', state);
                }
                return state;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // Library functions
                }
                else if(typeof callback === "function")
                        callback();
-       }
+       };
 
        NETDATA._loadCSS = function(filename) {
                // don't use jQuery here
 
                if (typeof fileref !== 'undefined')
                        document.getElementsByTagName("head")[0].appendChild(fileref);
-       }
+       };
 
        NETDATA.colorHex2Rgb = function(hex) {
                // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
                        g: parseInt(result[2], 16),
                        b: parseInt(result[3], 16)
                } : null;
-       }
+       };
 
        NETDATA.colorLuminance = function(hex, lum) {
                // validate hex string
                }
 
                return rgb;
-       }
+       };
 
        NETDATA.guid = function() {
                function s4() {
                        }
 
                        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
-       }
+       };
 
        NETDATA.zeropad = function(x) {
                if(x > -10 && x < 10) return '0' + x.toString();
                else return x.toString();
-       }
+       };
 
        // user function to signal us the DOM has been
        // updated.
        NETDATA.updatedDom = function() {
                NETDATA.options.updated_dom = true;
-       }
+       };
 
        NETDATA.ready = function(callback) {
                NETDATA.options.pauseCallback = callback;
-       }
+       };
 
        NETDATA.pause = function(callback) {
                if(NETDATA.options.pause === true)
                        callback();
                else
                        NETDATA.options.pauseCallback = callback;
-       }
+       };
 
        NETDATA.unpause = function() {
                NETDATA.options.pauseCallback = null;
                NETDATA.options.updated_dom = true;
                NETDATA.options.pause = false;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
 
                                }, NETDATA.options.current.idle_between_charts);
                        }
                }
-       }
+       };
 
        // this is part of the parallel refresher
        // its cause is to refresh sequencially all the charts
        // it will call the parallel refresher back
        // as soon as it sees a chart that its chart library
        // is initialized
-       NETDATA.chartRefresher_unitialized = function() {
+       NETDATA.chartRefresher_uninitialized = function() {
                if(NETDATA.options.updated_dom === true) {
                        // the dom has been updated
                        // get the dom parts again
                        if(state.library.initialized === true)
                                NETDATA.chartRefresher();
                        else
-                               state.autoRefresh(NETDATA.chartRefresher_unitialized);
+                               state.autoRefresh(NETDATA.chartRefresher_uninitialized);
                }
-       }
+       };
 
        NETDATA.chartRefresherWaitTime = function() {
                return NETDATA.options.current.idle_parallel_loops;
-       }
+       };
 
        // the default refresher
        // it will create 2 sets of charts:
        // - the ones that can be refreshed in parallel
        // - the ones that depend on something else
        // the first set will be executed in parallel
-       // the second will be given to NETDATA.chartRefresher_unitialized()
+       // the second will be given to NETDATA.chartRefresher_uninitialized()
        NETDATA.chartRefresher = function() {
                if(NETDATA.options.pause === true) {
                        // console.log('auto-refresher is paused');
                        setTimeout(NETDATA.chartRefresher,
                                NETDATA.chartRefresherWaitTime());
                }
-       }
+       };
 
        NETDATA.parseDom = function(callback) {
                NETDATA.options.last_page_scroll = new Date().getTime();
                }
 
                if(typeof callback === 'function') callback();
-       }
+       };
 
        // this is the main function - where everything starts
        NETDATA.start = function() {
                // bootstrap modal switching
                $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
                $('.modal').on('shown.bs.modal', NETDATA.onscroll);
-               
+
                NETDATA.parseDom(NETDATA.chartRefresher);
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // peity
                if(state.peity_options.stroke !== state.chartColors()[0]) {
                        state.peity_options.stroke = state.chartColors()[0];
                        if(state.chart.chart_type === 'line')
-                               state.peity_options.fill = '#FFF';
+                               state.peity_options.fill = NETDATA.themes.current.background;
                        else
                                state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance);
                }
 
                $(state.peity_instance).peity('line', state.peity_options);
                return true;
-       }
+       };
 
        NETDATA.peityChartCreate = function(state, data) {
                state.peity_instance = document.createElement('div');
 
                var self = $(state.element);
                state.peity_options = {
-                       stroke: '#000',
+                       stroke: NETDATA.themes.current.foreground,
                        strokeWidth: self.data('peity-strokewidth') || 1,
                        width: state.chartWidth(),
                        height: state.chartHeight(),
-                       fill: '#000'
+                       fill: NETDATA.themes.current.foreground
                };
 
                NETDATA.peityChartUpdate(state, data);
                return true;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // sparkline
                }
                else {
                        NETDATA.chartLibraries.sparkline.enabled = false;
-                       if(typeof callback === "function") 
+                       if(typeof callback === "function")
                                callback();
                }
        };
 
                $(state.element_chart).sparkline(data.result, state.sparkline_options);
                return true;
-       }
+       };
 
        NETDATA.sparklineChartCreate = function(state, data) {
                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')?'#FFF':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;
                var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
                var xvalues = self.data('sparkline-xvalues') || undefined;
                var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
-               var xvalues = self.data('sparkline-xvalues') || undefined;
                var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
                var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
                var disableInteraction = self.data('sparkline-disableinteraction') || false;
                smooth: false
        };
 
+       NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
+               if(after < state.netdata_first)
+                       after = state.netdata_first;
+
+               if(before > state.netdata_last)
+                       before = state.netdata_last;
+
+               state.setMode('zoom');
+               state.globalSelectionSyncStop();
+               state.globalSelectionSyncDelay();
+               state.dygraph_user_action = true;
+               state.dygraph_force_zoom = true;
+               state.updateChartPanOrZoom(after, before);
+               NETDATA.globalPanAndZoom.setMaster(state, after, before);
+       };
+
        NETDATA.dygraphSetSelection = function(state, t) {
                if(typeof state.dygraph_instance !== 'undefined') {
                        var r = state.calculateRowForTime(t);
                }
 
                return true;
-       }
+       };
 
        NETDATA.dygraphClearSelection = function(state, t) {
                if(typeof state.dygraph_instance !== 'undefined') {
                        state.dygraph_instance.clearSelection();
                }
                return true;
-       }
+       };
 
        NETDATA.dygraphSmoothInitialize = function(callback) {
                $.ajax({
                        if(typeof callback === "function")
                                callback();
                });
-       }
+       };
 
        NETDATA.dygraphInitialize = function(callback) {
                if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
                                labels: data.result.labels,
                                labelsDivWidth: state.chartWidth() - 70,
                                visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
-               }
+               };
 
-               if(state.current.name !== 'auto') {
+               if(state.dygraph_force_zoom === true) {
+                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                               state.log('dygraphChartUpdate() forced zoom update');
+
+                       options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
+                       options.valueRange = null;
+                       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');
                }
                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                state.log('dygraphChartUpdate() strict update');
 
-                       options.dateWindow = null;
+                       options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
                        options.valueRange = null;
+                       options.isZoomedIgnoreProgrammaticZoom = true;
                }
 
                if(state.dygraph_smooth_eligible === true) {
                }
 
                dygraph.updateOptions(options);
+
                state.dygraph_last_rendered = new Date().getTime();
                return true;
        };
                        stepPlot: self.data('dygraph-stepplot') || false,
 
                        // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
-                       strokeBorderColor: self.data('dygraph-strokebordercolor') || 'white',
+                       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,
 
                        drawAxis: self.data('dygraph-drawaxis') || true,
                        axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
-                       axisLineColor: self.data('dygraph-axislinecolor') || '#CCC',
+                       axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis,
                        axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
 
                        drawGrid: self.data('dygraph-drawgrid') || true,
                        drawYGrid: self.data('dygraph-drawygrid') || undefined,
                        gridLinePattern: self.data('dygraph-gridlinepattern') || null,
                        gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
-                       gridLineColor: self.data('dygraph-gridlinecolor') || '#DDD',
+                       gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid,
 
                        maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
                        sigFigs: self.data('dygraph-sigfigs') || null,
                                return '';
                        },
                        drawCallback: function(dygraph, is_initial) {
-                               if(state.current.name !== 'auto') {
-                                       if(NETDATA.options.debug.dygraph === true)
-                                               state.log('dygraphDrawCallback()');
-
-                                       var first = state.data.first_entry * 1000;
-                                       var last = state.data.last_entry * 1000;
+                               if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
+                                       state.dygraph_user_action = false;
 
                                        var x_range = dygraph.xAxisRange();
                                        var after = Math.round(x_range[0]);
                                        var before = Math.round(x_range[1]);
 
-                                       if(before <= last && after >= first)
+                                       if(NETDATA.options.debug.dygraph === true)
+                                               state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
+
+                                       if(before <= state.netdata_last && after >= state.netdata_first)
                                                state.updateChartPanOrZoom(after, before);
                                }
                        },
 
                                state.globalSelectionSyncStop();
                                state.globalSelectionSyncDelay();
+                               state.setMode('zoom');
 
-                               if(state.updateChartPanOrZoom(minDate, maxDate) === false) {
-                                       // we should not zoom that much
-                                       state.dygraph_instance.updateOptions({
-                                               dateWindow: null,
-                                               valueRange: null
-                                       });
-                               }
+                               // refresh it to the greatest possible zoom level
+                               state.dygraph_user_action = true;
+                               state.dygraph_force_zoom = true;
+                               state.updateChartPanOrZoom(minDate, maxDate);
                        },
                        highlightCallback: function(event, x, points, row, seriesName) {
                                if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                state.log('interactionModel.mousedown()');
 
+                                       state.dygraph_user_action = true;
                                        state.globalSelectionSyncStop();
 
                                        if(NETDATA.options.debug.dygraph === true)
                                                state.log('interactionModel.mousemove()');
 
                                        if(context.isPanning) {
+                                               state.dygraph_user_action = true;
                                                state.globalSelectionSyncStop();
                                                state.globalSelectionSyncDelay();
                                                state.setMode('pan');
                                                Dygraph.movePan(event, dygraph, context);
                                        }
                                        else if(context.isZooming) {
+                                               state.dygraph_user_action = true;
                                                state.globalSelectionSyncStop();
                                                state.globalSelectionSyncDelay();
                                                state.setMode('zoom');
                                                state.log('interactionModel.mouseup()');
 
                                        if (context.isPanning) {
+                                               state.dygraph_user_action = true;
                                                state.globalSelectionSyncDelay();
                                                Dygraph.endPan(event, dygraph, context);
                                        }
                                        else if (context.isZooming) {
+                                               state.dygraph_user_action = true;
                                                state.globalSelectionSyncDelay();
                                                Dygraph.endZoom(event, dygraph, context);
                                        }
                                                state.log('interactionModel.mousewheel()');
 
                                        // Take the offset of a mouse event on the dygraph canvas and
-                                       // convert it to a pair of percentages from the bottom left. 
+                                       // convert it to a pair of percentages from the bottom left.
                                        // (Not top left, bottom is where the lower value is.)
                                        function offsetToPercentage(g, offsetX, offsetY) {
                                                // This is calculating the pixel offset of the leftmost date.
                                        }
 
                                        if(event.altKey || event.shiftKey) {
+                                               state.dygraph_user_action = true;
+
                                                state.globalSelectionSyncStop();
                                                state.globalSelectionSyncDelay();
 
                                                var after = new_x_range[0];
                                                var before = new_x_range[1];
 
-                                               var first = (state.data.first_entry + state.data.view_update_every) * 1000;
-                                               var last = (state.data.last_entry + state.data.view_update_every) * 1000;
+                                               var first = state.netdata_first + state.data_update_every;
+                                               var last = state.netdata_last + state.data_update_every;
 
                                                if(before > last) {
                                                        after -= (before - last);
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                state.log('interactionModel.touchstart()');
 
+                                       state.dygraph_user_action = true;
                                        state.setMode('zoom');
                                        state.pauseChart();
 
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                state.log('interactionModel.touchmove()');
 
+                                       state.dygraph_user_action = true;
                                        Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
 
                                        state.dygraph_last_touch_move = new Date().getTime();
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                state.log('interactionModel.touchend()');
 
+                                       state.dygraph_user_action = true;
                                        Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
 
                                        // if it didn't move, it is a selection
                state.dygraph_instance = new Dygraph(state.element_chart,
                        data.result.data, state.dygraph_options);
 
+               state.dygraph_force_zoom = false;
+               state.dygraph_user_action = false;
                state.dygraph_last_rendered = new Date().getTime();
                return true;
        };
                }
                else // stacked
                        state.morris_instance = new Morris.Area(state.morris_options);
-               
+
                return true;
        };
 
        NETDATA.percentFromValueMax = function(value, max) {
                if(value === null) value = 0;
                if(max < value) max = value;
-               
+
                var pcent = 0;
                if(max !== 0) {
                        pcent = Math.round(value * 100 / max);
                }
 
                return pcent;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // easy-pie-chart
                                        NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
                                })
                                .fail(function() {
+                                       NETDATA.chartLibraries.easypiechart.enabled = false;
                                        NETDATA.error(100, NETDATA.easypiechart_js);
                                })
                                .always(function() {
        };
 
        NETDATA.easypiechartSetSelection = function(state, t) {
-               if(t < state.data_after || t > state.data_before)
+               if(state.timeIsVisible(t) !== true)
                        return NETDATA.easypiechartClearSelection(state);
 
-               var slot = Math.floor(state.data.result.length - 1 - (t - state.data_after) / state.data_update_every);
+               var slot = state.calculateRowForTime(t);
                if(slot < 0 || slot >= state.data.result.length)
                        return NETDATA.easypiechartClearSelection(state);
 
                        state.easyPieChartEvent = {
                                timer: null,
                                value: 0,
-                               pcent: 0,
+                               pcent: 0
                        };
                }
 
-               var value = state.data.result[slot];
+               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);
 
                        state.easyPieChartEvent.timer = setTimeout(function() {
                                state.easyPieChartEvent.timer = null;
                                state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
-                       }, 100);
+                       }, NETDATA.options.current.charts_selection_animation_delay);
                }
 
                return true;
                var value = data.result[0];
                var max = self.data('easypiechart-max-value') || null;
                var adjust = self.data('easypiechart-adjust') || null;
-               
+
                if(max === null) {
                        max = data.max;
                        state.easyPieChartMax = null;
 
                chart.easyPieChart({
                        barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
-                       trackColor: self.data('easypiechart-trackcolor') || '#f0f0f0',
-                       scaleColor: self.data('easypiechart-scalecolor') || '#dfe0e0',
+                       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,
                        lineCap: self.data('easypiechart-linecap') || 'round',
                        lineWidth: self.data('easypiechart-linewidth') || stroke,
                        animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
                        easing: self.data('easypiechart-easing') || undefined
                });
-               
+
                // when we just re-create the chart
                // do not animate the first update
                var animate = true;
                                        NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
                                })
                                .fail(function() {
+                                       NETDATA.chartLibraries.gauge.enabled = false;
                                        NETDATA.error(100, NETDATA.gauge_js);
                                })
                                .always(function() {
                        speed = 1000000000;
                else if(typeof status === 'number')
                        speed = status;
-               
+
                state.gauge_instance.animationSpeed = speed;
                state.___gaugeOld__.speed = speed;
-       }
+       };
 
        NETDATA.gaugeSet = function(state, value, min, max) {
                if(typeof value !== 'number') value = 0;
                if(typeof max !== 'number') max = 0;
                if(value > max) max = value;
                if(value < min) min = value;
+               if(min > max) {
+                       var t = min;
+                       min = max;
+                       max = t;
+               }
+               else if(min == max)
+                       max = min + 1;
 
-               var dvalue = state.gauge_instance.displayedValue;
+               // gauge.js has an issue if the needle
+               // is smaller than min or larger than max
+               // when we set the new values
+               // the needle will go crazy
 
-               if(dvalue >= min && dvalue <= max) {
-                       state.gauge_instance.minValue = min;
-                       state.gauge_instance.maxValue = max;
-                       state.gauge_instance.set(value);
-               }
-               else {
-                       // the current display value of the gauge
-                       // is outside min-max.
-                       // we have to disable animation
-                       // otherwise, the gauge will go crazy
-                       var old_speed = state.___gaugeOld__.speed;
-                       NETDATA.gaugeAnimation(state, false);
-                       state.gauge_instance.minValue = min;
-                       state.gauge_instance.maxValue = max;
-                       state.gauge_instance.set(value);
-                       NETDATA.gaugeAnimation(state, old_speed);
-               }
+               // to prevent it, we always feed it
+               // with a percentage, so that the needle
+               // is always between min and max
+               var pcent = (value - min) * 100 / (max - min);
+
+               // these should never happen
+               if(pcent < 0) pcent = 0;
+               if(pcent > 100) pcent = 100;
+
+               state.gauge_instance.set(pcent);
 
                state.___gaugeOld__.value = value;
                state.___gaugeOld__.min = min;
        };
 
        NETDATA.gaugeSetSelection = function(state, t) {
-               if(t < state.data_after || t > state.data_before)
+               if(state.timeIsVisible(t) !== true)
                        return NETDATA.gaugeClearSelection(state);
 
-               var slot = Math.floor(state.data.result.length - 1 - (t - state.data_after) / state.data_update_every);
+               var slot = state.calculateRowForTime(t);
                if(slot < 0 || slot >= state.data.result.length)
                        return NETDATA.gaugeClearSelection(state);
 
                        };
                }
 
-               var value = state.data.result[slot];
+               var value = state.data.result[state.data.result.length - 1 - slot];
                var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
                var min = 0;
 
                        state.gaugeEvent.timer = setTimeout(function() {
                                state.gaugeEvent.timer = null;
                                NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
-                       }, 100);
+                       }, NETDATA.options.current.charts_selection_animation_delay);
                }
 
                return true;
 
        NETDATA.gaugeChartCreate = function(state, data) {
                var self = $(state.element);
-               var chart = $(state.element_chart);
+               // var chart = $(state.element_chart);
 
                var value = data.result[0];
                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 strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke;
+               var startColor = self.data('gauge-start-color') || state.chartColors()[0];
+               var stopColor = self.data('gauge-stop-color') || void 0;
+               var generateGradient = self.data('gauge-generate-gradient') || false;
+
                if(max === null) {
                        max = data.max;
                        state.gaugeMax = null;
                        pointer: {
                                length: 0.8,                    // 0.9 The radius of the inner circle
                                strokeWidth: 0.035,             // The rotation offset
-                               color: '#C0C0C0'                // Fill color
+                               color: pointerColor             // Fill color
                        },
-                       colorStart: state.chartColors()[0],     // Colors
-                       colorStop: void 0,                      // just experiment with them
-                       strokeColor: '#F0F0F0',         // to see which ones work best for you
+                       colorStart: startColor,         // Colors
+                       colorStop: stopColor,           // just experiment with them
+                       strokeColor: strokeColor,       // to see which ones work best for you
                        limitMax: true,
-                       generateGradient: false,
-                       gradientType: 0,
-                       percentColors: [
-                               [0.0, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 0))],
-                               [0.1, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 1))],
-                               [0.2, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 2))],
-                               [0.3, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 3))],
-                               [0.4, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 4))],
-                               [0.5, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 5))],
-                               [0.6, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 6))],
-                               [0.7, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 7))],
-                               [0.8, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 8))],
-                               [0.9, NETDATA.colorLuminance(state.chartColors()[0], (lum_d * 10) - (lum_d * 9))],
-                               [1.0, state.chartColors()[0]]]
+                       generateGradient: generateGradient,
+                       gradientType: 0
                };
 
+               if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
+                       options.percentColors = [
+                               [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
+                               [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
+                               [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
+                               [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
+                               [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
+                               [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
+                               [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
+                               [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
+                               [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
+                               [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
+                               [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
+               }
+
                state.gauge_canvas = document.createElement('canvas');
                state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
                state.gauge_canvas.className = 'gaugeChart';
                        minLabel: null,
                        maxLabel: null
                };
-               
+
+               // we will always feed a percentage
+               state.gauge_instance.minValue = 0;
+               state.gauge_instance.maxValue = 100;
+
                NETDATA.gaugeAnimation(state, animate);
                NETDATA.gaugeSet(state, value, 0, max);
                NETDATA.gaugeSetLabels(state, value, 0, max);
                        },
                        setSelection: NETDATA.dygraphSetSelection,
                        clearSelection:  NETDATA.dygraphClearSelection,
+                       toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'ssvcomma'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        resize: null,
                        setSelection: undefined, //function(state, t) { return true; },
                        clearSelection: undefined, //function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'datatable'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'csvjsonarray'; },
                        resize: null,
                        setSelection: undefined, // function(state, t) { return true; },
                        clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        resize: null,
                        setSelection: NETDATA.easypiechartSetSelection,
                        clearSelection: NETDATA.easypiechartClearSelection,
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                        legend: function(state) { return null; },
                        autoresize: function(state) { return false; },
                        max_updates_to_recreate: function(state) { return 5000; },
-                       track_colors: function(state) { return false; },
+                       track_colors: function(state) { return true; },
                        pixels_per_point: function(state) { return 3; },
                        aspect_ratio: 100
                },
                        resize: null,
                        setSelection: NETDATA.gaugeSetSelection,
                        clearSelection: NETDATA.gaugeClearSelection,
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                        legend: function(state) { return null; },
                        autoresize: function(state) { return false; },
                        max_updates_to_recreate: function(state) { return 5000; },
-                       track_colors: function(state) { return false; },
+                       track_colors: function(state) { return true; },
                        pixels_per_point: function(state) { return 3; },
                        aspect_ratio: 70
                }
                NETDATA.chartLibraries[library].url = url;
                NETDATA.chartLibraries[library].initialized = true;
                NETDATA.chartLibraries[library].enabled = true;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // Start up
                {
                        url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
                        isAlreadyLoaded: function() {
+                               // check if bootstrap is loaded
                                if(typeof $().emulateTransitionEnd == 'function')
                                        return true;
                                else {
 
        NETDATA.requiredCSS = [
                {
-                       url: NETDATA.serverDefault + 'css/bootstrap.min.css',
+                       url: NETDATA.themes.current.bootstrap_css,
                        isAlreadyLoaded: function() {
                                if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
                                        return true;
                        isAlreadyLoaded: function() { return false; }
                },
                {
-                       url: NETDATA.dashboard_css,
+                       url: NETDATA.themes.current.dashboard_css,
                        isAlreadyLoaded: function() { return false; }
                },
                {
                .fail(function() {
                        alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
                })
-       }
+       };
 
        NETDATA.loadRequiredCSS = function(index) {
                if(index >= NETDATA.requiredCSS.length)
 
                NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
                NETDATA.loadRequiredCSS(++index);
-       }
+       };
 
        NETDATA.errorReset();
        NETDATA.loadRequiredCSS(0);
 
        NETDATA._loadjQuery(function() {
                NETDATA.loadRequiredJs(0, function() {
+                       if(typeof $().emulateTransitionEnd == 'function') {
+                               // bootstrap is not available
+                               NETDATA.options.current.show_help = false;
+                       }
+
                        if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
                                if(NETDATA.options.debug.main_loop === true)
                                        console.log('starting chart refresh thread');