]> arthur.barton.de Git - netdata.git/blobdiff - web/index.html
allow custom dashboards to link dimensions to DOM elements by name or id; fixes ...
[netdata.git] / web / index.html
index 375f91fa8ba8ff2cfa0f9cf4c7c68f98e2671793..f24389687878d32df7ca6057cf5abfb1c36b1a6d 100644 (file)
     <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
     <meta name="author" content="costa@tsaousis.gr">
 
-    <link rel="shortcut icon" href="images/seo-performance-multi-size.ico">
+    <!-- <link rel="shortcut icon" href="images/seo-performance-multi-size.ico"> -->
 
-    <link rel="apple-touch-icon" href="images/seo-performance-72.png">
-    <link rel="apple-touch-icon" sizes="72x72" href="images/seo-performance-72.png">
-    <link rel="apple-touch-icon" sizes="114x114" href="images/seo-performance-114.png">
+    <!-- <link rel="apple-touch-icon" href="images/seo-performance-72.png"> -->
+    <!-- <link rel="apple-touch-icon" sizes="72x72" href="images/seo-performance-72.png"> -->
+    <!-- <link rel="apple-touch-icon" sizes="114x114" href="images/seo-performance-114.png"> -->
 
     <!-- <link rel="icon" type="image/png" sizes="512x512" href="images/seo-performance-512.png"> -->
     <!-- <link rel="icon" type="image/png" sizes="256x256" href="images/seo-performance-256.png"> -->
@@ -30,7 +30,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACNklEQVRYhcXXv2tUQRAH8M+FEIJISBHCIWIhIQSUILERi4AiiqCggiIiomAjlhaC4j+ghYWISgqNohZaCBZBC8Ei8QdEUCutFBsxCBqDYkgci/cunkfuJffjJQPD8mZm5/vd2WV2HzlJ0Bs8CvrywsgCHwy+BpGOg0sJfjj4nYKX9FdwKG9gwZlgtgK8pLOpPxfw1mCoCnClDgWtzQTvCEYWCV7SkWAlFBoEb8dlDKBF8t2bMWUSH/AHr3CiEfz5CPUusPJLkRCdk5ZqyeqUrQv4R7E5TwK7M3zTeIKduRAIitiWEfIY69GdCwGcRFuG/xqONRkzkaA7+J5x+MaDtWmHvJ4HgeEM8Nn0bridfv9HoOFyBAdwJCPkqqTzHWwUaz7wgeBHxupfBKuCj2W25mxBsCGYyAB/FxTT27HcPlyep64tCLbjKbqqhLzBlgKfF8pVE4FgRXABI+ioEnYfOyzcFWsCbg+OV+xlpU4ER4O+4HVwL51b3xYEXcGu4Ao+YQhr5gmdxHmsQyfG0b/YxbWmLfRWmnxa0s06VbTMCpnBS9zFzQKTwR5cXCzwHIE02Sl8wSZsRI/kgLVJqjSd+t9LVjiG1diPszhdK3A5gR48k5zYMTwscC59sfT799CYKvA8EttbSeXgTr3gJQKl91kR+yTlvyG5uUbLYh9gb+ovltkb6qYtNSRo3kOygsBSzGlKsubf43USWLYK5CLLXoFWyU/CtzLbVDpW2n+m40yN9ukqdvAX9ac/EIgOapcAAAAASUVORK5CYII=">
 
     <meta property="og:locale" content="en_US" />
-    <meta property="og:image" content="http://my-netdata.io/images/post.png"/>
+    <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/>
     <meta property="og:url" content="http://my-netdata.io/"/>
     <meta property="og:type" content="website"/>
     <meta property="og:site_name" content="netdata"/>
             width: 233px !important;
         }
     }
-    
+
     @media (min-width: 1360px) {
         .container {
             padding-left: 3% !important;
 
     // enable registry updates
     var netdataRegistry = true;
-    
+
     // --------------------------------------------------------------------
     // urlOptions
 
         hash: '#',
         theme: null,
         help: null,
+        update_always: false,
         pan_and_zoom: false,
         after: 0,
         before: 0,
         alarm_unique_id: 0,
         alarm_id: 0,
         alarm_event_id: 0,
+
         hasProperty: function(property) {
             // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
             return typeof this[property] !== 'undefined';
-        }
-    };
-
-    function netdataPanAndZoomCallback(status, after, before) {
-        urlOptions.pan_and_zoom = status;
-        urlOptions.after = after;
-        urlOptions.before = before;
-        netdataHashUpdate();
-    }
+        },
 
-    function netdataHashUpdate() {
-        history.replaceState(null, '', netdataHash());
-    }
+        genHash: function() {
+            var hash = urlOptions.hash;
 
-    function netdataHash() {
-        var hash = urlOptions.hash;
+            if(urlOptions.pan_and_zoom === true) {
+                hash += ';after='  + urlOptions.after.toString() +
+                        ';before=' + urlOptions.before.toString();
+            }
 
-        if(urlOptions.pan_and_zoom === true) {
-            hash += ';after='  + urlOptions.after.toString() +
-                    ';before=' + urlOptions.before.toString();
-        }
+            if(urlOptions.theme !== null)
+                hash += ';theme=' + urlOptions.theme.toString();
 
-        if(urlOptions.theme !== null)
-            hash += ';theme=' + urlOptions.theme.toString();
+            if(urlOptions.help !== null)
+                hash += ';help=' + urlOptions.help.toString();
 
-        if(urlOptions.help !== null)
-            hash += ';help=' + urlOptions.help.toString();
+            if(urlOptions.update_always === true)
+                hash += ';update_always=true';
 
-        return hash;
-    }
+            return hash;
+        },
 
-    function netdataHashParse() {
-        var variables = document.location.hash.split(';');
-        var len = variables.length;
-        while(len--) {
-            if(len !== 0) {
-                var p = variables[len].split('=');
-                if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
-                    urlOptions[p[0]] = decodeURIComponent(p[1]);
+        parseHash: function() {
+            var variables = document.location.hash.split(';');
+            var len = variables.length;
+            while(len--) {
+                if(len !== 0) {
+                    var p = variables[len].split('=');
+                    if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
+                        urlOptions[p[0]] = decodeURIComponent(p[1]);
+                }
+                else {
+                    if(variables[len].length > 0)
+                        urlOptions.hash = variables[len];
+                }
             }
-            else {
-                if(variables[len].length > 0)
-                    urlOptions.hash = variables[len];
+
+            var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom', 'update_always' ];
+            len = booleans.length;
+            while(len--) {
+                if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1)
+                    urlOptions[booleans[len]] = true;
+                else
+                    urlOptions[booleans[len]] = false;
             }
-        }
 
-        var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom' ];
-        len = booleans.length;
-        while(len--) {
-            if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1)
-                urlOptions[booleans[len]] = true;
+            if(urlOptions.before > 0 && urlOptions.after > 0) {
+                urlOptions.pan_and_zoom = true;
+                urlOptions.nowelcome = true;
+            }
             else
-                urlOptions[booleans[len]] = false;
-        }
+                urlOptions.pan_and_zoom = false;
 
-        if(urlOptions.before > 0 && urlOptions.after > 0) {
-            urlOptions.pan_and_zoom = true;
-            urlOptions.nowelcome = true;
+            // console.log(urlOptions);
+        },
+
+        hashUpdate: function() {
+            history.replaceState(null, '', urlOptions.genHash());
+        },
+
+        netdataPanAndZoomCallback: function(status, after, before) {
+            urlOptions.pan_and_zoom = status;
+            urlOptions.after = after;
+            urlOptions.before = before;
+            urlOptions.hashUpdate();
         }
-        else
-            urlOptions.pan_and_zoom = false;
 
-        // console.log(urlOptions);
-    }
+    };
 
-    netdataHashParse();
+    urlOptions.parseHash();
 
     // --------------------------------------------------------------------
     // check options that should be processed before loading netdata.js
-    
+
     function loadLocalStorage(name) {
         var ret = null;
 
         if(url.indexOf('#') !== -1)
             url = url.substring(0, url.indexOf('#'));
 
-        var hash = netdataHash();
+        var hash = urlOptions.genHash();
 
         // console.log('netdataURL: ' + url + hash);
 
 
         else if(document.location.toString().startsWith('https://') && url.toString().startsWith('http://'))
             error = 'can\'t check';
-        
+
         var finalURL = netdataURL(url);
 
         setTimeout(function() {
     // scroll to a section, without changing the browser history
 
     function scrollToId(hash) {
-        if(hash && hash != '') {
+        if(hash && hash != '' && document.getElementById(hash) !== null) {
             var offset = $('#' + hash).offset();
             if(typeof offset !== 'undefined')
                 $('html, body').animate({ scrollTop: offset.top }, 0);
                 return '';
         },
 
+        contextValueRange: function(id) {
+            if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].valueRange !== 'undefined')
+                return netdataDashboard.context[id].valueRange;
+            else
+                return '[null, null]';
+        },
+
         contextHeight: function(id, def) {
             if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].height !== 'undefined')
                 return def * netdataDashboard.context[id].height;
 
             case 'apache':
             case 'exim':
+            case 'dovecot':
+            case 'hddtemp':
+            case 'ipfs':
             case 'memcached':
             case 'mysql':
             case 'named':
             case 'nut':
             case 'phpfpm':
             case 'postfix':
+            case 'postgres':
             case 'redis':
             case 'retroshare':
-            case 'ipfs':
             case 'smawebbox':
-            case 'squid':
             case 'snmp':
+            case 'squid':
             case 'tomcat':
                 chart.menu = chart.type;
                 chart.menu_pattern = tmp;
 
         if(typeof charts['system.swap'] !== 'undefined')
             head += '<div style="margin-right: 10px;" data-netdata="system.swap"'
-            + ' data-dimensions="free"'
+            + ' data-dimensions="used"'
             + ' data-append-options="percentage"'
             + ' data-chart-library="easypiechart"'
-            + ' data-title="Free Swap"'
+            + ' data-title="Used Swap"'
             + ' data-units="%"'
             + ' data-easypiechart-max-value="100"'
             + ' data-width="8%"'
 
         if(typeof charts['system.ram'] !== 'undefined')
             head += '<div style="margin-right: 10px;" data-netdata="system.ram"'
-            + ' data-dimensions="cached|free"'
+            + ' data-dimensions="used|buffers"'
             + ' data-append-options="percentage"'
             + ' data-chart-library="easypiechart"'
-            + ' data-title="Available RAM"'
+            + ' data-title="Used RAM"'
             + ' data-units="%"'
             + ' data-easypiechart-max-value="100"'
             + ' data-width="8%"'
                     chtml += netdataDashboard.contextInfo(chart.context) + '<div id="chart_' + NETDATA.name2id(chart.id) + '" data-netdata="' + chart.id + '"'
                         + ' data-width="' + pcent_width.toString() + '%"'
                         + ' data-height="' + netdataDashboard.contextHeight(chart.context, options.chartsHeight).toString() + 'px"'
+                        + ' data-dygraph-valuerange="' + netdataDashboard.contextValueRange(chart.context) + '"'
                         + ' data-before="0"'
                         + ' data-after="-' + duration.toString() + '"'
                         + ' data-id="' + NETDATA.name2id(options.hostname + '/' + chart.id) + '"'
             html += mhead + shtml + '</div></div><hr role="separator"/>';
         }
 
+        sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/firehol/netdata/wiki/Add-more-charts-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more charts</a></li>';
+        sidebar += '<li class=""><a href="https://github.com/firehol/netdata/wiki/Add-more-alarms-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more alarms</a></li>';
+        sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + Math.round(data.history / 3600).toLocaleString() + ' ' + ((data.history == 3600)?'hour':'hours').toString() + ' of real-time history.</small></li>';
         sidebar += '</ul>';
         div.innerHTML = html;
         document.getElementById('sidebar').innerHTML = sidebar;
                 return;
             }
 
+            function alarmid4human(id) {
+                if(id === 0)
+                    return '-';
+
+                return id.toString();
+            }
+
             function timestamp4human(timestamp, space) {
+                if(timestamp === 0)
+                    return '-';
+
                 if(typeof space === 'undefined')
                     space = '&nbsp;';
 
                 seconds -= (minutes * 60);
 
                 var txt = '';
-                
+
                 if(hours > 1) txt += hours.toString() + options.space + options.hours;
                 else if(hours === 1) txt += hours.toString() + options.space + options.hour;
 
             }
 
             // find the proper family of each alarm
-            var now = new Date().getTime();
+            var now = Date.now();
             var x;
             var count_active = 0;
             var count_all = 0;
                             title: 'Event Date',
                             valign: 'middle',
                             titleTooltip: 'The date and time the even took place',
-                            switchable: false,
-                            sortable: true,
                             formatter: function(value, row, index) { return timestamp4human(value, ' '); },
+                            align: 'center',
+                            valign: 'middle',
+                            switchable: false,
+                            sortable: true
                         },
                         {
                             field: 'hostname',
                             title: 'Host',
                             valign: 'middle',
                             titleTooltip: 'The host that generated this event',
+                            align: 'center',
+                            valign: 'middle',
                             visible: false,
                             sortable: true
                         },
                             field: 'unique_id',
                             title: 'Unique ID',
                             titleTooltip: 'The host unique ID for this event',
+                            formatter: function(value, row, index) { return alarmid4human(value); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'alarm_id',
                             title: 'Alarm ID',
                             titleTooltip: 'The ID of the alarm that generated this event',
+                            formatter: function(value, row, index) { return alarmid4human(value); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'alarm_event_id',
                             title: 'Alarm Event ID',
                             titleTooltip: 'The incremental ID of this event for the given alarm',
+                            formatter: function(value, row, index) { return alarmid4human(value); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'chart',
                             title: 'Chart',
                             titleTooltip: 'The chart the alarm is attached to',
+                            align: 'center',
                             valign: 'middle',
                             switchable: false,
                             sortable: true
                             field: 'family',
                             title: 'Family',
                             titleTooltip: 'The family of the chart the alarm is attached to',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             formatter: function(value, row, index) {
                                 return value.toString().replace(/_/g, ' ');
                             },
+                            align: 'center',
                             valign: 'middle',
                             switchable: false,
                             sortable: true
                             field: 'old_value',
                             title: 'Old Value',
                             titleTooltip: 'The value of the alarm, just before this event',
+                            formatter: function(value, row, index) {
+                                return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString();
+                            },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             title: 'Value',
                             titleTooltip: 'The value of the alarm, that triggered this event',
                             formatter: function(value, row, index) {
-                                return ((value !== null)?Math.floor(value):'NaN').toString();
+                                return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString();
                             },
                             align: 'right',
                             valign: 'middle',
                             field: 'units',
                             title: 'Units',
                             titleTooltip: 'The units of the value of the alarm',
+                            align: 'left',
                             valign: 'middle',
                             sortable: true
                         },
                             field: 'old_status',
                             title: 'Old Status',
                             titleTooltip: 'The status of the alarm, just before this event',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'status',
                             title: 'Status',
                             titleTooltip: 'The status of the alarm, that was set due to this event',
+                            align: 'center',
                             valign: 'middle',
                             switchable: false,
                             sortable: true
                             title: 'Last Duration',
                             titleTooltip: 'The duration the alarm was at its previous state, just before this event',
                             formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             title: 'Raised Duration',
                             titleTooltip: 'The duration the alarm was raised, just before this event',
                             formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'recipient',
                             title: 'Recipient',
                             titleTooltip: 'The recipient of this event',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'processed',
                             title: 'Processed Status',
                             titleTooltip: 'True when this event is processed',
+                            formatter: function(value, row, index) {
+                                if(value === true)
+                                    return 'DONE';
+                                else
+                                    return 'PENDING';
+                            },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'updated',
                             title: 'Updated Status',
                             titleTooltip: 'True when this event has been updated by another event',
+                            formatter: function(value, row, index) {
+                                if(value === true)
+                                    return 'UPDATED';
+                                else
+                                    return 'CURRENT';
+                            },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'updated_by_id',
                             title: 'Updated By ID',
                             titleTooltip: 'The unique ID of the event that obsoleted this one',
+                            formatter: function(value, row, index) { return alarmid4human(value); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'updates_id',
                             title: 'Updates ID',
                             titleTooltip: 'The unique ID of the event obsoleted because of this event',
+                            formatter: function(value, row, index) { return alarmid4human(value); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'exec',
                             title: 'Script',
                             titleTooltip: 'The script to handle the event notification',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             title: 'Script Run At',
                             titleTooltip: 'The date and time the script has been ran',
                             formatter: function(value, row, index) { return timestamp4human(value, ' '); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'exec_code',
                             title: 'Script Return Value',
                             titleTooltip: 'The return code of the script',
+                            formatter: function(value, row, index) {
+                                if(value === 0)
+                                    return 'OK (returned 0)';
+                                else
+                                    return 'FAILED (with code ' + value.toString() + ')';
+                            },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             title: 'Script Delay',
                             titleTooltip: 'The hysteresis of the notification',
                             formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             title: 'Script Delay Run At',
                             titleTooltip: 'The date and time the script should be run, after hysteresis',
                             formatter: function(value, row, index) { return timestamp4human(value, ' '); },
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'info',
                             title: 'Description',
                             titleTooltip: 'A short description of the alarm',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
                             field: 'source',
                             title: 'Alarm Source',
                             titleTooltip: 'The source of configuration of the alarm',
+                            align: 'center',
                             valign: 'middle',
                             visible: false,
                             sortable: true
     function notifyForUpdate(force) {
         versionLog('<p>checking for updates...</p>');
 
-        var now = new Date().getTime();
+        var now = Date.now();
 
         if(typeof force === 'undefined' || force !== true) {
             var last = loadLocalStorage('last_update_check');
         // ------------------------------------------------------------------------
 
         // callback for us to track PanAndZoom operations
-        NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback;
+        NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback;
 
         // let it run (update the charts)
         NETDATA.unpause();
             if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) {
                 urlOptions.hash = hash;
                 //console.log(urlOptions.hash);
-                netdataHashUpdate();
+                urlOptions.hashUpdate();
             }
             //else console.log('hash: not accepting ' + hash);
             //}
         $('#concurrent_refreshes').change(function()            { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); });
         $('#sync_selection').change(function()                  { NETDATA.setOption('sync_selection', $(this).prop('checked')); });
         $('#sync_pan_and_zoom').change(function()               { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); });
-        $('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
+        $('#stop_updates_when_focus_is_lost').change(function() {
+            urlOptions.update_always = !$(this).prop('checked');
+            urlOptions.hashUpdate();
+
+            NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
+        });
         $('#smooth_plot').change(function()                     { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
         $('#pan_and_zoom_data_padding').change(function()       { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
         $('#show_help').change(function()                       {
             urlOptions.help = $(this).prop('checked');
+            urlOptions.hashUpdate();
+
             NETDATA.setOption('show_help', urlOptions.help);
             netdataReload();
         });
         // it reloads the page
         $('#netdata_theme_control').change(function() {
             urlOptions.theme = $(this).prop('checked')?'slate':'white';
+            urlOptions.hashUpdate();
+
             if(setTheme(urlOptions.theme))
                 netdataReload();
         });
         });
 
         NETDATA.requiredJs.push({
-            url: NETDATA.serverDefault + 'dashboard_info.js?v20161002-1',
+            url: NETDATA.serverDefault + 'dashboard_info.js?v20161112-6',
             async: false,
             isAlreadyLoaded: function() { return false; }
         });
         if(isdemo()) {
             document.getElementById('masthead').style.display = 'block';
         }
+        else {
+            if(urlOptions.update_always === true)
+                NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
+        }
     }
 
     // our entry point
                             </div>
                             <hr/>
                             <div class="p">
-                                <h4>SHIFT + Mouse Wheel <small>(does not work on firefox and IE/Edge)</small></h4>
+                                <h4>SHIFT + Mouse Wheel</h4>
                                 While pressing the shift key and the mouse pointer is over the contents of a chart, scroll the mouse wheel to zoom in or out. This kind of zooming is aligned to center below the mouse pointer. The other charts will follow too.
                                 <br/>
                                 Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
                                         <td class="option-info"><strong>Which chart refresh policy to use?</strong><br/>
                                             <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequential</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequential if you work on an older/slower computer.</small>
                                         </td>
-                                        </tr>
+                                    </tr>
                                     <tr class="option-row" id="concurrent_refreshes_row">
-                                        <td class="option-control"></td>
-                                        <td class="option-info">
-                                            <table>
-                                            <tr class="option-row">
-                                            <td class="option-control">
-                                            <input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px">
-                                            </td>
-                                            <td class="option-info">
-                                            <strong>Shall we re-sync chart refreshes?</strong><br/>
+                                        <td class="option-control"><input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px"></td>
+                                        <td class="option-info"><strong>Shall we re-sync chart refreshes?</strong><br/>
                                             <small>When set to <b>Resync</b>, the dashboard will attempt to re-synchronize all the charts so that they are refreshed concurrently. When set to <b>Best Effort</b>, each chart may be refreshed with a little time difference to the others. Normally, the dashboard starts refreshing them in parallel, but depending on the speed of your computer and the network latencies, charts start having a slight time difference. Setting this to <b>Resync</b> will attempt to re-synchronize the charts on every update. Setting it to <b>Best Effort</b> may lower the pressure on your browser and the network.</small>
-                                            </td>
-                                            </tr>
-                                            </table>
                                         </td>
-                                        </tr>
+                                    </tr>
                                     <tr class="option-row">
                                         <td class="option-control"><input id="sync_selection" type="checkbox" checked data-toggle="toggle" data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td>
                                         <td class="option-info"><strong>Sync hover selection on all charts?</strong><br/>
     </div>
 </body>
 </html>
-<script type="text/javascript" src="dashboard.js?v20161002-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161208-1"></script>