From 61aedf201c2c4bf0e5aa4db32e74f4d860b88593 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Tue, 19 Dec 2017 23:39:21 +0000 Subject: New upstream version 1.9.0+dfsg --- web/Makefile.am | 14 +- web/Makefile.in | 14 +- web/css/bootstrap-slider-10.0.0.min.css | 41 + web/dashboard.css | 13 +- web/dashboard.js | 2569 ++++++++++++++++++++------ web/dashboard.slate.css | 13 +- web/dashboard_info.js | 229 ++- web/index.html | 3031 +++++++++++++++++++++++++------ web/lib/bootstrap-slider-10.0.0.min.js | 5 + web/lib/fontawesome-all-5.0.1.min.js | 5 + web/lib/lz-string-1.4.4.min.js | 1 + web/lib/pako-1.0.6.min.js | 1 + web/netdata-swagger.json | 1 + web/netdata-swagger.yaml | 2 +- web/version.txt | 2 +- 15 files changed, 4750 insertions(+), 1191 deletions(-) create mode 100644 web/css/bootstrap-slider-10.0.0.min.css create mode 100644 web/lib/bootstrap-slider-10.0.0.min.js create mode 100644 web/lib/fontawesome-all-5.0.1.min.js create mode 100644 web/lib/lz-string-1.4.4.min.js create mode 100644 web/lib/pako-1.0.6.min.js (limited to 'web') diff --git a/web/Makefile.am b/web/Makefile.am index b587f5a17..02d893174 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -39,6 +39,7 @@ dist_webold_DATA = \ weblibdir=$(webdir)/lib dist_weblib_DATA = \ lib/bootstrap-3.3.7.min.js \ + lib/bootstrap-slider-10.0.0.min.js \ lib/bootstrap-table-1.11.0.min.js \ lib/bootstrap-table-export-1.11.0.min.js \ lib/bootstrap-toggle-2.2.2.min.js \ @@ -46,13 +47,16 @@ dist_weblib_DATA = \ lib/d3-3.5.17.min.js \ lib/dygraph-combined-dd74404.js \ lib/dygraph-smooth-plotter-dd74404.js \ + lib/fontawesome-all-5.0.1.min.js \ lib/gauge-1.3.2.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ - lib/perfect-scrollbar-0.6.15.min.js \ lib/jquery.peity-3.2.0.min.js \ lib/jquery.sparkline-2.1.2.min.js \ + lib/lz-string-1.4.4.min.js \ lib/morris-0.5.1.min.js \ + lib/pako-1.0.6.min.js \ + lib/perfect-scrollbar-0.6.15.min.js \ lib/raphael-2.2.4-min.js \ lib/tableExport-1.6.0.min.js \ $(NULL) @@ -63,8 +67,8 @@ dist_webcss_DATA = \ css/bootstrap-3.3.7.css \ css/bootstrap-theme-3.3.7.min.css \ css/bootstrap-slate-flat-3.3.7.css \ + css/bootstrap-slider-10.0.0.min.css \ css/bootstrap-toggle-2.2.2.min.css \ - css/font-awesome.min.css \ css/c3-0.4.11.min.css \ $(NULL) @@ -75,12 +79,6 @@ dist_webfonts_DATA = \ fonts/glyphicons-halflings-regular.ttf \ fonts/glyphicons-halflings-regular.woff \ fonts/glyphicons-halflings-regular.woff2 \ - fonts/FontAwesome.otf \ - fonts/fontawesome-webfont.eot \ - fonts/fontawesome-webfont.svg \ - fonts/fontawesome-webfont.ttf \ - fonts/fontawesome-webfont.woff \ - fonts/fontawesome-webfont.woff2 \ $(NULL) webimagesdir=$(webdir)/images diff --git a/web/Makefile.in b/web/Makefile.in index 42939d2a5..92f5c0887 100644 --- a/web/Makefile.in +++ b/web/Makefile.in @@ -340,6 +340,7 @@ dist_webold_DATA = \ weblibdir = $(webdir)/lib dist_weblib_DATA = \ lib/bootstrap-3.3.7.min.js \ + lib/bootstrap-slider-10.0.0.min.js \ lib/bootstrap-table-1.11.0.min.js \ lib/bootstrap-table-export-1.11.0.min.js \ lib/bootstrap-toggle-2.2.2.min.js \ @@ -347,13 +348,16 @@ dist_weblib_DATA = \ lib/d3-3.5.17.min.js \ lib/dygraph-combined-dd74404.js \ lib/dygraph-smooth-plotter-dd74404.js \ + lib/fontawesome-all-5.0.1.min.js \ lib/gauge-1.3.2.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ - lib/perfect-scrollbar-0.6.15.min.js \ lib/jquery.peity-3.2.0.min.js \ lib/jquery.sparkline-2.1.2.min.js \ + lib/lz-string-1.4.4.min.js \ lib/morris-0.5.1.min.js \ + lib/pako-1.0.6.min.js \ + lib/perfect-scrollbar-0.6.15.min.js \ lib/raphael-2.2.4-min.js \ lib/tableExport-1.6.0.min.js \ $(NULL) @@ -364,8 +368,8 @@ dist_webcss_DATA = \ css/bootstrap-3.3.7.css \ css/bootstrap-theme-3.3.7.min.css \ css/bootstrap-slate-flat-3.3.7.css \ + css/bootstrap-slider-10.0.0.min.css \ css/bootstrap-toggle-2.2.2.min.css \ - css/font-awesome.min.css \ css/c3-0.4.11.min.css \ $(NULL) @@ -376,12 +380,6 @@ dist_webfonts_DATA = \ fonts/glyphicons-halflings-regular.ttf \ fonts/glyphicons-halflings-regular.woff \ fonts/glyphicons-halflings-regular.woff2 \ - fonts/FontAwesome.otf \ - fonts/fontawesome-webfont.eot \ - fonts/fontawesome-webfont.svg \ - fonts/fontawesome-webfont.ttf \ - fonts/fontawesome-webfont.woff \ - fonts/fontawesome-webfont.woff2 \ $(NULL) webimagesdir = $(webdir)/images diff --git a/web/css/bootstrap-slider-10.0.0.min.css b/web/css/bootstrap-slider-10.0.0.min.css new file mode 100644 index 000000000..1cf68b5e7 --- /dev/null +++ b/web/css/bootstrap-slider-10.0.0.min.css @@ -0,0 +1,41 @@ +/*! ======================================================= + VERSION 10.0.0 +========================================================= */ +/*! ========================================================= + * bootstrap-slider.js + * + * Maintainers: + * Kyle Kemp + * - Twitter: @seiyria + * - Github: seiyria + * Rohit Kalkur + * - Twitter: @Rovolutionary + * - Github: rovolution + * + * ========================================================= + * + * bootstrap-slider is released under the MIT License + * Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ========================================================= */.slider{display:inline-block;vertical-align:middle;position:relative}.slider.slider-horizontal{width:210px;height:20px}.slider.slider-horizontal .slider-track{height:10px;width:100%;margin-top:-5px;top:50%;left:0}.slider.slider-horizontal .slider-selection,.slider.slider-horizontal .slider-track-low,.slider.slider-horizontal .slider-track-high{height:100%;top:0;bottom:0}.slider.slider-horizontal .slider-tick,.slider.slider-horizontal .slider-handle{margin-left:-10px}.slider.slider-horizontal .slider-tick.triangle,.slider.slider-horizontal .slider-handle.triangle{position:relative;top:50%;-ms-transform:translateY(-50%);transform:translateY(-50%);border-width:0 10px 10px 10px;width:0;height:0;border-bottom-color:#2e6da4;margin-top:0}.slider.slider-horizontal .slider-tick-container{white-space:nowrap;position:absolute;top:0;left:0;width:100%}.slider.slider-horizontal .slider-tick-label-container{white-space:nowrap;margin-top:20px}.slider.slider-horizontal .slider-tick-label-container .slider-tick-label{padding-top:4px;display:inline-block;text-align:center}.slider.slider-horizontal .tooltip{-ms-transform:translateX(-50%);transform:translateX(-50%)}.slider.slider-horizontal.slider-rtl .slider-track{left:initial;right:0}.slider.slider-horizontal.slider-rtl .slider-tick,.slider.slider-horizontal.slider-rtl .slider-handle{margin-left:initial;margin-right:-10px}.slider.slider-horizontal.slider-rtl .slider-tick-container{left:initial;right:0}.slider.slider-horizontal.slider-rtl .tooltip{-ms-transform:translateX(50%);transform:translateX(50%)}.slider.slider-vertical{height:210px;width:20px}.slider.slider-vertical .slider-track{width:10px;height:100%;left:25%;top:0}.slider.slider-vertical .slider-selection{width:100%;left:0;top:0;bottom:0}.slider.slider-vertical .slider-track-low,.slider.slider-vertical .slider-track-high{width:100%;left:0;right:0}.slider.slider-vertical .slider-tick,.slider.slider-vertical .slider-handle{margin-top:-10px}.slider.slider-vertical .slider-tick.triangle,.slider.slider-vertical .slider-handle.triangle{border-width:10px 0 10px 10px;width:1px;height:1px;border-left-color:#2e6da4;border-right-color:#2e6da4;margin-left:0;margin-right:0}.slider.slider-vertical .slider-tick-label-container{white-space:nowrap}.slider.slider-vertical .slider-tick-label-container .slider-tick-label{padding-left:4px}.slider.slider-vertical .tooltip{-ms-transform:translateY(-50%);transform:translateY(-50%)}.slider.slider-vertical.slider-rtl .slider-track{left:initial;right:25%}.slider.slider-vertical.slider-rtl .slider-selection{left:initial;right:0}.slider.slider-vertical.slider-rtl .slider-tick.triangle,.slider.slider-vertical.slider-rtl .slider-handle.triangle{border-width:10px 10px 10px 0}.slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label{padding-left:initial;padding-right:4px}.slider.slider-disabled .slider-handle{background-image:-webkit-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:-o-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:linear-gradient(to bottom,#dfdfdf 0,#bebebe 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf',endColorstr='#ffbebebe',GradientType=0)}.slider.slider-disabled .slider-track{background-image:-webkit-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:-o-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:linear-gradient(to bottom,#e5e5e5 0,#e9e9e9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5',endColorstr='#ffe9e9e9',GradientType=0);cursor:not-allowed}.slider input{display:none}.slider .tooltip.top{margin-top:-36px}.slider .tooltip-inner{white-space:nowrap;max-width:none}.slider .hide{display:none}.slider-track{position:absolute;cursor:pointer;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#f9f9f9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);border-radius:4px}.slider-selection{position:absolute;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-selection.tick-slider-selection{background-image:-webkit-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:-o-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:linear-gradient(to bottom,#8ac1ef 0,#82b3de 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef',endColorstr='#ff82b3de',GradientType=0)}.slider-track-low,.slider-track-high{position:absolute;background:transparent;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-handle{position:absolute;top:0;width:20px;height:20px;background-color:#337ab7;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7',endColorstr='#ff2e6da4',GradientType=0);filter:none;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);border:0 solid transparent}.slider-handle.round{border-radius:50%}.slider-handle.triangle{background:transparent none}.slider-handle.custom{background:transparent none}.slider-handle.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick{position:absolute;width:20px;height:20px;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;filter:none;opacity:.8;border:0 solid transparent}.slider-tick.round{border-radius:50%}.slider-tick.triangle{background:transparent none}.slider-tick.custom{background:transparent none}.slider-tick.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick.in-selection{background-image:-webkit-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:-o-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:linear-gradient(to bottom,#8ac1ef 0,#82b3de 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef',endColorstr='#ff82b3de',GradientType=0);opacity:1} \ No newline at end of file diff --git a/web/dashboard.css b/web/dashboard.css index 42ffa3ddb..79febaa4f 100644 --- a/web/dashboard.css +++ b/web/dashboard.css @@ -44,6 +44,8 @@ body { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -54,6 +56,8 @@ body { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -70,6 +74,8 @@ body { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -93,6 +99,8 @@ body { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* fix minimum scrollbar issue in firefox */ min-height: 99px; @@ -108,9 +116,9 @@ body { bottom: 0px; right: 0px; height: 15px; - width: 30px; + width: 20px; background-color: White; - font-size: 12px; + font-size: 15px; vertical-align: middle; line-height: 15px; cursor: ns-resize; @@ -286,6 +294,7 @@ body { white-space: nowrap; display: inline-block; cursor: pointer; + -webkit-print-color-adjust: exact; } .netdata-legend-value { diff --git a/web/dashboard.js b/web/dashboard.js index f119a5370..dc7f4ea32 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -45,6 +45,10 @@ * (default: null) */ /*global netdataServer *//* string, the URL of the netdata server to use * (default: the URL the page is hosted at) */ +/*global netdataServerStatic *//* string, the URL of the netdata server to use for static files + * (default: netdataServer) */ +/*global netdataSnapshotData *//* object, a netdata snapshot loaded + * (default: null) */ // ---------------------------------------------------------------------------- // global namespace @@ -123,32 +127,43 @@ var NETDATA = window.NETDATA || {}; else if(NETDATA.serverDefault.slice(-1) !== '/') NETDATA.serverDefault += '/'; + if(typeof netdataServerStatic !== 'undefined' && netdataServerStatic !== null && netdataServerStatic !== '') { + NETDATA.serverStatic = netdataServerStatic; + if(NETDATA.serverStatic.slice(-1) !== '/') + NETDATA.serverStatic += '/'; + } + else { + NETDATA.serverStatic = 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-2.2.4.min.js'; - NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js'; - NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js'; - NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js'; - NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-1.3.2.min.js'; - NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js'; - NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js'; - NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js'; - NETDATA.c3_js = NETDATA.serverDefault + 'lib/c3-0.4.11.min.js'; - NETDATA.c3_css = NETDATA.serverDefault + 'css/c3-0.4.11.min.css'; - NETDATA.d3_js = NETDATA.serverDefault + 'lib/d3-3.5.17.min.js'; - NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris-0.5.1.min.js'; - NETDATA.morris_css = NETDATA.serverDefault + 'css/morris-0.5.1.css'; + NETDATA.jQuery = NETDATA.serverStatic + 'lib/jquery-2.2.4.min.js'; + NETDATA.peity_js = NETDATA.serverStatic + 'lib/jquery.peity-3.2.0.min.js'; + NETDATA.sparkline_js = NETDATA.serverStatic + 'lib/jquery.sparkline-2.1.2.min.js'; + NETDATA.easypiechart_js = NETDATA.serverStatic + 'lib/jquery.easypiechart-97b5824.min.js'; + NETDATA.gauge_js = NETDATA.serverStatic + 'lib/gauge-1.3.2.min.js'; + NETDATA.dygraph_js = NETDATA.serverStatic + 'lib/dygraph-combined-dd74404.js'; + NETDATA.dygraph_smooth_js = NETDATA.serverStatic + 'lib/dygraph-smooth-plotter-dd74404.js'; + NETDATA.raphael_js = NETDATA.serverStatic + 'lib/raphael-2.2.4-min.js'; + NETDATA.c3_js = NETDATA.serverStatic + 'lib/c3-0.4.11.min.js'; + NETDATA.c3_css = NETDATA.serverStatic + 'css/c3-0.4.11.min.css'; + NETDATA.d3_js = NETDATA.serverStatic + 'lib/d3-3.5.17.min.js'; + NETDATA.morris_js = NETDATA.serverStatic + 'lib/morris-0.5.1.min.js'; + NETDATA.morris_css = NETDATA.serverStatic + 'css/morris-0.5.1.css'; NETDATA.google_js = 'https://www.google.com/jsapi'; NETDATA.themes = { white: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css', - dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20170725-1', + bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-3.3.7.css', + dashboard_css: NETDATA.serverStatic + 'dashboard.css?v20171208-1', background: '#FFFFFF', foreground: '#000000', grid: '#F0F0F0', axis: '#F0F0F0', + highlight: '#F5F5F5', colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707', @@ -160,12 +175,13 @@ var NETDATA = window.NETDATA || {}; gauge_gradient: false }, slate: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', - dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20170725-1', + bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', + dashboard_css: NETDATA.serverStatic + 'dashboard.slate.css?v20171208-1', background: '#272b30', foreground: '#C8C8C8', grid: '#283236', axis: '#283236', + highlight: '#383838', /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00', '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0', '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a', @@ -202,6 +218,9 @@ var NETDATA = window.NETDATA || {}; // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray) //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ]; + if(typeof netdataSnapshotData === 'undefined') + netdataSnapshotData = null; + if(typeof netdataShowHelp === 'undefined') netdataShowHelp = true; @@ -247,7 +266,6 @@ var NETDATA = window.NETDATA || {}; // if the user does not specify any of these, the following will be used NETDATA.chartDefaults = { - host: NETDATA.serverDefault, // the server to get data from width: '100%', // the chart width - can be null height: '100%', // the chart height - can be null min_width: null, // the chart minimum width - can be null @@ -287,13 +305,33 @@ var NETDATA = window.NETDATA || {}; // rendering the chart that is panned or zoomed). // Used with .current.global_pan_sync_time - last_page_resize: Date.now(), // the timestamp of the last resize request + on_scroll_refresher_stop_until: 0, // timestamp in ms - used to stop evaluating + // charts for some time, after a page scroll + + last_page_resize: Date.now(), // the timestamp of the last resize request last_page_scroll: 0, // the timestamp the last time the page was scrolled + browser_timezone: 'unknown', // timezone detected by javascript + server_timezone: 'unknown', // timezone reported by the server + + force_data_points: 0, // force the number of points to be returned for charts + fake_chart_rendering: false, // when set to true, the dashboard will download data but will not render the charts + + passive_events: null, // true if the browser supports passive events + // the current profile // we may have many... current: { + units: 'auto', // can be 'auto' or 'original' + temperature: 'celsius', // can be 'celsius' or 'fahrenheit' + seconds_as_time: true, // show seconds as DDd:HH:MM:SS ? + timezone: 'default', // the timezone to use, or 'default' + user_set_server_timezone: 'default', // as set by the user on the dashboard + + legend_toolbox: true, // show the legend toolbox on charts + resize_charts: true, // show the resize handler on charts + pixels_per_point: isSlowDevice()?5:1, // the minimum pixels per point for all charts // increase this to speed javascript up // each chart library has its own limit too @@ -318,11 +356,11 @@ var NETDATA = window.NETDATA || {}; idle_lost_focus: 500, // ms - when the window does not have focus, check // if focus has been regained, every this time - global_pan_sync_time: 1000, // ms - when you pan or zoom a chart, the background + global_pan_sync_time: 300, // ms - when you pan or zoom a chart, the background // auto-refreshing of charts is paused for this amount // of time - sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount + sync_selection_delay: 400, // ms - when you pan or zoom a chart, wait this amount // of time before setting up synchronized selections // on hover. @@ -355,8 +393,6 @@ var NETDATA = window.NETDATA || {}; smooth_plot: (isSlowDevice() === false), // 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, @@ -368,7 +404,7 @@ var NETDATA = window.NETDATA || {}; abort_ajax_on_scroll: false, // kill pending ajax page scroll async_on_scroll: false, // sync/async onscroll handler - onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler + onscroll_worker_duration_threshold: 30, // time in ms, for async scroll handler retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server @@ -381,11 +417,13 @@ var NETDATA = window.NETDATA || {}; focus: false, visibility: false, chart_data_url: false, - chart_errors: false, // FIXME: remember to set it to false before merging + chart_errors: true, // FIXME: remember to set it to false before merging chart_timing: false, chart_calls: false, libraries: false, - dygraph: false + dygraph: false, + globalSelectionSync:false, + globalPanAndZoom: false } }; @@ -396,6 +434,114 @@ var NETDATA = window.NETDATA || {}; }; + // ---------------------------------------------------------------------------------------------------------------- + + NETDATA.timeout = { + // by default, these are just wrappers to setTimeout() / clearTimeout() + + step: function(callback) { + return window.setTimeout(callback, 1000 / 60); + }, + + set: function(callback, delay) { + return window.setTimeout(callback, delay); + }, + + clear: function(id) { + return window.clearTimeout(id); + }, + + init: function() { + var custom = true; + + if(window.requestAnimationFrame) { + this.step = function(callback) { + return window.requestAnimationFrame(callback); + }; + + this.clear = function(handle) { + return window.cancelAnimationFrame(handle.value); + }; + } + else if(window.webkitRequestAnimationFrame) { + this.step = function(callback) { + return window.webkitRequestAnimationFrame(callback); + }; + + if(window.webkitCancelAnimationFrame) { + this.clear = function (handle) { + return window.webkitCancelAnimationFrame(handle.value); + }; + } + else if(window.webkitCancelRequestAnimationFrame) { + this.clear = function (handle) { + return window.webkitCancelRequestAnimationFrame(handle.value); + }; + } + } + else if(window.mozRequestAnimationFrame) { + this.step = function(callback) { + return window.mozRequestAnimationFrame(callback); + }; + + this.clear = function(handle) { + return window.mozCancelRequestAnimationFrame(handle.value); + }; + } + else if(window.oRequestAnimationFrame) { + this.step = function(callback) { + return window.oRequestAnimationFrame(callback); + }; + + this.clear = function(handle) { + return window.oCancelRequestAnimationFrame(handle.value); + }; + } + else if(window.msRequestAnimationFrame) { + this.step = function(callback) { + return window.msRequestAnimationFrame(callback); + }; + + this.clear = function(handle) { + return window.msCancelRequestAnimationFrame(handle.value); + }; + } + else + custom = false; + + + if(custom === true) { + // we have installed custom .step() / .clear() functions + // overwrite the .set() too + + this.set = function(callback, delay) { + var that = this; + + var start = Date.now(), + handle = new Object(); + + function loop() { + var current = Date.now(), + delta = current - start; + + if(delta >= delay) { + callback.call(); + } + else { + handle.value = that.step(loop); + } + } + + handle.value = that.step(loop); + return handle; + }; + } + } + }; + + NETDATA.timeout.init(); + + // ---------------------------------------------------------------------------------------------------------------- // local storage options @@ -562,6 +708,8 @@ var NETDATA = window.NETDATA || {}; } } } + + NETDATA.dateTime.init(NETDATA.options.current.timezone); }; // ---------------------------------------------------------------------------------------------------------------- @@ -578,113 +726,111 @@ var NETDATA = window.NETDATA || {}; NETDATA.onresizeCallback(); }; - NETDATA.onscroll_updater_count = 0; - NETDATA.onscroll_updater_running = false; - NETDATA.onscroll_updater_last_run = 0; - NETDATA.onscroll_updater_watchdog = null; - NETDATA.onscroll_updater_max_duration = 0; - NETDATA.onscroll_updater_above_threshold_count = 0; - NETDATA.onscroll_updater = function() { - NETDATA.onscroll_updater_running = true; - NETDATA.onscroll_updater_count++; - var start = Date.now(); - + NETDATA.abort_all_refreshes = function() { var targets = NETDATA.options.targets; var len = targets.length; + while (len--) { + if (targets[len].fetching_data === true) { + if (typeof targets[len].xhr !== 'undefined') { + targets[len].xhr.abort(); + targets[len].running = false; + targets[len].fetching_data = false; + } + } + } + }; + + NETDATA.onscroll_start_delay = function() { + NETDATA.options.last_page_scroll = Date.now(); + + NETDATA.options.on_scroll_refresher_stop_until = + NETDATA.options.last_page_scroll + + ((NETDATA.options.current.async_on_scroll === true) ? 1000 : 0); + }; + + NETDATA.onscroll_end_delay = function() { + NETDATA.options.on_scroll_refresher_stop_until = + Date.now() + + ((NETDATA.options.current.async_on_scroll === true) ? NETDATA.options.current.onscroll_worker_duration_threshold : 0); + }; + + NETDATA.onscroll_updater_timeout_id = undefined; + NETDATA.onscroll_updater = function() { + if(NETDATA.options.targets === null) return; + + //var start = Date.now(); + + // console.log('onscroll_updater() begin'); + + NETDATA.globalSelectionSync.stop(); + // when the user scrolls he sees that we have // hidden all the not-visible charts // using this little function we try to switch // the charts back to visible quickly + if(NETDATA.options.abort_ajax_on_scroll === true) + NETDATA.abort_all_refreshes(); - if(NETDATA.options.abort_ajax_on_scroll === true) { - // we have to cancel pending requests too - - while (len--) { - if (targets[len].fetching_data === true) { - if (typeof targets[len].xhr !== 'undefined') { - targets[len].xhr.abort(); - targets[len].running = false; - targets[len].fetching_data = false; - } - targets[len].isVisible(); - } - } - } - else { - // just find which charts are visible + if(NETDATA.options.current.parallel_refresher === false) { + var targets = NETDATA.options.targets; + var len = targets.length; while (len--) targets[len].isVisible(); } - var end = Date.now(); - // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms'); + //var end = Date.now(); + //var dt = end - start; + // console.log('count = ' + targets.length + ', average = ' + Math.round(dt / targets.length).toString() + ', dt = ' + dt); - if(NETDATA.options.current.async_on_scroll === false) { - var dt = end - start; - if(dt > NETDATA.onscroll_updater_max_duration) { - // console.log('max onscroll event handler duration increased to ' + dt); - NETDATA.onscroll_updater_max_duration = dt; - } - - if(dt > NETDATA.options.current.onscroll_worker_duration_threshold) { - // console.log('slow: ' + dt); - NETDATA.onscroll_updater_above_threshold_count++; - - if(NETDATA.onscroll_updater_above_threshold_count > 2 && NETDATA.onscroll_updater_above_threshold_count * 100 / NETDATA.onscroll_updater_count > 2) { - NETDATA.setOption('async_on_scroll', true); - console.log('NETDATA: your browser is slow - enabling asynchronous onscroll event handler.'); - } - } - } - - NETDATA.onscroll_updater_last_run = start; - NETDATA.onscroll_updater_running = false; + NETDATA.onscroll_end_delay(); }; NETDATA.scrollUp = false; NETDATA.scrollY = window.scrollY; NETDATA.onscroll = function() { - // console.log('onscroll'); + //console.log('onscroll() begin'); + + NETDATA.onscroll_start_delay(); + NETDATA.chartRefresherReschedule(); NETDATA.scrollUp = (window.scrollY > NETDATA.scrollY); NETDATA.scrollY = window.scrollY; - NETDATA.options.last_page_scroll = Date.now(); - NETDATA.options.auto_refresher_stop_until = 0; - - if(NETDATA.options.targets === null) return; + if(NETDATA.onscroll_updater_timeout_id) + NETDATA.timeout.clear(NETDATA.onscroll_updater_timeout_id); - if(NETDATA.options.current.async_on_scroll === true) { - // async - if(NETDATA.onscroll_updater_running === false) { - NETDATA.onscroll_updater_running = true; - setTimeout(NETDATA.onscroll_updater, 0); - } - else { - if(NETDATA.onscroll_updater_watchdog !== null) - clearTimeout(NETDATA.onscroll_updater_watchdog); + NETDATA.onscroll_updater_timeout_id = NETDATA.timeout.set(NETDATA.onscroll_updater, 0); + //console.log('onscroll() end'); + }; - NETDATA.onscroll_updater_watchdog = setTimeout(function() { - if(NETDATA.onscroll_updater_running === false && NETDATA.options.last_page_scroll > NETDATA.onscroll_updater_last_run) { - // console.log('watchdog'); - NETDATA.onscroll_updater(); + NETDATA.supportsPassiveEvents = function() { + if(NETDATA.options.passive_events === null) { + var supportsPassive = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function () { + supportsPassive = true; } - - NETDATA.onscroll_updater_watchdog = null; - }, 200); + }); + window.addEventListener("test", null, opts); + } catch (e) { + console.log('browser does not support passive events'); } + + NETDATA.options.passive_events = supportsPassive; } - else { - // sync - NETDATA.onscroll_updater(); - } + + // console.log('passive ' + NETDATA.options.passive_events); + return NETDATA.options.passive_events; }; - window.onresize = NETDATA.onresize; - window.onscroll = NETDATA.onscroll; + window.addEventListener('resize', NETDATA.onresize, NETDATA.supportsPassiveEvents() ? { passive: true } : false); + window.addEventListener('scroll', NETDATA.onscroll, NETDATA.supportsPassiveEvents() ? { passive: true } : false); + // window.onresize = NETDATA.onresize; + // window.onscroll = NETDATA.onscroll; // ---------------------------------------------------------------------------------------------------------------- // Error Handling @@ -847,6 +993,7 @@ var NETDATA = window.NETDATA || {}; } }, + // the fallback getFixed: function(min, max) { var key = max; if(min === max) { @@ -882,7 +1029,7 @@ var NETDATA = window.NETDATA || {}; }, testIntlNumberFormat: function() { - var n = 1.12345; + var value = 1.12345; var e1 = "1.12", e2 = "1,12"; var s = ""; @@ -893,7 +1040,7 @@ var NETDATA = window.NETDATA || {}; maximumFractionDigits: 2 }); - s = x.format(n); + s = x.format(value); } catch(e) { s = ""; @@ -904,7 +1051,7 @@ var NETDATA = window.NETDATA || {}; }, testLocaleString: function() { - var n = 1.12345; + var value = 1.12345; var e1 = "1.12", e2 = "1,12"; var s = ""; @@ -993,6 +1140,11 @@ var NETDATA = window.NETDATA || {}; keys: {}, latest: {}, + globalReset: function() { + this.keys = {}; + this.latest = {}; + }, + get: function(state) { if(typeof state.tmp.__commonMin === 'undefined') { // get the commonMin setting @@ -1047,6 +1199,11 @@ var NETDATA = window.NETDATA || {}; keys: {}, latest: {}, + globalReset: function() { + this.keys = {}; + this.latest = {}; + }, + get: function(state) { if(typeof state.tmp.__commonMax === 'undefined') { // get the commonMax setting @@ -1117,14 +1274,11 @@ var NETDATA = window.NETDATA || {}; NETDATA.chartRegistry = { charts: {}, - fixid: function(id) { - return id.replace(/:/g, "_").replace(/\//g, "_"); + globalReset: function() { + this.charts = {}; }, add: function(host, id, data) { - host = this.fixid(host); - id = this.fixid(id); - if(typeof this.charts[host] === 'undefined') this.charts[host] = {}; @@ -1133,9 +1287,6 @@ var NETDATA = window.NETDATA || {}; }, get: function(host, id) { - host = this.fixid(host); - id = this.fixid(id); - if(typeof this.charts[host] === 'undefined') return null; @@ -1151,28 +1302,40 @@ var NETDATA = window.NETDATA || {}; var self = this; - $.ajax({ - url: host + '/api/v1/charts', - async: true, - cache: false, - xhrFields: { withCredentials: true } // required for the cookie - }) - .done(function(data) { + function got_data(h, data, callback) { if(data !== null) { - var h = NETDATA.chartRegistry.fixid(host); self.charts[h] = data.charts; + + // update the server timezone in our options + if(typeof data.timezone === 'string') + NETDATA.options.server_timezone = data.timezone; } - else NETDATA.error(406, host + '/api/v1/charts'); + else NETDATA.error(406, h + '/api/v1/charts'); if(typeof callback === 'function') - return callback(data); - }) - .fail(function() { - NETDATA.error(405, host + '/api/v1/charts'); + callback(data); + } - if(typeof callback === 'function') - return callback(null); - }); + if(netdataSnapshotData !== null) { + got_data(host, netdataSnapshotData.charts, callback); + } + else { + $.ajax({ + url: host + '/api/v1/charts', + async: true, + cache: false, + xhrFields: {withCredentials: true} // required for the cookie + }) + .done(function (data) { + got_data(host, data, callback); + }) + .fail(function () { + NETDATA.error(405, host + '/api/v1/charts'); + + if (typeof callback === 'function') + callback(null); + }); + } } }; @@ -1199,20 +1362,45 @@ var NETDATA = window.NETDATA || {}; callback: null, + globalReset: function() { + this.clearMaster(); + this.seq = 0; + this.master = null; + this.force_after_ms = null; + this.force_before_ms = null; + this.callback = null; + }, + + delay: function() { + if(NETDATA.options.debug.globalPanAndZoom === true) + console.log('globalPanAndZoom.delay()'); + + NETDATA.options.auto_refresher_stop_until = Date.now() + NETDATA.options.current.global_pan_sync_time; + }, + // set a new master setMaster: function(state, after, before) { + this.delay(); + if(NETDATA.options.current.sync_pan_and_zoom === false) return; - if(this.master !== null && this.master !== state) + if(this.master === null) { + if(NETDATA.options.debug.globalPanAndZoom === true) + console.log('globalPanAndZoom.setMaster(' + state.id + ', ' + after + ', ' + before + ') SET MASTER'); + } + else if(this.master !== state) { + if(NETDATA.options.debug.globalPanAndZoom === true) + console.log('globalPanAndZoom.setMaster(' + state.id + ', ' + after + ', ' + before + ') CHANGED MASTER'); + this.master.resetChart(true, true); + } var now = Date.now(); this.master = state; this.seq = now; this.force_after_ms = after; this.force_before_ms = before; - NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time; if(typeof this.callback === 'function') this.callback(true, after, before); @@ -1220,6 +1408,9 @@ var NETDATA = window.NETDATA || {}; // clear the master clearMaster: function() { + if(NETDATA.options.debug.globalPanAndZoom === true) + console.log('globalPanAndZoom.clearMaster()'); + if(this.master !== null) { var st = this.master; this.master = null; @@ -1260,6 +1451,86 @@ var NETDATA = window.NETDATA || {}; } }; + // ---------------------------------------------------------------------------------------------------------------- + // global chart underlay (time-frame highlighting) + + NETDATA.globalChartUnderlay = { + callback: null, // what to call when a highlighted range is setup + after: null, // highlight after this time + before: null, // highlight before this time + view_after: null, // the charts after_ms viewport when the highlight was setup + view_before: null, // the charts before_ms viewport, when the highlight was setup + state: null, // the chart the highlight was setup + + isActive: function() { + return (this.after !== null && this.before !== null); + }, + + hasViewport: function() { + return (this.state !== null && this.view_after !== null && this.view_before !== null); + }, + + init: function(state, after, before, view_after, view_before) { + this.state = (typeof state !== 'undefined') ? state : null; + this.after = (typeof after !== 'undefined' && after !== null && after > 0) ? after : null; + this.before = (typeof before !== 'undefined' && before !== null && before > 0) ? before : null; + this.view_after = (typeof view_after !== 'undefined' && view_after !== null && view_after > 0) ? view_after : null; + this.view_before = (typeof view_before !== 'undefined' && view_before !== null && view_before > 0) ? view_before : null; + }, + + setup: function() { + if(this.isActive() === true) { + if (this.state === null) + this.state = NETDATA.options.targets[0]; + + if (typeof this.callback === 'function') + this.callback(true, this.after, this.before); + } + else { + if (typeof this.callback === 'function') + this.callback(false, 0, 0); + } + }, + + set: function(state, after, before, view_after, view_before) { + if(after > before) { + var t = after; + after = before; + before = t; + } + + this.init(state, after, before, view_after, view_before); + + if (this.hasViewport() === true) + NETDATA.globalPanAndZoom.setMaster(this.state, this.view_after, this.view_before); + + this.setup(); + }, + + clear: function() { + this.after = null; + this.before = null; + this.state = null; + this.view_after = null; + this.view_before = null; + + if(typeof this.callback === 'function') + this.callback(false, 0, 0); + }, + + focus: function() { + if(this.isActive() === true && this.hasViewport() === true) { + if(this.state === null) + this.state = NETDATA.options.targets[0]; + + if(NETDATA.globalPanAndZoom.isMaster(this.state) === true) + NETDATA.globalPanAndZoom.clearMaster(); + + NETDATA.globalPanAndZoom.setMaster(this.state, this.view_after, this.view_before, true); + } + } + }; + // ---------------------------------------------------------------------------------------------------------------- // dimensions selection @@ -1290,7 +1561,7 @@ var NETDATA = window.NETDATA || {}; if(this.name_div !== name_div) { this.name_div = name_div; this.name_div.title = this.label; - this.name_div.style.color = this.color; + this.name_div.style.setProperty('color', this.color, 'important'); if(this.selected === false) this.name_div.className = 'netdata-legend-name not-selected'; else @@ -1300,7 +1571,7 @@ var NETDATA = window.NETDATA || {}; if(this.value_div !== value_div) { this.value_div = value_div; this.value_div.title = this.label; - this.value_div.style.color = this.color; + this.value_div.style.setProperty('color', this.color, 'important'); if(this.selected === false) this.value_div.className = 'netdata-legend-value not-selected'; else @@ -1463,6 +1734,422 @@ var NETDATA = window.NETDATA || {}; }; + // ---------------------------------------------------------------------------------------------------------------- + // date/time conversion + + NETDATA.dateTime = { + using_timezone: false, + + // these are the old netdata functions + // we fallback to these, if the new ones fail + + localeDateStringNative: function(d) { + return d.toLocaleDateString(); + }, + + localeTimeStringNative: function(d) { + return d.toLocaleTimeString(); + }, + + xAxisTimeStringNative: function(d) { + return NETDATA.zeropad(d.getHours()) + ":" + + NETDATA.zeropad(d.getMinutes()) + ":" + + NETDATA.zeropad(d.getSeconds()); + }, + + // initialize the new date/time conversion + // functions. + // if this fails, we fallback to the above + init: function(timezone) { + //console.log('init with timezone: ' + timezone); + + // detect browser timezone + try { + NETDATA.options.browser_timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + } + catch(e) { + console.log('failed to detect browser timezone: ' + e.toString()); + NETDATA.options.browser_timezone = 'cannot-detect-it'; + } + + var ret = false; + + try { + var dateOptions ={ + localeMatcher: 'best fit', + formatMatcher: 'best fit', + weekday: 'short', + year: 'numeric', + month: 'short', + day: '2-digit' + }; + + var timeOptions = { + localeMatcher: 'best fit', + hour12: false, + formatMatcher: 'best fit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }; + + var xAxisOptions = { + localeMatcher: 'best fit', + hour12: false, + formatMatcher: 'best fit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }; + + if(typeof timezone === 'string' && timezone !== '' && timezone !== 'default') { + dateOptions.timeZone = timezone; + timeOptions.timeZone = timezone; + timeOptions.timeZoneName = 'short'; + xAxisOptions.timeZone = timezone; + this.using_timezone = true; + } + else { + timezone = 'default'; + this.using_timezone = false; + } + + this.dateFormat = new Intl.DateTimeFormat(navigator.language, dateOptions); + this.timeFormat = new Intl.DateTimeFormat(navigator.language, timeOptions); + this.xAxisFormat = new Intl.DateTimeFormat(navigator.language, xAxisOptions); + + this.localeDateString = function(d) { + return this.dateFormat.format(d); + }; + + this.localeTimeString = function(d) { + return this.timeFormat.format(d); + }; + + this.xAxisTimeString = function(d) { + return this.xAxisFormat.format(d); + }; + + var d = new Date(); + var t = this.dateFormat.format(d) + ' ' + this.timeFormat.format(d) + ' ' + this.xAxisFormat.format(d); + + ret = true; + } + catch(e) { + console.log('Cannot setup Date/Time formatting: ' + e.toString()); + + timezone = 'default'; + this.localeDateString = this.localeDateStringNative; + this.localeTimeString = this.localeTimeStringNative; + this.xAxisTimeString = this.xAxisTimeStringNative; + this.using_timezone = false; + + ret = false; + } + + // save it + //console.log('init setOption timezone: ' + timezone); + NETDATA.setOption('timezone', timezone); + + return ret; + } + }; + NETDATA.dateTime.init(NETDATA.options.current.timezone); + + + // ---------------------------------------------------------------------------------------------------------------- + // units conversion + + NETDATA.unitsConversion = { + keys: {}, // keys for data-common-units + latest: {}, // latest selected units for data-common-units + + globalReset: function() { + this.keys = {}; + this.latest = {}; + }, + + scalableUnits: { + 'kilobits/s': { + 'bits/s': 1 / 1000, + 'kilobits/s': 1, + 'megabits/s': 1000, + 'gigabits/s': 1000000 + }, + 'kilobytes/s': { + 'bytes/s': 1 / 1024, + 'kilobytes/s': 1, + 'megabytes/s': 1024, + 'gigabytes/s': 1024 * 1024 + }, + 'KB/s': { + 'B/s': 1 / 1024, + 'KB/s': 1, + 'MB/s': 1024, + 'GB/s': 1024 * 1024 + }, + 'KB': { + 'B': 1 / 1024, + 'KB': 1, + 'MB': 1024, + 'GB': 1024 * 1024 + }, + 'MB': { + 'KB': 1 / 1024, + 'MB': 1, + 'GB': 1024, + 'TB': 1024 * 1024 + }, + 'GB': { + 'MB': 1 / 1024, + 'GB': 1, + 'TB': 1024 + } + /* + 'seconds': { + 'milliseconds': 0.001, + 'seconds': 1, + 'minutes': 60, + 'hours': 3600, + 'days': 86400 + } + */ + }, + + convertibleUnits: { + 'Celsius': { + 'Fahrenheit': { + check: function() { return NETDATA.options.current.temperature === 'fahrenheit'; }, + convert: function(value) { return value * 9 / 5 + 32; } + } + }, + 'celsius': { + 'fahrenheit': { + check: function() { return NETDATA.options.current.temperature === 'fahrenheit'; }, + convert: function(value) { return value * 9 / 5 + 32; } + } + }, + 'seconds': { + 'time': { + check: function () { return NETDATA.options.current.seconds_as_time; }, + convert: function (seconds) { + seconds = Math.abs(seconds); + + var days = Math.floor(seconds / 86400); + seconds -= days * 86400; + + var hours = Math.floor(seconds / 3600); + seconds -= hours * 3600; + + var minutes = Math.floor(seconds / 60); + seconds -= minutes * 60; + + seconds = Math.round(seconds); + + var ms_txt = ''; + /* + var ms = seconds - Math.floor(seconds); + seconds -= ms; + ms = Math.round(ms * 1000); + + if(ms > 1) { + if(ms < 10) + ms_txt = '.00' + ms.toString(); + else if(ms < 100) + ms_txt = '.0' + ms.toString(); + else + ms_txt = '.' + ms.toString(); + } + */ + + return ((days > 0)?days.toString() + 'd:':'').toString() + + NETDATA.zeropad(hours) + ':' + + NETDATA.zeropad(minutes) + ':' + + NETDATA.zeropad(seconds) + + ms_txt; + } + } + } + }, + + // get a function that converts the units + // + every time units are switched call the callback + get: function(uuid, min, max, units, desired_units, common_units_name, switch_units_callback) { + // validate the parameters + if(typeof units === 'undefined') + units = 'undefined'; + + // check if we support units conversion + if(typeof this.scalableUnits[units] === 'undefined' && typeof this.convertibleUnits[units] === 'undefined') { + // we can't convert these units + //console.log('DEBUG: ' + uuid.toString() + ' can\'t convert units: ' + units.toString()); + return function(value) { return value; }; + } + + // check if the caller wants the original units + if(typeof desired_units === 'undefined' || desired_units === null || desired_units === 'original' || desired_units === units) { + //console.log('DEBUG: ' + uuid.toString() + ' original units wanted'); + switch_units_callback(units); + return function(value) { return value; }; + } + + // now we know we can convert the units + // and the caller wants some kind of conversion + + var tunits = null; + var tdivider = 0; + var x; + + if(typeof this.scalableUnits[units] !== 'undefined') { + // units that can be scaled + // we decide a divider + + // console.log('NETDATA.unitsConversion.get(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString()); + + if (desired_units === 'auto') { + // the caller wants to auto-scale the units + + // find the absolute maximum value that is rendered on the chart + // based on this we decide the scale + min = Math.abs(min); + max = Math.abs(max); + if (min > max) max = min; + + // find the smallest scale that provides integers + for (x in this.scalableUnits[units]) { + if (this.scalableUnits[units].hasOwnProperty(x)) { + var m = this.scalableUnits[units][x]; + if (m <= max && m > tdivider) { + tunits = x; + tdivider = m; + } + } + } + + if(tunits === null || tdivider <= 0) { + // we couldn't find one + //console.log('DEBUG: ' + uuid.toString() + ' cannot find an auto-scaling candidate for units: ' + units.toString() + ' (max: ' + max.toString() + ')'); + switch_units_callback(units); + return function(value) { return value; }; + } + + if(typeof common_units_name === 'string' && typeof uuid === 'string') { + // the caller wants several charts to have the same units + // data-common-units + + var common_units_key = common_units_name + '-' + units; + + // add our divider into the list of keys + var t = this.keys[common_units_key]; + if(typeof t === 'undefined') { + this.keys[common_units_key] = {}; + t = this.keys[common_units_key]; + } + t[uuid] = { + units: tunits, + divider: tdivider + }; + + // find the max divider of all charts + var common_units = t[uuid]; + for(x in t) { + if (t.hasOwnProperty(x) && t[x].divider > common_units.divider) + common_units = t[x]; + } + + // save our common_max to the latest keys + var latest = this.latest[common_units_key]; + if(typeof latest === 'undefined') { + this.latest[common_units_key] = {}; + latest = this.latest[common_units_key]; + } + latest.units = common_units.units; + latest.divider = common_units.divider; + + tunits = latest.units; + tdivider = latest.divider; + + //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', common-units=' + common_units_name.toString() + ((t[uuid].divider !== tdivider)?' USED COMMON, mine was ' + t[uuid].units:' set common').toString()); + + // apply it to this chart + switch_units_callback(tunits); + return function(value) { + if(tdivider !== latest.divider) { + // another chart switched our common units + // we should switch them too + //console.log('DEBUG: ' + uuid + ' switching units due to a common-units change, from ' + tunits.toString() + ' to ' + latest.units.toString()); + tunits = latest.units; + tdivider = latest.divider; + switch_units_callback(tunits); + } + + return value / tdivider; + } + } + else { + // the caller did not give data-common-units + // this chart auto-scales independently of all others + //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', autonomously'); + + switch_units_callback(tunits); + return function (value) { return value / tdivider; }; + } + } + else { + // the caller wants specific units + + if(typeof this.scalableUnits[units][desired_units] !== 'undefined') { + // all good, set the new units + tdivider = this.scalableUnits[units][desired_units]; + // console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + desired_units.toString() + ' with divider ' + tdivider.toString() + ', by reference'); + switch_units_callback(desired_units); + return function (value) { return value / tdivider; }; + } + else { + // oops! switch back to original units + console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); + switch_units_callback(units); + return function (value) { return value; }; + } + } + } + else if(typeof this.convertibleUnits[units] !== 'undefined') { + // units that can be converted + if(desired_units === 'auto') { + for(x in this.convertibleUnits[units]) { + if (this.convertibleUnits[units].hasOwnProperty(x)) { + if (this.convertibleUnits[units][x].check()) { + //console.log('DEBUG: ' + uuid.toString() + ' converting ' + units.toString() + ' to: ' + x.toString()); + switch_units_callback(x); + return this.convertibleUnits[units][x].convert; + } + } + } + + // none checked ok + //console.log('DEBUG: ' + uuid.toString() + ' no conversion available for ' + units.toString() + ' to: ' + desired_units.toString()); + switch_units_callback(units); + return function (value) { return value; }; + } + else if(typeof this.convertibleUnits[units][desired_units] !== 'undefined') { + switch_units_callback(desired_units); + return this.convertibleUnits[units][desired_units].convert; + } + else { + console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); + switch_units_callback(units); + return function (value) { return value; }; + } + } + else { + // hm... did we forget to implement the new type? + console.log('Unmatched unit conversion method for units ' + units.toString()); + switch_units_callback(units); + return function (value) { return value; }; + } + } + }; + // ---------------------------------------------------------------------------------------------------------------- // global selection sync @@ -1471,15 +2158,126 @@ var NETDATA = window.NETDATA || {}; dont_sync_before: 0, last_t: 0, slaves: [], + timeout_id: undefined, + + globalReset: function() { + this.stop(); + this.state = null; + this.dont_sync_before = 0; + this.last_t = 0; + this.slaves = []; + this.timeout_id = undefined; + }, + + active: function() { + return (this.state !== null); + }, + + // return true if global selection sync can be enabled now + enabled: function() { + // console.log('enabled()'); + // can we globally apply selection sync? + if(NETDATA.options.current.sync_selection === false) + return false; + + return (this.dont_sync_before <= Date.now()); + }, + + // set the global selection sync master + setMaster: function(state) { + if(this.enabled() === false) { + this.stop(); + return; + } + + if(this.state === state) + return; - stop: function() { if(this.state !== null) - this.state.globalSelectionSyncStop(); + this.stop(); + + if(NETDATA.options.debug.globalSelectionSync === true) + console.log('globalSelectionSync.setMaster(' + state.id + ')'); + + state.selected = true; + this.state = state; + this.last_t = 0; + + // find all slaves + this.slaves = []; + var len = NETDATA.options.targets.length; + while(len--) { + var st = NETDATA.options.targets[len]; + if (this.state !== st && st.globalSelectionSyncIsEligible() === true) + this.slaves.push(st); + } + + // this.delay(100); }, - delay: function() { + // stop global selection sync + stop: function() { if(this.state !== null) { - this.state.globalSelectionSyncDelay(); + if(NETDATA.options.debug.globalSelectionSync === true) + console.log('globalSelectionSync.stop()'); + + var len = this.slaves.length; + while (len--) + this.slaves[len].clearSelection(); + + this.state.clearSelection(); + + this.last_t = 0; + this.slaves = []; + this.state = null; + } + }, + + // delay global selection sync for some time + delay: function(ms) { + if(NETDATA.options.current.sync_selection === true) { + if(NETDATA.options.debug.globalSelectionSync === true) + console.log('globalSelectionSync.delay()'); + + if(typeof ms === 'number') + this.dont_sync_before = Date.now() + ms; + else + this.dont_sync_before = Date.now() + NETDATA.options.current.sync_selection_delay; + } + }, + + __syncSlaves: function() { + if(NETDATA.globalSelectionSync.enabled() === true) { + if(NETDATA.options.debug.globalSelectionSync === true) + console.log('globalSelectionSync.__syncSlaves()'); + + var t = NETDATA.globalSelectionSync.last_t; + var len = NETDATA.globalSelectionSync.slaves.length; + while (len--) + NETDATA.globalSelectionSync.slaves[len].setSelection(t); + + this.timeout_id = undefined; + } + }, + + // sync all the visible charts to the given time + // this is to be called from the chart libraries + sync: function(state, t) { + if(NETDATA.options.current.sync_selection === true) { + if(NETDATA.options.debug.globalSelectionSync === true) + console.log('globalSelectionSync.sync(' + state.id + ', ' + t.toString() + ')'); + + this.setMaster(state); + + if(t === this.last_t) + return; + + this.last_t = t; + + if (this.timeout_id) + NETDATA.timeout.clear(this.timeout_id); + + this.timeout_id = NETDATA.timeout.set(this.__syncSlaves, 0); } } }; @@ -1653,7 +2451,7 @@ var NETDATA = window.NETDATA || {}; return; // string - the netdata server URL, without any path - that.host = NETDATA.dataAttribute(that.element, 'host', NETDATA.chartDefaults.host); + that.host = NETDATA.dataAttribute(that.element, 'host', NETDATA.serverDefault); // make sure the host does not end with / // all netdata API requests use absolute paths @@ -1676,6 +2474,10 @@ var NETDATA = window.NETDATA || {}; that.title = NETDATA.dataAttribute(that.element, 'title', null); // the title of the chart that.units = NETDATA.dataAttribute(that.element, 'units', null); // the units of the chart dimensions + that.units_desired = NETDATA.dataAttribute(that.element, 'desired-units', NETDATA.options.current.units); // the units of the chart dimensions + that.units_current = that.units; + that.units_common = NETDATA.dataAttribute(that.element, 'common-units', null); + that.append_options = NETDATA.dataAttribute(that.element, 'append-options', null); // additional options to pass to netdata that.override_options = NETDATA.dataAttribute(that.element, 'override-options', null); // override options to pass to netdata @@ -1723,6 +2525,7 @@ var NETDATA = window.NETDATA || {}; that.element_chart = null; // the element with the chart that.element_legend = null; // the element with the legend of the chart (if created by us) that.element_legend_childs = { + content: null, hidden: null, title_date: null, title_time: null, @@ -1742,7 +2545,7 @@ var NETDATA = window.NETDATA || {}; that.log('destroyDOM()'); // that.element.className = 'netdata-message icon'; - // that.element.innerHTML = ' netdata'; + // that.element.innerHTML = ' netdata'; that.element.innerHTML = ''; that.element_message = null; that.element_legend = null; @@ -1797,6 +2600,10 @@ var NETDATA = window.NETDATA || {}; that.element.style.min_width = NETDATA.chartDefaults.min_width; }; + var invisibleSearchableText = function() { + return '' + that.id + ''; + } + /* init() private * initialize state variables * destroy all (possibly) created state elements @@ -1806,6 +2613,7 @@ var NETDATA = window.NETDATA || {}; if(that.enabled === false) return; runtimeInit(); + that.element.innerHTML = invisibleSearchableText(); that.tm.last_initialized = Date.now(); that.setMode('auto'); @@ -1868,19 +2676,19 @@ var NETDATA = window.NETDATA || {}; var icon; if(that.chart !== null) { if(that.chart.chart_type === 'line') - icon = ''; + icon = ''; else - icon = ''; + icon = ''; } else - icon = ''; + icon = ''; - showMessageIcon(icon + ' netdata'); + showMessageIcon(icon + ' netdata' + invisibleSearchableText()); }; var showLoading = function() { if(that.chart_created === false) { - showMessageIcon(' netdata'); + showMessageIcon(' netdata'); return true; } return false; @@ -1896,13 +2704,34 @@ var NETDATA = window.NETDATA || {}; if(isHidden() === true) return; if(that.chart_created === true) { + if(NETDATA.options.current.show_help === true) { + if(that.element_legend_childs.toolbox !== null) { + $(that.element_legend_childs.toolbox_left).popover('hide'); + $(that.element_legend_childs.toolbox_reset).popover('hide'); + $(that.element_legend_childs.toolbox_right).popover('hide'); + $(that.element_legend_childs.toolbox_zoomin).popover('hide'); + $(that.element_legend_childs.toolbox_zoomout).popover('hide'); + } + + if(that.element_legend_childs.resize_handler !== null) + $(that.element_legend_childs.resize_handler).popover('hide'); + + if(that.element_legend_childs.content !== null) + $(that.element_legend_childs.content).popover('hide'); + } + if(NETDATA.options.current.destroy_on_hide === true) { + // that.log('hideChart() init'); + // we should destroy it init('force'); } else { + // that.log('hideChart()'); + showRendering(); that.element_chart.style.display = 'none'; + that.element.style.willChange = 'auto'; if(that.element_legend !== null) that.element_legend.style.display = 'none'; if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = 'none'; if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = 'none'; @@ -1928,11 +2757,14 @@ var NETDATA = window.NETDATA || {}; that.updates_since_last_unhide = 0; if(that.chart_created === false) { + // that.log('unhideChart() init'); // we need to re-initialize it, to show our background // logo in bootstrap tabs, until the chart loads init('force'); } else { + // that.log('unhideChart()'); + that.element.style.willChange = 'transform'; that.tm.last_unhidden = Date.now(); that.element_chart.style.display = ''; if(that.element_legend !== null) that.element_legend.style.display = ''; @@ -1943,16 +2775,34 @@ var NETDATA = window.NETDATA || {}; } }; - var canBeRendered = function() { - return (isHidden() === false && that.isVisible(true) === true); + var canBeRendered = function(uncached_visibility) { + return ( + ( + NETDATA.options.page_is_visible === true || + NETDATA.options.current.stop_updates_when_focus_is_lost === false || + that.updates_since_last_unhide === 0 + ) + && isHidden() === false && that.isVisible(uncached_visibility) === true + ); }; // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers var callChartLibraryUpdateSafely = function(data) { var status; - if(canBeRendered() === false) - return false; + // we should not do this here + // if we prevent rendering the chart then: + // 1. globalSelectionSync will be wrong + // 2. globalPanAndZoom will be wrong + //if(canBeRendered(true) === false) + // return false; + + if(NETDATA.options.fake_chart_rendering === true) + return true; + + that.updates_counter++; + that.updates_since_last_unhide++; + that.updates_since_last_creation++; if(NETDATA.options.debug.chart_errors === true) status = that.library.update(that, data); @@ -1977,8 +2827,19 @@ var NETDATA = window.NETDATA || {}; var callChartLibraryCreateSafely = function(data) { var status; - if(canBeRendered() === false) - return false; + // we should not do this here + // if we prevent rendering the chart then: + // 1. globalSelectionSync will be wrong + // 2. globalPanAndZoom will be wrong + //if(canBeRendered(true) === false) + // return false; + + if(NETDATA.options.fake_chart_rendering === true) + return true; + + that.updates_counter++; + that.updates_since_last_unhide++; + that.updates_since_last_creation++; if(NETDATA.options.debug.chart_errors === true) status = that.library.create(that, data); @@ -2049,6 +2910,21 @@ var NETDATA = window.NETDATA || {}; resizeChart(); }; + this.resizeForPrint = function() { + if(typeof this.element_legend_childs !== 'undefined' && this.element_legend_childs.perfect_scroller !== null) { + var current = this.element.clientHeight; + var optimal = current + + this.element_legend_childs.perfect_scroller.scrollHeight + - this.element_legend_childs.perfect_scroller.clientHeight; + + if(optimal > current) { + // this.log('resized'); + this.element.style.height = optimal + 'px'; + this.library.resize(this); + } + } + }; + this.resizeHandler = function(e) { e.preventDefault(); @@ -2178,7 +3054,7 @@ var NETDATA = window.NETDATA || {}; var noDataToShow = function() { - showMessageIcon(' empty'); + showMessageIcon(' empty'); that.legendUpdateDOM(); that.tm.last_autorefreshed = Date.now(); // that.data_update_every = 30 * 1000; @@ -2216,74 +3092,6 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- // global selection sync - // prevent to global selection sync for some time - this.globalSelectionSyncDelay = function(ms) { - if(NETDATA.options.current.sync_selection === false) - return; - - if(typeof ms === 'number') - NETDATA.globalSelectionSync.dont_sync_before = Date.now() + ms; - else - NETDATA.globalSelectionSync.dont_sync_before = Date.now() + NETDATA.options.current.sync_selection_delay; - }; - - // can we globally apply selection sync? - this.globalSelectionSyncAbility = function() { - if(NETDATA.options.current.sync_selection === false) - return false; - - return (NETDATA.globalSelectionSync.dont_sync_before <= Date.now()); - }; - - this.globalSelectionSyncIsMaster = function() { - return (NETDATA.globalSelectionSync.state === this); - }; - - // this chart is the master of the global selection sync - this.globalSelectionSyncBeMaster = function() { - // am I the master? - if(this.globalSelectionSyncIsMaster()) { - if(this.debug === true) - this.log('sync: I am the master already.'); - - return; - } - - if(NETDATA.globalSelectionSync.state) { - if(this.debug === true) - this.log('sync: I am not the sync master. Resetting global sync.'); - - this.globalSelectionSyncStop(); - } - - // become the master - if(this.debug === true) - this.log('sync: becoming sync master.'); - - this.selected = true; - NETDATA.globalSelectionSync.state = this; - - // find the all slaves - var targets = NETDATA.options.targets; - var len = targets.length; - while(len--) { - var st = targets[len]; - - if(st === this) { - if(this.debug === true) - st.log('sync: not adding me to sync'); - } - else if(st.globalSelectionSyncIsEligible()) { - if(this.debug === true) - st.log('sync: adding to sync as slave'); - - st.globalSelectionSyncBeSlave(); - } - } - - // this.globalSelectionSyncDelay(100); - }; - // can the chart participate to the global selection sync as a slave? this.globalSelectionSyncIsEligible = function() { return (this.enabled === true @@ -2293,61 +3101,6 @@ var NETDATA = window.NETDATA || {}; && this.chart_created === true); }; - // 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 - this.globalSelectionSync = function(t) { - if(this.globalSelectionSyncAbility() === false) - return; - - if(this.globalSelectionSyncIsMaster() === false) { - if(this.debug === true) - this.log('sync: trying to be sync master.'); - - this.globalSelectionSyncBeMaster(); - - if(this.globalSelectionSyncAbility() === false) - return; - } - - NETDATA.globalSelectionSync.last_t = t; - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { - st.setSelection(t); - }); - }; - - // stop syncing all charts to the given time - this.globalSelectionSyncStop = function() { - if(NETDATA.globalSelectionSync.slaves.length) { - if(this.debug === true) - this.log('sync: cleaning up...'); - - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { - if(st === that) { - if(that.debug === true) - st.log('sync: not adding me to sync stop'); - } - else { - if(that.debug === true) - st.log('sync: removed slave from sync'); - - st.clearSelection(); - } - }); - - NETDATA.globalSelectionSync.last_t = 0; - NETDATA.globalSelectionSync.slaves = []; - NETDATA.globalSelectionSync.state = null; - } - - this.clearSelection(); - }; - this.setSelection = function(t) { if(typeof this.library.setSelection === 'function') this.selected = (this.library.setSelection(this, t) === true); @@ -2376,6 +3129,8 @@ var NETDATA = window.NETDATA || {}; return this.selected; }; + // ---------------------------------------------------------------------------------------------------------------- + // find if a timestamp (ms) is shown in the current chart this.timeIsVisible = function(t) { return (t >= this.data_after && t <= this.data_before); @@ -2448,10 +3203,13 @@ var NETDATA = window.NETDATA || {}; } }; - this.updateChartPanOrZoom = function(after, before) { + this.updateChartPanOrZoom = function(after, before, callback) { var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): '; var ret = true; + NETDATA.globalPanAndZoom.delay(); + NETDATA.globalSelectionSync.delay(); + if(this.debug === true) this.log(logme); @@ -2521,9 +3279,58 @@ var NETDATA = window.NETDATA || {}; this.current.force_after_ms = after; this.current.force_before_ms = before; NETDATA.globalPanAndZoom.setMaster(this, after, before); + + if(ret === true && typeof callback === 'function') + callback(); + return ret; }; + this.updateChartPanOrZoomAsyncTimeOutId = undefined; + this.updateChartPanOrZoomAsync = function(after, before, callback) { + NETDATA.globalPanAndZoom.delay(); + NETDATA.globalSelectionSync.delay(); + + if(NETDATA.globalPanAndZoom.isMaster(this) === false) { + this.pauseChart(); + NETDATA.globalPanAndZoom.setMaster(this, after, before); + // NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.setMaster(this); + } + + if(this.updateChartPanOrZoomAsyncTimeOutId) + NETDATA.timeout.clear(this.updateChartPanOrZoomAsyncTimeOutId); + + NETDATA.timeout.set(function() { + that.updateChartPanOrZoomAsyncTimeOutId = undefined; + that.updateChartPanOrZoom(after, before, callback); + }, 0); + }; + + var __unitsConversionLastUnits = undefined; + var __unitsConversionLastUnitsDesired = undefined; + var __unitsConversionLastMin = undefined; + var __unitsConversionLastMax = undefined; + var __unitsConversion = function(value) { return value; }; + this.unitsConversionSetup = function(min, max) { + if(this.units !== __unitsConversionLastUnits + || this.units_desired !== __unitsConversionLastUnitsDesired + || min !== __unitsConversionLastMin + || max !== __unitsConversionLastMax) { + + __unitsConversionLastUnits = this.units; + __unitsConversionLastUnitsDesired = this.units_desired; + __unitsConversionLastMin = min; + __unitsConversionLastMax = max; + + __unitsConversion = NETDATA.unitsConversion.get(this.uuid, min, max, this.units, this.units_desired, this.units_common, function (units) { + // console.log('switching units from ' + that.units.toString() + ' to ' + units.toString()); + that.units_current = units; + that.legendSetUnitsString(that.units_current); + }); + } + }; + var __legendFormatValueChartDecimalsLastMin = undefined; var __legendFormatValueChartDecimalsLastMax = undefined; var __legendFormatValueChartDecimals = -1; @@ -2532,6 +3339,15 @@ var NETDATA = window.NETDATA || {}; if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax) return; + this.unitsConversionSetup(min, max); + if(__unitsConversion !== null) { + min = __unitsConversion(min); + max = __unitsConversion(max); + + if(typeof min !== 'number' || typeof max !== 'number') + return; + } + __legendFormatValueChartDecimalsLastMin = min; __legendFormatValueChartDecimalsLastMax = max; @@ -2576,7 +3392,13 @@ var NETDATA = window.NETDATA || {}; }; this.legendFormatValue = function(value) { - if(typeof value !== 'number') return '-'; + if(typeof value !== 'number') + return '-'; + + value = __unitsConversion(value); + + if(typeof value !== 'number') + return value; if(__intlNumberFormat !== null) return __intlNumberFormat.format(value); @@ -2619,11 +3441,11 @@ var NETDATA = window.NETDATA || {}; s = r = this.legendFormatValue(value); if(typeof series.last === 'number') { - if(v > series.last) s += ''; - else if(v < series.last) s += ''; - else s += ''; + if(v > series.last) s += ''; + else if(v < series.last) s += ''; + else s += ''; } - else s += ''; + else s += ''; series.last = v; } @@ -2647,22 +3469,22 @@ var NETDATA = window.NETDATA || {}; if(series.user !== null) series.user.innerText = s; }; - this.__legendSetDateString = function(date) { - if(date !== this.tmp.__last_shown_legend_date) { + this.legendSetDateString = function(date) { + if(this.element_legend_childs.title_date !== null && date !== this.tmp.__last_shown_legend_date) { this.element_legend_childs.title_date.innerText = date; this.tmp.__last_shown_legend_date = date; } }; - this.__legendSetTimeString = function(time) { - if(time !== this.tmp.__last_shown_legend_time) { + this.legendSetTimeString = function(time) { + if(this.element_legend_childs.title_time !== null && time !== this.tmp.__last_shown_legend_time) { this.element_legend_childs.title_time.innerText = time; this.tmp.__last_shown_legend_time = time; } }; - this.__legendSetUnitsString = function(units) { - if(units !== this.tmp.__last_shown_legend_units) { + this.legendSetUnitsString = function(units) { + if(this.element_legend_childs.title_units !== null && units !== this.tmp.__last_shown_legend_units) { this.element_legend_childs.title_units.innerText = units; this.tmp.__last_shown_legend_units = units; } @@ -2683,29 +3505,19 @@ var NETDATA = window.NETDATA || {}; if(this.legendSetDateLast.ms !== ms) { var d = new Date(ms); this.legendSetDateLast.ms = ms; - this.legendSetDateLast.date = d.toLocaleDateString(); - this.legendSetDateLast.time = d.toLocaleTimeString(); + this.legendSetDateLast.date = NETDATA.dateTime.localeDateString(d); + this.legendSetDateLast.time = NETDATA.dateTime.localeTimeString(d); } - if(this.element_legend_childs.title_date !== null) - this.__legendSetDateString(this.legendSetDateLast.date); - - if(this.element_legend_childs.title_time !== null) - this.__legendSetTimeString(this.legendSetDateLast.time); - - if(this.element_legend_childs.title_units !== null) - this.__legendSetUnitsString(this.units) + this.legendSetDateString(this.legendSetDateLast.date); + this.legendSetTimeString(this.legendSetDateLast.time); + this.legendSetUnitsString(this.units_current) }; this.legendShowUndefined = function() { - if(this.element_legend_childs.title_date !== null) - this.__legendSetDateString(' '); - - if(this.element_legend_childs.title_time !== null) - this.__legendSetTimeString(this.chart.name); - - if(this.element_legend_childs.title_units !== null) - this.__legendSetUnitsString(' '); + this.legendSetDateString(this.legendPluginModuleString(false)); + this.legendSetTimeString(this.chart.context.toString()); + // this.legendSetUnitsString(' '); if(this.data && this.element_legend_childs.series !== null) { var labels = this.data.dimension_names; @@ -2869,6 +3681,42 @@ var NETDATA = window.NETDATA || {}; return this.colors; }; + this.legendPluginModuleString = function(withContext) { + var str = ' '; + var context = ''; + + if(typeof this.chart !== 'undefined') { + if(withContext && typeof this.chart.context === 'string') + context = this.chart.context; + + if (typeof this.chart.plugin === 'string' && this.chart.plugin !== '') { + str = this.chart.plugin; + if (typeof this.chart.module === 'string' && this.chart.module !== '') { + str += '/' + this.chart.module; + } + + if (withContext && context !== '') + str += ', ' + context; + } + else if (withContext && context !== '') + str = context; + } + + return str; + }; + + this.legendResolutionTooltip = function () { + if(!this.chart) return ''; + + var collected = this.chart.update_every; + var viewed = (this.data)?this.data.view_update_every:collected; + + if(collected === viewed) + return "resolution " + NETDATA.seconds4human(collected); + + return "resolution " + NETDATA.seconds4human(viewed) + ", collected every " + NETDATA.seconds4human(collected); + }; + this.legendUpdateDOM = function() { var needed = false, dim, keys, len, i; @@ -2961,7 +3809,7 @@ var NETDATA = window.NETDATA || {}; label.name.innerHTML = '
'; var text = document.createTextNode(' ' + name); @@ -2998,14 +3846,14 @@ var NETDATA = window.NETDATA || {}; 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'), + 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: document.createElement('span'), title_time: document.createElement('span'), title_units: document.createElement('span'), @@ -3013,7 +3861,15 @@ var NETDATA = window.NETDATA || {}; series: {} }; - if(this.library.toolboxPanAndZoom !== null) { + if(NETDATA.options.current.legend_toolbox === true && this.library.toolboxPanAndZoom !== null) { + this.element_legend_childs.toolbox = document.createElement('div'); + this.element_legend_childs.toolbox_left = document.createElement('div'); + this.element_legend_childs.toolbox_right = document.createElement('div'); + this.element_legend_childs.toolbox_reset = document.createElement('div'); + this.element_legend_childs.toolbox_zoomin = document.createElement('div'); + this.element_legend_childs.toolbox_zoomout = document.createElement('div'); + this.element_legend_childs.toolbox_volume = document.createElement('div'); + var get_pan_and_zoom_step = function(event) { if (event.ctrlKey) return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control; @@ -3032,7 +3888,7 @@ var NETDATA = window.NETDATA || {}; 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 = ''; + this.element_legend_childs.toolbox_left.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left); this.element_legend_childs.toolbox_left.onclick = function(e) { e.preventDefault(); @@ -3052,12 +3908,12 @@ var NETDATA = window.NETDATA || {}; 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 drag it with your mouse or your finger (on touch devices).
Help, can be disabled from the settings.' + content: 'Pan the chart to the left. You can also drag it with your mouse or your finger (on touch devices).
Help can be disabled from the settings.' }); this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button'; - this.element_legend_childs.toolbox_reset.innerHTML = ''; + this.element_legend_childs.toolbox_reset.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset); this.element_legend_childs.toolbox_reset.onclick = function(e) { e.preventDefault(); @@ -3072,11 +3928,11 @@ var NETDATA = window.NETDATA || {}; 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 double click the chart contents with your mouse or your finger (on touch devices).
Help, can be disabled from the settings.' + content: 'Reset all the charts to their default auto-refreshing state. You can also double click the chart contents with your mouse or your finger (on touch devices).
Help can be disabled from the settings.' }); this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button'; - this.element_legend_childs.toolbox_right.innerHTML = ''; + this.element_legend_childs.toolbox_right.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right); this.element_legend_childs.toolbox_right.onclick = function(e) { e.preventDefault(); @@ -3100,7 +3956,7 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button'; - this.element_legend_childs.toolbox_zoomin.innerHTML = ''; + this.element_legend_childs.toolbox_zoomin.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin); this.element_legend_childs.toolbox_zoomin.onclick = function(e) { e.preventDefault(); @@ -3118,11 +3974,11 @@ var NETDATA = window.NETDATA || {}; 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.
Help, can be disabled from the settings.' + content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart, or press SHIFT or ALT and use the mouse wheel or 2-finger touchpad scroll to zoom in or out.
Help, can be disabled from the settings.' }); this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button'; - this.element_legend_childs.toolbox_zoomout.innerHTML = ''; + this.element_legend_childs.toolbox_zoomout.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout); this.element_legend_childs.toolbox_zoomout.onclick = function(e) { e.preventDefault(); @@ -3141,11 +3997,11 @@ var NETDATA = window.NETDATA || {}; 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.
Help, can be disabled from the settings.' + content: 'Zoom out the chart. You can also press SHIFT or ALT and use the mouse wheel, or 2-finger touchpad scroll to zoom in or out.
Help, can be disabled from the settings.' }); //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button'; - //this.element_legend_childs.toolbox_volume.innerHTML = ''; + //this.element_legend_childs.toolbox_volume.innerHTML = ''; //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) { @@ -3153,41 +4009,44 @@ var NETDATA = window.NETDATA || {}; //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 = ''; - 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 double click it or double tap it to reset between 2 states: the default and the one that fits all the values.
Help, can be disabled from the settings.' - }); + if(NETDATA.options.current.resize_charts === true) { + this.element_legend_childs.resize_handler = document.createElement('div'); + + this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler"; + this.element_legend_childs.resize_handler.innerHTML = ''; + 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 double click it or double tap it to reset between 2 states: the default and the one that fits all the values.
Help, can be disabled from the settings.' + }); - // mousedown event - this.element_legend_childs.resize_handler.onmousedown = - function(e) { + // mousedown event + this.element_legend_childs.resize_handler.onmousedown = + function (e) { + that.resizeHandler(e); + }; + + // touchstart event + this.element_legend_childs.resize_handler.addEventListener('touchstart', function (e) { that.resizeHandler(e); - }; + }, false); + } - // touchstart event - this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) { - that.resizeHandler(e); - }, false); + if(this.chart) { + this.element_legend_childs.title_date.title = this.legendPluginModuleString(true); + this.element_legend_childs.title_time.title = this.legendResolutionTooltip(); + } this.element_legend_childs.title_date.className += " netdata-legend-title-date"; this.element_legend.appendChild(this.element_legend_childs.title_date); @@ -3202,6 +4061,7 @@ var NETDATA = window.NETDATA || {}; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_units.className += " netdata-legend-title-units"; + this.element_legend_childs.title_units.innerText = this.units_current; this.element_legend.appendChild(this.element_legend_childs.title_units); this.tmp.__last_shown_legend_units = undefined; @@ -3213,6 +4073,8 @@ var NETDATA = window.NETDATA || {}; content.className = 'netdata-legend-series-content'; this.element_legend_childs.perfect_scroller.appendChild(content); + this.element_legend_childs.content = content; + if(NETDATA.options.current.show_help === true) $(content).popover({ container: "body", @@ -3351,37 +4213,62 @@ var NETDATA = window.NETDATA || {}; return ret; }; - this.chartURL = function() { - var after, before, points_multiplier = 1; - if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) { - this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq; + this.chartDataUniqueID = function() { + return this.id + ',' + this.library_name + ',' + this.dimensions + ',' + this.chartURLOptions(); + }; - after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000); - before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000); - this.view_after = after * 1000; - this.view_before = before * 1000; + this.chartURLOptions = function() { + var ret = ''; - 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; + if(this.override_options !== null) + ret = this.override_options.toString(); + else + ret = this.library.options(this); - 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(this.append_options !== null) + ret += '|' + this.append_options.toString(); + + ret += '|jsonwrap'; + + if(NETDATA.options.current.eliminate_zero_dimensions === true) + ret += '|nonzero'; + + return ret; + }; + + this.chartURL = function() { + var after, before, points_multiplier = 1; + if(NETDATA.globalPanAndZoom.isActive()) { + 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; + } - 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 { + 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.view_after = after * 1000; + this.view_before = before * 1000; - this.current.force_before_ms = null; - this.current.force_after_ms = null; + this.requested_padding = null; + points_multiplier = 1; + } } else { this.tm.pan_and_zoom_seq = 0; @@ -3398,26 +4285,22 @@ var NETDATA = window.NETDATA || {}; this.requested_after = after * 1000; this.requested_before = before * 1000; - this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint()); + var data_points; + if(NETDATA.options.force_data_points !== 0) { + data_points = NETDATA.options.force_data_points; + this.data_points = data_points; + } + else { + this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint()); + data_points = this.data_points * points_multiplier; + } // build the data URL this.data_url = this.host + this.chart.data_url; this.data_url += "&format=" + this.library.format(); - this.data_url += "&points=" + (this.data_points * points_multiplier).toString(); + this.data_url += "&points=" + (data_points).toString(); this.data_url += "&group=" + this.method; - - if(this.override_options !== null) - this.data_url += "&options=" + this.override_options.toString(); - else - 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(); + this.data_url += "&options=" + this.chartURLOptions(); if(after) this.data_url += "&after=" + after.toString(); @@ -3429,7 +4312,7 @@ var NETDATA = window.NETDATA || {}; this.data_url += "&dimensions=" + this.dimensions; 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.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + data_points.toString() + ' library: ' + this.library_name); }; this.redrawChart = function() { @@ -3445,19 +4328,24 @@ var NETDATA = window.NETDATA || {}; resizeChart(); this.data = data; - this.updates_counter++; - this.updates_since_last_unhide++; - this.updates_since_last_creation++; var started = Date.now(); + var view_update_every = data.view_update_every * 1000; + + + if(this.data_update_every !== view_update_every) { + if(this.element_legend_childs.title_time) + this.element_legend_childs.title_time.title = this.legendResolutionTooltip(); + } // if the result is JSON, find the latest update-every - this.data_update_every = data.view_update_every * 1000; + this.data_update_every = view_update_every; 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) { @@ -3549,6 +4437,58 @@ var NETDATA = window.NETDATA || {}; this.refresh_dt_element.innerText = this.refresh_dt_ms.toString(); }; + this.getSnapshotData = function(key) { + if(this.debug === true) + this.log('updating from snapshot: ' + key); + + if(typeof netdataSnapshotData.data[key] === 'undefined') { + this.log('snapshot does not include data for key "' + key + '"'); + return null; + } + + if(typeof netdataSnapshotData.data[key] !== 'string') { + this.log('snapshot data for key "' + key + '" is not string'); + return null; + } + + var uncompressed; + try { + uncompressed = netdataSnapshotData.uncompress(netdataSnapshotData.data[key]); + + if(uncompressed === null) { + this.log('uncompressed snapshot data for key ' + key + ' is null'); + return null; + } + + if(typeof uncompressed === 'undefined') { + this.log('uncompressed snapshot data for key ' + key + ' is undefined'); + return null; + } + } + catch(e) { + this.log('decompression of snapshot data for key ' + key + ' failed'); + console.log(e); + uncompressed = null; + } + + if(typeof uncompressed !== 'string') { + this.log('uncompressed snapshot data for key ' + key + ' is not string'); + return null; + } + + var data; + try { + data = JSON.parse(uncompressed); + } + catch(e) { + this.log('parsing snapshot data for key ' + key + ' failed'); + console.log(e); + data = null; + } + + return data; + }; + this.updateChart = function(callback) { if(this.debug === true) this.log('updateChart()'); @@ -3558,7 +4498,7 @@ var NETDATA = window.NETDATA || {}; this.log('I am already updating...'); if(typeof callback === 'function') - return callback(); + return callback(false, 'already running'); return; } @@ -3570,14 +4510,14 @@ var NETDATA = window.NETDATA || {}; this.log('I am not enabled'); if(typeof callback === 'function') - return callback(); + return callback(false, 'not enabled'); return; } if(canBeRendered() === false) { if(typeof callback === 'function') - return callback(); + return callback(false, 'cannot be rendered'); return; } @@ -3600,7 +4540,7 @@ var NETDATA = window.NETDATA || {}; error('chart library "' + this.library_name + '" is not available.'); if(typeof callback === 'function') - return callback(); + return callback(false, 'library not available'); return; } @@ -3609,17 +4549,40 @@ var NETDATA = window.NETDATA || {}; this.clearSelection(); this.chartURL(); - if(this.debug === true) - this.log('updating from ' + this.data_url); - NETDATA.statistics.refreshes_total++; NETDATA.statistics.refreshes_active++; if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max) NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active; + var ok = false; this.fetching_data = true; + if(netdataSnapshotData !== null) { + var key = this.chartDataUniqueID(); + var data = this.getSnapshotData(key); + if (data !== null) { + ok = true; + this.updateChartWithData(data); + } + else { + ok = false; + error('cannot get data from snapshot for key: "' + key + '"'); + that.tm.last_autorefreshed = Date.now(); + } + + NETDATA.statistics.refreshes_active--; + this.fetching_data = false; + + if(typeof callback === 'function') + callback(ok, 'snapshot'); + + return; + } + + if(this.debug === true) + this.log('updating from ' + this.data_url); + this.xhr = $.ajax( { url: this.data_url, cache: false, @@ -3633,6 +4596,7 @@ var NETDATA = window.NETDATA || {}; .done(function(data) { that.xhr = undefined; that.retries_on_data_failures = 0; + ok = true; if(that.debug === true) that.log('data received. updating chart.'); @@ -3662,11 +4626,14 @@ var NETDATA = window.NETDATA || {}; that.fetching_data = false; if(typeof callback === 'function') - return callback(); + return callback(ok, 'download'); }); }; var __isVisible = function() { + if(NETDATA.options.current.update_only_visible === false) + return true; + // tolerance is the number of pixels a chart can be off-screen // to consider it as visible and refresh it as if was visible var tolerance = 0; @@ -3697,6 +4664,7 @@ var NETDATA = window.NETDATA || {}; this.tmp.___isVisible___ = __isVisible(); if(this.tmp.___isVisible___ === true) unhideChart(); else hideChart(); + return this.tmp.___isVisible___; }; @@ -3705,18 +4673,16 @@ var NETDATA = window.NETDATA || {}; }; this.canBeAutoRefreshed = function() { - var now = Date.now(); - - if(this.running === true) { + if(this.enabled === false) { if(this.debug === true) - this.log('I am already running'); + this.log('canBeAutoRefreshed() -> not enabled'); return false; } - if(this.enabled === false) { + if(this.running === true) { if(this.debug === true) - this.log('I am not enabled'); + this.log('canBeAutoRefreshed() -> already running'); return false; } @@ -3724,99 +4690,112 @@ var NETDATA = window.NETDATA || {}; if(this.library === null || this.library.enabled === false) { error('charting library "' + this.library_name + '" is not available'); if(this.debug === true) - this.log('My chart library ' + this.library_name + ' is not available'); + this.log('canBeAutoRefreshed() -> chart library ' + this.library_name + ' is not available'); return false; } if(this.isVisible() === false) { if(NETDATA.options.debug.visibility === true || this.debug === true) - this.log('I am not visible'); + this.log('canBeAutoRefreshed() -> not visible'); return false; } + var now = Date.now(); + if(this.current.force_update_at !== 0 && this.current.force_update_at < now) { if(this.debug === true) - this.log('timed force update detected - allowing this update'); + this.log('canBeAutoRefreshed() -> timed force update - allowing this update'); this.current.force_update_at = 0; return true; } - if(this.isAutoRefreshable() === true) { - // allow the first update, even if the page is not visible - 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'); + if(this.isAutoRefreshable() === false) { + if(this.debug === true) + this.log('canBeAutoRefreshed() -> not auto-refreshable'); - return false; - } + return false; + } + + // allow the first update, even if the page is not visible + if(NETDATA.options.page_is_visible === false && this.updates_counter && this.updates_since_last_unhide) { + if(NETDATA.options.debug.focus === true || this.debug === true) + this.log('canBeAutoRefreshed() -> not the first update, and page does not have focus'); + + return false; + } + + if(this.needsRecreation() === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed() -> needs re-creation.'); + + return true; + } - if(this.needsRecreation() === true) { + if(NETDATA.options.auto_refresher_stop_until >= now) { + if(this.debug === true) + this.log('canBeAutoRefreshed() -> stopped until is in future.'); + + return false; + } + + // options valid only for autoRefresh() + if(NETDATA.globalPanAndZoom.isActive()) { + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) { if(this.debug === true) - this.log('canBeAutoRefreshed(): needs re-creation.'); + this.log('canBeAutoRefreshed(): global panning: I need an update.'); return true; } + else { + if(this.debug === true) + this.log('canBeAutoRefreshed(): global panning: I am already up to date.'); - // options valid only for autoRefresh() - if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) { - if(NETDATA.globalPanAndZoom.isActive()) { - if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): global panning: I need an update.'); - - return true; - } - else { - if(this.debug === true) - this.log('canBeAutoRefreshed(): global panning: I am already up to date.'); - - return false; - } - } + return false; + } + } - if(this.selected === true) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): I have a selection in place.'); + if(this.selected === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): I have a selection in place.'); - return false; - } + return false; + } - if(this.paused === true) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): I am paused.'); + if(this.paused === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): I am paused.'); - return false; - } + return false; + } - if(now - this.tm.last_autorefreshed >= this.data_update_every) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): It is time to update me.'); + if(now - this.tm.last_autorefreshed >= this.data_update_every) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): It is time to update me.'); - return true; - } - } + return true; } return false; }; this.autoRefresh = function(callback) { - if(this.canBeAutoRefreshed() === true && this.running === false) { - var state = this; + var state = that; + + if(state.canBeAutoRefreshed() === true && state.running === false) { state.running = true; state.updateChart(function() { state.running = false; - if(typeof callback !== 'undefined') + if(typeof callback === 'function') return callback(); }); } else { - if(typeof callback !== 'undefined') + if(typeof callback === 'function') return callback(); } }; @@ -3831,8 +4810,10 @@ var NETDATA = window.NETDATA || {}; if(this.title === null) this.title = chart.title; - if(this.units === null) + if(this.units === null) { this.units = chart.units; + this.units_current = this.units; + } }; // fetch the chart description from the netdata server @@ -3844,6 +4825,15 @@ var NETDATA = window.NETDATA || {}; if(typeof callback === 'function') return callback(); } + else if(netdataSnapshotData !== null) { + // console.log(this); + // console.log(NETDATA.chartRegistry); + NETDATA.error(404, 'host: ' + this.host + ', chart: ' + this.id); + error('chart not found in snapshot'); + + if(typeof callback === 'function') + return callback(); + } else { this.chart_url = "/api/v1/chart?chart=" + this.id; @@ -3882,7 +4872,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.resetAllCharts = function(state) { // first clear the global selection sync // to make sure no chart is in selected state - state.globalSelectionSyncStop(); + NETDATA.globalSelectionSync.stop(); // there are 2 possibilities here // a. state is the global Pan and Zoom master @@ -4036,18 +5026,86 @@ var NETDATA = window.NETDATA || {}; NETDATA.options.pause = false; }; + NETDATA.seconds4human = function (seconds, options) { + var default_options = { + now: 'now', + space: ' ', + negative_suffix: 'ago', + day: 'day', + days: 'days', + hour: 'hour', + hours: 'hours', + minute: 'min', + minutes: 'mins', + second: 'sec', + seconds: 'secs', + and: 'and' + }; + + if(typeof options !== 'object') + options = default_options; + else { + var x; + for(x in default_options) { + if(typeof options[x] !== 'string') + options[x] = default_options[x]; + } + } + + if(typeof seconds === 'string') + seconds = parseInt(seconds, 10); + + if(seconds === 0) + return options.now; + + var suffix = ''; + if(seconds < 0) { + seconds = -seconds; + if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; + } + + var days = Math.floor(seconds / 86400); + seconds -= (days * 86400); + + var hours = Math.floor(seconds / 3600); + seconds -= (hours * 3600); + + var minutes = Math.floor(seconds / 60); + seconds -= (minutes * 60); + + var strings = []; + + if(days > 1) strings.push(days.toString() + options.space + options.days); + else if(days === 1) strings.push(days.toString() + options.space + options.day); + + if(hours > 1) strings.push(hours.toString() + options.space + options.hours); + else if(hours === 1) strings.push(hours.toString() + options.space + options.hour); + + if(minutes > 1) strings.push(minutes.toString() + options.space + options.minutes); + else if(minutes === 1) strings.push(minutes.toString() + options.space + options.minute); + + if(seconds > 1) strings.push(Math.floor(seconds).toString() + options.space + options.seconds); + else if(seconds === 1) strings.push(Math.floor(seconds).toString() + options.space + options.second); + + if(strings.length === 1) + return strings.pop() + suffix; + + var last = strings.pop(); + return strings.join(", ") + " " + options.and + " " + last + suffix; + }; + // ---------------------------------------------------------------------------------------------------------------- // this is purely sequential charts refresher // it is meant to be autonomous - NETDATA.chartRefresherNoParallel = function(index) { + NETDATA.chartRefresherNoParallel = function(index, callback) { if(NETDATA.options.debug.main_loop === true) console.log('NETDATA.chartRefresherNoParallel(' + index + ')'); if(NETDATA.options.updated_dom === true) { // the dom has been updated // get the dom parts again - NETDATA.parseDom(NETDATA.chartRefresher); + NETDATA.parseDom(callback); return; } if(index >= NETDATA.options.targets.length) { @@ -4055,10 +5113,7 @@ var NETDATA = window.NETDATA || {}; console.log('waiting to restart main loop...'); NETDATA.options.auto_refresher_fast_weight = 0; - - setTimeout(function() { - NETDATA.chartRefresher(); - }, NETDATA.options.current.idle_between_loops); + callback(); } else { var state = NETDATA.options.targets[index]; @@ -4067,19 +5122,22 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.main_loop === true) console.log('fast rendering...'); - setTimeout(function() { - state.autoRefresh(function () { - NETDATA.chartRefresherNoParallel(++index); - }); - }, 0); + if(state.isVisible() === true) + NETDATA.timeout.set(function() { + state.autoRefresh(function () { + NETDATA.chartRefresherNoParallel(++index, callback); + }); + }, 0); + else + NETDATA.chartRefresherNoParallel(++index, callback); } else { if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...'); NETDATA.options.auto_refresher_fast_weight = 0; - setTimeout(function() { + NETDATA.timeout.set(function() { state.autoRefresh(function() { - NETDATA.chartRefresherNoParallel(++index); + NETDATA.chartRefresherNoParallel(++index, callback); }); }, NETDATA.options.current.idle_between_charts); } @@ -4091,27 +5149,92 @@ var NETDATA = window.NETDATA || {}; }; // the default refresher + NETDATA.chartRefresherLastRun = 0; + NETDATA.chartRefresherRunsAfterParseDom = 0; + NETDATA.chartRefresherTimeoutId = undefined; + + NETDATA.chartRefresherReschedule = function() { + if(NETDATA.options.current.async_on_scroll === true) { + if(NETDATA.chartRefresherTimeoutId) + NETDATA.timeout.clear(NETDATA.chartRefresherTimeoutId); + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set(NETDATA.chartRefresher, NETDATA.options.current.onscroll_worker_duration_threshold); + //console.log('chartRefresherReschedule()'); + } + }; + NETDATA.chartRefresher = function() { - // console.log('auto-refresher...'); + // console.log('chartRefresher() begin ' + (Date.now() - NETDATA.chartRefresherLastRun).toString() + ' ms since last run'); + + if(NETDATA.options.page_is_visible === false + && NETDATA.options.current.stop_updates_when_focus_is_lost === true + && NETDATA.chartRefresherLastRun > NETDATA.options.last_page_resize + && NETDATA.chartRefresherLastRun > NETDATA.options.last_page_scroll + && NETDATA.chartRefresherRunsAfterParseDom > 10 + ) { + setTimeout( + NETDATA.chartRefresher, + NETDATA.options.current.idle_lost_focus + ); + + // console.log('chartRefresher() page without focus, will run in ' + NETDATA.options.current.idle_lost_focus.toString() + ' ms, ' + NETDATA.chartRefresherRunsAfterParseDom.toString()); + return; + } + NETDATA.chartRefresherRunsAfterParseDom++; + + var now = Date.now(); + NETDATA.chartRefresherLastRun = now; + + if( now < NETDATA.options.on_scroll_refresher_stop_until ) { + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set( + NETDATA.chartRefresher, + NETDATA.chartRefresherWaitTime() + ); + + // console.log('chartRefresher() end1 will run in ' + NETDATA.chartRefresherWaitTime().toString() + ' ms'); + return; + } + + if( now < NETDATA.options.auto_refresher_stop_until ) { + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set( + NETDATA.chartRefresher, + NETDATA.chartRefresherWaitTime() + ); + + // console.log('chartRefresher() end2 will run in ' + NETDATA.chartRefresherWaitTime().toString() + ' ms'); + return; + } if(NETDATA.options.pause === true) { // console.log('auto-refresher is paused'); - setTimeout(NETDATA.chartRefresher, - NETDATA.chartRefresherWaitTime()); + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set( + NETDATA.chartRefresher, + NETDATA.chartRefresherWaitTime() + ); + + // console.log('chartRefresher() end3 will run in ' + NETDATA.chartRefresherWaitTime().toString() + ' ms'); return; } if(typeof NETDATA.options.pauseCallback === 'function') { // console.log('auto-refresher is calling pauseCallback'); + NETDATA.options.pause = true; NETDATA.options.pauseCallback(); NETDATA.chartRefresher(); + + // console.log('chartRefresher() end4 (nested)'); return; } if(NETDATA.options.current.parallel_refresher === false) { // console.log('auto-refresher is calling chartRefresherNoParallel(0)'); - NETDATA.chartRefresherNoParallel(0); + NETDATA.chartRefresherNoParallel(0, function() { + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set( + NETDATA.chartRefresher, + NETDATA.options.current.idle_between_loops + ); + }); + // console.log('chartRefresher() end5 (no parallel, nested)'); return; } @@ -4120,53 +5243,66 @@ var NETDATA = window.NETDATA || {}; // get the dom parts again // console.log('auto-refresher is calling parseDom()'); NETDATA.parseDom(NETDATA.chartRefresher); + // console.log('chartRefresher() end6 (parseDom)'); return; } - var parallel = []; - var targets = NETDATA.options.targets; - var len = targets.length; - var state; - while(len--) { - state = targets[len]; - if(state.isVisible() === false || state.running === true) - continue; - - if(state.library.initialized === false) { - if(state.library.enabled === true) { - state.library.initialize(NETDATA.chartRefresher); - return; - } - else { - state.error('chart library "' + state.library_name + '" is not enabled.'); + if(NETDATA.globalSelectionSync.active() === false) { + var parallel = []; + var targets = NETDATA.options.targets; + var len = targets.length; + var state; + while(len--) { + state = targets[len]; + if(state.isVisible() === false || state.running === true) + continue; + + if(state.library.initialized === false) { + if(state.library.enabled === true) { + state.library.initialize(NETDATA.chartRefresher); + //console.log('chartRefresher() end6 (library init)'); + return; + } + else { + state.error('chart library "' + state.library_name + '" is not enabled.'); + } } + + if(NETDATA.scrollUp === true) + parallel.unshift(state); + else + parallel.push(state); } - if(NETDATA.scrollUp === true) - parallel.unshift(state); - else - parallel.push(state); - } + len = parallel.length; + while (len--) { + state = parallel[len]; + // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts'); + // this will execute the jobs in parallel - if(parallel.length > 0) { - // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts'); - // this will execute the jobs in parallel - $(parallel).each(function() { - this.autoRefresh(); - }) + if (state.running === false) + NETDATA.timeout.set(state.autoRefresh, 0); + } + //else { + // console.log('auto-refresher nothing to do'); + //} } - //else { - // console.log('auto-refresher nothing to do'); - //} // run the next refresh iteration - setTimeout(NETDATA.chartRefresher, - NETDATA.chartRefresherWaitTime()); + NETDATA.chartRefresherTimeoutId = NETDATA.timeout.set( + NETDATA.chartRefresher, + NETDATA.chartRefresherWaitTime() + ); + + //console.log('chartRefresher() completed in ' + (Date.now() - now).toString() + ' ms'); }; NETDATA.parseDom = function(callback) { + //console.log('parseDom()'); + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.updated_dom = false; + NETDATA.chartRefresherRunsAfterParseDom = 0; var targets = $('div[data-netdata]'); //.filter(':visible'); @@ -4181,14 +5317,26 @@ var NETDATA = window.NETDATA || {}; NETDATA.options.targets.push(NETDATA.chartState(targets[len])); } + if(NETDATA.globalChartUnderlay.isActive() === true) + NETDATA.globalChartUnderlay.setup(); + else + NETDATA.globalChartUnderlay.clear(); + if(typeof callback === 'function') return callback(); }; // this is the main function - where everything starts + NETDATA.started = false; NETDATA.start = function() { // this should be called only once + if(NETDATA.started === true) { + console.log('netdata is already started'); + return; + } + + NETDATA.started = true; NETDATA.options.page_is_visible = true; $(window).blur(function() { @@ -4240,6 +5388,18 @@ var NETDATA = window.NETDATA || {}; netdataCallback(); }; + NETDATA.globalReset = function() { + NETDATA.globalSelectionSync.globalReset(); + NETDATA.globalPanAndZoom.globalReset(); + NETDATA.chartRegistry.globalReset(); + NETDATA.commonMin.globalReset(); + NETDATA.commonMax.globalReset(); + NETDATA.unitsConversion.globalReset(); + NETDATA.options.targets = null; + NETDATA.parseDom(); + NETDATA.unpause(); + }; + // ---------------------------------------------------------------------------------------------------------------- // peity @@ -4375,7 +5535,7 @@ var NETDATA = window.NETDATA || {}; var tooltipClassname = NETDATA.dataAttribute(state.element, 'sparkline-tooltipclassname', undefined); var tooltipFormat = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformat', undefined); var tooltipPrefix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipprefix', undefined); - var tooltipSuffix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipsuffix', ' ' + state.units); + var tooltipSuffix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipsuffix', ' ' + state.units_current); var tooltipSkipNull = NETDATA.dataAttributeBoolean(state.element, 'sparkline-tooltipskipnull', true); var tooltipValueLookups = NETDATA.dataAttribute(state.element, 'sparkline-tooltipvaluelookups', undefined); var tooltipFormatFieldlist = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformatfieldlist', undefined); @@ -4444,6 +5604,7 @@ var NETDATA = window.NETDATA || {}; }; $(state.element_chart).sparkline(data.result, state.sparkline_options); + return true; }; @@ -4462,10 +5623,11 @@ var NETDATA = window.NETDATA || {}; before = state.netdata_last; state.setMode('zoom'); - state.globalSelectionSyncStop(); - state.globalSelectionSyncDelay(); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); state.tmp.dygraph_user_action = true; state.tmp.dygraph_force_zoom = true; + // state.log('toolboxPanAndZoom'); state.updateChartPanOrZoom(after, before); NETDATA.globalPanAndZoom.setMaster(state, after, before); }; @@ -4473,15 +5635,17 @@ var NETDATA = window.NETDATA || {}; NETDATA.dygraphSetSelection = function(state, t) { if(typeof state.tmp.dygraph_instance !== 'undefined') { var r = state.calculateRowForTime(t); - if(r !== -1) + if(r !== -1) { state.tmp.dygraph_instance.setSelection(r); + return true; + } else { state.tmp.dygraph_instance.clearSelection(); state.legendShowUndefined(); } } - return true; + return false; }; NETDATA.dygraphClearSelection = function(state) { @@ -4562,6 +5726,10 @@ var NETDATA = window.NETDATA || {}; visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names) }; + if(!NETDATA.chartLibraries.dygraph.isSparkline(state)) { + options.ylabel = state.units_current; // (state.units_desired === 'auto')?"":state.units_current; + } + if(state.tmp.dygraph_force_zoom === true) { if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartUpdate() forced zoom update'); @@ -4602,6 +5770,12 @@ var NETDATA = window.NETDATA || {}; } } + if(netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() === true && NETDATA.globalPanAndZoom.isMaster(state) === false) { + // pan and zoom on snapshots + options.dateWindow = [ NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms ]; + options.isZoomedIgnoreProgrammaticZoom = true; + } + dygraph.updateOptions(options); var redraw = false; @@ -4662,7 +5836,7 @@ var NETDATA = window.NETDATA || {}; xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0), yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1), valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [ null, null ]), - ylabel: state.units, + ylabel: state.units_current, // (state.units_desired === 'auto')?"":state.units_current, yLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-ylabelwidth', 12), // the function to plot the chart @@ -4724,7 +5898,7 @@ var NETDATA = window.NETDATA || {}; ticker: Dygraph.dateTicker, axisLabelFormatter: function (d, gran) { void(gran); - return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); + return NETDATA.dateTime.xAxisTimeString(d); } }, y: { @@ -4737,11 +5911,41 @@ var NETDATA = window.NETDATA || {}; this.axes_[0].extremeRange[1] ); - return state.legendFormatValue(y); + var old_units = this.user_attrs_.ylabel; + var v = state.legendFormatValue(y); + var new_units = state.units_current; + + if(state.units_desired === 'auto' && typeof old_units !== 'undefined' && new_units !== old_units && !NETDATA.chartLibraries.dygraph.isSparkline(state)) { + // console.log(this); + // state.log('units discrepancy: old = ' + old_units + ', new = ' + new_units); + var len = this.plugins_.length; + while(len--) { + // console.log(this.plugins_[len]); + if(typeof this.plugins_[len].plugin.ylabel_div_ !== 'undefined' + && this.plugins_[len].plugin.ylabel_div_ !== null + && typeof this.plugins_[len].plugin.ylabel_div_.children !== 'undefined' + && this.plugins_[len].plugin.ylabel_div_.children !== null + && typeof this.plugins_[len].plugin.ylabel_div_.children[0].children !== 'undefined' + && this.plugins_[len].plugin.ylabel_div_.children[0].children !== null + ) { + this.plugins_[len].plugin.ylabel_div_.children[0].children[0].innerHTML = new_units; + this.user_attrs_.ylabel = new_units; + break; + } + } + + if(len < 0) + state.log('units discrepancy, but cannot find dygraphs div to change: old = ' + old_units + ', new = ' + new_units); + } + + return v; } } }, legendFormatter: function(data) { + if(state.tmp.dygraph_mouse_down === true) + return; + var elements = state.element_legend_childs; // if the hidden div is not there @@ -4763,6 +5967,16 @@ var NETDATA = window.NETDATA || {}; return ''; }, drawCallback: function(dygraph, is_initial) { + + // the user has panned the chart and this is called to re-draw the chart + // 1. refresh this chart by adding data to it + // 2. notify all the other charts about the update they need + + // to prevent an infinite loop (feedback), we use + // state.tmp.dygraph_user_action + // - when true, this is initiated by a user + // - when false, this is feedback + if(state.current.name !== 'auto' && state.tmp.dygraph_user_action === true) { state.tmp.dygraph_user_action = false; @@ -4771,20 +5985,27 @@ var NETDATA = window.NETDATA || {}; var before = Math.round(x_range[1]); if(NETDATA.options.debug.dygraph === true) - state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString()); + state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): mode ' + state.current.name + ' ' + (after / 1000).toString() + ' - ' + (before / 1000).toString()); + //console.log(state); if(before <= state.netdata_last && after >= state.netdata_first) + // update only when we are within the data limits state.updateChartPanOrZoom(after, before); } }, zoomCallback: function(minDate, maxDate, yRanges) { + + // the user has selected a range on the chart + // 1. refresh this chart by adding data to it + // 2. notify all the other charts about the update they need + void(yRanges); if(NETDATA.options.debug.dygraph === true) - state.log('dygraphZoomCallback()'); + state.log('dygraphZoomCallback(): ' + state.current.name); - state.globalSelectionSyncStop(); - state.globalSelectionSyncDelay(); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); state.setMode('zoom'); // refresh it to the greatest possible zoom level @@ -4795,9 +6016,6 @@ var NETDATA = window.NETDATA || {}; highlightCallback: function(event, x, points, row, seriesName) { void(seriesName); - if(NETDATA.options.debug.dygraph === true || state.debug === true) - state.log('dygraphHighlightCallback()'); - state.pauseChart(); // there is a bug in dygraph when the chart is zoomed enough @@ -4807,7 +6025,8 @@ var NETDATA = window.NETDATA || {}; // var t = state.data_after + row * 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(x); + if(state.tmp.dygraph_mouse_down !== true) + NETDATA.globalSelectionSync.sync(state, x); // fix legend zIndex using the internal structures of dygraph legend module // this works, but it is a hack! @@ -4816,11 +6035,41 @@ var NETDATA = window.NETDATA || {}; unhighlightCallback: function(event) { void(event); + if(state.tmp.dygraph_mouse_down === true) + return; + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphUnhighlightCallback()'); state.unpauseChart(); - state.globalSelectionSyncStop(); + NETDATA.globalSelectionSync.stop(); + }, + underlayCallback: function(canvas, area, g) { + + // the chart is about to be drawn + // this function renders global highlighted time-frame + + if(NETDATA.globalChartUnderlay.isActive()) { + var after = NETDATA.globalChartUnderlay.after; + var before = NETDATA.globalChartUnderlay.before; + + if(after < state.view_after) + after = state.view_after; + + if(before > state.view_before) + before = state.view_before; + + if(after < before) { + var bottom_left = g.toDomCoords(after, -20); + var top_right = g.toDomCoords(before, +20); + + var left = bottom_left[0]; + var right = top_right[0]; + + canvas.fillStyle = NETDATA.themes.current.highlight; + canvas.fillRect(left, area.y, right - left, area.h); + } + } }, interactionModel : { mousedown: function(event, dygraph, context) { @@ -4828,37 +6077,73 @@ var NETDATA = window.NETDATA || {}; state.log('interactionModel.mousedown()'); state.tmp.dygraph_user_action = true; - state.globalSelectionSyncStop(); if(NETDATA.options.debug.dygraph === true) state.log('dygraphMouseDown()'); - // Right-click should not initiate a zoom. + // Right-click should not initiate anything. if(event.button && event.button === 2) return; + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + + state.tmp.dygraph_mouse_down = true; context.initializeMouseDown(event, dygraph, context); + //console.log(event); if(event.button && event.button === 1) { - if (event.altKey || event.shiftKey) { + if (event.shiftKey) { + //console.log('middle mouse button dragging (PAN)'); + state.setMode('pan'); - state.globalSelectionSyncDelay(); + // NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_highlight_after = null; Dygraph.startPan(event, dygraph, context); } + else if(event.altKey || event.ctrlKey || event.metaKey) { + //console.log('middle mouse button highlight'); + + if (!(event.offsetX && event.offsetY)) { + event.offsetX = event.layerX - event.target.offsetLeft; + event.offsetY = event.layerY - event.target.offsetTop; + } + state.tmp.dygraph_highlight_after = dygraph.toDataXCoord(event.offsetX); + Dygraph.startZoom(event, dygraph, context); + } else { + //console.log('middle mouse button selection for zoom (ZOOM)'); + state.setMode('zoom'); - state.globalSelectionSyncDelay(); + // NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_highlight_after = null; Dygraph.startZoom(event, dygraph, context); } } else { - if (event.altKey || event.shiftKey) { + if (event.shiftKey) { + //console.log('left mouse button selection for zoom (ZOOM)'); + state.setMode('zoom'); - state.globalSelectionSyncDelay(); + // NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_highlight_after = null; + Dygraph.startZoom(event, dygraph, context); + } + else if(event.altKey || event.ctrlKey || event.metaKey) { + //console.log('left mouse button highlight'); + + if (!(event.offsetX && event.offsetY)) { + event.offsetX = event.layerX - event.target.offsetLeft; + event.offsetY = event.layerY - event.target.offsetTop; + } + state.tmp.dygraph_highlight_after = dygraph.toDataXCoord(event.offsetX); Dygraph.startZoom(event, dygraph, context); } else { + //console.log('left mouse button dragging (PAN)'); + state.setMode('pan'); - state.globalSelectionSyncDelay(); + // NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_highlight_after = null; Dygraph.startPan(event, dygraph, context); } } @@ -4867,35 +6152,98 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.mousemove()'); - if(context.isPanning) { + if(state.tmp.dygraph_highlight_after !== null) { + //console.log('highlight selection...'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + + state.tmp.dygraph_user_action = true; + Dygraph.moveZoom(event, dygraph, context); + event.preventDefault(); + } + else if(context.isPanning) { + //console.log('panning...'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; - state.globalSelectionSyncStop(); - state.globalSelectionSyncDelay(); + //NETDATA.globalSelectionSync.stop(); + //NETDATA.globalSelectionSync.delay(); state.setMode('pan'); context.is2DPan = false; Dygraph.movePan(event, dygraph, context); } else if(context.isZooming) { + //console.log('zooming...'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; - state.globalSelectionSyncStop(); - state.globalSelectionSyncDelay(); + //NETDATA.globalSelectionSync.stop(); + //NETDATA.globalSelectionSync.delay(); state.setMode('zoom'); Dygraph.moveZoom(event, dygraph, context); } }, mouseup: function(event, dygraph, context) { + state.tmp.dygraph_mouse_down = false; + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.mouseup()'); - if (context.isPanning) { + if(state.tmp.dygraph_highlight_after !== null) { + //console.log('done highlight selection'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + + if (!(event.offsetX && event.offsetY)){ + event.offsetX = event.layerX - event.target.offsetLeft; + event.offsetY = event.layerY - event.target.offsetTop; + } + + NETDATA.globalChartUnderlay.set(state + , state.tmp.dygraph_highlight_after + , dygraph.toDataXCoord(event.offsetX) + , state.view_after + , state.view_before + ); + + state.tmp.dygraph_highlight_after = null; + + context.isZooming = false; + dygraph.clearZoomRect_(); + dygraph.drawGraph_(false); + + // refresh all the charts immediately + NETDATA.options.auto_refresher_stop_until = 0; + } + else if (context.isPanning) { + //console.log('done panning'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; - state.globalSelectionSyncDelay(); Dygraph.endPan(event, dygraph, context); + + // refresh all the charts immediately + NETDATA.options.auto_refresher_stop_until = 0; } else if (context.isZooming) { + //console.log('done zomming'); + + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; - state.globalSelectionSyncDelay(); Dygraph.endZoom(event, dygraph, context); + + // refresh all the charts immediately + NETDATA.options.auto_refresher_stop_until = 0; } }, click: function(event, dygraph, context) { @@ -4984,8 +6332,8 @@ var NETDATA = window.NETDATA || {}; if(event.altKey || event.shiftKey) { state.tmp.dygraph_user_action = true; - state.globalSelectionSyncStop(); - state.globalSelectionSyncDelay(); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); // http://dygraphs.com/gallery/interaction-api.js var normal_def; @@ -5024,13 +6372,16 @@ var NETDATA = window.NETDATA || {}; } state.setMode('zoom'); - if(state.updateChartPanOrZoom(after, before) === true) + state.updateChartPanOrZoom(after, before, function() { dygraph.updateOptions({ dateWindow: [ after, before ] }); + }); event.preventDefault(); } }, touchstart: function(event, dygraph, context) { + state.tmp.dygraph_mouse_down = true; + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchstart()'); @@ -5038,6 +6389,9 @@ var NETDATA = window.NETDATA || {}; state.setMode('zoom'); state.pauseChart(); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + Dygraph.defaultInteractionModel.touchstart(event, dygraph, context); // we overwrite the touch directions at the end, to overwrite @@ -5056,25 +6410,39 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchmove()'); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchmove(event, dygraph, context); state.dygraph_last_touch_move = Date.now(); }, touchend: function(event, dygraph, context) { + state.tmp.dygraph_mouse_down = false; + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchend()'); + NETDATA.globalSelectionSync.stop(); + NETDATA.globalSelectionSync.delay(); + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchend(event, dygraph, context); // if it didn't move, it is a selection if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) { + NETDATA.globalSelectionSync.dont_sync_before = 0; + NETDATA.globalSelectionSync.setMaster(state); + // internal api of dygraph var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w; - var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct); - if(NETDATA.dygraphSetSelection(state, t) === true) - state.globalSelectionSync(t); + console.log('pct: ' + pct.toString()); + + var t = Math.round(state.view_after + (state.view_before - state.view_after) * pct); + if(NETDATA.dygraphSetSelection(state, t) === true) { + NETDATA.globalSelectionSync.sync(state, t); + } } // if it was double tap within double click time, reset the charts @@ -5089,6 +6457,9 @@ var NETDATA = window.NETDATA || {}; // remember the timestamp of the last touch end state.dygraph_last_touch_end = now; + + // refresh all the charts immediately + NETDATA.options.auto_refresher_stop_until = 0; } } }; @@ -5114,12 +6485,19 @@ var NETDATA = window.NETDATA || {}; } else state.tmp.dygraph_smooth_eligible = false; + if(netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() === true && NETDATA.globalPanAndZoom.isMaster(state) === false) { + // pan and zoom on snapshots + state.tmp.dygraph_options.dateWindow = [ NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms ]; + state.tmp.dygraph_options.isZoomedIgnoreProgrammaticZoom = true; + } + state.tmp.dygraph_instance = new Dygraph(state.element_chart, data.result.data, state.tmp.dygraph_options); state.tmp.dygraph_force_zoom = false; state.tmp.dygraph_user_action = false; state.tmp.dygraph_last_rendered = Date.now(); + state.tmp.dygraph_highlight_after = null; if(state.tmp.dygraph_options.valueRange[0] === null && state.tmp.dygraph_options.valueRange[1] === null) { if (typeof state.tmp.dygraph_instance.axes_[0].extremeRange !== 'undefined') { @@ -5356,7 +6734,7 @@ var NETDATA = window.NETDATA || {}; type: 'timeseries', tick: { format: function(x) { - return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds()); + return NETDATA.dateTime.xAxisTimeString(x); } } } @@ -5496,7 +6874,7 @@ var NETDATA = window.NETDATA || {}; } }, vAxis: { - title: state.units, + title: state.units_current, viewWindowMode: 'pretty', minValue: -0.1, maxValue: 0.1, @@ -5578,6 +6956,8 @@ var NETDATA = window.NETDATA || {}; if(min > value) min = value; if(max < value) max = value; + state.legendFormatValueDecimalsFromMinMax(min, max); + if(state.tmp.easyPieChartMin === null && min > 0) min = 0; if(state.tmp.easyPieChartMax === null && max < 0) max = 0; @@ -5635,8 +7015,8 @@ var NETDATA = window.NETDATA || {}; NETDATA.easypiechartClearSelection = function(state) { if(typeof state.tmp.easyPieChartEvent !== 'undefined') { - if(state.tmp.easyPieChartEvent.timer !== undefined) { - clearTimeout(state.tmp.easyPieChartEvent.timer); + if(state.tmp.easyPieChartEvent.timer) { + NETDATA.timeout.clear(state.tmp.easyPieChartEvent.timer); } state.tmp.easyPieChartEvent.timer = undefined; @@ -5682,10 +7062,10 @@ var NETDATA = window.NETDATA || {}; if(state.tmp.easyPieChartEvent.timer === undefined) { state.tmp.easyPieChart_instance.disableAnimation(); - state.tmp.easyPieChartEvent.timer = setTimeout(function() { + state.tmp.easyPieChartEvent.timer = NETDATA.timeout.set(function() { state.tmp.easyPieChartEvent.timer = undefined; state.tmp.easyPieChart_instance.update(state.tmp.easyPieChartEvent.pcent); - }, NETDATA.options.current.charts_selection_animation_delay); + }, 0); } return true; @@ -5731,10 +7111,6 @@ var NETDATA = window.NETDATA || {}; else state.tmp.easyPieChartMax = max; - var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); - - chart.data('data-percent', pcent); - var size = state.chartWidth(); var stroke = Math.floor(size / 22); if(stroke < 3) stroke = 2; @@ -5762,7 +7138,7 @@ var NETDATA = window.NETDATA || {}; var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40)); state.tmp.easyPieChartUnits = document.createElement('span'); state.tmp.easyPieChartUnits.className = 'easyPieChartUnits'; - state.tmp.easyPieChartUnits.innerText = state.units; + state.tmp.easyPieChartUnits.innerText = state.units_current; state.tmp.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; state.tmp.easyPieChartUnits.style.top = unittop.toString() + 'px'; state.element_chart.appendChild(state.tmp.easyPieChartUnits); @@ -5777,6 +7153,9 @@ var NETDATA = window.NETDATA || {}; barColor = tmp; } + var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); + chart.data('data-percent', pcent); + chart.easyPieChart({ barColor: barColor, trackColor: NETDATA.dataAttribute(state.element, 'easypiechart-trackcolor', NETDATA.themes.current.easypiechart_track), @@ -5801,6 +7180,16 @@ var NETDATA = window.NETDATA || {}; if(animate === false) state.tmp.easyPieChart_instance.disableAnimation(); state.tmp.easyPieChart_instance.update(pcent); if(animate === false) state.tmp.easyPieChart_instance.enableAnimation(); + + state.legendSetUnitsString = function(units) { + if(typeof state.tmp.easyPieChartUnits !== 'undefined') + state.tmp.easyPieChartUnits.innerText = units; + }; + state.legendShowUndefined = function() { + if(typeof state.tmp.easyPieChart_instance !== 'undefined') + NETDATA.easypiechartClearSelection(state); + }; + return true; }; @@ -5861,6 +7250,8 @@ var NETDATA = window.NETDATA || {}; else if(min === max) max = min + 1; + state.legendFormatValueDecimalsFromMinMax(min, max); + // gauge.js has an issue if the needle // is smaller than min or larger than max // when we set the new values @@ -5901,8 +7292,8 @@ var NETDATA = window.NETDATA || {}; NETDATA.gaugeClearSelection = function(state) { if(typeof state.tmp.gaugeEvent !== 'undefined') { - if(state.tmp.gaugeEvent.timer !== undefined) { - clearTimeout(state.tmp.gaugeEvent.timer); + if(state.tmp.gaugeEvent.timer) { + NETDATA.timeout.clear(state.tmp.gaugeEvent.timer); } state.tmp.gaugeEvent.timer = undefined; @@ -5955,10 +7346,10 @@ var NETDATA = window.NETDATA || {}; if(state.tmp.gaugeEvent.timer === undefined) { NETDATA.gaugeAnimation(state, false); - state.tmp.gaugeEvent.timer = setTimeout(function() { + state.tmp.gaugeEvent.timer = NETDATA.timeout.set(function() { state.tmp.gaugeEvent.timer = undefined; NETDATA.gaugeSet(state, state.tmp.gaugeEvent.value, state.tmp.gaugeEvent.min, state.tmp.gaugeEvent.max); - }, NETDATA.options.current.charts_selection_animation_delay); + }, 0); } return true; @@ -5968,10 +7359,8 @@ var NETDATA = window.NETDATA || {}; var value, min, max; if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) { - value = 0; - min = 0; - max = 1; NETDATA.gaugeSetLabels(state, null, null, null); + state.tmp.gauge_instance.set(0); } else { value = data.result[0]; @@ -5985,10 +7374,10 @@ var NETDATA = window.NETDATA || {}; if(state.tmp.gaugeMin === null && min > 0) min = 0; if(state.tmp.gaugeMax === null && max < 0) max = 0; + NETDATA.gaugeSet(state, value, min, max); NETDATA.gaugeSetLabels(state, value, min, max); } - NETDATA.gaugeSet(state, value, min, max); return true; }; @@ -6003,7 +7392,7 @@ var NETDATA = window.NETDATA || {}; var strokeColor = NETDATA.dataAttribute(state.element, 'gauge-stroke-color', NETDATA.themes.current.gauge_stroke); var startColor = NETDATA.dataAttribute(state.element, 'gauge-start-color', state.chartCustomColors()[0]); var stopColor = NETDATA.dataAttribute(state.element, 'gauge-stop-color', void 0); - var generateGradient = NETDATA.dataAttributeBoolean(state.element, 'gauge-generate-gradient', false); + var generateGradient = NETDATA.dataAttribute(state.element, 'gauge-generate-gradient', false); if(min === null) { min = NETDATA.commonMin.get(state); @@ -6122,7 +7511,7 @@ var NETDATA = window.NETDATA || {}; var unitfontsize = Math.round(titlefontsize * 0.9); state.tmp.gaugeChartUnits = document.createElement('span'); state.tmp.gaugeChartUnits.className = 'gaugeChartUnits'; - state.tmp.gaugeChartUnits.innerText = state.units; + state.tmp.gaugeChartUnits.innerText = state.units_current; state.tmp.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; state.element_chart.appendChild(state.tmp.gaugeChartUnits); @@ -6161,6 +7550,20 @@ var NETDATA = window.NETDATA || {}; NETDATA.gaugeSet(state, value, min, max); NETDATA.gaugeSetLabels(state, value, min, max); NETDATA.gaugeAnimation(state, true); + + state.legendSetUnitsString = function(units) { + if(typeof state.tmp.gaugeChartUnits !== 'undefined') { + state.tmp.gaugeChartUnits.innerText = units; + state.tmp.___gaugeOld__.valueLabel = null; + state.tmp.___gaugeOld__.minLabel = null; + state.tmp.___gaugeOld__.maxLabel = null; + } + }; + state.legendShowUndefined = function() { + if(typeof state.tmp.gauge_instance !== 'undefined') + NETDATA.gaugeClearSelection(state); + }; + return true; }; @@ -6173,7 +7576,7 @@ var NETDATA = window.NETDATA || {}; create: NETDATA.dygraphChartCreate, update: NETDATA.dygraphChartUpdate, resize: function(state) { - if(typeof state.tmp.dygraph_instance.resize === 'function') + if(typeof state.tmp.dygraph_instance !== 'undefined' && typeof state.tmp.dygraph_instance.resize === 'function') state.tmp.dygraph_instance.resize(); }, setSelection: NETDATA.dygraphSetSelection, @@ -6394,7 +7797,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.requiredJs = [ { - url: NETDATA.serverDefault + 'lib/bootstrap-3.3.7.min.js', + url: NETDATA.serverStatic + 'lib/bootstrap-3.3.7.min.js', async: false, isAlreadyLoaded: function() { // check if bootstrap is loaded @@ -6406,7 +7809,12 @@ var NETDATA = window.NETDATA || {}; } }, { - url: NETDATA.serverDefault + 'lib/perfect-scrollbar-0.6.15.min.js', + url: NETDATA.serverStatic + 'lib/fontawesome-all-5.0.1.min.js', + async: true, + isAlreadyLoaded: function() { return false; } + }, + { + url: NETDATA.serverStatic + 'lib/perfect-scrollbar-0.6.15.min.js', isAlreadyLoaded: function() { return false; } } ]; @@ -6418,10 +7826,6 @@ var NETDATA = window.NETDATA || {}; return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true); } }, - { - url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.7.0', - isAlreadyLoaded: function() { return false; } - }, { url: NETDATA.themes.current.dashboard_css, isAlreadyLoaded: function() { return false; } @@ -6619,7 +8023,7 @@ var NETDATA = window.NETDATA || {}; body: entry.hostname + ' - ' + entry.chart + ' (' + entry.family + ') - ' + status + ': ' + entry.info, tag: tag, requireInteraction: interaction, - icon: NETDATA.serverDefault + icon, + icon: NETDATA.serverStatic + icon, data: data }); @@ -6753,6 +8157,9 @@ var NETDATA = window.NETDATA || {}; }, update_forever: function() { + if(netdataShowAlarms !== true || netdataSnapshotData !== null) + return; + NETDATA.alarms.get('active', function(data) { if(data !== null) { NETDATA.alarms.current = data; diff --git a/web/dashboard.slate.css b/web/dashboard.slate.css index 7445d532c..00e0d0dce 100644 --- a/web/dashboard.slate.css +++ b/web/dashboard.slate.css @@ -58,6 +58,8 @@ code { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -68,6 +70,8 @@ code { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -84,6 +88,8 @@ code { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* required for child elements to have absolute position */ position: relative; @@ -107,6 +113,8 @@ code { display: inline-block; overflow: hidden; + transform: translate3d(0,0,0); + /* fix minimum scrollbar issue in firefox */ min-height: 99px; @@ -122,9 +130,9 @@ code { bottom: 0px; right: 0px; height: 15px; - width: 30px; + width: 20px; background-color: #272b30; - font-size: 12px; + font-size: 15px; vertical-align: middle; line-height: 15px; cursor: ns-resize; @@ -300,6 +308,7 @@ code { white-space: nowrap; display: inline-block; cursor: pointer; + -webkit-print-color-adjust: exact; } .netdata-legend-value { diff --git a/web/dashboard_info.js b/web/dashboard_info.js index bb2f95991..a3c48640c 100644 --- a/web/dashboard_info.js +++ b/web/dashboard_info.js @@ -9,142 +9,142 @@ var netdataDashboard = window.netdataDashboard || {}; netdataDashboard.menu = { 'system': { title: 'System Overview', - icon: '', + icon: '', info: 'Overview of the key system metrics.' }, 'services': { title: 'systemd Services', - icon: '', + icon: '', info: 'Resources utilization of systemd services. netdata monitors all systemd services via cgroups (the resources accounting used by containers). ' }, 'ap': { title: 'Access Points', - icon: '', + icon: '', info: 'Performance metrics for the access points (i.e. wireless interfaces in AP mode) found on the system.' }, 'tc': { title: 'Quality of Service', - icon: '', + icon: '', info: 'Netdata collects and visualizes tc class utilization using its tc-helper plugin. If you also use FireQOS for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). QoS data collection may have a slight time difference compared to the interface (QoS data collection uses a BASH script, so a shift in data collection of a few milliseconds should be justified).' }, 'net': { title: 'Network Interfaces', - icon: '', + icon: '', info: 'Performance metrics for network interfaces.' }, 'ipv4': { title: 'IPv4 Networking', - icon: '', + icon: '', info: 'Metrics for the IPv4 stack of the system. Internet Protocol version 4 (IPv4) is the fourth version of the Internet Protocol (IP). It is one of the core protocols of standards-based internetworking methods in the Internet. IPv4 is a connectionless protocol for use on packet-switched networks. It operates on a best effort delivery model, in that it does not guarantee delivery, nor does it assure proper sequencing or avoidance of duplicate delivery. These aspects, including data integrity, are addressed by an upper layer transport protocol, such as the Transmission Control Protocol (TCP).' }, 'ipv6': { title: 'IPv6 Networking', - icon: '', + icon: '', info: 'Metrics for the IPv6 stack of the system. Internet Protocol version 6 (IPv6) is the most recent version of the Internet Protocol (IP), the communications protocol that provides an identification and location system for computers on networks and routes traffic across the Internet. IPv6 was developed by the Internet Engineering Task Force (IETF) to deal with the long-anticipated problem of IPv4 address exhaustion. IPv6 is intended to replace IPv4.' }, 'ipvs': { title: 'IP Virtual Server', - icon: '', + icon: '', info: 'IPVS (IP Virtual Server) implements transport-layer load balancing inside the Linux kernel, so called Layer-4 switching. IPVS running on a host acts as a load balancer at the front of a cluster of real servers, it can direct requests for TCP/UDP based services to the real servers, and makes services of the real servers to appear as a virtual service on a single IP address.' }, 'netfilter': { title: 'Firewall (netfilter)', - icon: '', + icon: '', info: 'Performance metrics of the netfilter components.' }, 'ipfw': { title: 'Firewall (ipfw)', - icon: '', + icon: '', info: 'Counters and memory usage for the ipfw rules.' }, 'cpu': { title: 'CPUs', - icon: '', + icon: '', info: 'Detailed information for each CPU of the system. A summary of the system for all CPUs can be found at the System Overview section.' }, 'mem': { title: 'Memory', - icon: '', + icon: '', info: 'Detailed information about the memory management of the system.' }, 'disk': { title: 'Disks', - icon: '', + icon: '', info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with iostat -x. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by configuring the relative settings in the netdata configuration file.' }, 'sensors': { title: 'Sensors', - icon: '', + icon: '', info: 'Readings of the configured system sensors.' }, 'ipmi': { title: 'IPMI', - icon: '', + icon: '', info: 'The Intelligent Platform Management Interface (IPMI) is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system\'s CPU, firmware (BIOS or UEFI) and operating system.' }, 'samba': { title: 'Samba', - icon: "", + icon: '', info: 'Performance metrics of the Samba file share operations of this system. Samba is a implementation of Windows services, including Windows SMB protocol file shares.' }, 'nfsd': { title: 'NFS Server', - icon: '', + icon: '', info: 'Performance metrics of the Network File Server. NFS is a distributed file system protocol, allowing a user on a client computer to access files over a network, much like local storage is accessed. NFS, like many other protocols, builds on the Open Network Computing Remote Procedure Call (ONC RPC) system. The NFS is an open standard defined in Request for Comments (RFC).' }, 'nfs': { title: 'NFS Client', - icon: '', + icon: '', info: 'Performance metrics of the NFS operations of this system, acting as an NFS client.' }, 'zfs': { title: 'ZFS filesystem', - icon: '', + icon: '', info: 'Performance metrics of the ZFS filesystem. The following charts visualize all metrics reported by arcstat.py and arc_summary.py.' }, 'apps': { title: 'Applications', - icon: '', + icon: '', info: 'Per application statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics for applications of interest, defined in /etc/netdata/apps_groups.conf (the default is here). The plugin internally builds a process tree (much like ps fax does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'users': { title: 'Users', - icon: '', + icon: '', info: 'Per user statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics per user. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'groups': { title: 'User Groups', - icon: '', + icon: '', info: 'Per user group statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics per user group. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'netdata': { title: 'Netdata Monitoring', - icon: '', + icon: '', info: 'Performance metrics for the operation of netdata itself and its plugins.' }, @@ -155,163 +155,175 @@ netdataDashboard.menu = { 'cgroup': { title: '', - icon: '', + icon: '', info: 'Container resource utilization metrics. Netdata reads this information from cgroups (abbreviated from control groups), a Linux kernel feature that limits and accounts resource usage (CPU, memory, disk I/O, network, etc.) of a collection of processes. cgroups together with namespaces (that offer isolation between processes) provide what we usually call: containers.' }, 'cgqemu': { title: '', - icon: '', + icon: '', info: 'QEMU virtual machine resource utilization metrics. QEMU (short for Quick Emulator) is a free and open-source hosted hypervisor that performs hardware virtualization.' }, 'fping': { title: 'fping', - icon: '', + icon: '', info: 'Network latency statistics, via fping. fping is a program to send ICMP echo probes to network hosts, similar to ping, but much better performing when pinging multiple hosts. fping versions after 3.15 can be directly used as netdata plugins.' }, 'memcached': { title: 'memcached', - icon: '', + icon: '', info: 'Performance metrics for memcached. Memcached is a general-purpose distributed memory caching system. It is often used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source (such as a database or API) must be read.' }, 'mysql': { title: 'MySQL', - icon: '', + icon: '', info: 'Performance metrics for mysql, the open-source relational database management system (RDBMS).' }, 'postgres': { title: 'Postgres', - icon: '', + icon: '', info: 'Performance metrics for PostgresSQL, the object-relational database (ORDBMS).' }, 'redis': { title: 'Redis', - icon: '', + icon: '', info: 'Performance metrics for redis. Redis (REmote DIctionary Server) is a software project that implements data structure servers. It is open-source, networked, in-memory, and stores keys with optional durability.' }, 'retroshare': { title: 'RetroShare', - icon: '', + icon: '', info: 'Performance metrics for RetroShare. RetroShare is open source software for encrypted filesharing, serverless email, instant messaging, online chat, and BBS, based on a friend-to-friend network built on GNU Privacy Guard (GPG).' }, 'ipfs': { title: 'IPFS', - icon: '', + icon: '', info: 'Performance metrics for the InterPlanetary File System (IPFS), a content-addressable, peer-to-peer hypermedia distribution protocol.' }, 'phpfpm': { title: 'PHP-FPM', - icon: '', + icon: '', info: 'Performance metrics for PHP-FPM, an alternative FastCGI implementation for PHP.' }, 'postfix': { title: 'postfix', - icon: '', + icon: '', info: undefined }, 'dovecot': { title: 'Dovecot', - icon: '', + icon: '', info: undefined }, 'hddtemp': { title: 'HDD Temp', - icon: '', + icon: '', info: undefined }, 'nginx': { title: 'nginx', - icon: '', + icon: '', info: undefined }, 'apache': { title: 'Apache', - icon: '', + icon: '', info: undefined }, 'lighttpd': { title: 'Lighttpd', - icon: '', + icon: '', info: undefined }, 'web_log': { title: undefined, - icon: '', + icon: '', info: 'Information extracted from a server log file. web_log plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' }, 'named': { title: 'named', - icon: '', + icon: '', info: undefined }, 'squid': { title: 'squid', - icon: '', + icon: '', info: undefined }, 'nut': { title: 'UPS', - icon: '', + icon: '', info: undefined }, 'apcupsd': { title: 'UPS', - icon: '', + icon: '', info: undefined }, 'smawebbox': { title: 'Solar Power', - icon: '', + icon: '', info: undefined }, 'fronius': { title: 'Fronius', - icon: '', + icon: '', info: undefined }, 'stiebeleltron': { title: 'Stiebel Eltron', - icon: '', + icon: '', info: undefined }, 'snmp': { title: 'SNMP', - icon: '', + icon: '', info: undefined }, 'go_expvar': { title: 'Go - expvars', - icon: '', + icon: '', info: 'Statistics about running Go applications exposed by the expvar package.' }, 'chrony': { - icon: '', + icon: '', info: 'chronyd parameters about the system’s clock performance.' + }, + + 'couchdb': { + icon: '', + info: 'Performance metrics for CouchDB, the open-source, JSON document-based database with an HTTP API and multi-master replication.' + }, + + + 'beanstalk': { + title: 'Beanstalkd', + icon: '', + info: 'Provides statistics on the beanstalkd server and any tubes available on that server using data pulled from beanstalkc' } }; @@ -434,6 +446,30 @@ netdataDashboard.submenu = { 'go_expvar.memstats': { title: 'Memory statistics', info: 'Go runtime memory statistics. See runtime.MemStats documentation for more info about each chart and the values.' + }, + + 'couchdb.dbactivity': { + title: 'DB activity', + info: 'Overall database reads and writes for the entire server. This includes any external HTTP traffic, as well as internal replication traffic performed in a cluster to ensure node consistency.' + }, + + 'couchdb.httptraffic': { + title: 'HTTP traffic breakdown', + info: 'All HTTP traffic, broken down by type of request (GET, PUT, POST, etc.) and response status code (200, 201, 4xx, etc.)

Any 5xx errors here indicate a likely CouchDB bug; check the logfile for further information.' + }, + + 'couchdb.ops': { + title: 'Server operations' + }, + + 'couchdb.perdbstats': { + title: 'Per-DB statistics', + info: 'Statistics per database. This includes 3 size graphs per database: active (the size of live data in the database), external (the uncompressed size of the database contents), and file (the size of the file on disk, exclusive of any views and indexes). It also includes the number of documents and number of deleted documents per database.' + }, + + 'couchdb.erlang': { + title: 'Erlang statistics', + info: 'Detailed information about the status of the Erlang VM that hosts CouchDB. These are intended for advanced users only. High values of the peak message queue (>10e6) generally indicate an overload condition.' } }; @@ -468,7 +504,18 @@ netdataDashboard.context = { }, 'system.io': { - info: 'Total Disk I/O, for all disks. You can get detailed information about each disk at the Disks section and per application Disk usage at the Applications Monitoring section.' + info: function(os) { + var s = 'Total Disk I/O, for all physical disks. You can get detailed information about each disk at the Disks section and per application Disk usage at the Applications Monitoring section.'; + + if(os === 'linux') + return s + ' Physical are all the disks that are listed in /sys/block, but do not exist in /sys/devices/virtual/block.'; + else + return s; + } + }, + + 'system.pgpgio': { + info: 'Memory paged from/to disk. This is usually the total disk I/O of the system.' }, 'system.swapio': { @@ -518,6 +565,17 @@ netdataDashboard.context = { info: 'Idle jitter is calculated by netdata. A thread is spawned that requests to sleep for a few microseconds. When the system wakes it up, it measures how many microseconds have passed. The difference between the requested and the actual duration of the sleep, is the idle jitter. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).' }, + 'system.net': { + info: function(os) { + var s = 'Total bandwidth of all physical network interfaces. This does not include lo, VPNs, network bridges, IFB devices, bond interfaces, etc. Only the bandwidth of physical network interfaces is aggregated.'; + + if(os === 'linux') + return s + ' Physical are all the network interfaces that are listed in /proc/net/dev, but do not exist in /sys/devices/virtual/net.'; + else + return s; + } + }, + 'system.ipv4': { info: 'Total IPv4 Traffic.' }, @@ -1151,6 +1209,63 @@ netdataDashboard.context = { } ] }, + + // ------------------------------------------------------------------------ + // beanstalkd + // system charts + 'beanstalk.cpu_usage': { + info: 'Amount of CPU Time for user and system used by beanstalkd.' + }, + + // This is also a per-tube stat + 'beanstalk.jobs_rate': { + info: 'The rate of jobs processed by the beanstalkd served.' + }, + + 'beanstalk.connections_rate': { + info: 'Tthe rate of connections opened to beanstalkd.' + }, + + 'beanstalk.commands_rate': { + info: 'The rate of commands received by beanstalkd.' + }, + + 'beanstalk.current_tubes': { + info: 'Total number of current tubes on the server including the default tube (which always exists).' + }, + + 'beanstalk.current_jobs': { + info: 'Current number of jobs in all tubes grouped by status: urgent, ready, reserved, delayed and buried.' + }, + + 'beanstalk.current_connections': { + info: 'Current number of connections group by connection type: written, producers, workers, waiting.' + }, + + 'beanstalk.binlog': { + info: 'The rate of records written to binlog and migrated as part of compaction.' + }, + + 'beanstalk.uptime': { + info: 'Total time beanstalkd server has been up for.' + }, + + // tube charts + 'beanstalk.jobs': { + info: 'Number of jobs currently in the tube grouped by status: urgent, ready, reserved, delayed and buried.' + }, + + 'beanstalk.connections': { + info: 'The current number of connections to this tube grouped by connection type; using, waiting and watching.' + }, + + 'beanstalk.commands': { + info: 'The rate of delete and pause commands executed by beanstalkd.' + }, + + 'beanstalk.pause': { + info: 'Shows info on how long the tube has been paused for, and how long is left remaining on the pause.' + }, // ------------------------------------------------------------------------ // web_log @@ -1534,5 +1649,17 @@ netdataDashboard.context = { info: 'The estimated error bound on the frequency.', height: 0.5, colors: NETDATA.colors[5] + }, + + 'couchdb.active_tasks': { + info: 'Active tasks running on this CouchDB cluster. Four types of tasks currently exist: indexer (view building), replication, database compaction and view compaction.' + }, + + 'couchdb.replicator_jobs': { + info: 'Detailed breakdown of any replication jobs in progress on this node. For more information, see the replicator documentation.' + }, + + 'couchdb.open_files': { + info: 'Count of all files held open by CouchDB. If this value seems pegged at 1024 or 4096, your server process is probably hitting the open file handle limit and needs to be increased.' } }; diff --git a/web/index.html b/web/index.html index cd8239d26..e3c039091 100644 --- a/web/index.html +++ b/web/index.html @@ -46,6 +46,12 @@