X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=web%2Fdashboard.js;h=b34accdecb0948a45a1769631e1f2d5a02681b8a;hb=b060bae83cc36fdc4f14120228e4c9b0d1ec42e8;hp=01af8a67998c59da161463e7b11b54ea44adb302;hpb=89059a7d848801a20154ab764c86b04c68d03ffd;p=netdata.git diff --git a/web/dashboard.js b/web/dashboard.js index 01af8a67..b34accde 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -1,36 +1,57 @@ +// ---------------------------------------------------------------------------- // You can set the following variables before loading this script: -// -// var netdataNoDygraphs = true; // do not use dygraph -// var netdataNoSparklines = true; // do not use sparkline -// var netdataNoPeitys = true; // do not use peity -// var netdataNoGoogleCharts = true; // do not use google -// var netdataNoMorris = true; // do not use morris -// var netdataNoEasyPieChart = true; // do not use easy pie chart -// var netdataNoGauge = true; // do not use gauge.js -// var netdataNoD3 = true; // do not use D3 -// var netdataNoC3 = true; // do not use C3 -// var netdataNoBootstrap = true; // do not load bootstrap -// var netdataDontStart = true; // do not start the thread to process the charts -// var netdataErrorCallback = null; // Callback function that will be invoked upon error -// var netdataRegistry = true; // Update the registry (default disabled) -// var netdataRegistryCallback = null; // Callback function that will be invoked with one param, -// the URLs from the registry -// var netdataShowHelp = false; // enable/disable help (default enabled) -// var netdataShowAlarms = true; // enable/disable alarms checks and notifications (default disabled) -// -// var netdataRegistryAfterMs = 1500 // the time to consult to registry on startup -// -// var netdataCallback = null; // a function to call when netdata is ready -// // netdata will be running while this is called (call NETDATA.pause to stop it) -// var netdataPrepCallback = null; // a callback to be called before netdata does anything else -// -// You can also set the default netdata server, using the following. -// When this variable is not set, we assume the page is hosted on your -// netdata server already. -// var netdataServer = "http://yourhost:19999"; // set your NetData server - -//(function(window, document, undefined) { +/*global netdataNoDygraphs *//* boolean, disable dygraph charts + * (default: false) */ +/*global netdataNoSparklines *//* boolean, disable sparkline charts + * (default: false) */ +/*global netdataNoPeitys *//* boolean, disable peity charts + * (default: false) */ +/*global netdataNoGoogleCharts *//* boolean, disable google charts + * (default: false) */ +/*global netdataNoMorris *//* boolean, disable morris charts + * (default: false) */ +/*global netdataNoEasyPieChart *//* boolean, disable easypiechart charts + * (default: false) */ +/*global netdataNoGauge *//* boolean, disable gauge.js charts + * (default: false) */ +/*global netdataNoD3 *//* boolean, disable d3 charts + * (default: false) */ +/*global netdataNoC3 *//* boolean, disable c3 charts + * (default: false) */ +/*global netdataNoBootstrap *//* boolean, disable bootstrap - disables help too + * (default: false) */ +/*global netdataDontStart *//* boolean, do not start the thread to process the charts + * (default: false) */ +/*global netdataErrorCallback *//* function, callback to be called when the dashboard encounters an error + * (default: null) */ +/*global netdataRegistry:true *//* boolean, use the netdata registry + * (default: false) */ +/*global netdataNoRegistry *//* boolean, included only for compatibility with existing custom dashboard + * (obsolete - do not use this any more) */ +/*global netdataRegistryCallback *//* function, callback that will be invoked with one param: the URLs from the registry + * (default: null) */ +/*global netdataShowHelp:true *//* boolean, disable charts help + * (default: true) */ +/*global netdataShowAlarms:true *//* boolean, enable alarms checks and notifications + * (default: false) */ +/*global netdataRegistryAfterMs:true *//* ms, delay registry use at started + * (default: 1500) */ +/*global netdataCallback *//* function, callback to be called when netdata is ready to start + * (default: null) + * netdata will be running while this is called + * (call NETDATA.pause to stop it) */ +/*global netdataPrepCallback *//* function, callback to be called before netdata does anything else + * (default: null) */ +/*global netdataServer *//* string, the URL of the netdata server to use + * (default: the URL the page is hosted at) */ + +// ---------------------------------------------------------------------------- +// global namespace + +var NETDATA = window.NETDATA || {}; + +(function(window, document) { // ------------------------------------------------------------------------ // compatibility fixes @@ -53,9 +74,6 @@ }; } - // global namespace - var NETDATA = window.NETDATA || {}; - NETDATA.name2id = function(s) { return s .replace(/ /g, '_') @@ -112,7 +130,7 @@ 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-d5260c3.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'; @@ -125,12 +143,12 @@ NETDATA.themes = { white: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css', - dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161002-1', + bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css', + dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2', background: '#FFFFFF', foreground: '#000000', - grid: '#DDDDDD', - axis: '#CCCCCC', + grid: '#F0F0F0', + axis: '#F0F0F0', colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707', @@ -142,8 +160,8 @@ gauge_gradient: false }, slate: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css?v20161002-1', - dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-1', + bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', + dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2', background: '#272b30', foreground: '#C8C8C8', grid: '#283236', @@ -195,14 +213,34 @@ if(typeof netdataRegistry === 'undefined') { // backward compatibility - if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false) - netdataRegistry = true; - else - netdataRegistry = false; + netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false); } if(netdataRegistry === false && typeof netdataRegistryCallback === 'function') netdataRegistry = true; + + // ---------------------------------------------------------------------------------------------------------------- + // detect if this is probably a slow device + + var isSlowDeviceResult = undefined; + var isSlowDevice = function() { + if(isSlowDeviceResult !== undefined) + return isSlowDeviceResult; + + try { + var ua = navigator.userAgent.toLowerCase(); + + var iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream; + var android = /android/.test(ua) && !window.MSStream; + isSlowDeviceResult = (iOS === true || android === true); + } + catch (e) { + isSlowDeviceResult = false; + } + + return isSlowDeviceResult; + }; + // ---------------------------------------------------------------------------------------------------------------- // the defaults for all charts @@ -218,7 +256,7 @@ before: 0, // panning after: -600, // panning pixels_per_point: 1, // the detail of the chart - fill_luminance: 0.8 // luminance of colors in solit areas + fill_luminance: 0.8 // luminance of colors in solid areas }; // ---------------------------------------------------------------------------------------------------------------- @@ -237,26 +275,26 @@ // new elements we have to check. auto_refresher_fast_weight: 0, // this is the current time in ms, spent - // rendering charts continiously. + // rendering charts continuously. // used with .current.fast_render_timeframe page_is_visible: true, // when true, this page is visible - auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the + auto_refresher_stop_until: 0, // timestamp in ms - used internally, to stop the // auto-refresher for some time (when a chart is // performing pan or zoom, we need to stop refreshing // all other charts, to have the maximum speed for // rendering the chart that is panned or zoomed). // Used with .current.global_pan_sync_time - last_resized: new Date().getTime(), // the timestamp of the last resize request + last_resized: Date.now(), // the timestamp of the last resize request last_page_scroll: 0, // the timestamp the last time the page was scrolled // the current profile // we may have many... current: { - pixels_per_point: 1, // the minimum pixels per point for all 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 // the max of this and the chart library is used @@ -266,7 +304,7 @@ idle_between_charts: 100, // ms - how much time to wait between chart updates - fast_render_timeframe: 200, // ms - render continously until this time of continious + fast_render_timeframe: 200, // ms - render continuously until this time of continuous // rendering has been reached // this setting is used to make it render e.g. 10 // charts at once, sleep idle_between_charts time @@ -280,8 +318,8 @@ 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 zoon a chart, the background - // autorefreshing of charts is paused for this amount + global_pan_sync_time: 1000, // 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 @@ -298,11 +336,11 @@ update_only_visible: true, // enable or disable visibility management - parallel_refresher: true, // enable parallel refresh of charts + parallel_refresher: (isSlowDevice() === false), // enable parallel refresh of charts concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts - destroy_on_hide: false, // destroy charts when they are not visible + destroy_on_hide: (isSlowDevice() === true), // destroy charts when they are not visible show_help: netdataShowHelp, // when enabled the charts will show some help show_help_delay_show_ms: 500, @@ -315,7 +353,7 @@ double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap - smooth_plot: true, // enable smooth plot, where possible + smooth_plot: (isSlowDevice() === false), // enable smooth plot, where possible charts_selection_animation_delay: 50, // delay to animate charts when syncing selection @@ -332,7 +370,9 @@ async_on_scroll: false, // sync/async onscroll handler onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler - setOptionCallback: function() { ; } + retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server + + setOptionCallback: function() { } }, debug: { @@ -341,7 +381,7 @@ focus: false, visibility: false, chart_data_url: false, - chart_errors: false, // FIXME + chart_errors: false, // FIXME: remember to set it to false before merging chart_timing: false, chart_calls: false, libraries: false, @@ -365,6 +405,28 @@ callback: {} // only used for resetting back to defaults }; + NETDATA.localStorageTested = -1; + NETDATA.localStorageTest = function() { + if(NETDATA.localStorageTested !== -1) + return NETDATA.localStorageTested; + + if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + var test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + NETDATA.localStorageTested = true; + } + catch (e) { + NETDATA.localStorageTested = false; + } + } + else + NETDATA.localStorageTested = false; + + return NETDATA.localStorageTested; + }; + NETDATA.localStorageGet = function(key, def, callback) { var ret = def; @@ -373,7 +435,7 @@ NETDATA.localStorage.callback[key.toString()] = callback; } - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(NETDATA.localStorageTest() === true) { try { // console.log('localStorage: loading "' + key.toString() + '"'); ret = localStorage.getItem(key.toString()); @@ -415,7 +477,7 @@ NETDATA.localStorage.callback[key.toString()] = callback; } - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(NETDATA.localStorageTest() === true) { // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"'); try { localStorage.setItem(key.toString(), JSON.stringify(value)); @@ -430,7 +492,11 @@ }; NETDATA.localStorageGetRecursive = function(obj, prefix, callback) { - for(var i in obj) { + var keys = Object.keys(obj); + var len = keys.length; + while(len--) { + var i = keys[len]; + if(typeof obj[i] === 'object') { //console.log('object ' + prefix + '.' + i.toString()); NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback); @@ -477,7 +543,10 @@ NETDATA.setOption('stop_updates_when_focus_is_lost', true); NETDATA.resetOptions = function() { - for(var i in NETDATA.localStorage.default) { + var keys = Object.keys(NETDATA.localStorage.default); + var len = keys.length; + while(len--) { + var i = keys[len]; var a = i.split('.'); if(a[0] === 'options') { @@ -493,16 +562,20 @@ } } } - } + }; // ---------------------------------------------------------------------------------------------------------------- if(NETDATA.options.debug.main_loop === true) console.log('welcome to NETDATA'); + NETDATA.onresizeCallback = null; NETDATA.onresize = function() { - NETDATA.options.last_resized = new Date().getTime(); + NETDATA.options.last_resized = Date.now(); NETDATA.onscroll(); + + if(typeof NETDATA.onresizeCallback === 'function') + NETDATA.onresizeCallback(); }; NETDATA.onscroll_updater_count = 0; @@ -514,7 +587,7 @@ NETDATA.onscroll_updater = function() { NETDATA.onscroll_updater_running = true; NETDATA.onscroll_updater_count++; - var start = new Date().getTime(); + var start = Date.now(); var targets = NETDATA.options.targets; var len = targets.length; @@ -546,7 +619,7 @@ targets[len].isVisible(); } - var end = new Date().getTime(); + var end = Date.now(); // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms'); if(NETDATA.options.current.async_on_scroll === false) { @@ -574,7 +647,7 @@ NETDATA.onscroll = function() { // console.log('onscroll'); - NETDATA.options.last_page_scroll = new Date().getTime(); + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.auto_refresher_stop_until = 0; if(NETDATA.options.targets === null) return; @@ -641,7 +714,7 @@ NETDATA.error = function(code, msg) { NETDATA.errorLast.code = code; NETDATA.errorLast.message = msg; - NETDATA.errorLast.datetime = new Date().getTime(); + NETDATA.errorLast.datetime = Date.now(); console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); @@ -710,7 +783,7 @@ // find the common min var m = min; for(var i in t) - if(t[i] < m) m = t[i]; + if(t.hasOwnProperty(i) && t[i] < m) m = t[i]; //state.log('commonMin ' + state.__commonMin + ' updated: ' + m); this.latest[name] = m; @@ -765,7 +838,7 @@ // find the common max var m = max; for(var i in t) - if(t[i] > m) m = t[i]; + if(t.hasOwnProperty(i) && t[i] > m) m = t[i]; //state.log('commonMax ' + state.__commonMax + ' updated: ' + m); this.latest[name] = m; @@ -783,6 +856,13 @@ // Every time we download a chart definition, we save it here with .add() // Then we try to get it back with .get(). If that fails, we download it. + NETDATA.fixHost = function(host) { + while(host.slice(-1) === '/') + host = host.substring(0, host.length - 1); + + return host; + }; + NETDATA.chartRegistry = { charts: {}, @@ -816,8 +896,7 @@ }, downloadAll: function(host, callback) { - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); + host = NETDATA.fixHost(host); var self = this; @@ -835,13 +914,13 @@ else NETDATA.error(406, host + '/api/v1/charts'); if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(405, host + '/api/v1/charts'); if(typeof callback === 'function') - callback(null); + return callback(null); }); } }; @@ -858,8 +937,8 @@ // every time a chart is panned or zoomed // we set the timestamp here // then we use it as a sequence number - // to find if other charts are syncronized - // to this timerange + // to find if other charts are synchronized + // to this time-range master: null, // the master chart (state), to which all others // are synchronized @@ -877,7 +956,7 @@ if(this.master !== null && this.master !== state) this.master.resetChart(true, true); - var now = new Date().getTime(); + var now = Date.now(); this.master = state; this.seq = now; this.force_after_ms = after; @@ -909,14 +988,12 @@ // is the given state the master of the global // pan and zoom sync? isMaster: function(state) { - if(this.master === state) return true; - return false; + return (this.master === state); }, // are we currently have a global pan and zoom sync? isActive: function() { - if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true; - return false; + return (this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0); }, // check if a chart, other than the master @@ -928,10 +1005,7 @@ //if(state.needsRecreation()) // return true; - if(state.tm.pan_and_zoom_seq === this.seq) - return false; - - return true; + return (state.tm.pan_and_zoom_seq !== this.seq); } }; @@ -941,18 +1015,14 @@ // FIXME // move color assignment to dimensions, here - dimensionStatus = function(parent, label, name_div, value_div, color) { + var dimensionStatus = function(parent, label, name_div, value_div, color) { this.enabled = false; this.parent = parent; this.label = label; this.name_div = null; this.value_div = null; this.color = NETDATA.themes.current.foreground; - - if(parent.unselected_count === 0) - this.selected = true; - else - this.selected = false; + this.selected = (parent.unselected_count === 0); this.setOptions(name_div, value_div, color); }; @@ -966,7 +1036,7 @@ dimensionStatus.prototype.setOptions = function(name_div, value_div, color) { this.color = color; - if(this.name_div != name_div) { + if(this.name_div !== name_div) { this.name_div = name_div; this.name_div.title = this.label; this.name_div.style.color = this.color; @@ -976,7 +1046,7 @@ this.name_div.className = 'netdata-legend-name selected'; } - if(this.value_div != value_div) { + if(this.value_div !== value_div) { this.value_div = value_div; this.value_div.title = this.label; this.value_div.style.color = this.color; @@ -1057,7 +1127,7 @@ // ---------------------------------------------------------------------------------------------------------------- - dimensionsVisibility = function(state) { + var dimensionsVisibility = function(state) { this.state = state; this.len = 0; this.dimensions = {}; @@ -1081,30 +1151,38 @@ }; dimensionsVisibility.prototype.invalidateAll = function() { - for(var d in this.dimensions) - this.dimensions[d].invalidate(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].invalidate(); }; dimensionsVisibility.prototype.selectAll = function() { - for(var d in this.dimensions) - this.dimensions[d].select(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].select(); }; dimensionsVisibility.prototype.countSelected = function() { - var i = 0; - for(var d in this.dimensions) - if(this.dimensions[d].isSelected()) i++; + var selected = 0; + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + if(this.dimensions[keys[len]].isSelected()) selected++; - return i; + return selected; }; dimensionsVisibility.prototype.selectNone = function() { - for(var d in this.dimensions) - this.dimensions[d].unselect(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].unselect(); }; dimensionsVisibility.prototype.selected2BooleanArray = function(array) { - var ret = new Array(); + var ret = []; this.selected_count = 0; this.unselected_count = 0; @@ -1158,7 +1236,7 @@ // ---------------------------------------------------------------------------------------------------------------- // Our state object, where all per-chart values are stored - chartState = function(element) { + var chartState = function(element) { var self = $(element); this.element = element; @@ -1195,6 +1273,7 @@ // the user given dimensions of the element this.width = self.data('width') || NETDATA.chartDefaults.width; this.height = self.data('height') || NETDATA.chartDefaults.height; + this.height_original = this.height; if(this.settings_id !== null) { this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) { @@ -1231,6 +1310,9 @@ // the chart library requested by the user this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library; + // how many retries we have made to load chart data from the server + this.retries_on_data_failures = 0; + // object - the chart library used this.library = null; @@ -1252,8 +1334,7 @@ title_date: null, title_time: null, title_units: null, - nano: null, - nano_options: null, + perfect_scroller: null, // the container to apply perfect scroller to series: null }; @@ -1262,10 +1343,10 @@ this.title = self.data('title') || null; // the title of the chart this.units = self.data('units') || null; // the units of the chart dimensions - this.append_options = self.data('append-options') || null; // the units of the chart dimensions + this.append_options = self.data('append-options') || null; // additional options to pass to netdata + this.override_options = self.data('override-options') || null; // override options to pass to netdata this.running = false; // boolean - true when the chart is being refreshed now - this.validated = false; // boolean - has the chart been validated? this.enabled = true; // boolean - is the chart enabled for refresh? this.paused = false; // boolean - is the chart paused for any reason? this.selected = false; // boolean - is the chart shown a selection? @@ -1280,13 +1361,9 @@ this.view_before = 0; this.value_decimal_detail = -1; - { - var d = self.data('decimal-digits'); - if(typeof d === 'number') { - this.value_decimal_detail = 1; - while(d-- > 0) - this.value_decimal_detail *= 10; - } + var d = self.data('decimal-digits'); + if(typeof d === 'number') { + this.value_decimal_detail = d; } this.auto = { @@ -1338,8 +1415,9 @@ // find the element that needs to be updated var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms - if(refresh_dt_element_name !== null) + if(refresh_dt_element_name !== null) { this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; + } else this.refresh_dt_element = null; @@ -1360,7 +1438,7 @@ that.element.innerHTML = ''; that.element_message = document.createElement('div'); - that.element_message.className = ' netdata-message hidden'; + that.element_message.className = 'netdata-message icon hidden'; that.element.appendChild(that.element_message); that.element_chart = document.createElement('div'); @@ -1390,9 +1468,9 @@ if(typeof(that.library.aspect_ratio) === 'undefined') { if(typeof(that.height) === 'string') - $(that.element).css('height', that.height); + that.element.style.height = that.height; else if(typeof(that.height) === 'number') - $(that.element).css('height', that.height + 'px'); + that.element.style.height = that.height.toString() + 'px'; } else { var w = that.element.offsetWidth; @@ -1402,13 +1480,13 @@ that.tm.last_resized = 0; } else - $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px'); + that.element.style.height = (w * that.library.aspect_ratio / 100).toString() + 'px'; } if(NETDATA.chartDefaults.min_width !== null) $(that.element).css('min-width', NETDATA.chartDefaults.min_width); - that.tm.last_dom_created = new Date().getTime(); + that.tm.last_dom_created = Date.now(); showLoading(); }; @@ -1453,15 +1531,18 @@ that.data_before = 0; // milliseconds - the last timestamp of the data that.data_update_every = 0; // milliseconds - the frequency to update the data - that.tm.last_initialized = new Date().getTime(); + that.tm.last_initialized = Date.now(); createDOM(); that.setMode('auto'); }; var maxMessageFontSize = function() { + var screenHeight = screen.height; + var el = that.element; + // normally we want a font size, as tall as the element - var h = that.element_message.clientHeight; + var h = el.clientHeight; // but give it some air, 20% let's say, or 5 pixels min var lost = Math.max(h * 0.2, 5); @@ -1472,7 +1553,7 @@ // but check the width too // it should fit 10 characters in it - var w = that.element_message.clientWidth / 10; + var w = el.clientWidth / 10; if(h > w) { paddingTop += (h - w) / 2; h = w; @@ -1480,9 +1561,9 @@ // and don't make it too huge // 5% of the screen size is good - if(h > screen.height / 20) { - paddingTop += (h - (screen.height / 20)) / 2; - h = screen.height / 20; + if(h > screenHeight / 20) { + paddingTop += (h - (screenHeight / 20)) / 2; + h = screenHeight / 20; } // set it @@ -1490,25 +1571,17 @@ that.element_message.style.paddingTop = paddingTop.toString() + 'px'; }; - var showMessage = function(msg) { - that.element_message.className = 'netdata-message'; - that.element_message.innerHTML = msg; - that.element_message.style.fontSize = 'x-small'; - that.element_message.style.paddingTop = '0px'; - that.___messageHidden___ = undefined; - }; - var showMessageIcon = function(icon) { that.element_message.innerHTML = icon; - that.element_message.className = 'netdata-message icon'; maxMessageFontSize(); + $(that.element_message).removeClass('hidden'); that.___messageHidden___ = undefined; }; var hideMessage = function() { if(typeof that.___messageHidden___ === 'undefined') { that.___messageHidden___ = true; - that.element_message.className = 'netdata-message hidden'; + $(that.element_message).addClass('hidden'); } }; @@ -1535,10 +1608,7 @@ }; var isHidden = function() { - if(typeof that.___chartIsHidden___ !== 'undefined') - return true; - - return false; + return (typeof that.___chartIsHidden___ !== 'undefined'); }; // hide the chart, when it is not visible - called from isVisible() @@ -1555,7 +1625,7 @@ showRendering(); that.element_chart.style.display = 'none'; if(that.element_legend !== null) that.element_legend.style.display = 'none'; - that.tm.last_hidden = new Date().getTime(); + that.tm.last_hidden = Date.now(); // de-allocate data // This works, but I not sure there are no corner cases somewhere @@ -1581,7 +1651,7 @@ init(); } else { - that.tm.last_unhidden = new Date().getTime(); + that.tm.last_unhidden = Date.now(); that.element_chart.style.display = ''; if(that.element_legend !== null) that.element_legend.style.display = ''; resizeChart(); @@ -1590,10 +1660,7 @@ }; var canBeRendered = function() { - if(isHidden() === true || that.isVisible(true) === false) - return false; - - return true; + return (isHidden() === false && that.isVisible(true) === true); }; // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers @@ -1666,13 +1733,13 @@ else if(typeof that.library.resize === 'function') { that.library.resize(that); - if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null) - $(that.element_legend_childs.nano).nanoScroller(); + if(that.element_legend_childs.perfect_scroller !== null) + Ps.update(that.element_legend_childs.perfect_scroller); maxMessageFontSize(); } - that.tm.last_resized = new Date().getTime(); + that.tm.last_resized = Date.now(); } }; @@ -1689,7 +1756,7 @@ if(that.settings_id !== null) NETDATA.localStorageSet('chart_heights.' + that.settings_id, h); - var now = new Date().getTime(); + var now = Date.now(); NETDATA.options.last_page_scroll = now; NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing; @@ -1724,24 +1791,54 @@ this.event_resize.chart_last_w = this.element.clientWidth; this.event_resize.chart_last_h = this.element.clientHeight; - var now = new Date().getTime(); - if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) { + var now = Date.now(); + if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed && this.element_legend_childs.perfect_scroller !== null) { // double click / double tap event + // console.dir(this.element_legend_childs.content); + // console.dir(this.element_legend_childs.perfect_scroller); + // the optimal height of the chart // showing the entire legend var optimal = this.event_resize.chart_last_h - + this.element_legend_childs.content.scrollHeight - - this.element_legend_childs.content.clientHeight; + + this.element_legend_childs.perfect_scroller.scrollHeight + - this.element_legend_childs.perfect_scroller.clientHeight; // if we are not optimal, be optimal - if(this.event_resize.chart_last_h != optimal) + if(this.event_resize.chart_last_h !== optimal) { + // this.log('resize to optimal, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); resizeChartToHeight(optimal.toString() + 'px'); + } - // else if we do not have the original height - // reset to the original height - else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h) + // else if the current height is not the original/saved height + // reset to the original/saved height + else if(this.event_resize.chart_last_h !== this.event_resize.chart_original_h) { + // this.log('resize to original, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px'); + } + + // else if the current height is not the internal default height + // reset to the internal default height + else if((this.event_resize.chart_last_h.toString() + 'px') !== this.height_original) { + // this.log('resize to internal default, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); + resizeChartToHeight(this.height_original.toString()); + } + + // else if the current height is not the firstchild's clientheight + // resize to it + else if(typeof this.element_legend_childs.perfect_scroller.firstChild !== 'undefined') { + var parent_rect = this.element.getBoundingClientRect(); + var content_rect = this.element_legend_childs.perfect_scroller.firstElementChild.getBoundingClientRect(); + var wanted = content_rect.top - parent_rect.top + this.element_legend_childs.perfect_scroller.firstChild.clientHeight + 18; // 15 = toolbox + 3 space + + // console.log(parent_rect); + // console.log(content_rect); + // console.log(wanted); + + // this.log('resize to firstChild, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString() + 'px, firstChild = ' + wanted.toString() + 'px' ); + if(this.event_resize.chart_last_h !== wanted) + resizeChartToHeight(wanted.toString() + 'px'); + } } else { this.event_resize.last = now; @@ -1775,6 +1872,8 @@ this.element_legend_childs.resize_handler.onmouseup = this.element_legend_childs.resize_handler.ontouchend = function(e) { + void(e); + // remove all the hooks document.onmouseup = document.onmousemove = @@ -1797,7 +1896,7 @@ var noDataToShow = function() { showMessageIcon(' empty'); that.legendUpdateDOM(); - that.tm.last_autorefreshed = new Date().getTime(); + that.tm.last_autorefreshed = Date.now(); // that.data_update_every = 30 * 1000; //that.element_chart.style.display = 'none'; //if(that.element_legend !== null) that.element_legend.style.display = 'none'; @@ -1827,7 +1926,7 @@ this.current.force_before_ms = null; this.current.force_after_ms = null; - this.tm.last_mode_switch = new Date().getTime(); + this.tm.last_mode_switch = Date.now(); }; // ---------------------------------------------------------------------------------------------------------------- @@ -1839,9 +1938,9 @@ return; if(typeof ms === 'number') - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms; + NETDATA.globalSelectionSync.dont_sync_before = Date.now() + ms; else - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay; + NETDATA.globalSelectionSync.dont_sync_before = Date.now() + NETDATA.options.current.sync_selection_delay; }; // can we globally apply selection sync? @@ -1849,17 +1948,11 @@ if(NETDATA.options.current.sync_selection === false) return false; - if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) - return false; - - return true; + return (NETDATA.globalSelectionSync.dont_sync_before <= Date.now()); }; this.globalSelectionSyncIsMaster = function() { - if(NETDATA.globalSelectionSync.state === this) - return true; - else - return false; + return (NETDATA.globalSelectionSync.state === this); }; // this chart is the master of the global selection sync @@ -1890,7 +1983,7 @@ var targets = NETDATA.options.targets; var len = targets.length; while(len--) { - st = targets[len]; + var st = targets[len]; if(st === this) { if(this.debug === true) @@ -1909,14 +2002,11 @@ // can the chart participate to the global selection sync as a slave? this.globalSelectionSyncIsEligible = function() { - if(this.enabled === true + return (this.enabled === true && this.library !== null && typeof this.library.setSelection === 'function' && this.isVisible() === true - && this.chart_created === true) - return true; - - return false; + && this.chart_created === true); }; // this chart becomes a slave of the global selection sync @@ -1928,12 +2018,8 @@ // 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) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); - + if(this.globalSelectionSyncAbility() === false) return; - } if(this.globalSelectionSyncIsMaster() === false) { if(this.debug === true) @@ -1941,12 +2027,8 @@ this.globalSelectionSyncBeMaster(); - if(this.globalSelectionSyncAbility() === false) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); - + if(this.globalSelectionSyncAbility() === false) return; - } } NETDATA.globalSelectionSync.last_t = t; @@ -1983,13 +2065,10 @@ }; this.setSelection = function(t) { - if(typeof this.library.setSelection === 'function') { - if(this.library.setSelection(this, t) === true) - this.selected = true; - else - this.selected = false; - } - else this.selected = true; + if(typeof this.library.setSelection === 'function') + this.selected = (this.library.setSelection(this, t) === true); + else + this.selected = true; if(this.selected === true && this.debug === true) this.log('selection set to ' + t.toString()); @@ -1999,13 +2078,10 @@ this.clearSelection = function() { if(this.selected === true) { - if(typeof this.library.clearSelection === 'function') { - if(this.library.clearSelection(this) === true) - this.selected = false; - else - this.selected = true; - } - else this.selected = false; + if(typeof this.library.clearSelection === 'function') + this.selected = (this.library.clearSelection(this) !== true); + else + this.selected = false; if(this.selected === false && this.debug === true) this.log('selection cleared'); @@ -2018,9 +2094,7 @@ // find if a timestamp (ms) is shown in the current chart this.timeIsVisible = function(t) { - if(t >= this.data_after && t <= this.data_before) - return true; - return false; + return (t >= this.data_after && t <= this.data_before); }; this.calculateRowForTime = function(t) { @@ -2164,26 +2238,76 @@ if(this.debug === true) this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret); - this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay; + this.current.force_update_at = Date.now() + NETDATA.options.current.pan_and_zoom_delay; this.current.force_after_ms = after; this.current.force_before_ms = before; NETDATA.globalPanAndZoom.setMaster(this, after, before); return ret; }; + var __legendFormatValueChartDecimalsLastMin = undefined; + var __legendFormatValueChartDecimalsLastMax = undefined; + var __legendFormatValueChartDecimals = -1; + this.legendFormatValueDecimalsFromMinMax = function(min, max) { + if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax) + return; + + __legendFormatValueChartDecimalsLastMin = min; + __legendFormatValueChartDecimalsLastMax = max; + + if(this.data !== null && this.data.min === this.data.max) + __legendFormatValueChartDecimals = -1; + + else if(this.value_decimal_detail !== -1) + __legendFormatValueChartDecimals = this.value_decimal_detail; + + else { + var delta; + + if (min === max) + delta = Math.abs(min); + else + delta = Math.abs(max - min); + + if (delta > 1000) __legendFormatValueChartDecimals = 0; + else if (delta > 10) __legendFormatValueChartDecimals = 1; + else if (delta > 1) __legendFormatValueChartDecimals = 2; + else if (delta > 0.1) __legendFormatValueChartDecimals = 2; + else __legendFormatValueChartDecimals = 4; + } + }; + this.legendFormatValue = function(value) { - if(value === null || value === 'undefined') return '-'; - if(typeof value !== 'number') return value; - - if(this.value_decimal_detail !== -1) - return (Math.round(value * this.value_decimal_detail) / this.value_decimal_detail).toLocaleString(); - - var abs = Math.abs(value); - if(abs >= 1000) return (Math.round(value)).toLocaleString(); - if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString(); - if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString(); - if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString(); - return (Math.round(value * 10000) / 10000).toLocaleString(); + if(typeof value !== 'number') return '-'; + + var dmin, dmax; + + if(__legendFormatValueChartDecimals < 0) { + dmin = 0; + var abs = value; + if(abs > 1000) dmax = 0; + else if(abs > 10 ) dmax = 1; + else if(abs > 1) dmax = 2; + else if(abs > 0.1) dmax = 2; + else dmax = 4; + } + else { + dmin = dmax = __legendFormatValueChartDecimals; + } + + if(this.value_decimal_detail !== -1) { + dmin = dmax = this.value_decimal_detail; + } + + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: dmin, + maximumFractionDigits: dmax + }); }; this.legendSetLabelValue = function(label, value) { @@ -2191,6 +2315,10 @@ if(typeof series === 'undefined') return; if(series.value === null && series.user === null) return; + /* + // this slows down firefox and edge significantly + // since it requires to use innerHTML(), instead of innerText() + // if the value has not changed, skip DOM update //if(series.last === value) return; @@ -2205,15 +2333,54 @@ else s += ''; } else s += ''; + series.last = v; } else { - s = r = value; + if(value === null) + s = r = ''; + else + s = r = value; + series.last = value; } + */ - if(series.value !== null) series.value.innerHTML = s; - if(series.user !== null) series.user.innerHTML = r; + var s = this.legendFormatValue(value); + + // caching: do not update the update to show the same value again + if(s === series.last_shown_value) return; + series.last_shown_value = s; + + if(series.value !== null) series.value.innerText = s; + if(series.user !== null) series.user.innerText = s; + }; + + this.__legendSetDateString = function(date) { + if(date !== this.__last_shown_legend_date) { + this.element_legend_childs.title_date.innerText = date; + this.__last_shown_legend_date = date; + } + }; + + this.__legendSetTimeString = function(time) { + if(time !== this.__last_shown_legend_time) { + this.element_legend_childs.title_time.innerText = time; + this.__last_shown_legend_time = time; + } + }; + + this.__legendSetUnitsString = function(units) { + if(units !== this.__last_shown_legend_units) { + this.element_legend_childs.title_units.innerText = units; + this.__last_shown_legend_units = units; + } + }; + + this.legendSetDateLast = { + ms: 0, + date: undefined, + time: undefined }; this.legendSetDate = function(ms) { @@ -2222,27 +2389,32 @@ return; } - var d = new Date(ms); + if(this.legendSetDateLast.ms !== ms) { + var d = new Date(ms); + this.legendSetDateLast.ms = ms; + this.legendSetDateLast.date = d.toLocaleDateString(); + this.legendSetDateLast.time = d.toLocaleTimeString(); + } - if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString(); + if(this.element_legend_childs.title_date !== null) + this.__legendSetDateString(this.legendSetDateLast.date); - if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString(); + if(this.element_legend_childs.title_time !== null) + this.__legendSetTimeString(this.legendSetDateLast.time); - if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = this.units; + if(this.element_legend_childs.title_units !== null) + this.__legendSetUnitsString(this.units) }; this.legendShowUndefined = function() { - if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = ' '; + if(this.element_legend_childs.title_date !== null) + this.__legendSetDateString(' '); - if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = this.chart.name; + if(this.element_legend_childs.title_time !== null) + this.__legendSetTimeString(this.chart.name); - if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = ' '; + if(this.element_legend_childs.title_units !== null) + this.__legendSetUnitsString(' '); if(this.data && this.element_legend_childs.series !== null) { var labels = this.data.dimension_names; @@ -2250,8 +2422,7 @@ while(i--) { var label = labels[i]; - if(typeof label === 'undefined') continue; - if(typeof this.element_legend_childs.series[label] === 'undefined') continue; + if(typeof label === 'undefined' || typeof this.element_legend_childs.series[label] === 'undefined') continue; this.legendSetLabelValue(label, null); } } @@ -2324,8 +2495,8 @@ this.chartColors = function() { if(this.colors !== null) return this.colors; - this.colors = new Array(); - this.colors_available = new Array(); + this.colors = []; + this.colors_available = []; // add the standard colors var len = NETDATA.themes.current.colors.length; @@ -2358,7 +2529,7 @@ }; this.legendUpdateDOM = function() { - var needed = false; + var needed = false, dim, keys, len, i; // check that the legend DOM is up to date for the downloaded dimensions if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) { @@ -2402,9 +2573,12 @@ if(this.colors === null) { // this is the first time we update the chart // let's assign colors to all dimensions - if(this.library.track_colors() === true) - for(var dim in this.chart.dimensions) - this._chartDimensionColor(this.chart.dimensions[dim].name); + if(this.library.track_colors() === true) { + keys = Object.keys(this.chart.dimensions); + len = keys.length; + for(i = 0; i < len ;i++) + this._chartDimensionColor(this.chart.dimensions[keys[i]].name); + } } // we will re-generate the colors for the chart // based on the selected dimensions @@ -2420,10 +2594,12 @@ var color = state._chartDimensionColor(name); var user_element = null; - var user_id = self.data('show-value-of-' + dim + '-at') || null; + var user_id = self.data('show-value-of-' + name.toLowerCase() + '-at') || null; + if(user_id === null) + user_id = self.data('show-value-of-' + dim.toLowerCase() + '-at') || null; if(user_id !== null) { user_element = document.getElementById(user_id) || null; - if(user_element === null) + if (user_element === null) state.log('Cannot find element with id: ' + user_id); } @@ -2431,7 +2607,8 @@ name: document.createElement('span'), value: document.createElement('span'), user: user_element, - last: null + last: null, + last_shown_value: null }; var label = state.element_legend_childs.series[name]; @@ -2444,7 +2621,7 @@ + state.chart.chart_type + '" style="background-color: ' + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')' - + '">' + + '">'; var text = document.createTextNode(' ' + name); label.name.appendChild(text); @@ -2472,18 +2649,7 @@ title_date: document.createElement('span'), title_time: document.createElement('span'), title_units: document.createElement('span'), - nano: document.createElement('div'), - nano_options: { - paneClass: 'netdata-legend-series-pane', - sliderClass: 'netdata-legend-series-slider', - contentClass: 'netdata-legend-series-content', - enabledClass: '__enabled', - flashedClass: '__flashed', - activeClass: '__active', - tabIndex: -1, - alwaysVisible: true, - sliderMinHeight: 10 - }, + perfect_scroller: document.createElement('div'), series: {} }; @@ -2491,7 +2657,7 @@ if(this.library.toolboxPanAndZoom !== null) { - function get_pan_and_zoom_step(event) { + 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; @@ -2503,7 +2669,7 @@ else return NETDATA.options.current.pan_and_zoom_factor; - } + }; this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox'; this.element.appendChild(this.element_legend_childs.toolbox); @@ -2668,24 +2834,27 @@ this.element_legend_childs.title_date.className += " netdata-legend-title-date"; this.element_legend.appendChild(this.element_legend_childs.title_date); + this.__last_shown_legend_date = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_time.className += " netdata-legend-title-time"; this.element_legend.appendChild(this.element_legend_childs.title_time); + this.__last_shown_legend_time = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_units.className += " netdata-legend-title-units"; this.element_legend.appendChild(this.element_legend_childs.title_units); + this.__last_shown_legend_units = undefined; this.element_legend.appendChild(document.createElement('br')); - this.element_legend_childs.nano.className = 'netdata-legend-series'; - this.element_legend.appendChild(this.element_legend_childs.nano); + this.element_legend_childs.perfect_scroller.className = 'netdata-legend-series'; + this.element_legend.appendChild(this.element_legend_childs.perfect_scroller); content.className = 'netdata-legend-series-content'; - this.element_legend_childs.nano.appendChild(content); + this.element_legend_childs.perfect_scroller.appendChild(content); if(NETDATA.options.current.show_help === true) $(content).popover({ @@ -2696,7 +2865,7 @@ placement: 'bottom', title: 'Chart Legend', delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms }, - content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.
Help, can be disabled from the settings.' + content: 'You can click or tap on the values or the labels to select dimensions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.
Help, can be disabled from the settings.' }); } else { @@ -2713,8 +2882,7 @@ title_date: null, title_time: null, title_units: null, - nano: null, - nano_options: null, + perfect_scroller: null, series: {} }; } @@ -2724,13 +2892,15 @@ if(this.debug === true) this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"'); - for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) { + for(i = 0, len = this.data.dimension_names.length; i < len ;i++) { genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i); } } else { - var tmp = new Array(); - for(var dim in this.chart.dimensions) { + var tmp = []; + keys = Object.keys(this.chart.dimensions); + for(i = 0, len = keys.length; i < len ;i++) { + dim = keys[i]; tmp.push(this.chart.dimensions[dim].name); genLabel(this, content, dim, this.chart.dimensions[dim].name, i); } @@ -2749,8 +2919,22 @@ this.element_legend_childs.hidden = document.createElement('div'); el.appendChild(this.element_legend_childs.hidden); - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options); + if(this.element_legend_childs.perfect_scroller !== null) { + Ps.initialize(this.element_legend_childs.perfect_scroller, { + wheelSpeed: 0.2, + wheelPropagation: true, + swipePropagation: true, + minScrollbarLength: null, + maxScrollbarLength: null, + useBothWheelAxes: false, + suppressScrollX: true, + suppressScrollY: false, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + theme: 'default' + }); + Ps.update(this.element_legend_childs.perfect_scroller); + } this.legendShowLatestValues(); }; @@ -2861,7 +3045,12 @@ this.data_url += "&format=" + this.library.format(); this.data_url += "&points=" + (this.data_points * points_multiplier).toString(); this.data_url += "&group=" + this.method; - this.data_url += "&options=" + this.library.options(this); + + 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) @@ -2900,7 +3089,7 @@ this.updates_since_last_unhide++; this.updates_since_last_creation++; - var started = new Date().getTime(); + var started = Date.now(); // if the result is JSON, find the latest update-every this.data_update_every = data.view_update_every * 1000; @@ -2913,12 +3102,12 @@ if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) { if(this.view_after < this.data_after) { - // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after); + // console.log('adjusting view_after from ' + this.view_after + ' to ' + this.data_after); this.view_after = this.data_after; } if(this.view_before > this.data_before) { - // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before); + // console.log('adjusting view_before from ' + this.view_before + ' to ' + this.data_before); this.view_before = this.data_before; } } @@ -2950,7 +3139,8 @@ if(this.debug === true) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); - this.chart_created = false; + init(); + return; } // check and update the legend @@ -2978,7 +3168,7 @@ NETDATA.globalSelectionSync.stop(); // update the performance counters - var now = new Date().getTime(); + var now = Date.now(); this.tm.last_updated = now; // don't update last_autorefreshed if this chart is @@ -2996,7 +3186,7 @@ NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms; if(this.refresh_dt_element !== null) - this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString(); + this.refresh_dt_element.innerText = this.refresh_dt_ms.toString(); }; this.updateChart = function(callback) { @@ -3007,8 +3197,10 @@ if(this.debug === true) this.log('I am already updating...'); - if(typeof callback === 'function') callback(); - return false; + if(typeof callback === 'function') + return callback(); + + return; } // due to late initialization of charts and libraries @@ -3017,29 +3209,37 @@ if(this.debug === true) this.log('I am not enabled'); - if(typeof callback === 'function') callback(); - return false; + if(typeof callback === 'function') + return callback(); + + return; } if(canBeRendered() === false) { - if(typeof callback === 'function') callback(); - return false; - } + if(typeof callback === 'function') + return callback(); - if(this.chart === null) { - this.getChart(function() { that.updateChart(callback); }); - return false; + return; } + if(this.chart === null) + return this.getChart(function() { + return that.updateChart(callback); + }); + if(this.library.initialized === false) { if(this.library.enabled === true) { - this.library.initialize(function() { that.updateChart(callback); }); - return false; + return this.library.initialize(function () { + return that.updateChart(callback); + }); } else { error('chart library "' + this.library_name + '" is not available.'); - if(typeof callback === 'function') callback(); - return false; + + if(typeof callback === 'function') + return callback(); + + return; } } @@ -3069,6 +3269,7 @@ }) .done(function(data) { that.xhr = undefined; + that.retries_on_data_failures = 0; if(that.debug === true) that.log('data received. updating chart.'); @@ -3078,18 +3279,28 @@ .fail(function(msg) { that.xhr = undefined; - if(msg.statusText !== 'abort') - error('data download failed for url: ' + that.data_url); + if(msg.statusText !== 'abort') { + that.retries_on_data_failures++; + if(that.retries_on_data_failures > NETDATA.options.current.retries_on_data_failures) { + // that.log('failed ' + that.retries_on_data_failures.toString() + ' times - giving up'); + that.retries_on_data_failures = 0; + error('data download failed for url: ' + that.data_url); + } + else { + that.tm.last_autorefreshed = Date.now(); + // that.log('failed ' + that.retries_on_data_failures.toString() + ' times, but I will retry'); + } + } }) .always(function() { that.xhr = undefined; NETDATA.statistics.refreshes_active--; that._updating = false; - if(typeof callback === 'function') callback(); - }); - return true; + if(typeof callback === 'function') + return callback(); + }); }; this.isVisible = function(nocache) { @@ -3103,7 +3314,7 @@ if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll) return this.___isVisible___; - this.tm.last_visible_check = new Date().getTime(); + this.tm.last_visible_check = Date.now(); var wh = window.innerHeight; var x = this.element.getBoundingClientRect(); @@ -3146,7 +3357,7 @@ }; this.canBeAutoRefreshed = function() { - var now = new Date().getTime(); + var now = Date.now(); if(this.running === true) { if(this.debug === true) @@ -3253,12 +3464,12 @@ state.running = false; if(typeof callback !== 'undefined') - callback(); + return callback(); }); } else { if(typeof callback !== 'undefined') - callback(); + return callback(); } }; @@ -3267,7 +3478,7 @@ this.chart_url = chart.url; this.data_update_every = chart.update_every * 1000; this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint()); - this.tm.last_info_downloaded = new Date().getTime(); + this.tm.last_info_downloaded = Date.now(); if(this.title === null) this.title = chart.title; @@ -3281,7 +3492,9 @@ this.chart = NETDATA.chartRegistry.get(this.host, this.id); if(this.chart) { this._defaultsFromDownloadedChart(this.chart); - if(typeof callback === 'function') callback(); + + if(typeof callback === 'function') + return callback(); } else { this.chart_url = "/api/v1/chart?chart=" + this.id; @@ -3305,7 +3518,8 @@ error('chart not found on url "' + that.chart_url + '"'); }) .always(function() { - if(typeof callback === 'function') callback(); + if(typeof callback === 'function') + return callback(); }); } }; @@ -3366,7 +3580,7 @@ script.src = NETDATA.jQuery; // script.onabort = onError; - script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); }; + script.onerror = function() { NETDATA.error(101, NETDATA.jQuery); }; if(typeof callback === "function") script.onload = callback; @@ -3374,7 +3588,7 @@ s.parentNode.insertBefore(script, s); } else if(typeof callback === "function") - callback(); + return callback(); }; NETDATA._loadCSS = function(filename) { @@ -3451,10 +3665,12 @@ }; NETDATA.pause = function(callback) { - if(NETDATA.options.pause === true) - callback(); - else - NETDATA.options.pauseCallback = callback; + if(typeof callback === 'function') { + if (NETDATA.options.pause === true) + return callback(); + else + NETDATA.options.pauseCallback = callback; + } }; NETDATA.unpause = function() { @@ -3465,10 +3681,10 @@ // ---------------------------------------------------------------------------------------------------------------- - // this is purely sequencial charts refresher + // this is purely sequential charts refresher // it is meant to be autonomous NETDATA.chartRefresherNoParallel = function(index) { - if(NETDATA.options.debug.mail_loop === true) + if(NETDATA.options.debug.main_loop === true) console.log('NETDATA.chartRefresherNoParallel(' + index + ')'); if(NETDATA.options.updated_dom === true) { @@ -3511,41 +3727,11 @@ } }; - // this is part of the parallel refresher - // its cause is to refresh sequencially all the charts - // that depend on chart library initialization - // it will call the parallel refresher back - // as soon as it sees a chart that its chart library - // is initialized - NETDATA.chartRefresher_uninitialized = function() { - if(NETDATA.options.updated_dom === true) { - // the dom has been updated - // get the dom parts again - NETDATA.parseDom(NETDATA.chartRefresher); - return; - } - - if(NETDATA.options.sequencial.length === 0) - NETDATA.chartRefresher(); - else { - var state = NETDATA.options.sequencial.pop(); - if(state.library.initialized === true) - NETDATA.chartRefresher(); - else - state.autoRefresh(NETDATA.chartRefresher_uninitialized); - } - }; - NETDATA.chartRefresherWaitTime = function() { return NETDATA.options.current.idle_parallel_loops; }; // the default refresher - // it will create 2 sets of charts: - // - the ones that can be refreshed in parallel - // - the ones that depend on something else - // the first set will be executed in parallel - // the second will be given to NETDATA.chartRefresher_uninitialized() NETDATA.chartRefresher = function() { // console.log('auto-refresher...'); @@ -3578,7 +3764,7 @@ return; } - var parallel = new Array(); + var parallel = []; var targets = NETDATA.options.targets; var len = targets.length; var state; @@ -3617,7 +3803,7 @@ }; NETDATA.parseDom = function(callback) { - NETDATA.options.last_page_scroll = new Date().getTime(); + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.updated_dom = false; var targets = $('div[data-netdata]'); //.filter(':visible'); @@ -3625,7 +3811,7 @@ if(NETDATA.options.debug.main_loop === true) console.log('DOM updated - there are ' + targets.length + ' charts on page.'); - NETDATA.options.targets = new Array(); + NETDATA.options.targets = []; var len = targets.length; while(len--) { // the initialization will take care of sizing @@ -3633,7 +3819,8 @@ NETDATA.options.targets.push(NETDATA.chartState(targets[len])); } - if(typeof callback === 'function') callback(); + if(typeof callback === 'function') + return callback(); }; // this is the main function - where everything starts @@ -3670,12 +3857,14 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll); // bootstrap modal switching - $('.modal').on('hidden.bs.modal', NETDATA.onscroll); - $('.modal').on('shown.bs.modal', NETDATA.onscroll); + var $modal = $('.modal'); + $modal.on('hidden.bs.modal', NETDATA.onscroll); + $modal.on('shown.bs.modal', NETDATA.onscroll); // bootstrap collapse switching - $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll); - $('.collapse').on('shown.bs.collapse', NETDATA.onscroll); + var $collapse = $('.collapse'); + $collapse.on('hidden.bs.collapse', NETDATA.onscroll); + $collapse.on('shown.bs.collapse', NETDATA.onscroll); NETDATA.parseDom(NETDATA.chartRefresher); @@ -3709,13 +3898,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.peity.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -3771,13 +3960,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.sparkline.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -3793,7 +3982,7 @@ var self = $(state.element); var type = self.data('sparkline-type') || 'line'; var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0]; - var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance); + var fillColor = self.data('sparkline-fillcolor') || ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance)); var chartRangeMin = self.data('sparkline-chartrangemin') || undefined; var chartRangeMax = self.data('sparkline-chartrangemax') || undefined; var composite = self.data('sparkline-composite') || undefined; @@ -3841,6 +4030,8 @@ if(minSpotColor === 'disable') minSpotColor=''; if(maxSpotColor === 'disable') maxSpotColor=''; + // state.log('sparkline type ' + type + ', lineColor: ' + lineColor + ', fillColor: ' + fillColor); + state.sparkline_options = { type: type, lineColor: lineColor, @@ -3933,7 +4124,7 @@ return true; }; - NETDATA.dygraphClearSelection = function(state, t) { + NETDATA.dygraphClearSelection = function(state) { if(typeof state.dygraph_instance !== 'undefined') { state.dygraph_instance.clearSelection(); } @@ -3956,7 +4147,7 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); }; @@ -3979,13 +4170,13 @@ if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true) NETDATA.dygraphSmoothInitialize(callback); else if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.dygraph.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4070,7 +4261,7 @@ dygraph.updateOptions(options); } - state.dygraph_last_rendered = new Date().getTime(); + state.dygraph_last_rendered = Date.now(); return true; }; @@ -4080,122 +4271,205 @@ var self = $(state.element); - var chart_type = state.chart.chart_type; + var chart_type = self.data('dygraph-type') || state.chart.chart_type; if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area'; - chart_type = self.data('dygraph-type') || chart_type; - - var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false; - smooth = self.data('dygraph-smooth') || smooth; - if(NETDATA.dygraph.smooth === false) - smooth = false; + var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state) === true)?3:4; - var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7) - var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4; + var smooth = (NETDATA.dygraph.smooth === true) + ?(self.data('dygraph-smooth') || (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false)) + :false; state.dygraph_options = { - colors: self.data('dygraph-colors') || state.chartColors(), + colors: self.data('dygraph-colors') || state.chartColors(), // leave a few pixels empty on the right of the chart - rightGap: self.data('dygraph-rightgap') || 5, - showRangeSelector: self.data('dygraph-showrangeselector') || false, - showRoller: self.data('dygraph-showroller') || false, - - title: self.data('dygraph-title') || state.title, - titleHeight: self.data('dygraph-titleheight') || 19, - - legend: self.data('dygraph-legend') || 'always', // we need this to get selection events - labels: data.result.labels, - labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden, - labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' }, - labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70, - labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true, - labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true, - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true, - hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true, - - includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false), - xRangePad: self.data('dygraph-xrangepad') || 0, - yRangePad: self.data('dygraph-yrangepad') || 1, - - valueRange: self.data('dygraph-valuerange') || [ null, null ], - - ylabel: state.units, - yLabelWidth: self.data('dygraph-ylabelwidth') || 12, - - // the function to plot the chart - plotter: null, - - // The width of the lines connecting data points. This can be used to increase the contrast or some graphs. - strokeWidth: self.data('dygraph-strokewidth') || strokeWidth, - strokePattern: self.data('dygraph-strokepattern') || undefined, - - // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated", - // i.e. there is a missing point on either side of it. This also controls the size of those dots. - drawPoints: self.data('dygraph-drawpoints') || false, - - // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities. - drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true, - - connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false, - pointSize: self.data('dygraph-pointsize') || 1, - - // enabling this makes the chart with little square lines - stepPlot: self.data('dygraph-stepplot') || false, - - // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines. - strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background, - strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0, - - fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false, - fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area, - stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false, - stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none', - - drawAxis: self.data('dygraph-drawaxis') || true, - axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10, - axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis, - axisLineWidth: self.data('dygraph-axislinewidth') || 1.0, - - drawGrid: self.data('dygraph-drawgrid') || true, - gridLinePattern: self.data('dygraph-gridlinepattern') || null, - gridLineWidth: self.data('dygraph-gridlinewidth') || 1.0, - gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid, - - maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8, - sigFigs: self.data('dygraph-sigfigs') || null, - digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2, - valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); }, - - highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize, - highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 }, - highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, - - pointClickCallback: self.data('dygraph-pointclickcallback') || undefined, - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), + rightGap: self.data('dygraph-rightgap') + || 5, + + showRangeSelector: self.data('dygraph-showrangeselector') + || false, + + showRoller: self.data('dygraph-showroller') + || false, + + title: self.data('dygraph-title') + || state.title, + + titleHeight: self.data('dygraph-titleheight') + || 19, + + legend: self.data('dygraph-legend') + || 'always', // we need this to get selection events + + labels: data.result.labels, + + labelsDiv: self.data('dygraph-labelsdiv') + || state.element_legend_childs.hidden, + + labelsDivStyles: self.data('dygraph-labelsdivstyles') + || { 'fontSize':'1px' }, + + labelsDivWidth: self.data('dygraph-labelsdivwidth') + || state.chartWidth() - 70, + + labelsSeparateLines: self.data('dygraph-labelsseparatelines') + || true, + + labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') + || true, + + labelsKMB: false, + labelsKMG2: false, + + showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') + || true, + + hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') + || true, + + includeZero: self.data('dygraph-includezero') + || (chart_type === 'stacked'), + + xRangePad: self.data('dygraph-xrangepad') + || 0, + + yRangePad: self.data('dygraph-yrangepad') + || 1, + + valueRange: self.data('dygraph-valuerange') + || [ null, null ], + + ylabel: state.units, + + yLabelWidth: self.data('dygraph-ylabelwidth') + || 12, + + // the function to plot the chart + plotter: null, + + // The width of the lines connecting data points. + // This can be used to increase the contrast or some graphs. + strokeWidth: self.data('dygraph-strokewidth') + || ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7)), + + strokePattern: self.data('dygraph-strokepattern') + || undefined, + + // The size of the dot to draw on each point in pixels (see drawPoints). + // A dot is always drawn when a point is "isolated", + // i.e. there is a missing point on either side of it. + // This also controls the size of those dots. + drawPoints: self.data('dygraph-drawpoints') + || false, + + // Draw points at the edges of gaps in the data. + // This improves visibility of small data segments or other data irregularities. + drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') + || true, + + connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') + || false, + + pointSize: self.data('dygraph-pointsize') + || 1, + + // enabling this makes the chart with little square lines + stepPlot: self.data('dygraph-stepplot') + || false, + + // Draw a border around graph lines to make crossing lines more easily + // distinguishable. Useful for graphs with many lines. + strokeBorderColor: self.data('dygraph-strokebordercolor') + || NETDATA.themes.current.background, + + strokeBorderWidth: self.data('dygraph-strokeborderwidth') + || (chart_type === 'stacked')?0.0:0.0, + + fillGraph: self.data('dygraph-fillgraph') + || (chart_type === 'area' || chart_type === 'stacked'), + + fillAlpha: self.data('dygraph-fillalpha') + || ((chart_type === 'stacked') + ?NETDATA.options.current.color_fill_opacity_stacked + :NETDATA.options.current.color_fill_opacity_area), + + stackedGraph: self.data('dygraph-stackedgraph') + || (chart_type === 'stacked'), + + stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') + || 'none', + + drawAxis: self.data('dygraph-drawaxis') + || true, + + axisLabelFontSize: self.data('dygraph-axislabelfontsize') + || 10, + + axisLineColor: self.data('dygraph-axislinecolor') + || NETDATA.themes.current.axis, + + axisLineWidth: self.data('dygraph-axislinewidth') + || 1.0, + + drawGrid: self.data('dygraph-drawgrid') + || true, + + gridLinePattern: self.data('dygraph-gridlinepattern') + || null, + + gridLineWidth: self.data('dygraph-gridlinewidth') + || 1.0, + + gridLineColor: self.data('dygraph-gridlinecolor') + || NETDATA.themes.current.grid, + + maxNumberWidth: self.data('dygraph-maxnumberwidth') + || 8, + + sigFigs: self.data('dygraph-sigfigs') + || null, + + digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') + || 2, + + valueFormatter: self.data('dygraph-valueformatter') + || undefined, + + highlightCircleSize: self.data('dygraph-highlightcirclesize') + || highlightCircleSize, + + highlightSeriesOpts: self.data('dygraph-highlightseriesopts') + || null, // TOO SLOW: { strokeWidth: 1.5 }, + + highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') + || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, + + pointClickCallback: self.data('dygraph-pointclickcallback') + || undefined, + + visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), + axes: { x: { pixelsPerLabel: 50, ticker: Dygraph.dateTicker, axisLabelFormatter: function (d, gran) { + void(gran); return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); - }, - valueFormatter: function (ms) { - var d = new Date(ms); - return d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); } }, y: { pixelsPerLabel: 15, - valueFormatter: function (x) { - // we format legends with the state object - // no need to do anything here - // return (Math.round(x*100) / 100).toLocaleString(); - // return state.legendFormatValue(x); - return x; + axisLabelFormatter: function (y) { + + // unfortunately, we have to call this every single time + state.legendFormatValueDecimalsFromMinMax( + this.axes_[0].extremeRange[0], + this.axes_[0].extremeRange[1] + ); + + return state.legendFormatValue(y); } } }, @@ -4211,8 +4485,10 @@ var i = data.series.length; while(i--) { var series = data.series[i]; - if(!series.isVisible) continue; - state.legendSetLabelValue(series.label, series.y); + if(series.isVisible === true) + state.legendSetLabelValue(series.label, series.y); + else + state.legendSetLabelValue(series.label, null); } } @@ -4234,6 +4510,8 @@ } }, zoomCallback: function(minDate, maxDate, yRanges) { + void(yRanges); + if(NETDATA.options.debug.dygraph === true) state.log('dygraphZoomCallback()'); @@ -4247,6 +4525,8 @@ state.updateChartPanOrZoom(minDate, maxDate); }, highlightCallback: function(event, x, points, row, seriesName) { + void(seriesName); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphHighlightCallback()'); @@ -4256,7 +4536,7 @@ // the time it thinks is selected is wrong // here we calculate the time t based on the row number selected // which is ok - var t = state.data_after + row * state.data_update_every; + // 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); @@ -4266,6 +4546,8 @@ // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; }, unhighlightCallback: function(event) { + void(event); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphUnhighlightCallback()'); @@ -4322,6 +4604,7 @@ state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('pan'); + context.is2DPan = false; Dygraph.movePan(event, dygraph, context); } else if(context.isZooming) { @@ -4348,17 +4631,26 @@ } }, click: function(event, dygraph, context) { + void(dygraph); + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.click()'); event.preventDefault(); }, dblclick: function(event, dygraph, context) { + void(event); + void(dygraph); + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.dblclick()'); NETDATA.resetAllCharts(state); }, wheel: function(event, dygraph, context) { + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.wheel()'); @@ -4370,7 +4662,7 @@ var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0]; var yar0 = g.yAxisRange(0); - // This is calculating the pixel of the higest value. (Top pixel) + // This is calculating the pixel of the highest value. (Top pixel) var yOffset = g.toDomCoords(null, yar0[1])[1]; // x y w and h are relative to the corner of the drawing area, @@ -4429,12 +4721,12 @@ // http://dygraphs.com/gallery/interaction-api.js var normal_def; - if(typeof event.wheelDelta === 'number' && event.wheelDelta != NaN) + if(typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta)) // chrome normal_def = event.wheelDelta / 40; else // firefox - normal_def = event.deltaY * -2; + normal_def = event.deltaY * -1.2; var normal = (event.detail) ? event.detail * -1 : normal_def; var percentage = normal / 50; @@ -4481,10 +4773,10 @@ Dygraph.defaultInteractionModel.touchstart(event, dygraph, context); // we overwrite the touch directions at the end, to overwrite - // the internal default of dygraphs + // the internal default of dygraph context.touchDirections = { x: true, y: false }; - state.dygraph_last_touch_start = new Date().getTime(); + state.dygraph_last_touch_start = Date.now(); state.dygraph_last_touch_move = 0; if(typeof event.touches[0].pageX === 'number') @@ -4499,7 +4791,7 @@ state.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchmove(event, dygraph, context); - state.dygraph_last_touch_move = new Date().getTime(); + state.dygraph_last_touch_move = Date.now(); }, touchend: function(event, dygraph, context) { if(NETDATA.options.debug.dygraph === true || state.debug === true) @@ -4510,7 +4802,7 @@ // if it didn't move, it is a selection if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) { - // internal api of dygraphs + // 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) @@ -4518,7 +4810,7 @@ } // if it was double tap within double click time, reset the charts - var now = new Date().getTime(); + var now = Date.now(); if(typeof state.dygraph_last_touch_end !== 'undefined') { if(state.dygraph_last_touch_move === 0) { var dt = now - state.dygraph_last_touch_end; @@ -4559,14 +4851,14 @@ state.dygraph_force_zoom = false; state.dygraph_user_action = false; - state.dygraph_last_rendered = new Date().getTime(); + state.dygraph_last_rendered = Date.now(); if(typeof state.dygraph_instance.axes_[0].extremeRange !== 'undefined') { state.__commonMin = self.data('common-min') || null; state.__commonMax = self.data('common-max') || null; } else { - state.log('incompatible version of dygraphs detected'); + state.log('incompatible version of Dygraph detected'); state.__commonMin = null; state.__commonMax = null; } @@ -4590,7 +4882,7 @@ else { NETDATA.chartLibraries.morris.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } } else { @@ -4611,14 +4903,14 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } } else { NETDATA.chartLibraries.morris.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4677,13 +4969,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.raphael.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4721,7 +5013,7 @@ else { NETDATA.chartLibraries.c3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } } else { @@ -4742,14 +5034,14 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } } else { NETDATA.chartLibraries.c3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4841,21 +5133,27 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.d3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; NETDATA.d3ChartUpdate = function(state, data) { + void(state); + void(data); + return false; }; NETDATA.d3ChartCreate = function(state, data) { + void(state); + void(data); + return false; }; @@ -4881,13 +5179,13 @@ NETDATA.chartLibraries.google.enabled = false; NETDATA.error(100, NETDATA.google_js); if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.google.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4991,14 +5289,28 @@ // ---------------------------------------------------------------------------------------------------------------- - NETDATA.percentFromValueMax = function(value, max) { - if(value === null) value = 0; + NETDATA.easypiechartPercentFromValueMinMax = function(value, min, max) { + if(typeof value !== 'number') value = 0; + if(typeof min !== 'number') min = 0; + if(typeof max !== 'number') max = 0; + + if(min > value) min = value; if(max < value) max = value; + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + var pcent = 0; - if(max !== 0) { - pcent = Math.round(value * 100 / max); - if(pcent === 0 && value > 0) pcent = 1; + if(value >= 0) { + if(max !== 0) + pcent = Math.round(value * 100 / max); + if(pcent === 0) pcent = 0.1; + } + else { + if(min !== 0) + pcent = Math.round(-value * 100 / min); + if(pcent === 0) pcent = -0.1; } return pcent; @@ -5024,29 +5336,30 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }) } else { NETDATA.chartLibraries.easypiechart.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; NETDATA.easypiechartClearSelection = function(state) { if(typeof state.easyPieChartEvent !== 'undefined') { - if(state.easyPieChartEvent.timer !== null) + if(state.easyPieChartEvent.timer !== undefined) { clearTimeout(state.easyPieChartEvent.timer); + } - state.easyPieChartEvent.timer = null; + state.easyPieChartEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { NETDATA.easypiechartChartUpdate(state, state.data); } else { - state.easyPieChartLabel.innerHTML = state.legendFormatValue(null); + state.easyPieChartLabel.innerText = state.legendFormatValue(null); state.easyPieChart_instance.update(0); } state.easyPieChart_instance.enableAnimation(); @@ -5064,25 +5377,26 @@ if(typeof state.easyPieChartEvent === 'undefined') { state.easyPieChartEvent = { - timer: null, + timer: undefined, value: 0, pcent: 0 }; } var value = state.data.result[state.data.result.length - 1 - slot]; + var min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; var max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; - var pcent = NETDATA.percentFromValueMax(value, max); + var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); state.easyPieChartEvent.value = value; state.easyPieChartEvent.pcent = pcent; - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); - if(state.easyPieChartEvent.timer === null) { + if(state.easyPieChartEvent.timer === undefined) { state.easyPieChart_instance.disableAnimation(); state.easyPieChartEvent.timer = setTimeout(function() { - state.easyPieChartEvent.timer = null; + state.easyPieChartEvent.timer = undefined; state.easyPieChart_instance.update(state.easyPieChartEvent.pcent); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5091,20 +5405,20 @@ }; NETDATA.easypiechartChartUpdate = function(state, data) { - var value, max, pcent; + var value, min, max, pcent; if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) { value = null; - max = 0; pcent = 0; } else { value = data.result[0]; + min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; - pcent = NETDATA.percentFromValueMax(value, max); + pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); } - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); state.easyPieChart_instance.update(pcent); return true; }; @@ -5114,9 +5428,17 @@ var chart = $(state.element_chart); var value = data.result[0]; + var min = self.data('easypiechart-min-value') || null; var max = self.data('easypiechart-max-value') || null; var adjust = self.data('easypiechart-adjust') || null; + if(min === null) { + min = NETDATA.commonMin.get(state); + state.easyPieChartMin = null; + } + else + state.easyPieChartMin = min; + if(max === null) { max = NETDATA.commonMax.get(state); state.easyPieChartMax = null; @@ -5124,7 +5446,7 @@ else state.easyPieChartMax = max; - var pcent = NETDATA.percentFromValueMax(value, max); + var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); chart.data('data-percent', pcent); @@ -5146,7 +5468,7 @@ var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2); state.easyPieChartLabel = document.createElement('span'); state.easyPieChartLabel.className = 'easyPieChartLabel'; - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); state.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; state.easyPieChartLabel.style.top = valuetop.toString() + 'px'; state.element_chart.appendChild(state.easyPieChartLabel); @@ -5155,7 +5477,7 @@ var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40)); state.easyPieChartTitle = document.createElement('span'); state.easyPieChartTitle.className = 'easyPieChartTitle'; - state.easyPieChartTitle.innerHTML = state.title; + state.easyPieChartTitle.innerText = state.title; state.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; state.easyPieChartTitle.style.top = titletop.toString() + 'px'; @@ -5165,7 +5487,7 @@ var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40)); state.easyPieChartUnits = document.createElement('span'); state.easyPieChartUnits.className = 'easyPieChartUnits'; - state.easyPieChartUnits.innerHTML = state.units; + state.easyPieChartUnits.innerText = state.units; state.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; state.easyPieChartUnits.style.top = unittop.toString() + 'px'; state.element_chart.appendChild(state.easyPieChartUnits); @@ -5190,7 +5512,7 @@ trackWidth: self.data('easypiechart-trackwidth') || undefined, size: self.data('easypiechart-size') || size, rotate: self.data('easypiechart-rotate') || 0, - animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true}, + animate: self.data('easypiechart-animate') || {duration: 500, enabled: true}, easing: self.data('easypiechart-easing') || undefined }); @@ -5227,13 +5549,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }) } else { NETDATA.chartLibraries.gauge.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -5261,7 +5583,7 @@ min = max; max = t; } - else if(min == max) + else if(min === max) max = min + 1; // gauge.js has an issue if the needle @@ -5274,9 +5596,10 @@ // is always between min and max var pcent = (value - min) * 100 / (max - min); - // these should never happen - if(pcent < 0) pcent = 0; - if(pcent > 100) pcent = 100; + // bug fix for gauge.js 1.3.1 + // if the value is the absolute min or max, the chart is broken + if(pcent < 0.001) pcent = 0.001; + if(pcent > 99.999) pcent = 99.999; state.gauge_instance.set(pcent); // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max); @@ -5289,24 +5612,25 @@ NETDATA.gaugeSetLabels = function(state, value, min, max) { if(state.___gaugeOld__.valueLabel !== value) { state.___gaugeOld__.valueLabel = value; - state.gaugeChartLabel.innerHTML = state.legendFormatValue(value); + state.gaugeChartLabel.innerText = state.legendFormatValue(value); } if(state.___gaugeOld__.minLabel !== min) { state.___gaugeOld__.minLabel = min; - state.gaugeChartMin.innerHTML = state.legendFormatValue(min); + state.gaugeChartMin.innerText = state.legendFormatValue(min); } if(state.___gaugeOld__.maxLabel !== max) { state.___gaugeOld__.maxLabel = max; - state.gaugeChartMax.innerHTML = state.legendFormatValue(max); + state.gaugeChartMax.innerText = state.legendFormatValue(max); } }; NETDATA.gaugeClearSelection = function(state) { if(typeof state.gaugeEvent !== 'undefined') { - if(state.gaugeEvent.timer !== null) + if(state.gaugeEvent.timer !== undefined) { clearTimeout(state.gaugeEvent.timer); + } - state.gaugeEvent.timer = null; + state.gaugeEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { @@ -5332,7 +5656,7 @@ if(typeof state.gaugeEvent === 'undefined') { state.gaugeEvent = { - timer: null, + timer: undefined, value: 0, min: 0, max: 0 @@ -5340,19 +5664,23 @@ } var value = state.data.result[state.data.result.length - 1 - slot]; + var min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; var max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; - var min = 0; + + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; state.gaugeEvent.value = value; - state.gaugeEvent.max = max; state.gaugeEvent.min = min; + state.gaugeEvent.max = max; NETDATA.gaugeSetLabels(state, value, min, max); - if(state.gaugeEvent.timer === null) { + if(state.gaugeEvent.timer === undefined) { NETDATA.gaugeAnimation(state, false); state.gaugeEvent.timer = setTimeout(function() { - state.gaugeEvent.timer = null; + state.gaugeEvent.timer = undefined; NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5371,9 +5699,15 @@ } else { value = data.result[0]; - min = 0; + min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + if(value < min) min = value; if(value > max) max = value; + + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + NETDATA.gaugeSetLabels(state, value, min, max); } @@ -5386,6 +5720,7 @@ // var chart = $(state.element_chart); var value = data.result[0]; + var min = self.data('gauge-min-value') || null; var max = self.data('gauge-max-value') || null; var adjust = self.data('gauge-adjust') || null; var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer; @@ -5394,6 +5729,13 @@ var stopColor = self.data('gauge-stop-color') || void 0; var generateGradient = self.data('gauge-generate-gradient') || false; + if(min === null) { + min = NETDATA.commonMin.get(state); + state.gaugeMin = null; + } + else + state.gaugeMin = min; + if(max === null) { max = NETDATA.commonMax.get(state); state.gaugeMax = null; @@ -5401,6 +5743,10 @@ else state.gaugeMax = max; + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5; //switch(adjust) { // case 'width': width = height * ratio; break; @@ -5414,19 +5760,22 @@ var options = { lines: 12, // The number of lines to draw - angle: 0.15, // The length of each line - lineWidth: 0.44, // 0.44 The line thickness + angle: 0.15, // The span of the gauge arc + lineWidth: 0.50, // The line thickness + radiusScale: 0.85, // Relative radius pointer: { length: 0.8, // 0.9 The radius of the inner circle strokeWidth: 0.035, // The rotation offset color: pointerColor // Fill color }, + limitMax: true, // If false, the max value of the gauge will be updated if value surpass max + limitMin: true, // If true, the min value of the gauge will be fixed unless you set it manually colorStart: startColor, // Colors colorStop: stopColor, // just experiment with them strokeColor: strokeColor, // to see which ones work best for you - limitMax: true, - generateGradient: (generateGradient === true)?true:false, - gradientType: 0 + generateGradient: (generateGradient === true), + gradientType: 0, + highDpiSupport: true // High resolution support }; if (generateGradient.constructor === Array) { @@ -5436,13 +5785,13 @@ // data-gauge-gradient-percent-color-50="#999900" // data-gauge-gradient-percent-color-100="#000000" - options.percentColors = new Array(); + options.percentColors = []; var len = generateGradient.length; while(len--) { var pcent = generateGradient[len]; - var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false; + var color = self.attr('data-gauge-gradient-percent-color-' + pcent.toString()) || false; if(color !== false) { - var a = new Array(); + var a = []; a[0] = pcent / 100; a[1] = color; options.percentColors.unshift(a); @@ -5452,6 +5801,7 @@ delete options.percentColors; } else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) { + //noinspection PointlessArithmeticExpressionJS options.percentColors = [ [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))], [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))], @@ -5485,7 +5835,7 @@ var titletop = 0; state.gaugeChartTitle = document.createElement('span'); state.gaugeChartTitle.className = 'gaugeChartTitle'; - state.gaugeChartTitle.innerHTML = state.title; + state.gaugeChartTitle.innerText = state.title; state.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; state.gaugeChartTitle.style.top = titletop.toString() + 'px'; @@ -5494,7 +5844,7 @@ var unitfontsize = Math.round(titlefontsize * 0.9); state.gaugeChartUnits = document.createElement('span'); state.gaugeChartUnits.className = 'gaugeChartUnits'; - state.gaugeChartUnits.innerHTML = state.units; + state.gaugeChartUnits.innerText = state.units; state.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; state.element_chart.appendChild(state.gaugeChartUnits); @@ -5518,7 +5868,7 @@ state.___gaugeOld__ = { value: value, - min: 0, + min: min, max: max, valueLabel: null, minLabel: null, @@ -5530,8 +5880,8 @@ state.gauge_instance.maxValue = 100; NETDATA.gaugeAnimation(state, animate); - NETDATA.gaugeSet(state, value, 0, max); - NETDATA.gaugeSetLabels(state, value, 0, max); + NETDATA.gaugeSet(state, value, min, max); + NETDATA.gaugeSetLabels(state, value, min, max); NETDATA.gaugeAnimation(state, true); return true; }; @@ -5553,31 +5903,21 @@ toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return 'ms|flip'; }, + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return 'ms|flip'; }, legend: function(state) { - if(this.isSparkline(state) === false) - return 'right-side'; - else - return null; + return (this.isSparkline(state) === false)?'right-side':null; }, - autoresize: function(state) { return true; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, + autoresize: function(state) { void(state); return true; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, pixels_per_point: function(state) { - if(this.isSparkline(state) === false) - return 3; - else - return 2; + return (this.isSparkline(state) === false)?3:2; }, - isSparkline: function(state) { if(typeof state.dygraph_sparkline === 'undefined') { var t = $(state.element).data('dygraph-theme'); - if(t === 'sparkline') - state.dygraph_sparkline = true; - else - state.dygraph_sparkline = false; + state.dygraph_sparkline = (t === 'sparkline'); } return state.dygraph_sparkline; } @@ -5587,126 +5927,126 @@ create: NETDATA.sparklineChartCreate, update: NETDATA.sparklineChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'flip|abs'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'flip|abs'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "peity": { initialize: NETDATA.peityInitialize, create: NETDATA.peityChartCreate, update: NETDATA.peityChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'ssvcomma'; }, - options: function(state) { return 'null2zero|flip|abs'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'ssvcomma'; }, + options: function(state) { void(state); return 'null2zero|flip|abs'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "morris": { initialize: NETDATA.morrisInitialize, create: NETDATA.morrisChartCreate, update: NETDATA.morrisChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return 'objectrows|ms'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 50; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 15; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return 'objectrows|ms'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 50; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 15; } }, "google": { initialize: NETDATA.googleInitialize, create: NETDATA.googleChartCreate, update: NETDATA.googleChartUpdate, resize: null, - setSelection: undefined, //function(state, t) { return true; }, - clearSelection: undefined, //function(state) { return true; }, + setSelection: undefined, //function(state, t) { void(state); return true; }, + clearSelection: undefined, //function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'datatable'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 300; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 4; } + format: function(state) { void(state); return 'datatable'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 300; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 4; } }, "raphael": { initialize: NETDATA.raphaelInitialize, create: NETDATA.raphaelChartCreate, update: NETDATA.raphaelChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "c3": { initialize: NETDATA.c3Initialize, create: NETDATA.c3ChartCreate, update: NETDATA.c3ChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'csvjsonarray'; }, - options: function(state) { return 'milliseconds'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 15; } + format: function(state) { void(state); return 'csvjsonarray'; }, + options: function(state) { void(state); return 'milliseconds'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 15; } }, "d3": { initialize: NETDATA.d3Initialize, create: NETDATA.d3ChartCreate, update: NETDATA.d3ChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "easypiechart": { initialize: NETDATA.easypiechartInitialize, @@ -5718,13 +6058,13 @@ toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'absolute'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, - pixels_per_point: function(state) { return 3; }, + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'absolute'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, + pixels_per_point: function(state) { void(state); return 3; }, aspect_ratio: 100 }, "gauge": { @@ -5737,13 +6077,13 @@ toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'absolute'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, - pixels_per_point: function(state) { return 3; }, + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'absolute'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, + pixels_per_point: function(state) { void(state); return 3; }, aspect_ratio: 70 } }; @@ -5766,18 +6106,15 @@ async: false, isAlreadyLoaded: function() { // check if bootstrap is loaded - if(typeof $().emulateTransitionEnd == 'function') + if(typeof $().emulateTransitionEnd === 'function') return true; else { - if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap) - return true; - else - return false; + return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true); } } }, { - url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js', + url: NETDATA.serverDefault + 'lib/perfect-scrollbar-0.6.15.min.js', isAlreadyLoaded: function() { return false; } } ]; @@ -5786,14 +6123,11 @@ { url: NETDATA.themes.current.bootstrap_css, isAlreadyLoaded: function() { - if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap) - return true; - else - return false; + return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true); } }, { - url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3', + url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.7.0', isAlreadyLoaded: function() { return false; } }, { @@ -5806,7 +6140,7 @@ NETDATA.loadRequiredJs = function(index, callback) { if(index >= NETDATA.requiredJs.length) { if(typeof callback === 'function') - callback(); + return callback(); return; } @@ -5841,7 +6175,7 @@ if(async === false) NETDATA.loadRequiredJs(++index, callback); - }) + }); if(async === true) NETDATA.loadRequiredJs(++index, callback); @@ -5881,7 +6215,7 @@ last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for first_notification_id: 0, // the id of the first alarm_log entry for this session // this is used to prevent CLEAR notifications for past events - // notifications_shown: new Array(), + // notifications_shown: [], server: null, // the server to connect to for fetching alarms current: null, // the list of raised alarms - updated in the background @@ -5895,16 +6229,18 @@ return; } - var value = entry.value; + var value_string = entry.value_string; + if(NETDATA.alarms.current !== null) { + // get the current value_string var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name]; - if(typeof t !== 'undefined' && entry.status == t.status) - value = t.value; + if(typeof t !== 'undefined' && entry.status === t.status && typeof t.value_string !== 'undefined') + value_string = t.value_string; } var name = entry.name.replace(/_/g, ' '); var status = entry.status.toLowerCase(); - var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units; + var title = name + ' = ' + value_string.toString(); var tag = entry.alarm_id; var icon = 'images/seo-performance-128.png'; var interaction = false; @@ -5933,8 +6269,12 @@ // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status); return; } - title = name + ' back to normal'; - icon = 'images/check-mark-2-128-green.png' + if(entry.no_clear_notification === true) { + // console.log('alarm' + entry.unique_id + ' is CLEAR but has no_clear_notification flag'); + return; + } + title = name + ' back to normal (' + value_string.toString() + ')'; + icon = 'images/check-mark-2-128-green.png'; interaction = false; break; @@ -5950,7 +6290,7 @@ if(entry.old_status === 'WARNING') status = 'escalated to ' + entry.status.toLowerCase(); - icon = 'images/alert-128-red.png' + icon = 'images/alert-128-red.png'; interaction = true; break; @@ -6110,13 +6450,13 @@ NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id; if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(415, NETDATA.alarms.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6155,23 +6495,21 @@ }) .done(function(data) { if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(416, NETDATA.alarms.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, init: function() { - var host = NETDATA.serverDefault; - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); - NETDATA.alarms.server = host; + NETDATA.alarms.server = NETDATA.fixHost(NETDATA.serverDefault); - NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null); + NETDATA.alarms.last_notification_id = + NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null); if(NETDATA.alarms.onclick === null) NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm; @@ -6194,12 +6532,12 @@ // Registry of netdata hosts NETDATA.registry = { - server: null, // the netdata registry server - person_guid: null, // the unique ID of this browser / user - machine_guid: null, // the unique ID the netdata server that served dashboard.js - hostname: null, // the hostname of the netdata server that served dashboard.js - machines: null, // the user's other URLs - machines_array: null, // the user's other URLs in an array + server: null, // the netdata registry server + person_guid: null, // the unique ID of this browser / user + machine_guid: null, // the unique ID the netdata server that served dashboard.js + hostname: 'unknown', // the hostname of the netdata server that served dashboard.js + machines: null, // the user's other URLs + machines_array: null, // the user's other URLs in an array person_urls: null, parsePersonUrls: function(person_urls) { @@ -6208,9 +6546,8 @@ if(person_urls) { NETDATA.registry.machines = {}; - NETDATA.registry.machines_array = new Array(); + NETDATA.registry.machines_array = []; - var now = new Date().getTime(); var apu = person_urls; var i = apu.length; while(i--) { @@ -6223,7 +6560,7 @@ last_t: apu[i][2], accesses: apu[i][3], name: apu[i][4], - alternate_urls: new Array() + alternate_urls: [] }; obj.alternate_urls.push(apu[i][1]); @@ -6267,8 +6604,7 @@ }, hello: function(host, callback) { - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); + host = NETDATA.fixHost(host); // send HELLO to a netdata server: // 1. verifies the server is reachable @@ -6290,13 +6626,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(407, host); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6333,7 +6669,7 @@ } else { if(typeof callback === 'function') - callback(null); + return callback(null); } } else { @@ -6341,14 +6677,14 @@ NETDATA.registry.person_guid = data.person_guid; if(typeof callback === 'function') - callback(data.urls); + return callback(data.urls); } }) .fail(function() { NETDATA.error(410, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6371,13 +6707,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(412, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6400,13 +6736,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(418, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6429,13 +6765,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(414, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); } }; @@ -6464,6 +6800,4 @@ } }); }); - - // window.NETDATA = NETDATA; -// })(window, document); +})(window, document);