]> 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 5ec0390..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
        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
                        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();
                        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;
                        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;
                                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();
+                       var i, len;
 
                        var c = $(this.element).data('colors');
                        // this.log('read colors: ' + c);
-                       if(typeof c !== 'undefined' && c !== null) {
+                       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)');
                                }
                                        var added = 0;
 
                                        while(added < 20) {
-                                               for(var i = 0, len = c.length; i < len ; i++) {
+                                               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;
                                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;
 
                        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)
                                        // 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);
                                        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)
                                error('data download failed for url: ' + that.data_url);
                        })
                        .always(function() {
-                               this._updating = false;
+                               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();
                                });
                        }
-               }
+               };
 
                // ============================================================================================================
                // 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);
                        if(typeof callback === "function")
                                callback();
                });
-       }
+       };
 
        NETDATA.dygraphInitialize = function(callback) {
                if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
                        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,
                                                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.
                }
                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() {
                        state.easyPieChartEvent = {
                                timer: null,
                                value: 0,
-                               pcent: 0,
+                               pcent: 0
                        };
                }
 
                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;
 
        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') || '#C0C0C0';
-               var strokeColor = self.data('gauge-stroke-color') || '#F0F0F0';
+               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;
                        strokeColor: strokeColor,       // to see which ones work best for you
                        limitMax: true,
                        generateGradient: generateGradient,
-                       gradientType: 0,
-                       percentColors: [
+                       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.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)]]
-               };
+                               [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
+               }
 
                state.gauge_canvas = document.createElement('canvas');
                state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
                        minLabel: null,
                        maxLabel: null
                };
-               
+
                // we will always feed a percentage
                state.gauge_instance.minValue = 0;
                state.gauge_instance.maxValue = 100;
                        },
                        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'; },
                        resize: null,
                        setSelection: NETDATA.gaugeSetSelection,
                        clearSelection: NETDATA.gaugeClearSelection,
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                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');