From a133c9c3b637b1dbe7b5b053f7e2572c1950cead Mon Sep 17 00:00:00 2001 From: Lennart Weller Date: Thu, 27 Jul 2017 11:55:47 +0200 Subject: New upstream version 1.7.0+dfsg --- web/Makefile.am | 1 + web/Makefile.in | 145 +++- web/dashboard.css | 47 +- web/dashboard.html | 2 +- web/dashboard.js | 1899 +++++++++++++++++++++++++++-------------------- web/dashboard.slate.css | 47 +- web/dashboard_info.js | 391 +++++++++- web/index.html | 186 +++-- web/infographic.html | 170 +++++ web/version.txt | 2 +- 10 files changed, 1971 insertions(+), 919 deletions(-) create mode 100644 web/infographic.html (limited to 'web') diff --git a/web/Makefile.am b/web/Makefile.am index 03a487597..b587f5a17 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -17,6 +17,7 @@ dist_web_DATA = \ favicon.ico \ goto-host-from-alarm.html \ index.html \ + infographic.html \ netdata-swagger.yaml \ netdata-swagger.json \ robots.txt \ diff --git a/web/Makefile.in b/web/Makefile.in index 9ec69e6aa..42939d2a5 100644 --- a/web/Makefile.in +++ b/web/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,6 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,11 +79,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = web -DIST_COMMON = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_webfonts_DATA) $(dist_webimages_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ - $(dist_webwellknown_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in + $(dist_webwellknown_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 $(top_srcdir)/m4/ax_c_lto.m4 \ @@ -55,8 +99,25 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -92,9 +153,11 @@ DATA = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_webfonts_DATA) $(dist_webimages_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ $(dist_webwellknown_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -255,6 +318,7 @@ dist_web_DATA = \ favicon.ico \ goto-host-from-alarm.html \ index.html \ + infographic.html \ netdata-swagger.yaml \ netdata-swagger.json \ robots.txt \ @@ -388,8 +452,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_webDATA: $(dist_web_DATA) @$(NORMAL_INSTALL) - test -z "$(webdir)" || $(MKDIR_P) "$(DESTDIR)$(webdir)" @list='$(dist_web_DATA)'; test -n "$(webdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -406,8 +473,11 @@ uninstall-dist_webDATA: dir='$(DESTDIR)$(webdir)'; $(am__uninstall_files_from_dir) install-dist_webcssDATA: $(dist_webcss_DATA) @$(NORMAL_INSTALL) - test -z "$(webcssdir)" || $(MKDIR_P) "$(DESTDIR)$(webcssdir)" @list='$(dist_webcss_DATA)'; test -n "$(webcssdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webcssdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webcssdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -424,8 +494,11 @@ uninstall-dist_webcssDATA: dir='$(DESTDIR)$(webcssdir)'; $(am__uninstall_files_from_dir) install-dist_webdntDATA: $(dist_webdnt_DATA) @$(NORMAL_INSTALL) - test -z "$(webdntdir)" || $(MKDIR_P) "$(DESTDIR)$(webdntdir)" @list='$(dist_webdnt_DATA)'; test -n "$(webdntdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webdntdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webdntdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -442,8 +515,11 @@ uninstall-dist_webdntDATA: dir='$(DESTDIR)$(webdntdir)'; $(am__uninstall_files_from_dir) install-dist_webfontsDATA: $(dist_webfonts_DATA) @$(NORMAL_INSTALL) - test -z "$(webfontsdir)" || $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" @list='$(dist_webfonts_DATA)'; test -n "$(webfontsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webfontsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -460,8 +536,11 @@ uninstall-dist_webfontsDATA: dir='$(DESTDIR)$(webfontsdir)'; $(am__uninstall_files_from_dir) install-dist_webimagesDATA: $(dist_webimages_DATA) @$(NORMAL_INSTALL) - test -z "$(webimagesdir)" || $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" @list='$(dist_webimages_DATA)'; test -n "$(webimagesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webimagesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -478,8 +557,11 @@ uninstall-dist_webimagesDATA: dir='$(DESTDIR)$(webimagesdir)'; $(am__uninstall_files_from_dir) install-dist_weblibDATA: $(dist_weblib_DATA) @$(NORMAL_INSTALL) - test -z "$(weblibdir)" || $(MKDIR_P) "$(DESTDIR)$(weblibdir)" @list='$(dist_weblib_DATA)'; test -n "$(weblibdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(weblibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(weblibdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -496,8 +578,11 @@ uninstall-dist_weblibDATA: dir='$(DESTDIR)$(weblibdir)'; $(am__uninstall_files_from_dir) install-dist_weboldDATA: $(dist_webold_DATA) @$(NORMAL_INSTALL) - test -z "$(webolddir)" || $(MKDIR_P) "$(DESTDIR)$(webolddir)" @list='$(dist_webold_DATA)'; test -n "$(webolddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webolddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webolddir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -514,8 +599,11 @@ uninstall-dist_weboldDATA: dir='$(DESTDIR)$(webolddir)'; $(am__uninstall_files_from_dir) install-dist_webwellknownDATA: $(dist_webwellknown_DATA) @$(NORMAL_INSTALL) - test -z "$(webwellknowndir)" || $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webwellknowndir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -530,11 +618,11 @@ uninstall-dist_webwellknownDATA: @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(webwellknowndir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -679,10 +767,10 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_webDATA install-dist_webcssDATA \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_webDATA install-dist_webcssDATA \ install-dist_webdntDATA install-dist_webfontsDATA \ install-dist_webimagesDATA install-dist_weblibDATA \ install-dist_weboldDATA install-dist_webwellknownDATA \ @@ -691,11 +779,12 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ - uninstall-am uninstall-dist_webDATA uninstall-dist_webcssDATA \ - uninstall-dist_webdntDATA uninstall-dist_webfontsDATA \ - uninstall-dist_webimagesDATA uninstall-dist_weblibDATA \ - uninstall-dist_weboldDATA uninstall-dist_webwellknownDATA + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ + uninstall uninstall-am uninstall-dist_webDATA \ + uninstall-dist_webcssDATA uninstall-dist_webdntDATA \ + uninstall-dist_webfontsDATA uninstall-dist_webimagesDATA \ + uninstall-dist_weblibDATA uninstall-dist_weboldDATA \ + uninstall-dist_webwellknownDATA version.txt: diff --git a/web/dashboard.css b/web/dashboard.css index 8eeaa8bec..2147c6038 100644 --- a/web/dashboard.css +++ b/web/dashboard.css @@ -49,6 +49,38 @@ body { /* width and height is given per chart with data-width and data-height */ } +.netdata-container-gauge { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-gauge:after { + padding-top: 60%; + display: block; + content: ''; +} + +.netdata-container-easypiechart { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-easypiechart:after { + padding-top: 100%; + display: block; + content: ''; +} + .netdata-aspect { position: relative; width: 100%; @@ -128,12 +160,15 @@ body { .netdata-message { display: inline-block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; text-align: left; vertical-align: top; font-weight: bold; font-size: x-small; - width: 100%; - height: 100%; overflow: hidden; background: inherit; z-index: 0; @@ -399,7 +434,7 @@ body { margin-left: 18%; text-align: center; color: #999999; - font-weight: normal; + font-weight: bold; } .easyPieChartUnits { @@ -423,6 +458,8 @@ body { position: absolute; top: 0; left: 0; + bottom: 0; + right: 0; z-index: 0; } @@ -471,7 +508,7 @@ body { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 92%; margin-left: 8%; text-align: left; @@ -484,7 +521,7 @@ body { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 95%; margin-right: 5%; text-align: right; diff --git a/web/dashboard.html b/web/dashboard.html index 75de65ad9..4453c996e 100644 --- a/web/dashboard.html +++ b/web/dashboard.html @@ -652,4 +652,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up - + diff --git a/web/dashboard.js b/web/dashboard.js index b34accdec..1f240a4c8 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -144,7 +144,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.themes = { white: { bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css', - dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2', + dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20170605-2', background: '#FFFFFF', foreground: '#000000', grid: '#F0F0F0', @@ -161,7 +161,7 @@ var NETDATA = window.NETDATA || {}; }, slate: { bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', - dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2', + dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20170605-2', background: '#272b30', foreground: '#C8C8C8', grid: '#283236', @@ -287,7 +287,7 @@ var NETDATA = window.NETDATA || {}; // rendering the chart that is panned or zoomed). // Used with .current.global_pan_sync_time - last_resized: Date.now(), // the timestamp of the last resize request + last_page_resize: Date.now(), // the timestamp of the last resize request last_page_scroll: 0, // the timestamp the last time the page was scrolled @@ -571,7 +571,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.onresizeCallback = null; NETDATA.onresize = function() { - NETDATA.options.last_resized = Date.now(); + NETDATA.options.last_page_resize = Date.now(); NETDATA.onscroll(); if(typeof NETDATA.onresizeCallback === 'function') @@ -602,18 +602,18 @@ var NETDATA = window.NETDATA || {}; // we have to cancel pending requests too while (len--) { - if (targets[len]._updating === true) { + if (targets[len].fetching_data === true) { if (typeof targets[len].xhr !== 'undefined') { targets[len].xhr.abort(); targets[len].running = false; - targets[len]._updating = false; + targets[len].fetching_data = false; } targets[len].isVisible(); } } } else { - // just find which chart is visible + // just find which charts are visible while (len--) targets[len].isVisible(); @@ -644,9 +644,14 @@ var NETDATA = window.NETDATA || {}; NETDATA.onscroll_updater_running = false; }; + NETDATA.scrollUp = false; + NETDATA.scrollY = window.scrollY; NETDATA.onscroll = function() { // console.log('onscroll'); + NETDATA.scrollUp = (window.scrollY > NETDATA.scrollY); + NETDATA.scrollY = window.scrollY; + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.auto_refresher_stop_until = 0; @@ -733,6 +738,254 @@ var NETDATA = window.NETDATA || {}; NETDATA.errorLast.datetime = 0; }; + // ---------------------------------------------------------------------------------------------------------------- + // fast numbers formatting + + NETDATA.fastNumberFormat = { + formatters_fixed: [], + formatters_zero_based: [], + + // this is the fastest and the preferred + getIntlNumberFormat: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + + return this.formatters_zero_based[key]; + } + else { + // this is never used + // it is added just for completeness + return new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }, + + // this respects locale + getLocaleString: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + + return this.formatters_zero_based[key]; + } + else { + return { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + } + }, + + getFixed: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + + return this.formatters_zero_based[key]; + } + else { + return { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + } + }, + + testIntlNumberFormat: function() { + var n = 1.12345; + var e1 = "1.12", e2 = "1,12"; + var s = ""; + + try { + var x = new Intl.NumberFormat(undefined, { + useGrouping: true, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); + + s = x.format(n); + } + catch(e) { + s = ""; + } + + // console.log('NumberFormat: ', s); + return (s === e1 || s === e2); + }, + + testLocaleString: function() { + var n = 1.12345; + var e1 = "1.12", e2 = "1,12"; + var s = ""; + + try { + s = value.toLocaleString(undefined, { + useGrouping: true, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); + } + catch(e) { + s = ""; + } + + // console.log('localeString: ', s); + return (s === e1 || s === e2); + }, + + // on first run we decide which formatter to use + get: function(min, max) { + if(this.testIntlNumberFormat()) { + // console.log('numberformat'); + this.get = this.getIntlNumberFormat; + } + else if(this.testLocaleString()) { + // console.log('localestring'); + this.get = this.getLocaleString; + } + else { + // console.log('fixed'); + this.get = this.getFixed; + } + return this.get(min, max); + } + }; + + // ---------------------------------------------------------------------------------------------------------------- + // element data attributes + + NETDATA.dataAttribute = function(element, attribute, def) { + var key = 'data-' + attribute.toString(); + if(element.hasAttribute(key) === true) { + var data = element.getAttribute(key); + + if(data === 'true') return true; + if(data === 'false') return false; + if(data === 'null') return null; + + // Only convert to a number if it doesn't change the string + if(data === +data + '') return +data; + + if(/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/.test(data)) + return JSON.parse(data); + + return data; + } + else return def; + }; + + NETDATA.dataAttributeBoolean = function(element, attribute, def) { + var value = NETDATA.dataAttribute(element, attribute, def); + + if(value === true || value === false) + return value; + + if(typeof(value) === 'string') { + if(value === 'yes' || value === 'on') + return true; + + if(value === '' || value === 'no' || value === 'off' || value === 'null') + return false; + + return def; + } + + if(typeof(value) === 'number') + return value !== 0; + + return def; + }; + // ---------------------------------------------------------------------------------------------------------------- // commonMin & commonMax @@ -741,14 +994,13 @@ var NETDATA = window.NETDATA || {}; latest: {}, get: function(state) { - if(typeof state.__commonMin === 'undefined') { + if(typeof state.tmp.__commonMin === 'undefined') { // get the commonMin setting - var self = $(state.element); - state.__commonMin = self.data('common-min') || null; + state.tmp.__commonMin = NETDATA.dataAttribute(state.element, 'common-min', null); } var min = state.data.min; - var name = state.__commonMin; + var name = state.tmp.__commonMin; if(name === null) { // we don't need commonMin @@ -766,11 +1018,11 @@ var NETDATA = window.NETDATA || {}; var uuid = state.uuid; if(typeof t[uuid] !== 'undefined') { if(t[uuid] === min) { - //state.log('commonMin ' + state.__commonMin + ' not changed: ' + this.latest[name]); + //state.log('commonMin ' + state.tmp.__commonMin + ' not changed: ' + this.latest[name]); return this.latest[name]; } else if(min < this.latest[name]) { - //state.log('commonMin ' + state.__commonMin + ' increased: ' + min); + //state.log('commonMin ' + state.tmp.__commonMin + ' increased: ' + min); t[uuid] = min; this.latest[name] = min; return min; @@ -785,7 +1037,7 @@ var NETDATA = window.NETDATA || {}; for(var i in t) if(t.hasOwnProperty(i) && t[i] < m) m = t[i]; - //state.log('commonMin ' + state.__commonMin + ' updated: ' + m); + //state.log('commonMin ' + state.tmp.__commonMin + ' updated: ' + m); this.latest[name] = m; return m; } @@ -796,14 +1048,13 @@ var NETDATA = window.NETDATA || {}; latest: {}, get: function(state) { - if(typeof state.__commonMax === 'undefined') { + if(typeof state.tmp.__commonMax === 'undefined') { // get the commonMax setting - var self = $(state.element); - state.__commonMax = self.data('common-max') || null; + state.tmp.__commonMax = NETDATA.dataAttribute(state.element, 'common-max', null); } var max = state.data.max; - var name = state.__commonMax; + var name = state.tmp.__commonMax; if(name === null) { // we don't need commonMax @@ -821,11 +1072,11 @@ var NETDATA = window.NETDATA || {}; var uuid = state.uuid; if(typeof t[uuid] !== 'undefined') { if(t[uuid] === max) { - //state.log('commonMax ' + state.__commonMax + ' not changed: ' + this.latest[name]); + //state.log('commonMax ' + state.tmp.__commonMax + ' not changed: ' + this.latest[name]); return this.latest[name]; } else if(max > this.latest[name]) { - //state.log('commonMax ' + state.__commonMax + ' increased: ' + max); + //state.log('commonMax ' + state.tmp.__commonMax + ' increased: ' + max); t[uuid] = max; this.latest[name] = max; return max; @@ -840,7 +1091,7 @@ var NETDATA = window.NETDATA || {}; for(var i in t) if(t.hasOwnProperty(i) && t[i] > m) m = t[i]; - //state.log('commonMax ' + state.__commonMax + ' updated: ' + m); + //state.log('commonMax ' + state.tmp.__commonMax + ' updated: ' + m); this.latest[name] = m; return m; } @@ -1237,13 +1488,15 @@ var NETDATA = window.NETDATA || {}; // Our state object, where all per-chart values are stored var chartState = function(element) { - var self = $(element); this.element = element; // IMPORTANT: // all private functions should use 'that', instead of 'this' var that = this; + // ============================================================================================================ + // ERROR HANDLING + /* error() - private * show an error instead of the chart */ @@ -1261,18 +1514,34 @@ var NETDATA = window.NETDATA || {}; } }; + // console logging + this.log = function(msg) { + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); + }; + + + // ============================================================================================================ + // EARLY INITIALIZATION + + // These are variables that should exist even if the chart is never to be rendered. + // Be careful what you add here - there may be thousands of charts on the page. + // GUID - a unique identifier for the chart this.uuid = NETDATA.guid(); // string - the name of chart - this.id = self.data('netdata'); + this.id = NETDATA.dataAttribute(this.element, 'netdata', undefined); + if(typeof this.id === 'undefined') { + error("netdata elements need data-netdata"); + return; + } // string - the key for localStorage settings - this.settings_id = self.data('id') || null; + this.settings_id = NETDATA.dataAttribute(this.element, 'id', null); // the user given dimensions of the element - this.width = self.data('width') || NETDATA.chartDefaults.width; - this.height = self.data('height') || NETDATA.chartDefaults.height; + this.width = NETDATA.dataAttribute(this.element, 'width', NETDATA.chartDefaults.width); + this.height = NETDATA.dataAttribute(this.element, 'height', NETDATA.chartDefaults.height); this.height_original = this.height; if(this.settings_id !== null) { @@ -1285,86 +1554,24 @@ var NETDATA = window.NETDATA || {}; }); } - // string - the netdata server URL, without any path - this.host = self.data('host') || NETDATA.chartDefaults.host; - - // make sure the host does not end with / - // all netdata API requests use absolute paths - while(this.host.slice(-1) === '/') - this.host = this.host.substring(0, this.host.length - 1); - - // string - the grouping method requested by the user - this.method = self.data('method') || NETDATA.chartDefaults.method; - - // the time-range requested by the user - this.after = self.data('after') || NETDATA.chartDefaults.after; - this.before = self.data('before') || NETDATA.chartDefaults.before; - - // the pixels per point requested by the user - this.pixels_per_point = self.data('pixels-per-point') || 1; - this.points = self.data('points') || null; - - // the dimensions requested by the user - this.dimensions = self.data('dimensions') || null; - // 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; - - // color management - this.colors = null; - this.colors_assigned = {}; - this.colors_available = null; - - // the element already created by the user - this.element_message = null; - - // the element with the chart - this.element_chart = null; - - // the element with the legend of the chart (if created by us) - this.element_legend = null; - this.element_legend_childs = { - hidden: null, - title_date: null, - title_time: null, - title_units: null, - perfect_scroller: null, // the container to apply perfect scroller to - series: null - }; - - this.chart_url = null; // string - the url to download chart info - this.chart = null; // object - the chart as downloaded from the server - - this.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; // 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.enabled = true; // boolean - is the chart enabled for refresh? - this.paused = false; // boolean - is the chart paused for any reason? - this.selected = false; // boolean - is the chart shown a selection? - this.debug = false; // boolean - console.log() debug info about this chart - - this.netdata_first = 0; // milliseconds - the first timestamp in netdata - this.netdata_last = 0; // milliseconds - the last timestamp in netdata - this.requested_after = null; // milliseconds - the timestamp of the request after param - this.requested_before = null; // milliseconds - the timestamp of the request before param - this.requested_padding = null; - this.view_after = 0; - this.view_before = 0; + this.library_name = NETDATA.dataAttribute(this.element, 'chart-library', NETDATA.chartDefaults.library); - this.value_decimal_detail = -1; - var d = self.data('decimal-digits'); - if(typeof d === 'number') { - this.value_decimal_detail = d; + // check the requested library is available + // we don't initialize it here - it will be initialized when + // this chart will be first used + if(typeof NETDATA.chartLibraries[this.library_name] === 'undefined') { + NETDATA.error(402, this.library_name); + error('chart library "' + this.library_name + '" is not found'); + this.enabled = false; + } + else if(NETDATA.chartLibraries[this.library_name].enabled === false) { + NETDATA.error(403, this.library_name); + error('chart library "' + this.library_name + '" is not enabled'); + this.enabled = false; } + else + this.library = NETDATA.chartLibraries[this.library_name]; this.auto = { name: 'auto', @@ -1392,79 +1599,192 @@ var NETDATA = window.NETDATA || {}; // auto, pan, zoom this.current = this.auto; - // check the requested library is available - // we don't initialize it here - it will be initialized when - // this chart will be first used - if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') { - NETDATA.error(402, that.library_name); - error('chart library "' + that.library_name + '" is not found'); - return; - } - else if(NETDATA.chartLibraries[that.library_name].enabled === false) { - NETDATA.error(403, that.library_name); - error('chart library "' + that.library_name + '" is not enabled'); - return; - } - else - that.library = NETDATA.chartLibraries[that.library_name]; + this.running = false; // boolean - true when the chart is being refreshed now + this.enabled = true; // boolean - is the chart enabled for refresh? - // milliseconds - the time the last refresh took - this.refresh_dt_ms = 0; + that.tmp = {}; - // if we need to report the rendering speed - // 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 + // ============================================================================================================ + // PRIVATE FUNCTIONS - if(refresh_dt_element_name !== null) { - this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; - } - else - this.refresh_dt_element = null; + // reset the runtime status variables to their defaults + var runtimeInit = function() { + that.paused = false; // boolean - is the chart paused for any reason? + that.selected = false; // boolean - is the chart shown a selection? - this.dimensions_visibility = new dimensionsVisibility(this); + that.chart_created = false; // boolean - is the library.create() been called? + that.dom_created = false; // boolean - is the chart DOM been created? + that.fetching_data = false; // boolean - true while we fetch data via ajax - this._updating = false; + that.updates_counter = 0; // numeric - the number of refreshes made so far + that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden + that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created - // ============================================================================================================ - // PRIVATE FUNCTIONS + that.tm = { + last_initialized: 0, // milliseconds - the timestamp it was last initialized + last_dom_created: 0, // milliseconds - the timestamp its DOM was last created + last_mode_switch: 0, // milliseconds - the timestamp it switched modes + + last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart + last_updated: 0, // the timestamp the chart last updated with data + pan_and_zoom_seq: 0, // the sequence number of the global synchronization + // between chart. + // Used with NETDATA.globalPanAndZoom.seq + last_visible_check: 0, // the time we last checked if it is visible + last_resized: 0, // the time the chart was resized + last_hidden: 0, // the time the chart was hidden + last_unhidden: 0, // the time the chart was unhidden + last_autorefreshed: 0 // the time the chart was last refreshed + }; - var createDOM = function() { + that.data = null; // the last data as downloaded from the netdata server + that.data_url = 'invalid://'; // string - the last url used to update the chart + that.data_points = 0; // number - the number of points returned from netdata + that.data_after = 0; // milliseconds - the first timestamp of the data + that.data_before = 0; // milliseconds - the last timestamp of the data + that.data_update_every = 0; // milliseconds - the frequency to update the data + + that.tmp = {}; // members that can be destroyed to save memory + }; + + // initialize all the variables that are required for the chart to be rendered + var lateInitialization = function() { + if(typeof that.host !== 'undefined') + return; + + // string - the netdata server URL, without any path + that.host = NETDATA.dataAttribute(that.element, 'host', NETDATA.chartDefaults.host); + + // make sure the host does not end with / + // all netdata API requests use absolute paths + while(that.host.slice(-1) === '/') + that.host = that.host.substring(0, that.host.length - 1); + + // string - the grouping method requested by the user + that.method = NETDATA.dataAttribute(that.element, 'method', NETDATA.chartDefaults.method); + + // the time-range requested by the user + that.after = NETDATA.dataAttribute(that.element, 'after', NETDATA.chartDefaults.after); + that.before = NETDATA.dataAttribute(that.element, 'before', NETDATA.chartDefaults.before); + + // the pixels per point requested by the user + that.pixels_per_point = NETDATA.dataAttribute(that.element, 'pixels-per-point', 1); + that.points = NETDATA.dataAttribute(that.element, 'points', null); + + // the dimensions requested by the user + that.dimensions = NETDATA.dataAttribute(that.element, 'dimensions', null); + + that.title = NETDATA.dataAttribute(that.element, 'title', null); // the title of the chart + that.units = NETDATA.dataAttribute(that.element, 'units', null); // the units of the chart dimensions + that.append_options = NETDATA.dataAttribute(that.element, 'append-options', null); // additional options to pass to netdata + that.override_options = NETDATA.dataAttribute(that.element, 'override-options', null); // override options to pass to netdata + + that.debug = NETDATA.dataAttributeBoolean(that.element, 'debug', false); + + that.value_decimal_detail = -1; + var d = NETDATA.dataAttribute(that.element, 'decimal-digits', -1); + if(typeof d === 'number') + that.value_decimal_detail = d; + else if(typeof d !== 'undefined') + that.log('ignoring decimal-digits value: ' + d.toString()); + + // if we need to report the rendering speed + // find the element that needs to be updated + var refresh_dt_element_name = NETDATA.dataAttribute(that.element, 'dt-element-name', null); // string - the element to print refresh_dt_ms + + if(refresh_dt_element_name !== null) { + that.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; + } + else + that.refresh_dt_element = null; + + that.dimensions_visibility = new dimensionsVisibility(that); + + that.netdata_first = 0; // milliseconds - the first timestamp in netdata + that.netdata_last = 0; // milliseconds - the last timestamp in netdata + that.requested_after = null; // milliseconds - the timestamp of the request after param + that.requested_before = null; // milliseconds - the timestamp of the request before param + that.requested_padding = null; + that.view_after = 0; + that.view_before = 0; + + that.refresh_dt_ms = 0; // milliseconds - the time the last refresh took + + // how many retries we have made to load chart data from the server + that.retries_on_data_failures = 0; + + // color management + that.colors = null; + that.colors_assigned = {}; + that.colors_available = null; + that.colors_custom = null; + + that.element_message = null; // the element already created by the user + that.element_chart = null; // the element with the chart + that.element_legend = null; // the element with the legend of the chart (if created by us) + that.element_legend_childs = { + hidden: null, + title_date: null, + title_time: null, + title_units: null, + perfect_scroller: null, // the container to apply perfect scroller to + series: null + }; + + that.chart_url = null; // string - the url to download chart info + that.chart = null; // object - the chart as downloaded from the server + }; + + var destroyDOM = function() { if(that.enabled === false) return; - if(that.element_message !== null) that.element_message.innerHTML = ''; - if(that.element_legend !== null) that.element_legend.innerHTML = ''; - if(that.element_chart !== null) that.element_chart.innerHTML = ''; + if(that.debug === true) + that.log('destroyDOM()'); + // that.element.className = 'netdata-message icon'; + // that.element.innerHTML = ' netdata'; that.element.innerHTML = ''; + that.element_message = null; + that.element_legend = null; + that.element_chart = null; + that.element_legend_childs.series = null; + + that.chart_created = false; + that.dom_created = false; + + that.tm.last_resized = 0; + that.tm.last_dom_created = 0; + }; + + var createDOM = function() { + if(that.enabled === false) return; + lateInitialization(); + + destroyDOM(); + + if(that.debug === true) + that.log('createDOM()'); that.element_message = document.createElement('div'); that.element_message.className = 'netdata-message icon hidden'; that.element.appendChild(that.element_message); - that.element_chart = document.createElement('div'); - that.element_chart.id = that.library_name + '-' + that.uuid + '-chart'; - that.element.appendChild(that.element_chart); + that.dom_created = true; + that.chart_created = false; - if(that.hasLegend() === true) { - that.element.className = "netdata-container-with-legend"; - that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right'; + that.tm.last_dom_created = + that.tm.last_resized = Date.now(); - that.element_legend = document.createElement('div'); - that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend'; - that.element.appendChild(that.element_legend); - } - else { - that.element.className = "netdata-container"; - that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart'; + showLoading(); + }; - that.element_legend = null; - } - that.element_legend_childs.series = null; + var initDOM = function() { + that.element.className = that.library.container_class(that); if(typeof(that.width) === 'string') - $(that.element).css('width', that.width); + that.element.style.width = that.width; else if(typeof(that.width) === 'number') - $(that.element).css('width', that.width + 'px'); + that.element.style.width = that.width.toString() + 'px'; if(typeof(that.library.aspect_ratio) === 'undefined') { if(typeof(that.height) === 'string') @@ -1472,23 +1792,9 @@ var NETDATA = window.NETDATA || {}; else if(typeof(that.height) === 'number') that.element.style.height = that.height.toString() + 'px'; } - else { - var w = that.element.offsetWidth; - if(w === null || w === 0) { - // the div is hidden - // this will resize the chart when next viewed - that.tm.last_resized = 0; - } - else - 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 = Date.now(); - - showLoading(); + that.element.style.min_width = NETDATA.chartDefaults.min_width; }; /* init() private @@ -1496,45 +1802,18 @@ var NETDATA = window.NETDATA || {}; * destroy all (possibly) created state elements * create the basic DOM for a chart */ - var init = function() { + var init = function(opt) { if(that.enabled === false) return; - that.paused = false; - that.selected = false; - - that.chart_created = false; // boolean - is the library.create() been called? - that.updates_counter = 0; // numeric - the number of refreshes made so far - that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden - that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created - - that.tm = { - last_initialized: 0, // milliseconds - the timestamp it was last initialized - last_dom_created: 0, // milliseconds - the timestamp its DOM was last created - last_mode_switch: 0, // milliseconds - the timestamp it switched modes - - last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart - last_updated: 0, // the timestamp the chart last updated with data - pan_and_zoom_seq: 0, // the sequence number of the global synchronization - // between chart. - // Used with NETDATA.globalPanAndZoom.seq - last_visible_check: 0, // the time we last checked if it is visible - last_resized: 0, // the time the chart was resized - last_hidden: 0, // the time the chart was hidden - last_unhidden: 0, // the time the chart was unhidden - last_autorefreshed: 0 // the time the chart was last refreshed - }; - - that.data = null; // the last data as downloaded from the netdata server - that.data_url = 'invalid://'; // string - the last url used to update the chart - that.data_points = 0; // number - the number of points returned from netdata - that.data_after = 0; // milliseconds - the first timestamp of the data - that.data_before = 0; // milliseconds - the last timestamp of the data - that.data_update_every = 0; // milliseconds - the frequency to update the data + runtimeInit(); that.tm.last_initialized = Date.now(); - createDOM(); - that.setMode('auto'); + + if(opt !== 'fast') { + if (that.isVisible(true) || opt === 'force') + createDOM(); + } }; var maxMessageFontSize = function() { @@ -1575,12 +1854,12 @@ var NETDATA = window.NETDATA || {}; that.element_message.innerHTML = icon; maxMessageFontSize(); $(that.element_message).removeClass('hidden'); - that.___messageHidden___ = undefined; + that.tmp.___messageHidden___ = undefined; }; var hideMessage = function() { - if(typeof that.___messageHidden___ === 'undefined') { - that.___messageHidden___ = true; + if(typeof that.tmp.___messageHidden___ === 'undefined') { + that.tmp.___messageHidden___ = true; $(that.element_message).addClass('hidden'); } }; @@ -1608,7 +1887,7 @@ var NETDATA = window.NETDATA || {}; }; var isHidden = function() { - return (typeof that.___chartIsHidden___ !== 'undefined'); + return (typeof that.tmp.___chartIsHidden___ !== 'undefined'); }; // hide the chart, when it is not visible - called from isVisible() @@ -1619,12 +1898,15 @@ var NETDATA = window.NETDATA || {}; if(that.chart_created === true) { if(NETDATA.options.current.destroy_on_hide === true) { // we should destroy it - init(); + init('force'); } else { showRendering(); that.element_chart.style.display = 'none'; if(that.element_legend !== null) that.element_legend.style.display = 'none'; + if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = 'none'; + if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = 'none'; + that.tm.last_hidden = Date.now(); // de-allocate data @@ -1635,25 +1917,27 @@ var NETDATA = window.NETDATA || {}; } } - that.___chartIsHidden___ = true; + that.tmp.___chartIsHidden___ = true; }; // unhide the chart, when it is visible - called from isVisible() var unhideChart = function() { if(isHidden() === false) return; - that.___chartIsHidden___ = undefined; + that.tmp.___chartIsHidden___ = undefined; that.updates_since_last_unhide = 0; if(that.chart_created === false) { // we need to re-initialize it, to show our background // logo in bootstrap tabs, until the chart loads - init(); + init('force'); } else { that.tm.last_unhidden = Date.now(); that.element_chart.style.display = ''; if(that.element_legend !== null) that.element_legend.style.display = ''; + if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = ''; + if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = ''; resizeChart(); hideMessage(); } @@ -1724,11 +2008,11 @@ var NETDATA = window.NETDATA || {}; // to be called just before the chart library to make sure that // a properly sized dom is available var resizeChart = function() { - if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) { + if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_page_resize) { if(that.chart_created === false) return; if(that.needsRecreation()) { - init(); + init('force'); } else if(typeof that.library.resize === 'function') { that.library.resize(that); @@ -1900,7 +2184,7 @@ var NETDATA = window.NETDATA || {}; // that.data_update_every = 30 * 1000; //that.element_chart.style.display = 'none'; //if(that.element_legend !== null) that.element_legend.style.display = 'none'; - //that.___chartIsHidden___ = true; + //that.tmp.___chartIsHidden___ = true; }; // ============================================================================================================ @@ -2104,11 +2388,6 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - // console logging - this.log = function(msg) { - console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); - }; - this.pauseChart = function() { if(this.paused === false) { if(this.debug === true) @@ -2248,6 +2527,7 @@ var NETDATA = window.NETDATA || {}; var __legendFormatValueChartDecimalsLastMin = undefined; var __legendFormatValueChartDecimalsLastMax = undefined; var __legendFormatValueChartDecimals = -1; + var __intlNumberFormat = null; this.legendFormatValueDecimalsFromMinMax = function(min, max) { if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax) return; @@ -2255,13 +2535,18 @@ var NETDATA = window.NETDATA || {}; __legendFormatValueChartDecimalsLastMin = min; __legendFormatValueChartDecimalsLastMax = max; + var old = __legendFormatValueChartDecimals; + if(this.data !== null && this.data.min === this.data.max) + // it is a fixed number, let the visualizer decide based on the value __legendFormatValueChartDecimals = -1; else if(this.value_decimal_detail !== -1) + // there is an override __legendFormatValueChartDecimals = this.value_decimal_detail; else { + // ok, let's calculate the proper number of decimal points var delta; if (min === max) @@ -2275,39 +2560,39 @@ var NETDATA = window.NETDATA || {}; else if (delta > 0.1) __legendFormatValueChartDecimals = 2; else __legendFormatValueChartDecimals = 4; } + + if(__legendFormatValueChartDecimals !== old) { + if(__legendFormatValueChartDecimals < 0) + __intlNumberFormat = null; + else + __intlNumberFormat = NETDATA.fastNumberFormat.get( + __legendFormatValueChartDecimals, + __legendFormatValueChartDecimals + ); + } }; this.legendFormatValue = function(value) { 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(__intlNumberFormat !== null) + return __intlNumberFormat.format(value); + var dmin, dmax; if(this.value_decimal_detail !== -1) { dmin = dmax = this.value_decimal_detail; } + else { + dmin = 0; + var abs = (value < 0) ? -value : 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; + } - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: dmin, - maximumFractionDigits: dmax - }); + return NETDATA.fastNumberFormat.get(dmin, dmax).format(value); }; this.legendSetLabelValue = function(label, value) { @@ -2357,23 +2642,23 @@ var NETDATA = window.NETDATA || {}; }; this.__legendSetDateString = function(date) { - if(date !== this.__last_shown_legend_date) { + if(date !== this.tmp.__last_shown_legend_date) { this.element_legend_childs.title_date.innerText = date; - this.__last_shown_legend_date = date; + this.tmp.__last_shown_legend_date = date; } }; this.__legendSetTimeString = function(time) { - if(time !== this.__last_shown_legend_time) { + if(time !== this.tmp.__last_shown_legend_time) { this.element_legend_childs.title_time.innerText = time; - this.__last_shown_legend_time = time; + this.tmp.__last_shown_legend_time = time; } }; this.__legendSetUnitsString = function(units) { - if(units !== this.__last_shown_legend_units) { + if(units !== this.tmp.__last_shown_legend_units) { this.element_legend_childs.title_units.innerText = units; - this.__last_shown_legend_units = units; + this.tmp.__last_shown_legend_units = units; } }; @@ -2468,16 +2753,28 @@ var NETDATA = window.NETDATA || {}; }; // this should be called just ONCE per dimension per chart - this._chartDimensionColor = function(label) { - if(this.colors === null) this.chartColors(); + this.__chartDimensionColor = function(label) { + this.chartPrepareColorPalette(); if(typeof this.colors_assigned[label] === 'undefined') { + if(this.colors_available.length === 0) { - var len = NETDATA.themes.current.colors.length; + var len; + + // copy the custom colors + if(this.colors_custom !== null) { + len = this.colors_custom.length; + while (len--) + this.colors_available.unshift(this.colors_custom[len]); + } + + // copy the standard palette colors + len = NETDATA.themes.current.colors.length; while(len--) this.colors_available.unshift(NETDATA.themes.current.colors[len]); } + // assign a color to this dimension this.colors_assigned[label] = this.colors_available.shift(); if(this.debug === true) @@ -2492,39 +2789,77 @@ var NETDATA = window.NETDATA || {}; return this.colors_assigned[label]; }; - this.chartColors = function() { - if(this.colors !== null) return this.colors; + this.chartPrepareColorPalette = function() { + var len; + + if(this.colors_custom !== null) return; + + if(this.debug === true) + this.log("Preparing chart color palette"); this.colors = []; this.colors_available = []; + this.colors_custom = []; // add the standard colors - var len = NETDATA.themes.current.colors.length; + len = NETDATA.themes.current.colors.length; while(len--) this.colors_available.unshift(NETDATA.themes.current.colors[len]); // add the user supplied colors - var c = $(this.element).data('colors'); + var c = NETDATA.dataAttribute(this.element, 'colors', undefined); // this.log('read colors: ' + c); - if(typeof c !== 'undefined' && c !== null && c.length > 0) { - if(typeof c !== 'string') { - this.log('invalid color given: ' + c + ' (give a space separated list of colors)'); - } - else { - c = c.split(' '); - var added = 0; - - while(added < 20) { - len = c.length; - while(len--) { - added++; - this.colors_available.unshift(c[len]); - // this.log('adding color: ' + c[len]); - } - } + if(typeof c === 'string' && c.length > 0) { + c = c.split(' '); + len = c.length; + while(len--) { + if(this.debug === true) + this.log("Adding custom color " + c[len].toString() + " to palette"); + + this.colors_custom.unshift(c[len]); + this.colors_available.unshift(c[len]); } } + if(this.debug === true) { + this.log("colors_custom:"); + this.log(this.colors_custom); + this.log("colors_available:"); + this.log(this.colors_available); + } + }; + + // get the ordered list of chart colors + // this includes user defined colors + this.chartCustomColors = function() { + this.chartPrepareColorPalette(); + + var colors; + if(this.colors_custom.length) + colors = this.colors_custom; + else + colors = this.colors; + + if(this.debug === true) { + this.log("chartCustomColors() returns:"); + this.log(colors); + } + + return colors; + }; + + // get the ordered list of chart ASSIGNED colors + // (this returns only the colors that have been + // assigned to dimensions, prepended with any + // custom colors defined) + this.chartColors = function() { + this.chartPrepareColorPalette(); + + if(this.debug === true) { + this.log("chartColors() returns:"); + this.log(this.colors); + } + return this.colors; }; @@ -2555,7 +2890,7 @@ var NETDATA = window.NETDATA || {}; if(needed === false) { // make sure colors available - this.chartColors(); + this.chartPrepareColorPalette(); // do we have to update the current values? // we do this, only when the visible chart is current @@ -2577,12 +2912,12 @@ var NETDATA = window.NETDATA || {}; keys = Object.keys(this.chart.dimensions); len = keys.length; for(i = 0; i < len ;i++) - this._chartDimensionColor(this.chart.dimensions[keys[i]].name); + this.__chartDimensionColor(this.chart.dimensions[keys[i]].name); } } // we will re-generate the colors for the chart - // based on the selected dimensions - this.colors = null; + // based on the dimensions this result has data for + this.colors = []; if(this.debug === true) this.log('updating Legend DOM'); @@ -2591,12 +2926,12 @@ var NETDATA = window.NETDATA || {}; this.dimensions_visibility.invalidateAll(); var genLabel = function(state, parent, dim, name, count) { - var color = state._chartDimensionColor(name); + var color = state.__chartDimensionColor(name); var user_element = null; - var user_id = self.data('show-value-of-' + name.toLowerCase() + '-at') || null; + var user_id = NETDATA.dataAttribute(state.element, 'show-value-of-' + name.toLowerCase() + '-at', null); if(user_id === null) - user_id = self.data('show-value-of-' + dim.toLowerCase() + '-at') || null; + user_id = NETDATA.dataAttribute(state.element, 'show-value-of-' + dim.toLowerCase() + '-at', null); if(user_id !== null) { user_element = document.getElementById(user_id) || null; if (user_element === null) @@ -2635,7 +2970,26 @@ var NETDATA = window.NETDATA || {}; var content = document.createElement('div'); - if(this.hasLegend()) { + if(this.element_chart === null) { + this.element_chart = document.createElement('div'); + this.element_chart.id = this.library_name + '-' + this.uuid + '-chart'; + this.element.appendChild(this.element_chart); + + if(this.hasLegend() === true) + this.element_chart.className = 'netdata-chart-with-legend-right netdata-' + this.library_name + '-chart-with-legend-right'; + else + this.element_chart.className = ' netdata-chart netdata-' + this.library_name + '-chart'; + } + + if(this.hasLegend() === true) { + if(this.element_legend === null) { + this.element_legend = document.createElement('div'); + this.element_legend.className = 'netdata-chart-legend netdata-' + this.library_name + '-legend'; + this.element.appendChild(this.element_legend); + } + else + this.element_legend.innerHTML = ''; + this.element_legend_childs = { content: content, resize_handler: document.createElement('div'), @@ -2653,10 +3007,7 @@ var NETDATA = window.NETDATA || {}; series: {} }; - this.element_legend.innerHTML = ''; - if(this.library.toolboxPanAndZoom !== null) { - 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; @@ -2717,7 +3068,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Reset', content: 'Reset all the charts to their default auto-refreshing state. You can also double click the chart contents with your mouse or your finger (on touch devices).
Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_right.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right); @@ -2741,7 +3092,7 @@ var NETDATA = window.NETDATA || {}; content: 'Pan the chart to the right. You can also drag it with your mouse or your finger (on touch devices).
Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_zoomin.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin); @@ -2763,7 +3114,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Zoom In', content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.
Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_zoomout.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout); @@ -2786,7 +3137,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Zoom Out', content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.
Help, can be disabled from the settings.' }); - + //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button'; //this.element_legend_childs.toolbox_volume.innerHTML = ''; //this.element_legend_childs.toolbox_volume.title = 'Visible Volume'; @@ -2805,7 +3156,7 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.toolbox_zoomout = null; this.element_legend_childs.toolbox_volume = null; } - + this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler"; this.element_legend_childs.resize_handler.innerHTML = ''; this.element.appendChild(this.element_legend_childs.resize_handler); @@ -2834,19 +3185,19 @@ var NETDATA = window.NETDATA || {}; 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.tmp.__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.tmp.__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.tmp.__last_shown_legend_units = undefined; this.element_legend.appendChild(document.createElement('br')); @@ -2940,16 +3291,14 @@ var NETDATA = window.NETDATA || {}; }; this.hasLegend = function() { - if(typeof this.___hasLegendCache___ !== 'undefined') - return this.___hasLegendCache___; + if(typeof this.tmp.___hasLegendCache___ !== 'undefined') + return this.tmp.___hasLegendCache___; var leg = false; - if(this.library && this.library.legend(this) === 'right-side') { - var legend = $(this.element).data('legend') || 'yes'; - if(legend === 'yes') leg = true; - } + if(this.library && this.library.legend(this) === 'right-side') + leg = NETDATA.dataAttributeBoolean(this.element, 'legend', true); - this.___hasLegendCache___ = leg; + this.tmp.___hasLegendCache___ = leg; return leg; }; @@ -2983,12 +3332,17 @@ var NETDATA = window.NETDATA || {}; }; this.needsRecreation = function() { - return ( + var ret = ( this.chart_created === true && this.library && this.library.autoresize() === false - && this.tm.last_resized < NETDATA.options.last_resized + && this.tm.last_resized < NETDATA.options.last_page_resize ); + + if(this.debug === true) + this.log('needsRecreation(): ' + ret.toString() + ', chart_created = ' + this.chart_created.toString()); + + return ret; }; this.chartURL = function() { @@ -3139,7 +3493,7 @@ var NETDATA = window.NETDATA || {}; if(this.debug === true) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); - init(); + init('force'); return; } @@ -3191,9 +3545,9 @@ var NETDATA = window.NETDATA || {}; this.updateChart = function(callback) { if(this.debug === true) - this.log('updateChart() called.'); + this.log('updateChart()'); - if(this._updating === true) { + if(this.fetching_data === true) { if(this.debug === true) this.log('I am already updating...'); @@ -3222,6 +3576,9 @@ var NETDATA = window.NETDATA || {}; return; } + if(that.dom_created !== true) + createDOM(); + if(this.chart === null) return this.getChart(function() { return that.updateChart(callback); @@ -3255,7 +3612,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max) NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active; - this._updating = true; + this.fetching_data = true; this.xhr = $.ajax( { url: this.data_url, @@ -3296,60 +3653,45 @@ var NETDATA = window.NETDATA || {}; that.xhr = undefined; NETDATA.statistics.refreshes_active--; - that._updating = false; + that.fetching_data = false; if(typeof callback === 'function') return callback(); }); }; - this.isVisible = function(nocache) { - if(typeof nocache === 'undefined') - nocache = false; - - // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); - - // caching - we do not evaluate the charts visibility - // if the page has not been scrolled since the last check - if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll) - return this.___isVisible___; + var __isVisible = function() { + // tolerance is the number of pixels a chart can be off-screen + // to consider it as visible and refresh it as if was visible + var tolerance = 0; - this.tm.last_visible_check = Date.now(); + that.tm.last_visible_check = Date.now(); - var wh = window.innerHeight; - var x = this.element.getBoundingClientRect(); - var ret = 0; - var tolerance = 0; + var rect = that.element.getBoundingClientRect(); - if(x.width === 0 || x.height === 0) { - hideChart(); - this.___isVisible___ = false; - return this.___isVisible___; - } + var screenTop = window.scrollY; + var screenBottom = screenTop + window.innerHeight; - if(x.top < 0 && -x.top > x.height) { - // the chart is entirely above - ret = -x.top - x.height; - } - else if(x.top > wh) { - // the chart is entirely below - ret = x.top - wh; - } + var chartTop = rect.top + screenTop; + var chartBottom = chartTop + rect.height; - if(ret > tolerance) { - // the chart is too far + return !(rect.width === 0 || rect.height === 0 || chartBottom + tolerance < screenTop || chartTop - tolerance > screenBottom); + }; - hideChart(); - this.___isVisible___ = false; - return this.___isVisible___; - } - else { - // the chart is inside or very close + this.isVisible = function(nocache) { + // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); - unhideChart(); - this.___isVisible___ = true; - return this.___isVisible___; - } + // caching - we do not evaluate the charts visibility + // if the page has not been scrolled since the last check + if((typeof nocache === 'undefined' || nocache === false) + && typeof this.tmp.___isVisible___ !== 'undefined' + && this.tm.last_visible_check > NETDATA.options.last_page_scroll) + return this.tmp.___isVisible___; + + this.tmp.___isVisible___ = __isVisible(); + if(this.tmp.___isVisible___ === true) unhideChart(); + else hideChart(); + return this.tmp.___isVisible___; }; this.isAutoRefreshable = function() { @@ -3399,8 +3741,8 @@ var NETDATA = window.NETDATA || {}; if(this.isAutoRefreshable() === true) { // allow the first update, even if the page is not visible if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) { - if(NETDATA.options.debug.focus === true || this.debug === true) - this.log('canBeAutoRefreshed(): page does not have focus'); + // if(NETDATA.options.debug.focus === true || this.debug === true) + // this.log('canBeAutoRefreshed(): page does not have focus'); return false; } @@ -3473,7 +3815,7 @@ var NETDATA = window.NETDATA || {}; } }; - this._defaultsFromDownloadedChart = function(chart) { + this.__defaultsFromDownloadedChart = function(chart) { this.chart = chart; this.chart_url = chart.url; this.data_update_every = chart.update_every * 1000; @@ -3491,7 +3833,7 @@ var NETDATA = window.NETDATA || {}; this.getChart = function(callback) { this.chart = NETDATA.chartRegistry.get(this.host, this.id); if(this.chart) { - this._defaultsFromDownloadedChart(this.chart); + this.__defaultsFromDownloadedChart(this.chart); if(typeof callback === 'function') return callback(); @@ -3510,7 +3852,7 @@ var NETDATA = window.NETDATA || {}; }) .done(function(chart) { chart.url = that.chart_url; - that._defaultsFromDownloadedChart(chart); + that.__defaultsFromDownloadedChart(chart); NETDATA.chartRegistry.add(that.host, that.id, chart); }) .fail(function() { @@ -3527,7 +3869,8 @@ var NETDATA = window.NETDATA || {}; // ============================================================================================================ // INITIALIZATION - init(); + initDOM(); + init('fast'); }; NETDATA.resetAllCharts = function(state) { @@ -3556,10 +3899,12 @@ var NETDATA = window.NETDATA || {}; // get or create a chart state, given a DOM element NETDATA.chartState = function(element) { - var state = $(element).data('netdata-state-object') || null; + var self = $(element); + + var state = self.data('netdata-state-object') || null; if(state === null) { state = new chartState(element); - $(element).data('netdata-state-object', state); + self.data('netdata-state-object', state); } return state; }; @@ -3710,9 +4055,11 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.main_loop === true) console.log('fast rendering...'); - state.autoRefresh(function() { - NETDATA.chartRefresherNoParallel(++index); - }); + setTimeout(function() { + state.autoRefresh(function () { + NETDATA.chartRefresherNoParallel(++index); + }); + }, 0); } else { if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...'); @@ -3783,7 +4130,10 @@ var NETDATA = window.NETDATA || {}; } } - parallel.unshift(state); + if(NETDATA.scrollUp === true) + parallel.unshift(state); + else + parallel.push(state); } if(parallel.length > 0) { @@ -3911,12 +4261,12 @@ var NETDATA = window.NETDATA || {}; NETDATA.peityChartUpdate = function(state, data) { state.peity_instance.innerHTML = data.result; - if(state.peity_options.stroke !== state.chartColors()[0]) { - state.peity_options.stroke = state.chartColors()[0]; + if(state.peity_options.stroke !== state.chartCustomColors()[0]) { + state.peity_options.stroke = state.chartCustomColors()[0]; if(state.chart.chart_type === 'line') state.peity_options.fill = NETDATA.themes.current.background; else - state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance); + state.peity_options.fill = NETDATA.colorLuminance(state.chartCustomColors()[0], NETDATA.chartDefaults.fill_luminance); } $(state.peity_instance).peity('line', state.peity_options); @@ -3927,10 +4277,9 @@ var NETDATA = window.NETDATA || {}; state.peity_instance = document.createElement('div'); state.element_chart.appendChild(state.peity_instance); - var self = $(state.element); state.peity_options = { stroke: NETDATA.themes.current.foreground, - strokeWidth: self.data('peity-strokewidth') || 1, + strokeWidth: NETDATA.dataAttribute(state.element, 'peity-strokewidth', 1), width: state.chartWidth(), height: state.chartHeight(), fill: NETDATA.themes.current.foreground @@ -3979,52 +4328,51 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.sparklineChartCreate = function(state, data) { - var self = $(state.element); - var type = self.data('sparkline-type') || 'line'; - var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0]; - var fillColor = self.data('sparkline-fillcolor') || ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance)); - var chartRangeMin = self.data('sparkline-chartrangemin') || undefined; - var chartRangeMax = self.data('sparkline-chartrangemax') || undefined; - var composite = self.data('sparkline-composite') || undefined; - var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined; - var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined; - var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined; - var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined; - var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined; - var spotColor = self.data('sparkline-spotcolor') || undefined; - var minSpotColor = self.data('sparkline-minspotcolor') || undefined; - var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined; - var spotRadius = self.data('sparkline-spotradius') || undefined; - var valueSpots = self.data('sparkline-valuespots') || undefined; - var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined; - var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined; - var lineWidth = self.data('sparkline-linewidth') || undefined; - var normalRangeMin = self.data('sparkline-normalrangemin') || undefined; - var normalRangeMax = self.data('sparkline-normalrangemax') || undefined; - var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined; - var xvalues = self.data('sparkline-xvalues') || undefined; - var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined; - var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined; - var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined; - var disableInteraction = self.data('sparkline-disableinteraction') || false; - var disableTooltips = self.data('sparkline-disabletooltips') || false; - var disableHighlight = self.data('sparkline-disablehighlight') || false; - var highlightLighten = self.data('sparkline-highlightlighten') || 1.4; - var highlightColor = self.data('sparkline-highlightcolor') || undefined; - var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined; - var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined; - var tooltipFormat = self.data('sparkline-tooltipformat') || undefined; - var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined; - var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units; - var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true; - var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined; - var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined; - var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined; - var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); }; - var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined; - var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined; - var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined; - var animatedZooms = self.data('sparkline-animatedzooms') || false; + var type = NETDATA.dataAttribute(state.element, 'sparkline-type', 'line'); + var lineColor = NETDATA.dataAttribute(state.element, 'sparkline-linecolor', state.chartCustomColors()[0]); + var fillColor = NETDATA.dataAttribute(state.element, 'sparkline-fillcolor', ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance))); + var chartRangeMin = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemin', undefined); + var chartRangeMax = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemax', undefined); + var composite = NETDATA.dataAttribute(state.element, 'sparkline-composite', undefined); + var enableTagOptions = NETDATA.dataAttribute(state.element, 'sparkline-enabletagoptions', undefined); + var tagOptionPrefix = NETDATA.dataAttribute(state.element, 'sparkline-tagoptionprefix', undefined); + var tagValuesAttribute = NETDATA.dataAttribute(state.element, 'sparkline-tagvaluesattribute', undefined); + var disableHiddenCheck = NETDATA.dataAttribute(state.element, 'sparkline-disablehiddencheck', undefined); + var defaultPixelsPerValue = NETDATA.dataAttribute(state.element, 'sparkline-defaultpixelspervalue', undefined); + var spotColor = NETDATA.dataAttribute(state.element, 'sparkline-spotcolor', undefined); + var minSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-minspotcolor', undefined); + var maxSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-maxspotcolor', undefined); + var spotRadius = NETDATA.dataAttribute(state.element, 'sparkline-spotradius', undefined); + var valueSpots = NETDATA.dataAttribute(state.element, 'sparkline-valuespots', undefined); + var highlightSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightspotcolor', undefined); + var highlightLineColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightlinecolor', undefined); + var lineWidth = NETDATA.dataAttribute(state.element, 'sparkline-linewidth', undefined); + var normalRangeMin = NETDATA.dataAttribute(state.element, 'sparkline-normalrangemin', undefined); + var normalRangeMax = NETDATA.dataAttribute(state.element, 'sparkline-normalrangemax', undefined); + var drawNormalOnTop = NETDATA.dataAttribute(state.element, 'sparkline-drawnormalontop', undefined); + var xvalues = NETDATA.dataAttribute(state.element, 'sparkline-xvalues', undefined); + var chartRangeClip = NETDATA.dataAttribute(state.element, 'sparkline-chartrangeclip', undefined); + var chartRangeMinX = NETDATA.dataAttribute(state.element, 'sparkline-chartrangeminx', undefined); + var chartRangeMaxX = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemaxx', undefined); + var disableInteraction = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disableinteraction', false); + var disableTooltips = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disabletooltips', false); + var disableHighlight = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disablehighlight', false); + var highlightLighten = NETDATA.dataAttribute(state.element, 'sparkline-highlightlighten', 1.4); + var highlightColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightcolor', undefined); + var tooltipContainer = NETDATA.dataAttribute(state.element, 'sparkline-tooltipcontainer', undefined); + var tooltipClassname = NETDATA.dataAttribute(state.element, 'sparkline-tooltipclassname', undefined); + var tooltipFormat = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformat', undefined); + var tooltipPrefix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipprefix', undefined); + var tooltipSuffix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipsuffix', ' ' + state.units); + var tooltipSkipNull = NETDATA.dataAttributeBoolean(state.element, 'sparkline-tooltipskipnull', true); + var tooltipValueLookups = NETDATA.dataAttribute(state.element, 'sparkline-tooltipvaluelookups', undefined); + var tooltipFormatFieldlist = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformatfieldlist', undefined); + var tooltipFormatFieldlistKey = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformatfieldlistkey', undefined); + var numberFormatter = NETDATA.dataAttribute(state.element, 'sparkline-numberformatter', function(n){ return n.toFixed(2); }); + var numberDigitGroupSep = NETDATA.dataAttribute(state.element, 'sparkline-numberdigitgroupsep', undefined); + var numberDecimalMark = NETDATA.dataAttribute(state.element, 'sparkline-numberdecimalmark', undefined); + var numberDigitGroupCount = NETDATA.dataAttribute(state.element, 'sparkline-numberdigitgroupcount', undefined); + var animatedZooms = NETDATA.dataAttributeBoolean(state.element, 'sparkline-animatedzooms', false); if(spotColor === 'disable') spotColor=''; if(minSpotColor === 'disable') minSpotColor=''; @@ -4104,19 +4452,19 @@ var NETDATA = window.NETDATA || {}; state.setMode('zoom'); state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); - state.dygraph_user_action = true; - state.dygraph_force_zoom = true; + state.tmp.dygraph_user_action = true; + state.tmp.dygraph_force_zoom = true; state.updateChartPanOrZoom(after, before); NETDATA.globalPanAndZoom.setMaster(state, after, before); }; NETDATA.dygraphSetSelection = function(state, t) { - if(typeof state.dygraph_instance !== 'undefined') { + if(typeof state.tmp.dygraph_instance !== 'undefined') { var r = state.calculateRowForTime(t); if(r !== -1) - state.dygraph_instance.setSelection(r); + state.tmp.dygraph_instance.setSelection(r); else { - state.dygraph_instance.clearSelection(); + state.tmp.dygraph_instance.clearSelection(); state.legendShowUndefined(); } } @@ -4125,8 +4473,8 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.dygraphClearSelection = function(state) { - if(typeof state.dygraph_instance !== 'undefined') { - state.dygraph_instance.clearSelection(); + if(typeof state.tmp.dygraph_instance !== 'undefined') { + state.tmp.dygraph_instance.clearSelection(); } return true; }; @@ -4181,7 +4529,7 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.dygraphChartUpdate = function(state, data) { - var dygraph = state.dygraph_instance; + var dygraph = state.tmp.dygraph_instance; if(typeof dygraph === 'undefined') return NETDATA.dygraphChartCreate(state, data); @@ -4191,7 +4539,7 @@ var NETDATA = window.NETDATA || {}; // its element size as 0x0. // this will make it re-appear properly - if(state.tm.last_unhidden > state.dygraph_last_rendered) + if(state.tm.last_unhidden > state.tmp.dygraph_last_rendered) dygraph.resize(); var options = { @@ -4202,13 +4550,13 @@ var NETDATA = window.NETDATA || {}; visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names) }; - if(state.dygraph_force_zoom === true) { + if(state.tmp.dygraph_force_zoom === true) { if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartUpdate() forced zoom update'); options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null; options.isZoomedIgnoreProgrammaticZoom = true; - state.dygraph_force_zoom = false; + state.tmp.dygraph_force_zoom = false; } else if(state.current.name !== 'auto') { if(NETDATA.options.debug.dygraph === true || state.debug === true) @@ -4222,21 +4570,21 @@ var NETDATA = window.NETDATA || {}; options.isZoomedIgnoreProgrammaticZoom = true; } - options.valueRange = state.dygraph_options.valueRange; + options.valueRange = state.tmp.dygraph_options.valueRange; var oldMax = null, oldMin = null; - if(state.__commonMin !== null) { - state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + if (state.tmp.__commonMin !== null) { + state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; oldMin = options.valueRange[0] = NETDATA.commonMin.get(state); } - if(state.__commonMax !== null) { - state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + if (state.tmp.__commonMax !== null) { + state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; oldMax = options.valueRange[1] = NETDATA.commonMax.get(state); } - if(state.dygraph_smooth_eligible === true) { - if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter) - || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) { + if(state.tmp.dygraph_smooth_eligible === true) { + if((NETDATA.options.current.smooth_plot === true && state.tmp.dygraph_options.plotter !== smoothPlotter) + || (NETDATA.options.current.smooth_plot === false && state.tmp.dygraph_options.plotter === smoothPlotter)) { NETDATA.dygraphChartCreate(state, data); return; } @@ -4245,13 +4593,13 @@ var NETDATA = window.NETDATA || {}; dygraph.updateOptions(options); var redraw = false; - if(oldMin !== null && oldMin > state.dygraph_instance.axes_[0].extremeRange[0]) { - state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + if(oldMin !== null && oldMin > state.tmp.dygraph_instance.axes_[0].extremeRange[0]) { + state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; options.valueRange[0] = NETDATA.commonMin.get(state); redraw = true; } - if(oldMax !== null && oldMax < state.dygraph_instance.axes_[0].extremeRange[1]) { - state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + if(oldMax !== null && oldMax < state.tmp.dygraph_instance.axes_[0].extremeRange[1]) { + state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; options.valueRange[1] = NETDATA.commonMax.get(state); redraw = true; } @@ -4261,7 +4609,7 @@ var NETDATA = window.NETDATA || {}; dygraph.updateOptions(options); } - state.dygraph_last_rendered = Date.now(); + state.tmp.dygraph_last_rendered = Date.now(); return true; }; @@ -4269,185 +4617,93 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartCreate()'); - var self = $(state.element); - - var chart_type = self.data('dygraph-type') || state.chart.chart_type; + var chart_type = NETDATA.dataAttribute(state.element, 'dygraph-type', state.chart.chart_type); if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area'; var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state) === true)?3:4; var smooth = (NETDATA.dygraph.smooth === true) - ?(self.data('dygraph-smooth') || (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false)) + ?(NETDATA.dataAttributeBoolean(state.element, 'dygraph-smooth', (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false))) :false; - state.dygraph_options = { - colors: self.data('dygraph-colors') || state.chartColors(), + state.tmp.dygraph_options = { + colors: NETDATA.dataAttribute(state.element, '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 - + rightGap: NETDATA.dataAttribute(state.element, 'dygraph-rightgap', 5), + showRangeSelector: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showrangeselector', false), + showRoller: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showroller', false), + title: NETDATA.dataAttribute(state.element, 'dygraph-title', state.title), + titleHeight: NETDATA.dataAttribute(state.element, 'dygraph-titleheight', 19), + legend: NETDATA.dataAttribute(state.element, '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, - + labelsDiv: NETDATA.dataAttribute(state.element, 'dygraph-labelsdiv', state.element_legend_childs.hidden), + labelsDivStyles: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivstyles', { 'fontSize':'1px' }), + labelsDivWidth: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivwidth', state.chartWidth() - 70), + labelsSeparateLines: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsseparatelines', true), + labelsShowZeroValues: NETDATA.dataAttributeBoolean(state.element, '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 ], - + showLabelsOnHighlight: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showlabelsonhighlight', true), + hideOverlayOnMouseOut: NETDATA.dataAttributeBoolean(state.element, 'dygraph-hideoverlayonmouseout', true), + includeZero: NETDATA.dataAttribute(state.element, 'dygraph-includezero', (chart_type === 'stacked')), + xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0), + yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1), + valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [ null, null ]), ylabel: state.units, - - yLabelWidth: self.data('dygraph-ylabelwidth') - || 12, + yLabelWidth: NETDATA.dataAttribute(state.element, '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, + strokeWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokewidth', ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7))), + strokePattern: NETDATA.dataAttribute(state.element, '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, + drawPoints: NETDATA.dataAttributeBoolean(state.element, '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, + drawGapEdgePoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgapedgepoints', true), + connectSeparatedPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-connectseparatedpoints', false), + pointSize: NETDATA.dataAttribute(state.element, 'dygraph-pointsize', 1), // enabling this makes the chart with little square lines - stepPlot: self.data('dygraph-stepplot') - || false, + stepPlot: NETDATA.dataAttributeBoolean(state.element, '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') + strokeBorderColor: NETDATA.dataAttribute(state.element, 'dygraph-strokebordercolor', NETDATA.themes.current.background), + strokeBorderWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokeborderwidth', (chart_type === 'stacked')?0.0:0.0), + fillGraph: NETDATA.dataAttribute(state.element, 'dygraph-fillgraph', (chart_type === 'area' || chart_type === 'stacked')), + fillAlpha: NETDATA.dataAttribute(state.element, '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, - + :NETDATA.options.current.color_fill_opacity_area) + ), + stackedGraph: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraph', (chart_type === 'stacked')), + stackedGraphNaNFill: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraphnanfill', 'none'), + drawAxis: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawaxis', true), + axisLabelFontSize: NETDATA.dataAttribute(state.element, 'dygraph-axislabelfontsize', 10), + axisLineColor: NETDATA.dataAttribute(state.element, 'dygraph-axislinecolor', NETDATA.themes.current.axis), + axisLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-axislinewidth', 1.0), + drawGrid: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgrid', true), + gridLinePattern: NETDATA.dataAttribute(state.element, 'dygraph-gridlinepattern', null), + gridLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-gridlinewidth', 1.0), + gridLineColor: NETDATA.dataAttribute(state.element, 'dygraph-gridlinecolor', NETDATA.themes.current.grid), + maxNumberWidth: NETDATA.dataAttribute(state.element, 'dygraph-maxnumberwidth', 8), + sigFigs: NETDATA.dataAttribute(state.element, 'dygraph-sigfigs', null), + digitsAfterDecimal: NETDATA.dataAttribute(state.element, 'dygraph-digitsafterdecimal', 2), + valueFormatter: NETDATA.dataAttribute(state.element, 'dygraph-valueformatter', undefined), + highlightCircleSize: NETDATA.dataAttribute(state.element, 'dygraph-highlightcirclesize', highlightCircleSize), + highlightSeriesOpts: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesopts', null), // TOO SLOW: { strokeWidth: 1.5 }, + highlightSeriesBackgroundAlpha: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesbackgroundalpha', null), // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, + pointClickCallback: NETDATA.dataAttribute(state.element, 'dygraph-pointclickcallback', undefined), visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), axes: { @@ -4495,8 +4751,8 @@ var NETDATA = window.NETDATA || {}; return ''; }, drawCallback: function(dygraph, is_initial) { - if(state.current.name !== 'auto' && state.dygraph_user_action === true) { - state.dygraph_user_action = false; + if(state.current.name !== 'auto' && state.tmp.dygraph_user_action === true) { + state.tmp.dygraph_user_action = false; var x_range = dygraph.xAxisRange(); var after = Math.round(x_range[0]); @@ -4520,8 +4776,8 @@ var NETDATA = window.NETDATA || {}; state.setMode('zoom'); // refresh it to the greatest possible zoom level - state.dygraph_user_action = true; - state.dygraph_force_zoom = true; + state.tmp.dygraph_user_action = true; + state.tmp.dygraph_force_zoom = true; state.updateChartPanOrZoom(minDate, maxDate); }, highlightCallback: function(event, x, points, row, seriesName) { @@ -4543,7 +4799,7 @@ var NETDATA = window.NETDATA || {}; // fix legend zIndex using the internal structures of dygraph legend module // this works, but it is a hack! - // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; + // state.tmp.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; }, unhighlightCallback: function(event) { void(event); @@ -4559,7 +4815,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.mousedown()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); if(NETDATA.options.debug.dygraph === true) @@ -4600,7 +4856,7 @@ var NETDATA = window.NETDATA || {}; state.log('interactionModel.mousemove()'); if(context.isPanning) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('pan'); @@ -4608,7 +4864,7 @@ var NETDATA = window.NETDATA || {}; Dygraph.movePan(event, dygraph, context); } else if(context.isZooming) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('zoom'); @@ -4620,12 +4876,12 @@ var NETDATA = window.NETDATA || {}; state.log('interactionModel.mouseup()'); if (context.isPanning) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncDelay(); Dygraph.endPan(event, dygraph, context); } else if (context.isZooming) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncDelay(); Dygraph.endZoom(event, dygraph, context); } @@ -4714,7 +4970,7 @@ var NETDATA = window.NETDATA || {}; } if(event.altKey || event.shiftKey) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); @@ -4766,7 +5022,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchstart()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.setMode('zoom'); state.pauseChart(); @@ -4788,7 +5044,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchmove()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchmove(event, dygraph, context); state.dygraph_last_touch_move = Date.now(); @@ -4797,7 +5053,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchend()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchend(event, dygraph, context); // if it didn't move, it is a selection @@ -4826,41 +5082,48 @@ var NETDATA = window.NETDATA || {}; }; if(NETDATA.chartLibraries.dygraph.isSparkline(state)) { - state.dygraph_options.drawGrid = false; - state.dygraph_options.drawAxis = false; - state.dygraph_options.title = undefined; - state.dygraph_options.ylabel = undefined; - state.dygraph_options.yLabelWidth = 0; - state.dygraph_options.labelsDivWidth = 120; - state.dygraph_options.labelsDivStyles.width = '120px'; - state.dygraph_options.labelsSeparateLines = true; - state.dygraph_options.rightGap = 0; - state.dygraph_options.yRangePad = 1; + state.tmp.dygraph_options.drawGrid = false; + state.tmp.dygraph_options.drawAxis = false; + state.tmp.dygraph_options.title = undefined; + state.tmp.dygraph_options.ylabel = undefined; + state.tmp.dygraph_options.yLabelWidth = 0; + state.tmp.dygraph_options.labelsDivWidth = 120; + state.tmp.dygraph_options.labelsDivStyles.width = '120px'; + state.tmp.dygraph_options.labelsSeparateLines = true; + state.tmp.dygraph_options.rightGap = 0; + state.tmp.dygraph_options.yRangePad = 1; } if(smooth === true) { - state.dygraph_smooth_eligible = true; + state.tmp.dygraph_smooth_eligible = true; if(NETDATA.options.current.smooth_plot === true) - state.dygraph_options.plotter = smoothPlotter; + state.tmp.dygraph_options.plotter = smoothPlotter; } - else state.dygraph_smooth_eligible = false; + else state.tmp.dygraph_smooth_eligible = false; - state.dygraph_instance = new Dygraph(state.element_chart, - data.result.data, state.dygraph_options); + state.tmp.dygraph_instance = new Dygraph(state.element_chart, + data.result.data, state.tmp.dygraph_options); - state.dygraph_force_zoom = false; - state.dygraph_user_action = false; - state.dygraph_last_rendered = Date.now(); + state.tmp.dygraph_force_zoom = false; + state.tmp.dygraph_user_action = false; + state.tmp.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; + if(state.tmp.dygraph_options.valueRange[0] === null && state.tmp.dygraph_options.valueRange[1] === null) { + if (typeof state.tmp.dygraph_instance.axes_[0].extremeRange !== 'undefined') { + state.tmp.__commonMin = NETDATA.dataAttribute(state.element, 'common-min', null); + state.tmp.__commonMax = NETDATA.dataAttribute(state.element, 'common-max', null); + } + else { + state.log('incompatible version of Dygraph detected'); + state.tmp.__commonMin = null; + state.tmp.__commonMax = null; + } } else { - state.log('incompatible version of Dygraph detected'); - state.__commonMin = null; - state.__commonMax = null; + // if the user gave a valueRange, respect it + state.tmp.__commonMin = null; + state.tmp.__commonMax = null; } return true; @@ -5289,7 +5552,7 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - NETDATA.easypiechartPercentFromValueMinMax = function(value, min, max) { + NETDATA.easypiechartPercentFromValueMinMax = function(state, value, min, max) { if(typeof value !== 'number') value = 0; if(typeof min !== 'number') min = 0; if(typeof max !== 'number') max = 0; @@ -5298,8 +5561,9 @@ var NETDATA = window.NETDATA || {}; if(max < value) max = value; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only they have not been set by the user + if(state.tmp.easyPieChartMin === null && min > 0) min = 0; + if(state.tmp.easyPieChartMax === null && max < 0) max = 0; var pcent = 0; if(value >= 0) { @@ -5347,22 +5611,22 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.easypiechartClearSelection = function(state) { - if(typeof state.easyPieChartEvent !== 'undefined') { - if(state.easyPieChartEvent.timer !== undefined) { - clearTimeout(state.easyPieChartEvent.timer); + if(typeof state.tmp.easyPieChartEvent !== 'undefined') { + if(state.tmp.easyPieChartEvent.timer !== undefined) { + clearTimeout(state.tmp.easyPieChartEvent.timer); } - state.easyPieChartEvent.timer = undefined; + state.tmp.easyPieChartEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { NETDATA.easypiechartChartUpdate(state, state.data); } else { - state.easyPieChartLabel.innerText = state.legendFormatValue(null); - state.easyPieChart_instance.update(0); + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(null); + state.tmp.easyPieChart_instance.update(0); } - state.easyPieChart_instance.enableAnimation(); + state.tmp.easyPieChart_instance.enableAnimation(); return true; }; @@ -5375,8 +5639,8 @@ var NETDATA = window.NETDATA || {}; if(slot < 0 || slot >= state.data.result.length) return NETDATA.easypiechartClearSelection(state); - if(typeof state.easyPieChartEvent === 'undefined') { - state.easyPieChartEvent = { + if(typeof state.tmp.easyPieChartEvent === 'undefined') { + state.tmp.easyPieChartEvent = { timer: undefined, value: 0, pcent: 0 @@ -5384,20 +5648,20 @@ var NETDATA = window.NETDATA || {}; } 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.easypiechartPercentFromValueMinMax(value, min, max); + var min = (state.tmp.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.tmp.easyPieChartMin; + var max = (state.tmp.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.tmp.easyPieChartMax; + var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); - state.easyPieChartEvent.value = value; - state.easyPieChartEvent.pcent = pcent; - state.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChartEvent.value = value; + state.tmp.easyPieChartEvent.pcent = pcent; + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); - if(state.easyPieChartEvent.timer === undefined) { - state.easyPieChart_instance.disableAnimation(); + if(state.tmp.easyPieChartEvent.timer === undefined) { + state.tmp.easyPieChart_instance.disableAnimation(); - state.easyPieChartEvent.timer = setTimeout(function() { - state.easyPieChartEvent.timer = undefined; - state.easyPieChart_instance.update(state.easyPieChartEvent.pcent); + state.tmp.easyPieChartEvent.timer = setTimeout(function() { + state.tmp.easyPieChartEvent.timer = undefined; + state.tmp.easyPieChart_instance.update(state.tmp.easyPieChartEvent.pcent); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5413,88 +5677,76 @@ var NETDATA = window.NETDATA || {}; } 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.easypiechartPercentFromValueMinMax(value, min, max); + min = (state.tmp.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.tmp.easyPieChartMin; + max = (state.tmp.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.tmp.easyPieChartMax; + pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); } - state.easyPieChartLabel.innerText = state.legendFormatValue(value); - state.easyPieChart_instance.update(pcent); + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChart_instance.update(pcent); return true; }; NETDATA.easypiechartChartCreate = function(state, data) { - var self = $(state.element); 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; + var min = NETDATA.dataAttribute(state.element, 'easypiechart-min-value', null); + var max = NETDATA.dataAttribute(state.element, 'easypiechart-max-value', null); if(min === null) { min = NETDATA.commonMin.get(state); - state.easyPieChartMin = null; + state.tmp.easyPieChartMin = null; } else - state.easyPieChartMin = min; + state.tmp.easyPieChartMin = min; if(max === null) { max = NETDATA.commonMax.get(state); - state.easyPieChartMax = null; + state.tmp.easyPieChartMax = null; } else - state.easyPieChartMax = max; + state.tmp.easyPieChartMax = max; - var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); + var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); chart.data('data-percent', pcent); - var size; - switch(adjust) { - case 'width': size = state.chartHeight(); break; - case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break; - case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break; - case 'height': - default: size = state.chartWidth(); break; - } - state.element.style.width = size + 'px'; - state.element.style.height = size + 'px'; - + var size = state.chartWidth(); var stroke = Math.floor(size / 22); if(stroke < 3) stroke = 2; var valuefontsize = Math.floor((size * 2 / 3) / 5); var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2); - state.easyPieChartLabel = document.createElement('span'); - state.easyPieChartLabel.className = 'easyPieChartLabel'; - state.easyPieChartLabel.innerText = state.legendFormatValue(value); - state.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; - state.easyPieChartLabel.style.top = valuetop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartLabel); + state.tmp.easyPieChartLabel = document.createElement('span'); + state.tmp.easyPieChartLabel.className = 'easyPieChartLabel'; + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; + state.tmp.easyPieChartLabel.style.top = valuetop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartLabel); var titlefontsize = Math.round(valuefontsize * 1.6 / 3); var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40)); - state.easyPieChartTitle = document.createElement('span'); - state.easyPieChartTitle.className = 'easyPieChartTitle'; - state.easyPieChartTitle.innerText = state.title; - state.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; - state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; - state.easyPieChartTitle.style.top = titletop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartTitle); + state.tmp.easyPieChartTitle = document.createElement('span'); + state.tmp.easyPieChartTitle.className = 'easyPieChartTitle'; + state.tmp.easyPieChartTitle.innerText = state.title; + state.tmp.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; + state.tmp.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; + state.tmp.easyPieChartTitle.style.top = titletop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartTitle); var unitfontsize = Math.round(titlefontsize * 0.9); var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40)); - state.easyPieChartUnits = document.createElement('span'); - state.easyPieChartUnits.className = 'easyPieChartUnits'; - state.easyPieChartUnits.innerText = state.units; - state.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; - state.easyPieChartUnits.style.top = unittop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartUnits); - - var barColor = self.data('easypiechart-barcolor'); + state.tmp.easyPieChartUnits = document.createElement('span'); + state.tmp.easyPieChartUnits.className = 'easyPieChartUnits'; + state.tmp.easyPieChartUnits.innerText = state.units; + state.tmp.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; + state.tmp.easyPieChartUnits.style.top = unittop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartUnits); + + var barColor = NETDATA.dataAttribute(state.element, 'easypiechart-barcolor', undefined); if(typeof barColor === 'undefined' || barColor === null) - barColor = state.chartColors()[0]; + barColor = state.chartCustomColors()[0]; else { //
var tmp = eval(barColor); @@ -5504,28 +5756,28 @@ var NETDATA = window.NETDATA || {}; chart.easyPieChart({ barColor: barColor, - trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track, - scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale, - scaleLength: self.data('easypiechart-scalelength') || 5, - lineCap: self.data('easypiechart-linecap') || 'round', - lineWidth: self.data('easypiechart-linewidth') || stroke, - trackWidth: self.data('easypiechart-trackwidth') || undefined, - size: self.data('easypiechart-size') || size, - rotate: self.data('easypiechart-rotate') || 0, - animate: self.data('easypiechart-animate') || {duration: 500, enabled: true}, - easing: self.data('easypiechart-easing') || undefined + trackColor: NETDATA.dataAttribute(state.element, 'easypiechart-trackcolor', NETDATA.themes.current.easypiechart_track), + scaleColor: NETDATA.dataAttribute(state.element, 'easypiechart-scalecolor', NETDATA.themes.current.easypiechart_scale), + scaleLength: NETDATA.dataAttribute(state.element, 'easypiechart-scalelength', 5), + lineCap: NETDATA.dataAttribute(state.element, 'easypiechart-linecap', 'round'), + lineWidth: NETDATA.dataAttribute(state.element, 'easypiechart-linewidth', stroke), + trackWidth: NETDATA.dataAttribute(state.element, 'easypiechart-trackwidth', undefined), + size: NETDATA.dataAttribute(state.element, 'easypiechart-size', size), + rotate: NETDATA.dataAttribute(state.element, 'easypiechart-rotate', 0), + animate: NETDATA.dataAttribute(state.element, 'easypiechart-animate', {duration: 500, enabled: true}), + easing: NETDATA.dataAttribute(state.element, 'easypiechart-easing', undefined) }); // when we just re-create the chart // do not animate the first update var animate = true; - if(typeof state.easyPieChart_instance !== 'undefined') + if(typeof state.tmp.easyPieChart_instance !== 'undefined') animate = false; - state.easyPieChart_instance = chart.data('easyPieChart'); - if(animate === false) state.easyPieChart_instance.disableAnimation(); - state.easyPieChart_instance.update(pcent); - if(animate === false) state.easyPieChart_instance.enableAnimation(); + state.tmp.easyPieChart_instance = chart.data('easyPieChart'); + if(animate === false) state.tmp.easyPieChart_instance.disableAnimation(); + state.tmp.easyPieChart_instance.update(pcent); + if(animate === false) state.tmp.easyPieChart_instance.enableAnimation(); return true; }; @@ -5568,8 +5820,8 @@ var NETDATA = window.NETDATA || {}; speed = status; // console.log('gauge speed ' + speed); - state.gauge_instance.animationSpeed = speed; - state.___gaugeOld__.speed = speed; + state.tmp.gauge_instance.animationSpeed = speed; + state.tmp.___gaugeOld__.speed = speed; }; NETDATA.gaugeSet = function(state, value, min, max) { @@ -5601,36 +5853,36 @@ var NETDATA = window.NETDATA || {}; if(pcent < 0.001) pcent = 0.001; if(pcent > 99.999) pcent = 99.999; - state.gauge_instance.set(pcent); + state.tmp.gauge_instance.set(pcent); // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max); - state.___gaugeOld__.value = value; - state.___gaugeOld__.min = min; - state.___gaugeOld__.max = max; + state.tmp.___gaugeOld__.value = value; + state.tmp.___gaugeOld__.min = min; + state.tmp.___gaugeOld__.max = max; }; NETDATA.gaugeSetLabels = function(state, value, min, max) { - if(state.___gaugeOld__.valueLabel !== value) { - state.___gaugeOld__.valueLabel = value; - state.gaugeChartLabel.innerText = state.legendFormatValue(value); + if(state.tmp.___gaugeOld__.valueLabel !== value) { + state.tmp.___gaugeOld__.valueLabel = value; + state.tmp.gaugeChartLabel.innerText = state.legendFormatValue(value); } - if(state.___gaugeOld__.minLabel !== min) { - state.___gaugeOld__.minLabel = min; - state.gaugeChartMin.innerText = state.legendFormatValue(min); + if(state.tmp.___gaugeOld__.minLabel !== min) { + state.tmp.___gaugeOld__.minLabel = min; + state.tmp.gaugeChartMin.innerText = state.legendFormatValue(min); } - if(state.___gaugeOld__.maxLabel !== max) { - state.___gaugeOld__.maxLabel = max; - state.gaugeChartMax.innerText = state.legendFormatValue(max); + if(state.tmp.___gaugeOld__.maxLabel !== max) { + state.tmp.___gaugeOld__.maxLabel = max; + state.tmp.gaugeChartMax.innerText = state.legendFormatValue(max); } }; NETDATA.gaugeClearSelection = function(state) { - if(typeof state.gaugeEvent !== 'undefined') { - if(state.gaugeEvent.timer !== undefined) { - clearTimeout(state.gaugeEvent.timer); + if(typeof state.tmp.gaugeEvent !== 'undefined') { + if(state.tmp.gaugeEvent.timer !== undefined) { + clearTimeout(state.tmp.gaugeEvent.timer); } - state.gaugeEvent.timer = undefined; + state.tmp.gaugeEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { @@ -5654,8 +5906,8 @@ var NETDATA = window.NETDATA || {}; if(slot < 0 || slot >= state.data.result.length) return NETDATA.gaugeClearSelection(state); - if(typeof state.gaugeEvent === 'undefined') { - state.gaugeEvent = { + if(typeof state.tmp.gaugeEvent === 'undefined') { + state.tmp.gaugeEvent = { timer: undefined, value: 0, min: 0, @@ -5664,24 +5916,25 @@ var NETDATA = window.NETDATA || {}; } 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 = (state.tmp.gaugeMin === null)?NETDATA.commonMin.get(state):state.tmp.gaugeMin; + var max = (state.tmp.gaugeMax === null)?NETDATA.commonMax.get(state):state.tmp.gaugeMax; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; - state.gaugeEvent.value = value; - state.gaugeEvent.min = min; - state.gaugeEvent.max = max; + state.tmp.gaugeEvent.value = value; + state.tmp.gaugeEvent.min = min; + state.tmp.gaugeEvent.max = max; NETDATA.gaugeSetLabels(state, value, min, max); - if(state.gaugeEvent.timer === undefined) { + if(state.tmp.gaugeEvent.timer === undefined) { NETDATA.gaugeAnimation(state, false); - state.gaugeEvent.timer = setTimeout(function() { - state.gaugeEvent.timer = undefined; - NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max); + state.tmp.gaugeEvent.timer = setTimeout(function() { + state.tmp.gaugeEvent.timer = undefined; + NETDATA.gaugeSet(state, state.tmp.gaugeEvent.value, state.tmp.gaugeEvent.min, state.tmp.gaugeEvent.max); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5699,14 +5952,15 @@ var NETDATA = window.NETDATA || {}; } else { value = data.result[0]; - min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; - max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + min = (state.tmp.gaugeMin === null)?NETDATA.commonMin.get(state):state.tmp.gaugeMin; + max = (state.tmp.gaugeMax === null)?NETDATA.commonMax.get(state):state.tmp.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; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; NETDATA.gaugeSetLabels(state, value, min, max); } @@ -5716,38 +5970,39 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.gaugeChartCreate = function(state, data) { - var self = $(state.element); // 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; - var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke; - var startColor = self.data('gauge-start-color') || state.chartColors()[0]; - var stopColor = self.data('gauge-stop-color') || void 0; - var generateGradient = self.data('gauge-generate-gradient') || false; + var min = NETDATA.dataAttribute(state.element, 'gauge-min-value', null); + var max = NETDATA.dataAttribute(state.element, 'gauge-max-value', null); + // var adjust = NETDATA.dataAttribute(state.element, 'gauge-adjust', null); + var pointerColor = NETDATA.dataAttribute(state.element, 'gauge-pointer-color', NETDATA.themes.current.gauge_pointer); + var strokeColor = NETDATA.dataAttribute(state.element, 'gauge-stroke-color', NETDATA.themes.current.gauge_stroke); + var startColor = NETDATA.dataAttribute(state.element, 'gauge-start-color', state.chartCustomColors()[0]); + var stopColor = NETDATA.dataAttribute(state.element, 'gauge-stop-color', void 0); + var generateGradient = NETDATA.dataAttributeBoolean(state.element, 'gauge-generate-gradient', false); if(min === null) { min = NETDATA.commonMin.get(state); - state.gaugeMin = null; + state.tmp.gaugeMin = null; } else - state.gaugeMin = min; + state.tmp.gaugeMin = min; if(max === null) { max = NETDATA.commonMax.get(state); - state.gaugeMax = null; + state.tmp.gaugeMax = null; } else - state.gaugeMax = max; + state.tmp.gaugeMax = max; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5; + // console.log('gauge width: ' + width.toString() + ', height: ' + height.toString()); //switch(adjust) { // case 'width': width = height * ratio; break; // case 'height': @@ -5760,12 +6015,12 @@ var NETDATA = window.NETDATA || {}; var options = { lines: 12, // The number of lines to draw - angle: 0.15, // The span of the gauge arc - lineWidth: 0.50, // The line thickness - radiusScale: 0.85, // Relative radius + angle: 0.14, // The span of the gauge arc + lineWidth: 0.57, // The line thickness + radiusScale: 1.0, // Relative radius pointer: { - length: 0.8, // 0.9 The radius of the inner circle - strokeWidth: 0.035, // The rotation offset + length: 0.85, // 0.9 The radius of the inner circle + strokeWidth: 0.045, // The rotation offset color: pointerColor // Fill color }, limitMax: true, // If false, the max value of the gauge will be updated if value surpass max @@ -5789,7 +6044,7 @@ var NETDATA = window.NETDATA || {}; var len = generateGradient.length; while(len--) { var pcent = generateGradient[len]; - var color = self.attr('data-gauge-gradient-percent-color-' + pcent.toString()) || false; + var color = NETDATA.dataAttribute(state.element, 'gauge-gradient-percent-color-' + pcent.toString(), false); if(color !== false) { var a = []; a[0] = pcent / 100; @@ -5816,57 +6071,57 @@ var NETDATA = window.NETDATA || {}; [1.0, NETDATA.colorLuminance(startColor, 0.0)]]; } - state.gauge_canvas = document.createElement('canvas'); - state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas'; - state.gauge_canvas.className = 'gaugeChart'; - state.gauge_canvas.width = width; - state.gauge_canvas.height = height; - state.element_chart.appendChild(state.gauge_canvas); + state.tmp.gauge_canvas = document.createElement('canvas'); + state.tmp.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas'; + state.tmp.gauge_canvas.className = 'gaugeChart'; + state.tmp.gauge_canvas.width = width; + state.tmp.gauge_canvas.height = height; + state.element_chart.appendChild(state.tmp.gauge_canvas); - var valuefontsize = Math.floor(height / 6); - var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2); - state.gaugeChartLabel = document.createElement('span'); - state.gaugeChartLabel.className = 'gaugeChartLabel'; - state.gaugeChartLabel.style.fontSize = valuefontsize + 'px'; - state.gaugeChartLabel.style.top = valuetop.toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartLabel); + var valuefontsize = Math.floor(height / 5); + var valuetop = Math.round((height - valuefontsize) / 3.2); + state.tmp.gaugeChartLabel = document.createElement('span'); + state.tmp.gaugeChartLabel.className = 'gaugeChartLabel'; + state.tmp.gaugeChartLabel.style.fontSize = valuefontsize + 'px'; + state.tmp.gaugeChartLabel.style.top = valuetop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartLabel); - var titlefontsize = Math.round(valuefontsize / 2); + var titlefontsize = Math.round(valuefontsize / 2.1); var titletop = 0; - state.gaugeChartTitle = document.createElement('span'); - state.gaugeChartTitle.className = 'gaugeChartTitle'; - state.gaugeChartTitle.innerText = state.title; - state.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; - state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; - state.gaugeChartTitle.style.top = titletop.toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartTitle); + state.tmp.gaugeChartTitle = document.createElement('span'); + state.tmp.gaugeChartTitle.className = 'gaugeChartTitle'; + state.tmp.gaugeChartTitle.innerText = state.title; + state.tmp.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; + state.tmp.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; + state.tmp.gaugeChartTitle.style.top = titletop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartTitle); var unitfontsize = Math.round(titlefontsize * 0.9); - state.gaugeChartUnits = document.createElement('span'); - state.gaugeChartUnits.className = 'gaugeChartUnits'; - state.gaugeChartUnits.innerText = state.units; - state.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; - state.element_chart.appendChild(state.gaugeChartUnits); - - state.gaugeChartMin = document.createElement('span'); - state.gaugeChartMin.className = 'gaugeChartMin'; - state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartMin); - - state.gaugeChartMax = document.createElement('span'); - state.gaugeChartMax.className = 'gaugeChartMax'; - state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartMax); + state.tmp.gaugeChartUnits = document.createElement('span'); + state.tmp.gaugeChartUnits.className = 'gaugeChartUnits'; + state.tmp.gaugeChartUnits.innerText = state.units; + state.tmp.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartUnits); + + state.tmp.gaugeChartMin = document.createElement('span'); + state.tmp.gaugeChartMin.className = 'gaugeChartMin'; + state.tmp.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartMin); + + state.tmp.gaugeChartMax = document.createElement('span'); + state.tmp.gaugeChartMax.className = 'gaugeChartMax'; + state.tmp.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartMax); // when we just re-create the chart // do not animate the first update var animate = true; - if(typeof state.gauge_instance !== 'undefined') + if(typeof state.tmp.gauge_instance !== 'undefined') animate = false; - state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge! + state.tmp.gauge_instance = new Gauge(state.tmp.gauge_canvas).setOptions(options); // create sexy gauge! - state.___gaugeOld__ = { + state.tmp.___gaugeOld__ = { value: value, min: min, max: max, @@ -5876,8 +6131,8 @@ var NETDATA = window.NETDATA || {}; }; // we will always feed a percentage - state.gauge_instance.minValue = 0; - state.gauge_instance.maxValue = 100; + state.tmp.gauge_instance.minValue = 0; + state.tmp.gauge_instance.maxValue = 100; NETDATA.gaugeAnimation(state, animate); NETDATA.gaugeSet(state, value, min, max); @@ -5895,8 +6150,8 @@ var NETDATA = window.NETDATA || {}; create: NETDATA.dygraphChartCreate, update: NETDATA.dygraphChartUpdate, resize: function(state) { - if(typeof state.dygraph_instance.resize === 'function') - state.dygraph_instance.resize(); + if(typeof state.tmp.dygraph_instance.resize === 'function') + state.tmp.dygraph_instance.resize(); }, setSelection: NETDATA.dygraphSetSelection, clearSelection: NETDATA.dygraphClearSelection, @@ -5915,11 +6170,16 @@ var NETDATA = window.NETDATA || {}; return (this.isSparkline(state) === false)?3:2; }, isSparkline: function(state) { - if(typeof state.dygraph_sparkline === 'undefined') { - var t = $(state.element).data('dygraph-theme'); - state.dygraph_sparkline = (t === 'sparkline'); + if(typeof state.tmp.dygraph_sparkline === 'undefined') { + var t = NETDATA.dataAttribute(state.element, 'dygraph-theme', undefined); + state.tmp.dygraph_sparkline = (t === 'sparkline'); } - return state.dygraph_sparkline; + return state.tmp.dygraph_sparkline; + }, + container_class: function(state) { + if(this.legend(state) !== null) + return 'netdata-container-with-legend'; + return 'netdata-container'; } }, "sparkline": { @@ -5938,7 +6198,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "peity": { initialize: NETDATA.peityInitialize, @@ -5956,7 +6217,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "morris": { initialize: NETDATA.morrisInitialize, @@ -5974,7 +6236,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 15; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "google": { initialize: NETDATA.googleInitialize, @@ -5992,7 +6255,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 4; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "raphael": { initialize: NETDATA.raphaelInitialize, @@ -6010,7 +6274,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "c3": { initialize: NETDATA.c3Initialize, @@ -6028,7 +6293,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 15; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "d3": { initialize: NETDATA.d3Initialize, @@ -6046,7 +6312,8 @@ var NETDATA = window.NETDATA || {}; 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; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "easypiechart": { initialize: NETDATA.easypiechartInitialize, @@ -6065,7 +6332,8 @@ var NETDATA = window.NETDATA || {}; 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 + aspect_ratio: 100, + container_class: function(state) { void(state); return 'netdata-container-easypiechart'; } }, "gauge": { initialize: NETDATA.gaugeInitialize, @@ -6084,7 +6352,8 @@ var NETDATA = window.NETDATA || {}; 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 + aspect_ratio: 60, + container_class: function(state) { void(state); return 'netdata-container-gauge'; } } }; @@ -6203,7 +6472,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.alarms = { onclick: null, // the callback to handle the click - it will be called with the alarm log entry - chart_div_offset: 100, // give that space above the chart when scrolling to it + chart_div_offset: -50, // give that space above the chart when scrolling to it chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id)) chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart @@ -6349,7 +6618,7 @@ var NETDATA = window.NETDATA || {}; if(typeof chart_id === 'string') { var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset(); if(typeof offset !== 'undefined') { - $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration); + $('html, body').animate({ scrollTop: offset.top + NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration); return true; } } diff --git a/web/dashboard.slate.css b/web/dashboard.slate.css index 36ea6dc6a..f12a6aab9 100644 --- a/web/dashboard.slate.css +++ b/web/dashboard.slate.css @@ -63,6 +63,38 @@ code { /* width and height is given per chart with data-width and data-height */ } +.netdata-container-gauge { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-gauge:after { + padding-top: 60%; + display: block; + content: ''; +} + +.netdata-container-easypiechart { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-easypiechart:after { + padding-top: 100%; + display: block; + content: ''; +} + .netdata-aspect { position: relative; width: 100%; @@ -142,12 +174,15 @@ code { .netdata-message { display: inline-block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; text-align: left; vertical-align: top; font-weight: bold; font-size: x-small; - width: 100%; - height: 100%; overflow: hidden; background: inherit; z-index: 0; @@ -417,7 +452,7 @@ code { margin-left: 18%; text-align: center; color: #676b70; - font-weight: normal; + font-weight: bold; } .easyPieChartUnits { @@ -441,6 +476,8 @@ code { position: absolute; top: 0; left: 0; + bottom: 0; + right: 0; z-index: 0; } @@ -489,7 +526,7 @@ code { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 92%; margin-left: 8%; text-align: left; @@ -502,7 +539,7 @@ code { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 95%; margin-right: 5%; text-align: right; diff --git a/web/dashboard_info.js b/web/dashboard_info.js index c348da30d..91e007a1d 100644 --- a/web/dashboard_info.js +++ b/web/dashboard_info.js @@ -61,6 +61,12 @@ netdataDashboard.menu = { info: 'Performance metrics of the netfilter components.' }, + 'ipfw': { + title: 'Firewall (ipfw)', + icon: '', + info: 'Counters and memory usage for the ipfw rules.' + }, + 'cpu': { title: 'CPUs', icon: '', @@ -91,6 +97,12 @@ netdataDashboard.menu = { info: 'The Intelligent Platform Management Interface (IPMI) is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system\'s CPU, firmware (BIOS or UEFI) and operating system.' }, + 'samba': { + title: 'Samba', + icon: "", + info: 'Performance metrics of the Samba file share operations of this system. Samba is a implementation of Windows services, including Windows SMB protocol file shares.' + }, + 'nfsd': { title: 'NFS Server', icon: '', @@ -103,6 +115,12 @@ netdataDashboard.menu = { info: 'Performance metrics of the NFS operations of this system, acting as an NFS client.' }, + 'zfs': { + title: 'ZFS filesystem', + icon: '', + info: 'Performance metrics of the ZFS filesystem. The following charts visualize all metrics reported by arcstat.py and arc_summary.py.' + }, + 'apps': { title: 'Applications', icon: '', @@ -225,10 +243,16 @@ netdataDashboard.menu = { info: undefined }, + 'lighttpd': { + title: 'Lighttpd', + icon: '', + info: undefined + }, + 'web_log': { title: undefined, icon: '', - info: 'Information extracted from a web server log file. web_log plugin incrementally parses the web server log file to provide, in real-time, a break down of key web server performance metrics. An extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' + info: 'Information extracted from a server log file. web_log plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' }, 'named': { @@ -261,10 +285,22 @@ netdataDashboard.menu = { info: undefined }, + 'fronius': { + title: 'Fronius', + icon: '', + info: undefined + }, + 'snmp': { title: 'SNMP', icon: '', info: undefined + }, + + 'go_expvar': { + title: 'Go - expvars', + icon: '', + info: 'Statistics about running Go applications exposed by the expvar package.' } }; @@ -277,6 +313,44 @@ netdataDashboard.menu = { // information about the submenus netdataDashboard.submenu = { + 'web_log.squid_bandwidth': { + title: 'bandwidth', + info: 'Bandwidth of responses (sent) by squid. This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the server bandwidth.' + }, + + 'web_log.squid_responses': { + title: 'responses', + info: 'Information related to the responses sent by squid.' + }, + + 'web_log.squid_requests': { + title: 'requests', + info: 'Information related to the requests squid has received.' + }, + + 'web_log.squid_hierarchy': { + title: 'hierarchy', + info: 'Performance metrics for the squid hierarchy used to serve the requests.' + }, + + 'web_log.squid_squid_transport': { + title: 'transport' + }, + + 'web_log.squid_squid_cache': { + title: 'cache', + info: 'Performance metrics for the performance of the squid cache.' + }, + + 'web_log.squid_timings': { + title: 'timings', + info: 'Duration of squid requests. Unrealistic spikes may be reported, since squid logs the total time of the requests, when they complete. Especially for HTTPS, the clients get a tunnel from the proxy and exchange requests directly with the upstream servers, so squid cannot evaluate the individual requests and reports the total time the tunnel was open.' + }, + + 'web_log.squid_clients': { + title: 'clients' + }, + 'web_log.bandwidth': { info: 'Bandwidth of requests (received) and responses (sent). received requires an extended log format (without it, the web server log does not have this information). This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the web server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the web server bandwidth.' }, @@ -321,6 +395,11 @@ netdataDashboard.submenu = { info: 'DDoS protection performance metrics. SYNPROXY is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.' }, + 'ipfw.dynamic_rules': { + title: 'dynamic rules', + info: 'Number of dynamic rules, created by correspondent stateful firewall rules.' + }, + 'system.softnet_stat': { title: 'softnet', info: function(os) { @@ -339,6 +418,11 @@ netdataDashboard.submenu = { else return 'Statistics for per CPUs core SoftIRQs related to network receive work. Total for all CPU cores can be found at System / softnet statistics.'; } + }, + + 'go_expvar.memstats': { + title: 'Memory statistics', + info: 'Go runtime memory statistics. See runtime.MemStats documentation for more info about each chart and the values.' } }; @@ -420,7 +504,6 @@ netdataDashboard.context = { }, 'system.idlejitter': { - colors: '#5555AA', info: 'Idle jitter is calculated by netdata. A thread is spawned that requests to sleep for a few microseconds. When the system wakes it up, it measures how many microseconds have passed. The difference between the requested and the actual duration of the sleep, is the idle jitter. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).' }, @@ -440,6 +523,30 @@ netdataDashboard.context = { info: 'System swap memory usage. Swap space is used when the amount of physical memory (RAM) is full. When the system needs more memory resources and the RAM is full, inactive pages in memory are moved to the swap space (usually a disk, a disk partition or a file).' }, + // ------------------------------------------------------------------------ + // CPU charts + + 'cpu.cpu': { + commonMin: true, + commonMax: true, + valueRange: "[0, 100]" + }, + + 'cpu.interrupts': { + commonMin: true, + commonMax: true + }, + + 'cpu.softirqs': { + commonMin: true, + commonMax: true + }, + + 'cpu.softnet_stat': { + commonMin: true, + commonMax: true + }, + // ------------------------------------------------------------------------ // MEMORY @@ -782,6 +889,66 @@ netdataDashboard.context = { }, + // ------------------------------------------------------------------------ + // LIGHTTPD + + 'lighttpd.connections': { + colors: NETDATA.colors[4], + mainheads: [ + netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4]) + ] + }, + + 'lighttpd.requests': { + colors: NETDATA.colors[0], + mainheads: [ + netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0]) + ] + }, + + 'lighttpd.net': { + colors: NETDATA.colors[3], + mainheads: [ + netdataDashboard.gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3]) + ] + }, + + 'lighttpd.workers': { + mainheads: [ + function(os, id) { + void(os); + return '
'; + } + ] + }, + + 'lighttpd.bytesperreq': { + colors: NETDATA.colors[3], + height: 0.5 + }, + + 'lighttpd.reqpersec': { + colors: NETDATA.colors[4], + height: 0.5 + }, + + 'lighttpd.bytespersec': { + colors: NETDATA.colors[6], + height: 0.5 + }, + // ------------------------------------------------------------------------ // NGINX @@ -853,6 +1020,9 @@ netdataDashboard.context = { height: 0.5 }, + // ------------------------------------------------------------------------ + // web_log + 'web_log.response_statuses': { info: 'Web server responses by type. success includes 1xx, 2xx and 304, error includes 5xx, redirect includes 3xx except 304, bad includes 4xx, other are all the other responses.', mainheads: [ @@ -931,7 +1101,14 @@ netdataDashboard.context = { }, 'web_log.response_codes': { - info: 'Web server responses by code family. According to the standards 1xx are informational responses, 2xx are successful responses, 3xx are redirects (although they include 304 which is used as "not modified"), 4xx are bad requests, 5xx are internal server errors, other are non-standard responses, unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' + info: 'Web server responses by code family. ' + + 'According to the standards 1xx are informational responses, ' + + '2xx are successful responses, ' + + '3xx are redirects (although they include 304 which is used as "not modified"), ' + + '4xx are bad requests, ' + + '5xx are internal server errors, ' + + 'other are non-standard responses, ' + + 'unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' }, 'web_log.response_time': { @@ -969,6 +1146,212 @@ netdataDashboard.context = { 'web_log.clients_all': { info: 'Unique client IPs accessing the web server since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the web server. On very busy web servers (several millions of unique IPs) you may want to disable this chart (check /etc/netdata/python.d/web_log.conf).' - } + }, + + // ------------------------------------------------------------------------ + // web_log for squid + + 'web_log.squid_response_statuses': { + info: 'Squid responses by type. ' + + 'success includes 1xx, 2xx, 000, 304, ' + + 'error includes 5xx and 6xx, ' + + 'redirect includes 3xx except 304, ' + + 'bad includes 4xx, ' + + 'other are all the other responses.', + mainheads: [ + function(os, id) { + void(os); + return '
'; + }, + + function(os, id) { + void(os); + return '
'; + }, + function(os, id) { + void(os); + return '
'; + }, + + function(os, id) { + void(os); + return '
'; + } + ] + }, + + 'web_log.squid_response_codes': { + info: 'Web server responses by code family. ' + + 'According to HTTP standards 1xx are informational responses, ' + + '2xx are successful responses, ' + + '3xx are redirects (although they include 304 which is used as "not modified"), ' + + '4xx are bad requests, ' + + '5xx are internal server errors. ' + + 'Squid also defines 000 mostly for UDP requests, and ' + + '6xx for broken upstream servers sending wrong headers. ' + + 'Finally, other are non-standard responses, and ' + + 'unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' + }, + + 'web_log.squid_duration': { + mainheads: [ + function(os, id) { + void(os); + return '
'; + } + ] + }, + + 'web_log.squid_detailed_response_codes': { + info: 'Number of responses for each response code individually.' + }, + + 'web_log.squid_clients': { + info: 'Unique client IPs accessing squid, within each data collection iteration. If data collection is per second, this chart shows unique client IPs per second.' + }, + + 'web_log.squid_clients_all': { + info: 'Unique client IPs accessing squid since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the server. On very busy squid servers (several millions of unique IPs) you may want to disable this chart (check /etc/netdata/python.d/web_log.conf).' + }, + + 'web_log.squid_transport_methods': { + info: 'Break down per delivery method: TCP are requests on the HTTP port (usually 3128), ' + + 'UDP are requests on the ICP port (usually 3130), or HTCP port (usually 4128). ' + + 'If ICP logging was disabled using the log_icp_queries option, no ICP replies will be logged. ' + + 'NONE are used to state that squid delivered an unusual response or no response at all. ' + + 'Seen with cachemgr requests and errors, usually when the transaction fails before being classified into one of the above outcomes. ' + + 'Also seen with responses to CONNECT requests.' + }, + + 'web_log.squid_code': { + info: 'These are combined squid result status codes. A break down per component is given in the following charts. ' + + 'Check the squid documentation about them.' + }, + + 'web_log.squid_handling_opts': { + info: 'These tags are optional and describe why the particular handling was performed or where the request came from. ' + + 'CLIENT means that the client request placed limits affecting the response. Usually seen with client issued a no-cache, or analogous cache control command along with the request. Thus, the cache has to validate the object.' + + 'IMS states that the client sent a revalidation (conditional) request. ' + + 'ASYNC, is used when the request was generated internally by Squid. Usually this is background fetches for cache information exchanges, background revalidation from stale-while-revalidate cache controls, or ESI sub-objects being loaded. ' + + 'SWAPFAIL is assigned when the object was believed to be in the cache, but could not be accessed. A new copy was requested from the server. ' + + 'REFRESH when a revalidation (conditional) request was sent to the server. ' + + 'SHARED when this request was combined with an existing transaction by collapsed forwarding. NOTE: the existing request is not marked as SHARED. ' + + 'REPLY when particular handling was requested in the HTTP reply from server or peer. Usually seen on DENIED due to http_reply_access ACLs preventing delivery of servers response object to the client.' + }, + + 'web_log.squid_object_types': { + info: 'These tags are optional and describe what type of object was produced. ' + + 'NEGATIVE is only seen on HIT responses, indicating the response was a cached error response. e.g. 404 not found. ' + + 'STALE means the object was cached and served stale. This is usually caused by stale-while-revalidate or stale-if-error cache controls. ' + + 'OFFLINE when the requested object was retrieved from the cache during offline_mode. The offline mode never validates any object. ' + + 'INVALID when an invalid request was received. An error response was delivered indicating what the problem was. ' + + 'FAIL is only seen on REFRESH to indicate the revalidation request failed. The response object may be the server provided network error or the stale object which was being revalidated depending on stale-if-error cache control. ' + + 'MODIFIED is only seen on REFRESH responses to indicate revalidation produced a new modified object. ' + + 'UNMODIFIED is only seen on REFRESH responses to indicate revalidation produced a 304 (Not Modified) status, which was relayed to the client. ' + + 'REDIRECT when squid generated an HTTP redirect response to this request.' + }, + + 'web_log.squid_cache_events': { + info: 'These tags are optional and describe whether the response was loaded from cache, network, or otherwise. ' + + 'HIT when the response object delivered was the local cache object. ' + + 'MEM when the response object came from memory cache, avoiding disk accesses. Only seen on HIT responses. ' + + 'MISS when the response object delivered was the network response object. ' + + 'DENIED when the request was denied by access controls. ' + + 'NOFETCH an ICP specific type, indicating service is alive, but not to be used for this request (sent during "-Y" startup, or during frequent failures, a cache in hit only mode will return either UDP_HIT or UDP_MISS_NOFETCH. Neighbours will thus only fetch hits). ' + + 'TUNNEL when a binary tunnel was established for this transaction.' + }, + + 'web_log.squid_transport_errors': { + info: 'These tags are optional and describe some error conditions which occured during response delivery (if any). ' + + 'ABORTED when the response was not completed due to the connection being aborted (usually by the client). ' + + 'TIMEOUT, when the response was not completed due to a connection timeout.' + }, + + // ------------------------------------------------------------------------ + // Fronius Solar Power + + 'fronius.power': { + info: 'Positive Grid values mean that power is coming from the grid. Negative values are excess power that is going back into the grid, possibly selling it. ' + + 'Photovoltaics is the power generated from the solar panels. ' + + 'Accumulator is the stored power in the accumulator, if one is present.' + }, + + 'fronius.autonomy': { + commonMin: true, + commonMax: true, + valueRange: "[0, 100]", + info: 'The Autonomy is the percentage of how autonomous the installation is. An autonomy of 100 % means that the installation is producing more energy than it is needed. ' + + 'The Self consumption indicates the ratio between the current power generated and the current load. When it reaches 100 %, the Autonomy declines, since the solar panels can not produce enough energy and need support from the grid.' + }, + + 'fronius.energy.today': { + commonMin: true, + commonMax: true, + valueRange: "[0, null]" + } }; diff --git a/web/index.html b/web/index.html index 250dbfed3..be944e34d 100644 --- a/web/index.html +++ b/web/index.html @@ -591,6 +591,43 @@ urlOptions.help = loadLocalStorage('options.show_help'); } + // -------------------------------------------------------------------- + // natural sorting + // http://www.davekoelle.com/files/alphanum.js - LGPL + + function naturalSortChunkify(t) { + var tz = []; + var x = 0, y = -1, n = 0, i, j; + + while (i = (j = t.charAt(x++)).charCodeAt(0)) { + var m = (i === 46 || (i >= 48 && i <= 57)); + if (m !== n) { + tz[++y] = ""; + n = m; + } + tz[y] += j; + } + + return tz; + } + + function naturalSortCompare(a, b) { + var aa = naturalSortChunkify(a.toLowerCase()); + var bb = naturalSortChunkify(b.toLowerCase()); + + for (var x = 0; aa[x] && bb[x]; x++) { + if (aa[x] !== bb[x]) { + var c = Number(aa[x]), d = Number(bb[x]); + if (c.toString() === aa[x] && d.toString() === bb[x]) + return c - d; + else + return (aa[x] > bb[x]) ? 1 : -1; + } + } + + return aa.length - bb.length; + } + // -------------------------------------------------------------------- // registry call back to render my-netdata menu @@ -616,16 +653,14 @@ var master = options.hosts[0].hostname; var sorted = options.hosts.sort(function(a, b) { if(a.hostname === master) return -1; - if(a.hostname === b.hostname) return 0; - else if(a.hostname > b.hostname) return 1; - return -1; + return naturalSortCompare(a.hostname, b.hostname); }); i = 0; len = sorted.length; while(len--) { hostname = sorted[i].hostname; - if(hostname == master) { + if(hostname === master) { url = base + "/"; icon = "home"; } @@ -656,14 +691,13 @@ saveLocalStorage("registryCallback", JSON.stringify(machines_array)); var machines = machines_array.sort(function (a, b) { - if (a.name > b.name) return -1; - if (a.name < b.name) return 1; - return 0; + return naturalSortCompare(a.name, b.name); }); + i = 0; len = machines.length; while(len--) { - var u = machines[len]; + var u = machines[i++]; found++; el += '
  • ' + u.name + '
  • '; a1 += '
  • '; @@ -936,6 +970,8 @@ }; function chartsPerRow(total) { + void(total); + if(options.chartsPerRow === 0) { return 1; //var width = Math.floor(total / options.chartsMinWidth); @@ -948,8 +984,7 @@ function prioritySort(a, b) { if(a.priority < b.priority) return -1; if(a.priority > b.priority) return 1; - if(a.name < b.name) return -1; - return 1; + return naturalSortCompare(a.name, b.name); } function sortObjectByPriority(object) { @@ -968,8 +1003,7 @@ sorted.sort(function(a, b) { if(idx[a].priority < idx[b].priority) return -1; if(idx[a].priority > idx[b].priority) return 1; - if(a < b) return -1; - return 1; + return naturalSortCompare(a, b); }); return sorted; @@ -980,10 +1014,10 @@ // scroll to a section, without changing the browser history function scrollToId(hash) { - if(hash && hash != '' && document.getElementById(hash) !== null) { + if(hash && hash !== '' && document.getElementById(hash) !== null) { var offset = $('#' + hash).offset(); if(typeof offset !== 'undefined') - $('html, body').animate({ scrollTop: offset.top }, 0); + $('html, body').animate({ scrollTop: offset.top - 30 }, 0); } // we must return false to prevent the default action @@ -1035,7 +1069,7 @@ key = key + '.' + this.sparklines_registry[key].count; - return prefix + '
    (X' + units + ')' + suffix; + return prefix + '
    (X' + units + ')' + suffix; }, gaugeChart: function(title, width, dimensions, colors) { @@ -1045,7 +1079,7 @@ if(typeof dimensions === 'undefined') dimensions = ''; - return '
    2 && parts[1] === 'expvar') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + case 'isc': chart.menu = chart.type; if(parts.length > 2 && parts[1] === 'dhcpd') @@ -1260,14 +1303,14 @@ var head = ''; if(typeof charts['system.swap'] !== 'undefined') - head += '
    '; if(typeof charts['system.io'] !== 'undefined') { - head += '
    '; - head += '
    '; if(typeof charts['system.ipv4'] !== 'undefined') { - head += '
    '; - head += '
    '; } else if(typeof charts['system.ipv6'] !== 'undefined') { - head += '
    '; - head += '
    '; // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height); @@ -1471,7 +1532,7 @@ sidebar += '
  • add more charts
  • '; sidebar += '
  • add more alarms
  • '; - sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + seconds4human(data.update_every * data.history) + ' of real-time history.
     
    netdata
    v' + data.version.toString() +'
  • '; + sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every === 1)?'second':data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + seconds4human(data.update_every * data.history) + ' of real-time history.
     
    netdata
    v' + data.version.toString() +'
  • '; sidebar += ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -1619,7 +1680,7 @@ var t = new Date(timestamp * 1000); var now = new Date(); - if(t.toDateString() == now.toDateString()) + if(t.toDateString() === now.toDateString()) return t.toLocaleTimeString(); return t.toLocaleDateString() + space + t.toLocaleTimeString(); @@ -1676,8 +1737,8 @@ } var delay = ''; - if((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier != 0 && alarm.delay_max_duration > 0) { - if(alarm.delay_up_duration == alarm.delay_down_duration) { + if((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) { + if(alarm.delay_up_duration === alarm.delay_down_duration) { delay += '
    hysteresis ' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }); } else { @@ -1689,7 +1750,7 @@ delay += 'on recovery ' + seconds4human(alarm.delay_down_duration, { negative_suffix: '' }) + ', '; } } - if(alarm.delay_multiplier != 1.0) { + if(alarm.delay_multiplier !== 1.0) { delay += 'multiplied by ' + alarm.delay_multiplier.toString() + ''; delay += ', up to ' + seconds4human(alarm.delay_max_duration, { negative_suffix: '' }) + ''; } @@ -1762,15 +1823,16 @@ // sort the families, like the dashboard menu does var families_sorted = families_sort.sort(function (a, b) { - if (a.priority > b.priority) return -1; - if (a.priority < b.priority) return 1; - return 0; + if (a.priority < b.priority) return -1; + if (a.priority > b.priority) return 1; + return naturalSortCompare(a.name, b.name); }); + var i = 0; var fc = 0; var len = families_sorted.length; while(len--) { - family = families_sorted[len].name; + family = families_sorted[i++].name; var active_family_added = false; var expanded = 'true'; var collapsed = ''; @@ -2253,7 +2315,7 @@ if(hours > 1) txt += hours.toString() + options.space + options.hours; else if(hours === 1) txt += hours.toString() + options.space + options.hour; - if(hours > 0 && minutes > 0 && seconds == 0) + if(hours > 0 && minutes > 0 && seconds === 0) txt += options.space + options.and + options.space; else if(hours > 0 && minutes > 0 && seconds > 0) txt += ',' + options.space; @@ -2293,7 +2355,7 @@ options.version = data.version; netdataDashboard.os = data.os; - if(typeof data.hosts != 'undefined') + if(typeof data.hosts !=='undefined') options.hosts = data.hosts; // update the dashboard hostname @@ -2328,7 +2390,7 @@ netdata_url = NETDATA.serverDefault; // initialize clickable alarms - NETDATA.alarms.chart_div_offset = 100; + NETDATA.alarms.chart_div_offset = -50; NETDATA.alarms.chart_div_id_prefix = 'chart_'; NETDATA.alarms.chart_div_animation_duration = 0; @@ -2337,7 +2399,7 @@ // download all the charts the server knows NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { - if(data != null) { + if(data !== null) { if(typeof data.custom_info !== 'undefined' && data.custom_info !== "") { loadJs(data.custom_info, function () { $.extend(true, netdataDashboard, customDashboard); @@ -2362,7 +2424,7 @@ var s = options.version.split('-'); if(s.length !== 3) return null; - if(s[2][0] == 'g') { + if(s[2][0] === 'g') { var v = s[2].split('_')[0].substring(1, 8); if(v.length === 7) { versionLog('Installed git commit id of netdata is ' + v); @@ -2566,17 +2628,17 @@ var tagName = null; for (var i = 0, r = 0; r <= config.showChars; i++) { - if (content[i] == '<' && !inTag) { + if (content[i] === '<' && !inTag) { inTag = true; // This could be "tag" or "/tag" tagName = content.substring(i + 1, content.indexOf('>', i)); // If its a closing tag - if (tagName[0] == '/') { + if (tagName[0] === '/') { - if (tagName != '/' + openTags[0]) { + if (tagName !== ('/' + openTags[0])) { config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes'; } else { openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!) @@ -2584,12 +2646,12 @@ } else { // There are some nasty tags that don't have a close tag like
    - if (tagName.toLowerCase() != 'br') { + if (tagName.toLowerCase() !== 'br') { openTags.unshift(tagName); // Add to start the name of the tag that opens } } } - if (inTag && content[i] == '>') { + if (inTag && content[i] === '>') { inTag = false; } @@ -2668,17 +2730,21 @@ }); /* activate bootstrap scrollspy (needed for sidebar) */ + var scrollspyOffset = $(window).height() / 5; + if(scrollspyOffset > 200) scrollspyOffset = 200; + if(scrollspyOffset < 50) scrollspyOffset = 50; $(document.body).scrollspy({ target: '#sidebar', - offset: $(window).height() / 5 // controls the diff of the element to the top, to select it + offset: scrollspyOffset // controls the diff of the element to the top, to select it }); // change the URL based on the current position of the screen $sidebar.on('activate.bs.scrollspy', function (e) { - // console.log(e); + //console.log(e); var el = $(e.target); - //if(el.find('ul').size() == 0) { + //if(el.find('ul').size() === 0) { var hash = el.find('a').attr('href'); + // console.log(hash); if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { urlOptions.hash = hash; //console.log(urlOptions.hash); @@ -2895,7 +2961,7 @@ }); NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'dashboard_info.js?v20170308-1', + url: NETDATA.serverDefault + 'dashboard_info.js?v20170530-1', async: false, isAlreadyLoaded: function() { return false; } }); @@ -2993,7 +3059,7 @@ Hover on them too!
    -
    +
    @@ -3523,4 +3589,4 @@ - + diff --git a/web/infographic.html b/web/infographic.html new file mode 100644 index 000000000..0bb571870 --- /dev/null +++ b/web/infographic.html @@ -0,0 +1,170 @@ + + + + + NetData: Get control of your Linux Servers. Simple. Effective. Awesome. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    + Interactive infographic of netdata features and functions +

    +

    + Hover and click on the infographic, to open the related wiki page. +
    + + The links and the docs are still a work in progress. + The interactive infographic is a feature of draw.io. + +

    +
    + +
    +

    + New to netdata? Have a look at a netdata demo. You will love it! +

    +

    + + + +

    +
    +
    +
    +
    +
    +
    + + + + + + + + + +
    + + + + + + diff --git a/web/version.txt b/web/version.txt index a7ffee85b..4c72a5b03 100644 --- a/web/version.txt +++ b/web/version.txt @@ -1 +1 @@ -f5fa346a188e906a8f2cce3c2cf32a88ce81c666 +4016e2d9e3c2fcf5f6d59827bf5f81083d6645ba -- cgit v1.2.3