]> 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 dcddbea..a2089d4
@@ -6,8 +6,12 @@
 // var netdataNoGoogleCharts = true;   // do not use google
 // var netdataNoMorris = true;                 // do not use morris
 // var netdataNoEasyPieChart = true;   // do not use easy pie chart
+// var netdataNoGauge = true;                  // do not use gauge.js
+// var netdataNoD3 = true;                             // do not use D3
+// var netdataNoC3 = true;                             // do not use C3
 // var netdataNoBootstrap = true;              // do not load bootstrap
 // var netdataDontStart = true;                        // do not start the thread to process the charts
+// var netdataErrorCallback = null;            // Callback function that will be invoked upon error
 //
 // 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
@@ -26,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 = '';
        // default URLs for all the external files we need
        // make them RELATIVE so that the whole thing can also be
        // installed under a web server
-       NETDATA.jQuery                  = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
+       NETDATA.jQuery                  = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
        NETDATA.peity_js                = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
        NETDATA.sparkline_js            = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
        NETDATA.easypiechart_js         = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
+       NETDATA.gauge_js                        = NETDATA.serverDefault + 'lib/gauge.min.js';
        NETDATA.dygraph_js              = NETDATA.serverDefault + 'lib/dygraph-combined.js';
        NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
        NETDATA.raphael_js              = NETDATA.serverDefault + 'lib/raphael-min.js';
        NETDATA.morris_js               = NETDATA.serverDefault + 'lib/morris.min.js';
+       NETDATA.d3_js                           = NETDATA.serverDefault + 'lib/d3.min.js';
+       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
                                                                                // rendering the chart that is panned or zoomed).
                                                                                // Used with .current.global_pan_sync_time
 
-               last_resized: 0,                                // the timestamp of the last resize request
+               last_resized: new Date().getTime(), // the timestamp of the last resize request
 
                crossDomainAjax: false,                 // enable this to request crossDomain AJAX
 
 
                        sync_pan_and_zoom: true,        // enable or disable pan and zoom sync
 
+                       pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
+
                        update_only_visible: true,      // enable or disable visibility management
 
                        parallel_refresher: true,       // enable parallel refresh of charts
 
                        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() { ; }
                },
 
                        focus:                          false,
                        visibility:             false,
                        chart_data_url:         false,
-                       chart_errors:           true,
+                       chart_errors:           false, // FIXME
                        chart_timing:           false,
                        chart_calls:            false,
                        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);
        if(NETDATA.options.debug.main_loop === true)
                console.log('welcome to NETDATA');
 
-       window.onresize = function(event) {
-               NETDATA.options.last_page_scroll = new Date().getTime();
+       NETDATA.onresize = function() {
                NETDATA.options.last_resized = new Date().getTime();
+               NETDATA.onscroll();
        };
 
-       window.onscroll = function(event) {
+       NETDATA.onscroll = function() {
+               // console.log('onscroll');
+
                NETDATA.options.last_page_scroll = new Date().getTime();
                if(NETDATA.options.targets === null) return;
 
                var targets = NETDATA.options.targets;
                var len = targets.length;
                while(len--) targets[len].isVisible();
-       }
+       };
+
+       window.onresize = NETDATA.onresize;
+       window.onscroll = NETDATA.onscroll;
 
        // ----------------------------------------------------------------------------------------------------------------
        // Error Handling
                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;
 
                        if(this.master !== null && this.master !== state)
-                               this.master.resetChart();
+                               this.master.resetChart(true, true);
 
                        var now = new Date().getTime();
                        this.master = state;
 
                // clear the master
                clearMaster: function() {
-                       if(NETDATA.options.current.sync_pan_and_zoom === false)
-                               return;
-
                        if(this.master !== null) {
-                               var state = this.master;
-                               this.master = null; // prevent infinite recursion
-                               this.seq = 0;
-                               state.resetChart();
-                               NETDATA.options.auto_refresher_stop_until = 0;
+                               var st = this.master;
+                               this.master = null;
+                               st.resetChart();
                        }
 
                        this.master = null;
                        this.seq = 0;
                        this.force_after_ms = null;
                        this.force_before_ms = null;
+                       NETDATA.options.auto_refresher_stop_until = 0;
                },
 
                // is the given state the master of the global
                        if(this.master === null || this.seq === 0)
                                return false;
 
-                       if(state.needsRecreation())
-                               return true;
+                       //if(state.needsRecreation())
+                       //      return true;
 
                        if(state.tm.pan_and_zoom_seq === this.seq)
                                return false;
 
                        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();
                        series: null
                };
 
-               this.chart_url = null;          // string - the url to download chart info
-               this.chart = null;                      // object - the chart as downloaded from the server
+               this.chart_url = null;                                          // string - the url to download chart info
+               this.chart = null;                                                      // object - the chart as downloaded from the server
+
+               this.title = self.data('title') || null;        // the title of the chart
+               this.units = self.data('units') || null;        // the units of the chart dimensions
+               this.append_options = self.data('append-options') || null;      // the units of the chart dimensions
 
-               this.validated = false;                 // boolean - has the chart been validated?
-               this.enabled = true;                    // boolean - is the chart enabled for refresh?
-               this.paused = false;                    // boolean - is the chart paused for any reason?
-               this.selected = false;          // boolean - is the chart shown a selection?
-               this.debug = false;                     // boolean - console.log() debug info about this chart
+               this.validated = false;                                         // boolean - has the chart been validated?
+               this.enabled = true;                                            // boolean - is the chart enabled for refresh?
+               this.paused = false;                                            // boolean - is the chart paused for any reason?
+               this.selected = false;                                          // boolean - is the chart shown a selection?
+               this.debug = false;                                                     // boolean - console.log() debug info about this chart
 
-               this.netdata_first = 0;                 // milliseconds - the first timestamp in netdata
-               this.netdata_last = 0;                  // milliseconds - the last timestamp in netdata
-               this.requested_after = null;    // milliseconds - the timestamp of the request after param
-               this.requested_before = null;   // milliseconds - the timestamp of the request before param
+               this.netdata_first = 0;                                         // milliseconds - the first timestamp in netdata
+               this.netdata_last = 0;                                          // milliseconds - the last timestamp in netdata
+               this.requested_after = null;                            // milliseconds - the timestamp of the request after param
+               this.requested_before = null;                           // milliseconds - the timestamp of the request before param
+               this.requested_padding = null;
+               this.view_after = 0;
+               this.view_before = 0;
 
                this.auto = {
                        name: 'auto',
 
                this.dimensions_visibility = new dimensionsVisibility(this);
 
+               this._updating = false;
+
                // ============================================================================================================
                // PRIVATE FUNCTIONS
 
                var createDOM = function() {
-                       if(that.enabled == false) return;
+                       if(that.enabled === false) return;
 
                        if(that.element_message !== null) that.element_message.innerHTML = '';
                        if(that.element_legend !== null) that.element_legend.innerHTML = '';
                        }
                        that.element_legend_childs.series = null;
 
-                       if(that.width !== 0)
+                       if(typeof(that.width) === 'string')
                                $(that.element).css('width', that.width);
-
-                       if(that.height !== 0)
-                               $(that.element).css('height', that.height);
+                       else if(typeof(that.width) === 'number')
+                               $(that.element).css('width', that.width + 'px');
+
+                       if(typeof(that.library.aspect_ratio) === 'undefined') {
+                               if(typeof(that.height) === 'string')
+                                       $(that.element).css('height', that.height);
+                               else if(typeof(that.height) === 'number')
+                                       $(that.element).css('height', that.height + 'px');
+                       }
+                       else {
+                               var w = that.element.offsetWidth;
+                               if(w === null || w === 0) {
+                                       // the div is hidden
+                                       // this is resize the chart when next viewed
+                                       that.tm.last_resized = 0;
+                               }
+                               else
+                                       $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px');
+                       }
 
                        if(NETDATA.chartDefaults.min_width !== null)
                                $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
                        that.tm.last_dom_created = new Date().getTime();
 
                        showLoading();
-               }
+               };
 
                /* init() private
-                * initialize state viariables
+                * initialize state variables
                 * destroy all (possibly) created state elements
                 * create the basic DOM for a chart
                 */
                var init = function() {
-                       if(that.enabled == false) return;
+                       if(that.enabled === false) return;
 
                        that.paused = false;
                        that.selected = false;
 
-                       that.chart_created = false;             // boolean - is the library.create() been called?
-                       that.updates_counter = 0;               // numeric - the number of refreshes made so far
-                       that.updates_since_last_creation = 0;
+                       that.chart_created = false;                     // boolean - is the library.create() been called?
+                       that.updates_counter = 0;                       // numeric - the number of refreshes made so far
+                       that.updates_since_last_unhide = 0;     // numeric - the number of refreshes made since the last time the chart was unhidden
+                       that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created
 
                        that.tm = {
                                last_initialized: 0,            // milliseconds - the timestamp it was last initialized
                                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() {
-                       // no chart yet
-                       if(that.chart_created === false) return;
+                       // hide it, if it is not already hidden
+                       if(isHidden() === true) return;
 
-                       // we should destroy it
-                       if(NETDATA.options.current.destroy_on_hide === true) {
-                               init();
-                               that.___chartIsHidden___ = undefined;
-                               return;
+                       if(that.chart_created === true) {
+                               // we should destroy it
+                               if(NETDATA.options.current.destroy_on_hide === true) {
+                                       init();
+                               }
+                               else {
+                                       showRendering();
+                                       that.element_chart.style.display = 'none';
+                                       if(that.element_legend !== null) that.element_legend.style.display = 'none';
+                                       that.tm.last_hidden = new Date().getTime();
+                               }
                        }
 
-                       // just hide it, if it is not already hidden
-                       if(typeof that.___chartIsHidden___ === 'undefined') {
-                               showRendering();
-                               that.element_chart.style.display = 'none';
-                               if(that.element_legend !== null) that.element_legend.style.display = 'none';
-                               that.___chartIsHidden___ = true;
-                       }
-               }
+                       that.___chartIsHidden___ = true;
+               };
 
                // unhide the chart, when it is visible - called from isVisible()
                var unhideChart = function() {
-                       if(typeof that.___chartIsHidden___ !== 'undefined') {
-                               that.element_chart.style.display = 'inline-block';
-                               if(that.element_legend !== null) that.element_legend.style.display = 'inline-block';
-                               that.___chartIsHidden___ = undefined;
+                       if(isHidden() === false) return;
+
+                       that.___chartIsHidden___ = undefined;
+                       that.updates_since_last_unhide = 0;
+
+                       if(that.chart_created === false) {
+                               // we need to re-initialize it, to show our background
+                               // logo in bootstrap tabs, until the chart loads
+                               init();
+                       }
+                       else {
+                               that.tm.last_unhidden = new Date().getTime();
+                               that.element_chart.style.display = '';
+                               if(that.element_legend !== null) that.element_legend.style.display = '';
                                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) {
+                       var status;
+
+                       if(canBeRendered() === false)
+                               return false;
+
+                       if(NETDATA.options.debug.chart_errors === true)
+                               status = that.library.update(that, data);
+                       else {
+                               try {
+                                       status = that.library.update(that, data);
+                               }
+                               catch(err) {
+                                       status = false;
+                               }
+                       }
+
+                       if(status === false) {
+                               error('chart failed to be updated as ' + that.library_name);
+                               return false;
+                       }
+
+                       return true;
+               };
+
+               // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
+               var callChartLibraryCreateSafely = function(data) {
+                       var status;
+
+                       if(canBeRendered() === false)
+                               return false;
+
+                       if(NETDATA.options.debug.chart_errors === true)
+                               status = that.library.create(that, data);
+                       else {
+                               try {
+                                       status = that.library.create(that, data);
+                               }
+                               catch(err) {
+                                       status = false;
+                               }
+                       }
+
+                       if(status === false) {
+                               error('chart failed to be created as ' + that.library_name);
+                               return false;
+                       }
+
+                       that.chart_created = true;
+                       that.updates_since_last_creation = 0;
+                       return true;
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
                // Chart Resize
                // a properly sized dom is available
                var resizeChart = function() {
                        if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
-                               that.tm.last_resized = new Date().getTime();
                                if(that.chart_created === false) return;
 
-                               if(that.needsRecreation())
+                               if(that.needsRecreation()) {
                                        init();
-
+                               }
                                else if(typeof that.library.resize === 'function') {
                                        that.library.resize(that);
 
 
                                        maxMessageFontSize();
                                }
+
+                               that.tm.last_resized = new Date().getTime();
                        }
-               }
+               };
 
                // this is the actual chart resize algorithm
                // it will:
 
                        var now = new Date().getTime();
                        NETDATA.options.last_page_scroll = now;
-                       NETDATA.options.last_resized = now;
                        NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
 
                        // force a resize
                                        };
 
                                // 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() {
+                       showMessageIcon('<i class="fa fa-warning"></i> empty');
                        that.legendUpdateDOM();
                        that.tm.last_autorefreshed = new Date().getTime();
-                       that.data_update_every = 30 * 1000;
-               }
+                       // that.data_update_every = 30 * 1000;
+                       //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) {
                                if(this.debug === true)
-                                       this.log('paused');
+                                       this.log('pauseChart()');
 
                                this.paused = true;
                        }
-               }
+               };
 
                this.unpauseChart = function() {
-                       if(this.paused) {
+                       if(this.paused === true) {
                                if(this.debug === true)
-                                       this.log('unpaused');
+                                       this.log('unpauseChart()');
 
                                this.paused = false;
                        }
-               }
+               };
 
-               this.resetChart = function() {
-                       if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible())
-                               NETDATA.globalPanAndZoom.clearMaster();
+               this.resetChart = function(dont_clear_master, dont_update) {
+                       if(this.debug === true)
+                               this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
 
-                       this.tm.pan_and_zoom_seq = 0;
+                       if(typeof dont_clear_master === 'undefined')
+                               dont_clear_master = false;
+
+                       if(typeof dont_update === 'undefined')
+                               dont_update = false;
+
+                       if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
+                               if(this.debug === true)
+                                       this.log('resetChart() diverting to clearMaster().');
+                               // this will call us back with master === true
+                               NETDATA.globalPanAndZoom.clearMaster();
+                               return;
+                       }
 
                        this.clearSelection();
 
+                       this.tm.pan_and_zoom_seq = 0;
+
                        this.setMode('auto');
                        this.current.force_update_at = 0;
                        this.current.force_before_ms = null;
                        this.paused = false;
                        this.selected = false;
                        this.enabled = true;
-                       this.debug = false;
+                       // this.debug = false;
 
                        // do not update the chart here
                        // or the chart will flip-flop when it is the master
                        // of a selection sync and another chart becomes
                        // the new master
-                       if(NETDATA.options.current.sync_pan_and_zoom === false && this.isVisible() === true)
+
+                       if(dont_update !== true && this.isVisible() === true) {
                                this.updateChart();
-               }
+                       }
+               };
 
                this.updateChartPanOrZoom = function(after, before) {
-                       if(before < after) return false;
-
-                       var min_duration = Math.round((this.chartWidth() / 30 * this.chart.update_every * 1000));
+                       var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
+                       var ret = true;
 
                        if(this.debug === true)
-                               this.log('requested duration of ' + ((before - after) / 1000).toString() + ' (' + after + ' - ' + before + '), minimum ' + min_duration / 1000);
+                               this.log(logme);
+
+                       if(before < after) {
+                               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);
 
-                       if((before - after) < min_duration) return false;
+                       // round the numbers
+                       after = Math.round(after);
+                       before = Math.round(before);
 
-                       var current_duration = this.data_before - this.data_after;
+                       // align them to update_every
+                       // stretching them further away
+                       after -= after % this.data_update_every;
+                       before += this.data_update_every - (before % this.data_update_every);
+
+                       // the final wanted duration
                        var wanted_duration = before - after;
-                       var tolerance = this.data_update_every * 2;
-                       var movement = Math.abs(before - this.data_before);
 
-                       if(this.debug === true)
-                               this.log('current duration: ' + current_duration / 1000 + ', wanted duration: ' + wanted_duration / 1000 + ', movement: ' + movement / 1000 + ', tolerance: ' + tolerance / 1000);
+                       // 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;
 
-                       if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) {
+                       // 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(wanted_duration < current_duration && wanted_duration < min_duration) {
                                if(this.debug === true)
-                                       this.log('IGNORED');
+                                       this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
+
+                               min_duration = this.fixed_min_duration;
 
+                               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 && ret === true) {
+                               if(this.debug === true)
+                                       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.current.name === 'auto') {
+                               this.log(logme + 'caller called me with mode: ' + this.current.name);
                                this.setMode('pan');
-
-                               if(this.debug === true)
-                                       this.log('updateChartPanOrZoom(): caller did not set proper mode');
                        }
 
+                       if(this.debug === true)
+                               this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
+
                        this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
                        this.current.force_after_ms = after;
                        this.current.force_before_ms = before;
                        NETDATA.globalPanAndZoom.setMaster(this, after, before);
-                       return true;
-               }
+                       return ret;
+               };
 
                this.legendFormatValue = function(value) {
                        if(value === null || value === 'undefined') return '-';
                        if(typeof value !== 'number') return value;
 
                        var abs = Math.abs(value);
-                       if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
-                       if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString();
+                       if(abs >= 1000) return (Math.round(value)).toLocaleString();
+                       if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString();
+                       if(abs >= 1   ) return (Math.round(value * 100) / 100).toLocaleString();
+                       if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
                        return (Math.round(value * 10000) / 10000).toLocaleString();
-               }
+               };
 
                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') {
                                this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
 
                        if(this.element_legend_childs.title_units)
-                               this.element_legend_childs.title_units.innerHTML = this.chart.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] = {
                                var label = state.element_legend_childs.series[name];
 
                                // create the dimension visibility tracking for this label
-                               var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
+                               state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
 
                                var rgb = NETDATA.colorHex2Rgb(color);
                                label.name.innerHTML = '<table class="netdata-legend-name-table-'
                                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 before;
-                       var after;
-                       if(NETDATA.globalPanAndZoom.isActive()) {
+                       var after, before, points_multiplier = 1;
+                       if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
+                               this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
+
                                after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
                                before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
-                               this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
+                               this.view_after = after * 1000;
+                               this.view_before = before * 1000;
+
+                               this.requested_padding = null;
+                               points_multiplier = 1;
+                       }
+                       else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
+                               this.tm.pan_and_zoom_seq = 0;
+
+                               before = Math.round(this.current.force_before_ms / 1000);
+                               after  = Math.round(this.current.force_after_ms / 1000);
+                               this.view_after = after * 1000;
+                               this.view_before = before * 1000;
+
+                               if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
+                                       this.requested_padding = Math.round((before - after) / 2);
+                                       after -= this.requested_padding;
+                                       before += this.requested_padding;
+                                       this.requested_padding *= 1000;
+                                       points_multiplier = 2;
+                               }
+
+                               this.current.force_before_ms = null;
+                               this.current.force_after_ms = null;
                        }
                        else {
-                               before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before;
-                               after  = this.current.force_after_ms  !== null ? Math.round(this.current.force_after_ms / 1000) : this.after;
                                this.tm.pan_and_zoom_seq = 0;
+
+                               before = this.before;
+                               after  = this.after;
+                               this.view_after = after * 1000;
+                               this.view_before = before * 1000;
+
+                               this.requested_padding = null;
+                               points_multiplier = 1;
                        }
 
                        this.requested_after = after * 1000;
                        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.toString();
+                       this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
                        this.data_url += "&group="   + this.method;
                        this.data_url += "&options=" + this.library.options(this);
                        this.data_url += '|jsonwrap';
                        if(NETDATA.options.current.eliminate_zero_dimensions === true)
                                this.data_url += '|nonzero';
 
+                       if(this.append_options !== null)
+                               this.data_url += '|' + this.append_options.toString();
+
                        if(after)
                                this.data_url += "&after="  + after.toString();
 
 
                        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('got data from netdata server');
+                               this.log('updateChartWithData() called.');
+
+                       this._updating = false;
 
                        // this may force the chart to be re-created
                        resizeChart();
 
                        this.data = data;
                        this.updates_counter++;
+                       this.updates_since_last_unhide++;
+                       this.updates_since_last_creation++;
 
                        var started = new Date().getTime();
 
                        // if the result is JSON, find the latest update-every
-                       if(typeof data === 'object') {
-                               if(typeof data.view_update_every !== 'undefined')
-                                       this.data_update_every = data.view_update_every * 1000;
-
-                               if(typeof data.after !== 'undefined')
-                                       this.data_after = data.after * 1000;
-
-                               if(typeof data.before !== 'undefined')
-                                       this.data_before = data.before * 1000;
-
-                               if(typeof data.first_entry !== 'undefined')
-                                       this.netdata_first = data.first_entry * 1000;
-
-                               if(typeof data.last_entry !== 'undefined')
-                                       this.netdata_last = data.last_entry * 1000;
-
-                               if(typeof data.points !== 'undefined')
-                                       this.data_points = data.points;
+                       this.data_update_every = data.view_update_every * 1000;
+                       this.data_after = data.after * 1000;
+                       this.data_before = data.before * 1000;
+                       this.netdata_first = data.first_entry * 1000;
+                       this.netdata_last = data.last_entry * 1000;
+                       this.data_points = data.points;
+                       data.state = this;
+
+                       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);
+                                       this.view_after = this.data_after;
+                               }
 
-                               data.state = this;
+                               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;
+                               }
+                       }
+                       else {
+                               this.view_after = this.data_after;
+                               this.view_before = this.data_before;
                        }
 
                        if(this.debug === true) {
                                this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
 
                                if(this.current.force_after_ms)
-                                       this.log('STATUS: forced   : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
+                                       this.log('STATUS: forced    : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
                                else
-                                       this.log('STATUS: forced: unset');
+                                       this.log('STATUS: forced    : unset');
 
-                               this.log('STATUS: requested: ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
-                               this.log('STATUS: rendered : ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
-                               this.log('STATUS: points   : ' + (this.data_points).toString());
+                               this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
+                               this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
+                               this.log('STATUS: rendered  : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
+                               this.log('STATUS: points    : ' + (this.data_points).toString());
                        }
 
                        if(this.data_points === 0) {
                                if(this.debug === true)
                                        this.log('updating chart...');
 
-                               this.updates_since_last_creation++;
-                               if(NETDATA.options.debug.chart_errors === true) {
-                                       this.library.update(this, data);
-                               }
-                               else {
-                                       try {
-                                               this.library.update(this, data);
-                                       }
-                                       catch(err) {
-                                               error('chart failed to be updated as ' + this.library_name);
-                                       }
-                               }
+                               if(callChartLibraryUpdateSafely(data) === false)
+                                       return;
                        }
                        else {
                                if(this.debug === true)
                                        this.log('creating chart...');
 
-                               if(NETDATA.options.debug.chart_errors === true) {
-                                       this.library.create(this, data);
-                                       this.chart_created = true;
-                                       this.updates_since_last_creation = 0;
-                               }
-                               else {
-                                       try {
-                                               this.library.create(this, data);
-                                               this.chart_created = true;
-                                               this.updates_since_last_creation = 0;
-                                       }
-                                       catch(err) {
-                                               error('chart failed to be created as ' + this.library_name);
-                                       }
-                               }
+                               if(callChartLibraryCreateSafely(data) === false)
+                                       return;
                        }
                        hideMessage();
                        this.legendShowLatestValues();
-                       NETDATA.globalSelectionSync.stop();
+                       if(this.selected === true)
+                               NETDATA.globalSelectionSync.stop();
 
                        // update the performance counters
                        var now = new Date().getTime();
 
                        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) {
                                return false;
                        }
 
+                       if(canBeRendered() === false) {
+                               if(typeof callback === 'function') callback();
+                               return false;
+                       }
+
                        if(this.chart === null) {
                                this.getChart(function() { that.updateChart(callback); });
-                               return;
+                               return false;
                        }
 
                        if(this.library.initialized === false) {
                                if(this.library.enabled === true) {
                                        this.library.initialize(function() { that.updateChart(callback); });
-                                       return;
+                                       return false;
                                }
                                else {
                                        error('chart library "' + this.library_name + '" is not available.');
                        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();
                        });
-               }
 
-               this.isVisible = function() {
+                       return true;
+               };
+
+               this.isVisible = function(nocache) {
+                       if(typeof nocache === 'undefined')
+                               nocache = false;
+
                        // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
 
                        // caching - we do not evaluate the charts visibility
                        // if the page has not been scrolled since the last check
-                       if(this.tm.last_visible_check > NETDATA.options.last_page_scroll) {
-                               if(this.debug === true)
-                                       this.log('isVisible: ' + this.___isVisible___);
-
+                       if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll)
                                return this.___isVisible___;
-                       }
 
                        this.tm.last_visible_check = new Date().getTime();
 
                        var ret = 0;
                        var tolerance = 0;
 
+                       if(x.width === 0 || x.height === 0) {
+                               hideChart();
+                               this.___isVisible___ = false;
+                               return this.___isVisible___;
+                       }
+
                        if(x.top < 0 && -x.top > x.height) {
                                // the chart is entirely above
                                ret = -x.top - x.height;
 
                                hideChart();
                                this.___isVisible___ = false;
-
-                               if(this.debug === true)
-                                       this.log('isVisible: ' + this.___isVisible___);
-
                                return this.___isVisible___;
                        }
                        else {
 
                                unhideChart();
                                this.___isVisible___ = true;
-
-                               if(this.debug === true)
-                                       this.log('isVisible: ' + this.___isVisible___);
-
                                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)
 
                        if(this.isAutoRefreshed() === true) {
                                // allow the first update, even if the page is not visible
-                               if(this.updates_counter && NETDATA.options.page_is_visible === false) {
+                               if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) {
                                        if(NETDATA.options.debug.focus === true || this.debug === true)
                                                this.log('canBeAutoRefreshed(): page does not have focus');
 
                        }
 
                        return false;
-               }
+               };
 
                this.autoRefresh = function(callback) {
                        if(this.canBeAutoRefreshed() === true) {
                                if(typeof callback !== 'undefined')
                                        callback();
                        }
-               }
+               };
 
                this._defaultsFromDownloadedChart = function(chart) {
                        this.chart = chart;
                        this.data_update_every = chart.update_every * 1000;
                        this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
                        this.tm.last_info_downloaded = new Date().getTime();
-               }
+
+                       if(this.title === null)
+                               this.title = chart.title;
+
+                       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
+               // to make sure no chart is in selected state
+               state.globalSelectionSyncStop();
+
+               // there are 2 possibilities here
+               // a. state is the global Pan and Zoom master
+               // b. state is not the global Pan and Zoom master
+               var master = true;
+               if(NETDATA.globalPanAndZoom.isMaster(state) === false)
+                       master = false;
+
+               // clear the global Pan and Zoom
+               // this will also refresh the master
+               // and unblock any charts currently mirroring the master
+               NETDATA.globalPanAndZoom.clearMaster();
+
+               // 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 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();
                NETDATA.options.updated_dom = false;
 
-               var targets = $('div[data-netdata]').filter(':visible');
+               var targets = $('div[data-netdata]'); //.filter(':visible');
 
                if(NETDATA.options.debug.main_loop === true)
                        console.log('DOM updated - there are ' + targets.length + ' charts on page.');
                }
 
                if(typeof callback === 'function') callback();
-       }
+       };
 
        // this is the main function - where everything starts
        NETDATA.start = function() {
                        }
                }
 
+               // bootstrap tab switching
+               $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll);
+
+               // bootstrap modal switching
+               $('.modal').on('hidden.bs.modal', NETDATA.onscroll);
+               $('.modal').on('shown.bs.modal', NETDATA.onscroll);
+
                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.sparkline_options.height = state.chartHeight();
 
                $(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;
                var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
                var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
                var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
-               var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.chart.units;
+               var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units;
                var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
                var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
                var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
                        highlightColor: highlightColor,
                        tooltipContainer: tooltipContainer,
                        tooltipClassname: tooltipClassname,
-                       tooltipChartTitle: state.chart.title,
+                       tooltipChartTitle: state.title,
                        tooltipFormat: tooltipFormat,
                        tooltipPrefix: tooltipPrefix,
                        tooltipSuffix: tooltipSuffix,
                };
 
                $(state.element_chart).sparkline(data.result, state.sparkline_options);
+               return true;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                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) {
                var dygraph = state.dygraph_instance;
 
                if(typeof dygraph === 'undefined')
-                       NETDATA.dygraphChartCreate(state, data);
+                       return NETDATA.dygraphChartCreate(state, data);
 
                // when the chart is not visible, and hidden
                // if there is a window resize, dygraph detects
                                labels: data.result.labels,
                                labelsDivWidth: state.chartWidth() - 70,
                                visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
-               }
+               };
 
-               if(state.current.name === 'pan') {
+               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;
        };
 
        NETDATA.dygraphChartCreate = function(state, data) {
                        showRangeSelector: self.data('dygraph-showrangeselector') || false,
                        showRoller: self.data('dygraph-showroller') || false,
 
-                       title: self.data('dygraph-title') || state.chart.title,
+                       title: self.data('dygraph-title') || state.title,
                        titleHeight: self.data('dygraph-titleheight') || 19,
 
                        legend: self.data('dygraph-legend') || 'always', // 'onmouseover',
                        showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
                        hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
 
-                       ylabel: state.chart.units,
+                       ylabel: state.units,
                        yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
 
                        // the function to plot the chart
                        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,
                                }
                        },
                        legendFormatter: function(data) {
-                               var g = data.dygraph;
-                               var html;
                                var elements = state.element_legend_childs;
 
                                // if the hidden div is not there
                                // we are not managing the legend
                                if(elements.hidden === null) return;
 
-                               if (typeof data.x === 'undefined') {
-                                       //state.legendReset();
-                               }
-                               else {
+                               if (typeof data.x !== 'undefined') {
                                        state.legendSetDate(data.x);
                                        var i = data.series.length;
                                        while(i--) {
                                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)
                                // here we calculate the time t based on the row number selected
                                // which is ok
                                var t = state.data_after + row * state.data_update_every;
-                               // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
+                               // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
 
-                               state.globalSelectionSync(t);
+                               state.globalSelectionSync(x);
 
                                // fix legend zIndex using the internal structures of dygraph legend module
                                // this works, but it is a hack!
                                        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);
                                        }
                                dblclick: function(event, dygraph, context) {
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                state.log('interactionModel.dblclick()');
-
-                                       state.globalSelectionSyncStop();
-                                       NETDATA.globalPanAndZoom.clearMaster();
-                                       state.resetChart();
+                                       NETDATA.resetAllCharts(state);
                                },
                                mousewheel: function(event, dygraph, context) {
                                        if(NETDATA.options.debug.dygraph === true || state.debug === true)
                                                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
                                        if(typeof state.dygraph_last_touch_end !== 'undefined') {
                                                if(state.dygraph_last_touch_move === 0) {
                                                        var dt = now - state.dygraph_last_touch_end;
-                                                       if(dt <= NETDATA.options.current.double_click_speed) {
-                                                               state.globalSelectionSyncStop();
-                                                               NETDATA.globalPanAndZoom.clearMaster();
-                                                               state.resetChart();
-                                                       }
+                                                       if(dt <= NETDATA.options.current.double_click_speed)
+                                                               NETDATA.resetAllCharts(state);
                                                }
                                        }
 
                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;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
 
        NETDATA.morrisChartUpdate = function(state, data) {
                state.morris_instance.setData(data.result.data);
+               return true;
        };
 
        NETDATA.morrisChartCreate = function(state, data) {
                }
                else // stacked
                        state.morris_instance = new Morris.Area(state.morris_options);
+
+               return true;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                $(state.element_chart).raphael(data.result, {
                        width: state.chartWidth(),
                        height: state.chartHeight()
-               })
+               });
+
+               return false;
        };
 
        NETDATA.raphaelChartCreate = function(state, data) {
                $(state.element_chart).raphael(data.result, {
                        width: state.chartWidth(),
                        height: state.chartHeight()
-               })
+               });
+
+               return false;
+       };
+
+       // ----------------------------------------------------------------------------------------------------------------
+       // C3
+
+       NETDATA.c3Initialize = function(callback) {
+               if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
+
+                       // C3 requires D3
+                       if(!NETDATA.chartLibraries.d3.initialized) {
+                               if(NETDATA.chartLibraries.d3.enabled) {
+                                       NETDATA.d3Initialize(function() {
+                                               NETDATA.c3Initialize(callback);
+                                       });
+                               }
+                               else {
+                                       NETDATA.chartLibraries.c3.enabled = false;
+                                       if(typeof callback === "function")
+                                               callback();
+                               }
+                       }
+                       else {
+                               NETDATA._loadCSS(NETDATA.c3_css);
+
+                               $.ajax({
+                                       url: NETDATA.c3_js,
+                                       cache: true,
+                                       dataType: "script"
+                               })
+                               .done(function() {
+                                       NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
+                               })
+                               .fail(function() {
+                                       NETDATA.chartLibraries.c3.enabled = false;
+                                       NETDATA.error(100, NETDATA.c3_js);
+                               })
+                               .always(function() {
+                                       if(typeof callback === "function")
+                                               callback();
+                               });
+                       }
+               }
+               else {
+                       NETDATA.chartLibraries.c3.enabled = false;
+                       if(typeof callback === "function")
+                               callback();
+               }
+       };
+
+       NETDATA.c3ChartUpdate = function(state, data) {
+               state.c3_instance.destroy();
+               return NETDATA.c3ChartCreate(state, data);
+
+               //state.c3_instance.load({
+               //      rows: data.result,
+               //      unload: true
+               //});
+
+               //return true;
+       };
+
+       NETDATA.c3ChartCreate = function(state, data) {
+
+               state.element_chart.id = 'c3-' + state.uuid;
+               // console.log('id = ' + state.element_chart.id);
+
+               state.c3_instance = c3.generate({
+                       bindto: '#' + state.element_chart.id,
+                       size: {
+                               width: state.chartWidth(),
+                               height: state.chartHeight()
+                       },
+                       color: {
+                               pattern: state.chartColors()
+                       },
+                       data: {
+                               x: 'time',
+                               rows: data.result,
+                               type: (state.chart.chart_type === 'line')?'spline':'area-spline'
+                       },
+                       axis: {
+                               x: {
+                                       type: 'timeseries',
+                                       tick: {
+                                               format: function(x) {
+                                                       return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
+                                               }
+                                       }
+                               }
+                       },
+                       grid: {
+                               x: {
+                                       show: true
+                               },
+                               y: {
+                                       show: true
+                               }
+                       },
+                       point: {
+                               show: false
+                       },
+                       line: {
+                               connectNull: false
+                       },
+                       transition: {
+                               duration: 0
+                       },
+                       interaction: {
+                               enabled: true
+                       }
+               });
+
+               // console.log(state.c3_instance);
+
+               return true;
+       };
+
+       // ----------------------------------------------------------------------------------------------------------------
+       // D3
+
+       NETDATA.d3Initialize = function(callback) {
+               if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
+                       $.ajax({
+                               url: NETDATA.d3_js,
+                               cache: true,
+                               dataType: "script"
+                       })
+                       .done(function() {
+                               NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
+                       })
+                       .fail(function() {
+                               NETDATA.chartLibraries.d3.enabled = false;
+                               NETDATA.error(100, NETDATA.d3_js);
+                       })
+                       .always(function() {
+                               if(typeof callback === "function")
+                                       callback();
+                       });
+               }
+               else {
+                       NETDATA.chartLibraries.d3.enabled = false;
+                       if(typeof callback === "function")
+                               callback();
+               }
+       };
+
+       NETDATA.d3ChartUpdate = function(state, data) {
+               return false;
+       };
+
+       NETDATA.d3ChartCreate = function(state, data) {
+               return false;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
        NETDATA.googleChartUpdate = function(state, data) {
                var datatable = new google.visualization.DataTable(data.result);
                state.google_instance.draw(datatable, state.google_options);
+               return true;
        };
 
        NETDATA.googleChartCreate = function(state, data) {
                        //width: state.chartWidth(),
                        //height: state.chartHeight(),
                        lineWidth: 1,
-                       title: state.chart.title,
+                       title: state.title,
                        fontSize: 11,
                        hAxis: {
                        //      title: "Time of Day",
                                }
                        },
                        vAxis: {
-                               title: state.chart.units,
+                               title: state.units,
                                viewWindowMode: 'pretty',
                                minValue: -0.1,
                                maxValue: 0.1,
                }
 
                state.google_instance.draw(datatable, state.google_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);
+                       if(pcent === 0 && value > 0) pcent = 1;
+               }
+
+               return pcent;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                                        NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
                                })
                                .fail(function() {
+                                       NETDATA.chartLibraries.easypiechart.enabled = false;
                                        NETDATA.error(100, NETDATA.easypiechart_js);
                                })
                                .always(function() {
        };
 
        NETDATA.easypiechartClearSelection = function(state) {
-               var chart = $(state.element_chart);
-               state.easyPieChartLabel.innerHTML = '';
-               chart.data('easyPieChart').update(0);
+               if(typeof state.easyPieChartEvent !== 'undefined') {
+                       if(state.easyPieChartEvent.timer !== null)
+                               clearTimeout(state.easyPieChartEvent.timer);
+
+                       state.easyPieChartEvent.timer = null;
+               }
+
+               if(state.isAutoRefreshed() === true && state.data !== null) {
+                       NETDATA.easypiechartChartUpdate(state, state.data);
+               }
+               else {
+                       state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
+                       state.easyPieChart_instance.update(0);
+               }
+               state.easyPieChart_instance.enableAnimation();
+
                return true;
        };
 
        NETDATA.easypiechartSetSelection = function(state, t) {
-               var chart = $(state.element_chart);
-
-               if(t < state.data_after || t > state.data_before)
+               if(state.timeIsVisible(t) !== true)
                        return NETDATA.easypiechartClearSelection(state);
 
-               var slot = 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);
 
-               var value = state.data.result[slot];
-               var max = state.data.max;
-               var pcent = Math.round(value * 100 / max);
+               if(typeof state.easyPieChartEvent === 'undefined') {
+                       state.easyPieChartEvent = {
+                               timer: null,
+                               value: 0,
+                               pcent: 0
+                       };
+               }
 
+               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.value = value;
+               state.easyPieChartEvent.pcent = pcent;
                state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
-               chart.data('easyPieChart').update(pcent);
+
+               if(state.easyPieChartEvent.timer === null) {
+                       state.easyPieChart_instance.disableAnimation();
+
+                       state.easyPieChartEvent.timer = setTimeout(function() {
+                               state.easyPieChartEvent.timer = null;
+                               state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
+                       }, NETDATA.options.current.charts_selection_animation_delay);
+               }
 
                return true;
        };
 
        NETDATA.easypiechartChartUpdate = function(state, data) {
-               var chart = $(state.element_chart);
-
-               var value = data.result[0];
-               var max = data.max;
-               var pcent = Math.round(value * 100 / max);
+               var value, max, pcent;
 
-               // console.log('value = ' + value + ' max = ' + max + ' pcent = ' + pcent);
+               if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshed() === false) {
+                       value = null;
+                       max = 0;
+                       pcent = 0;
+               }
+               else {
+                       value = data.result[0];
+                       max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
+                       pcent = NETDATA.percentFromValueMax(value, max);
+               }
 
                state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
-               chart.data('easyPieChart').update(pcent);
+               state.easyPieChart_instance.update(pcent);
+               return true;
        };
 
        NETDATA.easypiechartChartCreate = function(state, data) {
                var chart = $(state.element_chart);
 
                var value = data.result[0];
-               var max = data.max;
-               var pcent = Math.round(value * 100 / max);
+               var max = self.data('easypiechart-max-value') || null;
+               var adjust = self.data('easypiechart-adjust') || null;
+
+               if(max === null) {
+                       max = data.max;
+                       state.easyPieChartMax = null;
+               }
+               else
+                       state.easyPieChartMax = max;
+
+               var pcent = NETDATA.percentFromValueMax(value, max);
 
-               // console.log('value = ' + value + ' max = ' + max + ' pcent = ' + pcent);
                chart.data('data-percent', pcent);
 
-               var size = Math.min(state.chartWidth(), state.chartHeight());
-               var stroke = size / 15;
-               if(stroke < 3) stroke = 3;
+               var size;
+               switch(adjust) {
+                       case 'width': size = state.chartHeight(); break;
+                       case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break;
+                       case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break;
+                       case 'height':
+                       default: size = state.chartWidth(); break;
+               }
+               state.element.style.width = size + 'px';
+               state.element.style.height = size + 'px';
+
+               var stroke = Math.floor(size / 22);
+               if(stroke < 3) stroke = 2;
 
-               var valuefontsize = Math.floor((size * 2 / 3) / 6);
-               var valuetop = Math.round((size - valuefontsize - 10) / 2);
+               var valuefontsize = Math.floor((size * 2 / 3) / 5);
+               var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2);
                state.easyPieChartLabel = document.createElement('span');
                state.easyPieChartLabel.className = 'easyPieChartLabel';
                state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
                state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
                state.element_chart.appendChild(state.easyPieChartLabel);
 
-               var titlefontsize = Math.floor((size * 2 / 3) / 10);
-               var titletop = Math.round(valuetop - titlefontsize - (size / 20));
+               var titlefontsize = Math.round(valuefontsize * 1.6 / 3);
+               var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40));
                state.easyPieChartTitle = document.createElement('span');
                state.easyPieChartTitle.className = 'easyPieChartTitle';
-               state.easyPieChartTitle.innerHTML = state.chart.name;
+               state.easyPieChartTitle.innerHTML = state.title;
                state.easyPieChartTitle.style.fontSize = titlefontsize + 'px';
                state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px';
                state.easyPieChartTitle.style.top = titletop.toString() + 'px';
                state.element_chart.appendChild(state.easyPieChartTitle);
 
-               var unitfontsize = Math.floor((size * 2 / 3) / 10);
-               var unittop = Math.round(valuetop + valuefontsize + (size / 20));
+               var unitfontsize = Math.round(titlefontsize * 0.9);
+               var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40));
                state.easyPieChartUnits = document.createElement('span');
                state.easyPieChartUnits.className = 'easyPieChartUnits';
-               state.easyPieChartUnits.innerHTML = state.chart.units;
+               state.easyPieChartUnits.innerHTML = state.units;
                state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
                state.easyPieChartUnits.style.top = unittop.toString() + 'px';
                state.element_chart.appendChild(state.easyPieChartUnits);
 
                chart.easyPieChart({
                        barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
-                       trackColor: self.data('easypiechart-trackcolor') || '#f2f2f2',
-                       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,
                        trackWidth: self.data('easypiechart-trackwidth') || undefined,
                        size: self.data('easypiechart-size') || size,
                        rotate: self.data('easypiechart-rotate') || 0,
-                       animate: self.data('easypiechart-rotate') || {duration: data.view_update_every * 1000 / 2, enabled: true},
+                       animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true},
                        easing: self.data('easypiechart-easing') || undefined
                });
-               
-               chart.data('easyPieChart').update(pcent);
+
+               // when we just re-create the chart
+               // do not animate the first update
+               var animate = true;
+               if(typeof state.easyPieChart_instance !== 'undefined')
+                       animate = false;
+
+               state.easyPieChart_instance = chart.data('easyPieChart');
+               if(animate === false) state.easyPieChart_instance.disableAnimation();
+               state.easyPieChart_instance.update(pcent);
+               if(animate === false) state.easyPieChart_instance.enableAnimation();
+               return true;
+       };
+
+       // ----------------------------------------------------------------------------------------------------------------
+       // gauge.js
+
+       NETDATA.gaugeInitialize = function(callback) {
+               if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
+                       $.ajax({
+                               url: NETDATA.gauge_js,
+                               cache: true,
+                               dataType: "script"
+                       })
+                               .done(function() {
+                                       NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
+                               })
+                               .fail(function() {
+                                       NETDATA.chartLibraries.gauge.enabled = false;
+                                       NETDATA.error(100, NETDATA.gauge_js);
+                               })
+                               .always(function() {
+                                       if(typeof callback === "function")
+                                               callback();
+                               })
+               }
+               else {
+                       NETDATA.chartLibraries.gauge.enabled = false;
+                       if(typeof callback === "function")
+                               callback();
+               }
+       };
+
+       NETDATA.gaugeAnimation = function(state, status) {
+               var speed = 32;
+
+               if(typeof status === 'boolean' && status === false)
+                       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 min !== 'number') min = 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;
+
+               // 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
+
+               // 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;
+               state.___gaugeOld__.max = max;
+       };
+
+       NETDATA.gaugeSetLabels = function(state, value, min, max) {
+               if(state.___gaugeOld__.valueLabel !== value) {
+                       state.___gaugeOld__.valueLabel = value;
+                       state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
+               }
+               if(state.___gaugeOld__.minLabel !== min) {
+                       state.___gaugeOld__.minLabel = min;
+                       state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
+               }
+               if(state.___gaugeOld__.maxLabel !== max) {
+                       state.___gaugeOld__.maxLabel = max;
+                       state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
+               }
+       };
+
+       NETDATA.gaugeClearSelection = function(state) {
+               if(typeof state.gaugeEvent !== 'undefined') {
+                       if(state.gaugeEvent.timer !== null)
+                               clearTimeout(state.gaugeEvent.timer);
+
+                       state.gaugeEvent.timer = null;
+               }
+
+               if(state.isAutoRefreshed() === true && state.data !== null) {
+                       NETDATA.gaugeChartUpdate(state, state.data);
+               }
+               else {
+                       NETDATA.gaugeAnimation(state, false);
+                       NETDATA.gaugeSet(state, null, null, null);
+                       NETDATA.gaugeSetLabels(state, null, null, null);
+               }
+
+               NETDATA.gaugeAnimation(state, true);
+               return true;
+       };
+
+       NETDATA.gaugeSetSelection = function(state, t) {
+               if(state.timeIsVisible(t) !== true)
+                       return NETDATA.gaugeClearSelection(state);
+
+               var slot = state.calculateRowForTime(t);
+               if(slot < 0 || slot >= state.data.result.length)
+                       return NETDATA.gaugeClearSelection(state);
+
+               if(typeof state.gaugeEvent === 'undefined') {
+                       state.gaugeEvent = {
+                               timer: null,
+                               value: 0,
+                               min: 0,
+                               max: 0
+                       };
+               }
+
+               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.value = value;
+               state.gaugeEvent.max = max;
+               state.gaugeEvent.min = min;
+               NETDATA.gaugeSetLabels(state, value, min, max);
+
+               if(state.gaugeEvent.timer === null) {
+                       NETDATA.gaugeAnimation(state, false);
+
+                       state.gaugeEvent.timer = setTimeout(function() {
+                               state.gaugeEvent.timer = null;
+                               NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
+                       }, NETDATA.options.current.charts_selection_animation_delay);
+               }
+
+               return true;
+       };
+
+       NETDATA.gaugeChartUpdate = function(state, data) {
+               var value, min, max;
+
+               if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshed() === false) {
+                       value = 0;
+                       min = 0;
+                       max = 1;
+                       NETDATA.gaugeSetLabels(state, null, null, null);
+               }
+               else {
+                       value = data.result[0];
+                       min = 0;
+                       max = (state.gaugeMax === null)?data.max:state.gaugeMax;
+                       if(value > max) max = value;
+                       NETDATA.gaugeSetLabels(state, value, min, max);
+               }
+
+               NETDATA.gaugeSet(state, value, min, max);
+               return true;
+       };
+
+       NETDATA.gaugeChartCreate = function(state, data) {
+               var self = $(state.element);
+               // 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;
+               }
+               else
+                       state.gaugeMax = max;
+
+               var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
+               //switch(adjust) {
+               //      case 'width': width = height * ratio; break;
+               //      case 'height':
+               //      default: height = width / ratio; break;
+               //}
+               //state.element.style.width = width.toString() + 'px';
+               //state.element.style.height = height.toString() + 'px';
+
+               var lum_d = 0.05;
+
+               var options = {
+                       lines: 12,                                      // The number of lines to draw
+                       angle: 0.15,                            // The length of each line
+                       lineWidth: 0.44,                        // 0.44 The line thickness
+                       pointer: {
+                               length: 0.8,                    // 0.9 The radius of the inner circle
+                               strokeWidth: 0.035,             // The rotation offset
+                               color: pointerColor             // Fill color
+                       },
+                       colorStart: startColor,         // Colors
+                       colorStop: stopColor,           // just experiment with them
+                       strokeColor: strokeColor,       // to see which ones work best for you
+                       limitMax: true,
+                       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';
+               state.gauge_canvas.width  = width;
+               state.gauge_canvas.height = height;
+               state.element_chart.appendChild(state.gauge_canvas);
+
+               var valuefontsize = Math.floor(height / 6);
+               var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2);
+               state.gaugeChartLabel = document.createElement('span');
+               state.gaugeChartLabel.className = 'gaugeChartLabel';
+               state.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
+               state.gaugeChartLabel.style.top = valuetop.toString() + 'px';
+               state.element_chart.appendChild(state.gaugeChartLabel);
+
+               var titlefontsize = Math.round(valuefontsize / 2);
+               var titletop = 0;
+               state.gaugeChartTitle = document.createElement('span');
+               state.gaugeChartTitle.className = 'gaugeChartTitle';
+               state.gaugeChartTitle.innerHTML = state.title;
+               state.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
+               state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
+               state.gaugeChartTitle.style.top = titletop.toString() + 'px';
+               state.element_chart.appendChild(state.gaugeChartTitle);
+
+               var unitfontsize = Math.round(titlefontsize * 0.9);
+               state.gaugeChartUnits = document.createElement('span');
+               state.gaugeChartUnits.className = 'gaugeChartUnits';
+               state.gaugeChartUnits.innerHTML = state.units;
+               state.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
+               state.element_chart.appendChild(state.gaugeChartUnits);
+
+               state.gaugeChartMin = document.createElement('span');
+               state.gaugeChartMin.className = 'gaugeChartMin';
+               state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
+               state.element_chart.appendChild(state.gaugeChartMin);
+
+               state.gaugeChartMax = document.createElement('span');
+               state.gaugeChartMax.className = 'gaugeChartMax';
+               state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
+               state.element_chart.appendChild(state.gaugeChartMax);
+
+               // when we just re-create the chart
+               // do not animate the first update
+               var animate = true;
+               if(typeof state.gauge_instance !== 'undefined')
+                       animate = false;
+
+               state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
+
+               state.___gaugeOld__ = {
+                       value: value,
+                       min: 0,
+                       max: max,
+                       valueLabel: null,
+                       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);
+               NETDATA.gaugeAnimation(state, true);
+               return true;
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                        },
                        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'; },
+                       options: function(state) { return ''; },
+                       legend: function(state) { return null; },
+                       autoresize: function(state) { return false; },
+                       max_updates_to_recreate: function(state) { return 5000; },
+                       track_colors: function(state) { return false; },
+                       pixels_per_point: function(state) { return 3; }
+               },
+               "c3": {
+                       initialize: NETDATA.c3Initialize,
+                       create: NETDATA.c3ChartCreate,
+                       update: NETDATA.c3ChartUpdate,
+                       resize: null,
+                       setSelection: undefined, // function(state, t) { return true; },
+                       clearSelection: undefined, // function(state) { return true; },
+                       toolboxPanAndZoom: null,
+                       initialized: false,
+                       enabled: true,
+                       format: function(state) { return 'csvjsonarray'; },
+                       options: function(state) { return 'milliseconds'; },
+                       legend: function(state) { return null; },
+                       autoresize: function(state) { return false; },
+                       max_updates_to_recreate: function(state) { return 5000; },
+                       track_colors: function(state) { return false; },
+                       pixels_per_point: function(state) { return 15; }
+               },
+               "d3": {
+                       initialize: NETDATA.d3Initialize,
+                       create: NETDATA.d3ChartCreate,
+                       update: NETDATA.d3ChartUpdate,
+                       resize: null,
+                       setSelection: undefined, // function(state, t) { return true; },
+                       clearSelection: undefined, // function(state) { return true; },
+                       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'; },
-                       options: function(state) { if(state.chart.chart_type === 'stacked') return ''; else return 'min2max'; },
+                       options: function(state) { return 'absolute'; },
                        legend: function(state) { return null; },
                        autoresize: function(state) { return false; },
                        max_updates_to_recreate: function(state) { return 5000; },
-                       track_colors: function(state) { return false; },
-                       pixels_per_point: function(state) { return 3; }
+                       track_colors: function(state) { return true; },
+                       pixels_per_point: function(state) { return 3; },
+                       aspect_ratio: 100
+               },
+               "gauge": {
+                       initialize: NETDATA.gaugeInitialize,
+                       create: NETDATA.gaugeChartCreate,
+                       update: NETDATA.gaugeChartUpdate,
+                       resize: null,
+                       setSelection: NETDATA.gaugeSetSelection,
+                       clearSelection: NETDATA.gaugeClearSelection,
+                       toolboxPanAndZoom: null,
+                       initialized: false,
+                       enabled: true,
+                       format: function(state) { return 'array'; },
+                       options: function(state) { return 'absolute'; },
+                       legend: function(state) { return null; },
+                       autoresize: function(state) { return false; },
+                       max_updates_to_recreate: function(state) { return 5000; },
+                       track_colors: function(state) { return true; },
+                       pixels_per_point: function(state) { return 3; },
+                       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');