@cryptotaxi247 / netdata-1 / commits / bd2d05eda

more optimizations; a working example of a full dashboard

Costa Tsaousis (ktsaou) committed Dec 13, 2015 at 20:19 UTC bd2d05eda4e7f306eea0b7e65c2d6e4fce730d50
4 files changed +516 -97
web/Makefile.am
+1
@@ -10,6 +10,7 @@ dist_web_DATA = \
10 netdata.js \
11 robots.txt \
12 theme.css \
13 + dashboard_full.html \
14 dashboard.html \
15 dashboard.js \
16 dashboard.css \
web/dashboard.css
+4
@@ -2,6 +2,10 @@ html {
2 font-family: sans-serif;
3 }
4
5 +.netdata-chart-alignment {
6 + margin-left: 55px;
7 +}
8 +
9 .netdata-container {
10 display: -webkit-flex; /* Safari */
11 -webkit-flex-wrap: wrap; /* Safari 6.1+ */
web/dashboard.js
+159 -97
@@ -122,8 +122,8 @@
122
123 pause: false, // when enabled we don't auto-refresh the charts
124
125 - targets: null, // an array of all the DOM elements that are
126 - // currently visible (independently of their
125 + targets: null, // an array of all the state objects that are
126 + // currently active (independently of their
127 // viewport visibility)
128
129 updated_dom: true, // when true, the DOM has been updated with
@@ -217,11 +217,13 @@
217 };
218
219 window.onscroll = function(event) {
220 - // FIXME targets should be states not DOM elements
221 - for(i = 0; i < NETDATA.options.targets.length ;i++) {
222 - var state = NETDATA.chartState(NETDATA.options.targets[i])
223 - state.isVisible();
224 - }
220 + // when the user scrolls he sees that we have
221 + // hidden all the not-visible charts
222 + // using this little function we try to switch
223 + // the charts back to visible quickly
224 + var targets = NETDATA.options.targets;
225 + var len = targets.length;
226 + while(len--) targets[len].isVisible();
227 }
228
229 // ----------------------------------------------------------------------------------------------------------------
@@ -443,6 +445,8 @@
445 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
446 library: null, // object - the chart library used
447
448 + colors: null,
449 +
450 element: element, // the element already created by the user
451 element_message: null,
452 element_loading: null,
@@ -554,9 +558,12 @@
558 };
559
560 // prevent to global selection sync for some time
557 - chartState.prototype.globalSelectionSyncDelay = function() {
561 + chartState.prototype.globalSelectionSyncDelay = function(ms) {
562 if(!NETDATA.options.current.sync_selection) return;
559 - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
563 + if(typeof ms === 'number')
564 + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
565 + else
566 + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
567 }
568
569 // can we globally apply selection sync?
@@ -566,10 +573,17 @@
573 return true;
574 }
575
576 + chartState.prototype.globalSelectionSyncIsMaster = function() {
577 + if(NETDATA.globalSelectionSync.state === this)
578 + return true;
579 + else
580 + return false;
581 + }
582 +
583 // this chart is the master of the global selection sync
584 chartState.prototype.globalSelectionSyncBeMaster = function() {
585 // am I the master?
572 - if(NETDATA.globalSelectionSync.state === this) {
586 + if(this.globalSelectionSyncIsMaster()) {
587 if(this.debug) this.log('sync: I am the master already.');
588 return;
589 }
@@ -585,18 +599,21 @@
599 NETDATA.globalSelectionSync.state = this;
600
601 // find the all slaves
588 - var self = this;
589 - // FIXME targets should be states not DOM elements
590 - $.each(NETDATA.options.targets, function(i, target) {
591 - var st = NETDATA.chartState(target);
592 - if(st === self) {
593 - if(self.debug) st.log('sync: not adding me to sync');
602 + var targets = NETDATA.options.targets;
603 + var len = targets.length;
604 + while(len--) {
605 + st = targets[len];
606 +
607 + if(st === this) {
608 + if(this.debug) st.log('sync: not adding me to sync');
609 }
610 else if(st.globalSelectionSyncIsEligible()) {
596 - if(self.debug) st.log('sync: adding to sync as slave');
611 + if(this.debug) st.log('sync: adding to sync as slave');
612 st.globalSelectionSyncBeSlave();
613 }
599 - });
614 + }
615 +
616 + this.globalSelectionSyncDelay(100);
617 }
618
619 // can the chart participate to the global selection sync as a slave?
@@ -620,21 +637,26 @@
637 return;
638 }
639
623 - if(this.debug) this.log('sync: trying to be sync master.');
624 - this.globalSelectionSyncBeMaster();
640 + if(!this.globalSelectionSyncIsMaster()) {
641 + if(this.debug) this.log('sync: trying to be sync master.');
642 + this.globalSelectionSyncBeMaster();
643
626 - var self = this;
627 - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
628 - if(st === self) {
629 - // since we are the sync master, we should not call state.setSelection()
630 - // the chart library is taking care of visualizing our selection.
631 - if(self.debug) st.log('sync: ignoring me from set selection');
632 - }
633 - else {
634 - if(self.debug) st.log('sync: showing master selection');
635 - st.setSelection(t);
644 + if(!this.globalSelectionSyncAbility()) {
645 + if(this.debug) this.log('sync: cannot sync (yet?).');
646 + return;
647 }
648 + }
649 +
650 + // FIXME
651 + // var start = new Date().getTime();
652 +
653 + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
654 + st.setSelection(t);
655 });
656 +
657 + // FIXME
658 + // var end = new Date().getTime();
659 + // console.log(end - start);
660 }
661
662 // stop syncing all charts to the given time
@@ -848,7 +870,11 @@
870 }
871
872 chartState.prototype.legendFormatValue = function(value) {
851 - if(typeof value !== 'number' || value === null) return '';
873 + // FIXME
874 + // return '';
875 + // return value;
876 + if(value === null) return '';
877 + if(typeof value !== 'number') return value;
878
879 var abs = Math.abs(value);
880 if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
@@ -856,15 +882,17 @@
882 return (Math.round(value * 10000) / 10000).toLocaleString();
883 }
884
859 - chartState.prototype.legendSetLabelValue = function(label, string) {
860 - if(typeof this.element_legend_childs.series[label] === 'undefined')
885 + chartState.prototype.legendSetLabelValue = function(label, value) {
886 + var series = this.element_legend_childs.series[label];
887 +
888 + if(typeof series === 'undefined' || series.last === value)
889 return;
890
863 - if(this.element_legend_childs.series[label].value !== null)
864 - this.element_legend_childs.series[label].value.innerHTML = string;
891 + series.last = value;
892 + value = this.legendFormatValue(value);
893
866 - if(this.element_legend_childs.series[label].user !== null)
867 - this.element_legend_childs.series[label].user.innerHTML = string;
894 + if(series.value !== null) series.value.innerHTML = value;
895 + if(series.user !== null) series.user.innerHTML = value;
896 }
897
898 chartState.prototype.legendSetDate = function(ms) {
@@ -910,17 +938,18 @@
938 else
939 this.legendUndefined();
940
913 - for(var i = 0; i < this.current.data.dimension_names.length; i++) {
914 - if(typeof this.current.data.dimension_names[i] === 'undefined')
915 - continue;
941 + var labels = this.current.data.dimension_names;
942 + var i = labels.length;
943 + while(i--) {
944 + var label = labels[i];
945
917 - if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] === 'undefined')
918 - continue;
946 + if(typeof label === 'undefined') continue;
947 + if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
948
949 if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every)
921 - this.legendSetLabelValue(this.current.data.dimension_names[i], this.legendFormatValue(this.current.data.result_latest_values[i]));
950 + this.legendSetLabelValue(label, this.current.data.result_latest_values[i]);
951 else
923 - this.legendSetLabelValue(this.current.data.dimension_names[i], '');
952 + this.legendSetLabelValue(label, null);
953 }
954 }
955
@@ -928,6 +957,33 @@
957 this.legendShowLatestValues();
958 }
959
960 + chartState.prototype.chartColors = function() {
961 + if(this.colors !== null) return this.colors;
962 +
963 + this.colors = $(this.element).data('colors');
964 + if(typeof this.colors === 'undefined' || this.colors === null)
965 + this.colors = NETDATA.colors;
966 + else {
967 + // console.log(this.colors);
968 + var s = this.colors;
969 + if(typeof s === 'string') s = s.split(' ');
970 +
971 + this.colors = new Array();
972 + var self = this;
973 + $.each(s, function(i, c) {
974 + self.colors.push(c);
975 + });
976 +
977 + // push the default colors too
978 + var self = this;
979 + $.each(NETDATA.colors, function(i, c) {
980 + self.colors.push(c);
981 + });
982 + }
983 +
984 + return this.colors;
985 + }
986 +
987 chartState.prototype.legendUpdateDOM = function() {
988 if(!this.hasLegend()) return;
989
@@ -944,15 +1000,18 @@
1000 }
1001 else {
1002 // this.log('checking existing legend');
947 - for(var i = 0; i < this.current.data.dimension_names.length; i++) {
948 - if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] === 'undefined') {
949 - // this.log('legend is incosistent - missing dimension:' + this.current.data.dimension_names[i]);
1003 + var labels = this.current.data.dimension_names;
1004 + var i = labels.length;
1005 + while(i--) {
1006 + var name = labels[i];
1007 + if(typeof this.element_legend_childs.series[name] === 'undefined') {
1008 + // this.log('legend is incosistent - missing dimension:' + name);
1009 needed = true;
1010 break;
1011 }
1012 else if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every) {
954 - // this.log('setting legend of ' + this.current.data.dimension_names[i] + ' to ' + this.current.data.latest_values[i]);
955 - this.legendSetLabelValue(this.current.data.dimension_names[i], this.legendFormatValue(this.current.data.latest_values[i]));
1013 + // this.log('setting legend of ' + name + ' to ' + this.current.data.latest_values[i]);
1014 + this.legendSetLabelValue(name, this.current.data.latest_values[i]);
1015 }
1016 }
1017 }
@@ -995,7 +1054,7 @@
1054
1055 self = $(this);
1056 var genLabel = function(state, parent, name, count) {
998 - var c = count % NETDATA.colors.length;
1057 + var c = count % state.chartColors().length;
1058
1059 var user_element = null;
1060 var user_id = self.data('show-value-of-' + name + '-at') || null;
@@ -1004,7 +1063,8 @@
1063 state.element_legend_childs.series[name] = {
1064 name: document.createElement('span'),
1065 value: document.createElement('span'),
1007 - user: user_element
1066 + user: user_element,
1067 + last: null
1068 };
1069
1070 var label = state.element_legend_childs.series[name];
@@ -1014,7 +1074,7 @@
1074 label.name.title = name;
1075 label.value.title = name;
1076
1017 - var rgb = NETDATA.colorHex2Rgb(NETDATA.colors[c]);
1077 + var rgb = NETDATA.colorHex2Rgb(state.chartColors()[c]);
1078 label.name.innerHTML = '<table class="netdata-legend-name-table-'
1079 + state.chart.chart_type
1080 + '" style="background-color: '
@@ -1024,8 +1084,8 @@
1084 var text = document.createTextNode(' ' + name);
1085 label.name.appendChild(text);
1086
1027 - label.name.style.color = NETDATA.colors[c];
1028 - label.value.style.color = NETDATA.colors[c];
1087 + label.name.style.color = state.chartColors()[c];
1088 + label.value.style.color = state.chartColors()[c];
1089
1090 if(count > 0)
1091 parent.appendChild(document.createElement('br'));
@@ -1753,7 +1813,7 @@
1813 // ----------------------------------------------------------------------------------------------------------------
1814
1815 NETDATA.chartRefresherNoParallel = function(index) {
1756 - // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
1816 + if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
1817
1818 if(NETDATA.options.updated_dom) {
1819 // the dom has been updated
@@ -1761,9 +1821,7 @@
1821 NETDATA.getDomCharts(NETDATA.chartRefresher);
1822 return;
1823 }
1764 - // FIXME targets should be states not DOM elements
1765 - var target = NETDATA.options.targets.get(index);
1766 - if(typeof target === 'undefined') {
1824 + if(index >= NETDATA.options.targets.length) {
1825 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
1826 NETDATA.options.auto_refresher_fast_weight = 0;
1827
@@ -1772,7 +1830,7 @@
1830 }, NETDATA.options.current.idle_between_loops);
1831 }
1832 else {
1775 - var state = NETDATA.chartState(target);
1833 + var state = NETDATA.options.targets[index];
1834
1835 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
1836 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
@@ -1843,10 +1901,10 @@
1901 NETDATA.options.parallel = new Array();
1902 NETDATA.options.sequencial = new Array();
1903
1846 - // FIXME targets should be states not DOM elements
1847 - for(var i = 0; i < NETDATA.options.targets.length ; i++) {
1848 - var target = NETDATA.options.targets.get(i);
1849 - var state = NETDATA.chartState(target);
1904 + var targets = NETDATA.options.targets;
1905 + var len = targets.length;
1906 + while(len--) {
1907 + var state = targets[len];
1908
1909 if(!state.library.initialized)
1910 NETDATA.options.sequencial.push(state);
@@ -1876,20 +1934,18 @@
1934 NETDATA.getDomCharts = function(callback) {
1935 NETDATA.options.updated_dom = false;
1936
1879 - // FIXME targets should be states not DOM elements
1880 - NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
1937 + var targets = $('div[data-netdata]').filter(':visible');
1938
1939 if(NETDATA.options.debug.main_loop)
1883 - console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
1940 + console.log('DOM updated - there are ' + targets.length + ' charts on page.');
1941
1885 - // we need to re-size all the charts quickly
1886 - // before making any external calls
1887 - // FIXME targets should be states not DOM elements
1888 - $.each(NETDATA.options.targets, function(i, target) {
1942 + NETDATA.options.targets = new Array();
1943 + var len = targets.length;
1944 + while(len--) {
1945 // the initialization will take care of sizing
1946 // and the "loading..." message
1891 - var state = NETDATA.chartState(target);
1892 - });
1947 + NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
1948 + }
1949
1950 if(typeof callback === 'function') callback();
1951 }
@@ -2000,7 +2056,7 @@
2056 NETDATA.sparklineChartCreate = function(state, data) {
2057 var self = $(state.element);
2058 var type = self.data('sparkline-type') || 'line';
2003 - var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
2059 + var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
2060 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?'#FFF':NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
2061 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
2062 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
@@ -2110,15 +2166,13 @@
2166 NETDATA.dygraphSetSelection = function(state, t) {
2167 if(typeof state.dygraph_instance !== 'undefined') {
2168 var r = state.calculateRowForTime(t);
2113 - if(r !== -1) {
2169 + if(r !== -1)
2170 state.dygraph_instance.setSelection(r);
2115 - return true;
2116 - }
2117 - else {
2171 + else
2172 state.dygraph_instance.clearSelection();
2119 - return false;
2120 - }
2173 }
2174 +
2175 + return true;
2176 }
2177
2178 NETDATA.dygraphClearSelection = function(state, t) {
@@ -2170,6 +2224,12 @@
2224
2225 NETDATA.dygraphChartUpdate = function(state, data) {
2226 var dygraph = state.dygraph_instance;
2227 +
2228 + // when the chart is not visible, and hidden
2229 + // if there is a window resize, dygraph detects
2230 + // its element size as 0x0.
2231 + // this will make it re-appear properly
2232 + dygraph.resize();
2233
2234 if(state.current.name === 'pan') {
2235 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() loose update');
@@ -2197,7 +2257,7 @@
2257 var self = $(state.element);
2258
2259 state.dygraph_options = {
2200 - colors: self.data('dygraph-colors') || NETDATA.colors,
2260 + colors: self.data('dygraph-colors') || state.chartColors(),
2261
2262 // leave a few pixels empty on the right of the chart
2263 rightGap: self.data('dygraph-rightgap') || 5,
@@ -2290,7 +2350,9 @@
2350 pixelsPerLabel: 15,
2351 valueFormatter: function (x) {
2352 // return (Math.round(x*100) / 100).toLocaleString();
2293 - return state.legendFormatValue(x);
2353 + //return state.legendFormatValue(x);
2354 + //FIXME
2355 + return x;
2356 }
2357 }
2358 },
@@ -2300,7 +2362,7 @@
2362 var elements = state.element_legend_childs;
2363
2364 // if the hidden div is not there
2303 - // state is not managing the legend
2365 + // we are not managing the legend
2366 if(elements.hidden === null) return;
2367
2368 if (typeof data.x === 'undefined') {
@@ -2308,11 +2370,11 @@
2370 }
2371 else {
2372 state.legendSetDate(data.x);
2311 - for (var i = 0; i < data.series.length; i++) {
2373 + var i = data.series.length;
2374 + while(i--) {
2375 var series = data.series[i];
2376 if(!series.isVisible) continue;
2314 - state.legendSetLabelValue(series.label, series.yHTML);
2315 - // elements.series[series.label].value.innerHTML = series.yHTML;
2377 + state.legendSetLabelValue(series.label, series.y);
2378 }
2379 }
2380
@@ -2852,8 +2914,8 @@
2914 initialize: NETDATA.sparklineInitialize,
2915 create: NETDATA.sparklineChartCreate,
2916 update: NETDATA.sparklineChartUpdate,
2855 - setSelection: null,
2856 - clearSelection: null,
2917 + setSelection: function(t) { return true; },
2918 + clearSelection: function() { return true; },
2919 initialized: false,
2920 enabled: true,
2921 format: function(state) { return 'array'; },
@@ -2867,8 +2929,8 @@
2929 initialize: NETDATA.peityInitialize,
2930 create: NETDATA.peityChartCreate,
2931 update: NETDATA.peityChartUpdate,
2870 - setSelection: null,
2871 - clearSelection: null,
2932 + setSelection: function(t) { return true; },
2933 + clearSelection: function() { return true; },
2934 initialized: false,
2935 enabled: true,
2936 format: function(state) { return 'ssvcomma'; },
@@ -2882,8 +2944,8 @@
2944 initialize: NETDATA.morrisInitialize,
2945 create: NETDATA.morrisChartCreate,
2946 update: NETDATA.morrisChartUpdate,
2885 - setSelection: null,
2886 - clearSelection: null,
2947 + setSelection: function(t) { return true; },
2948 + clearSelection: function() { return true; },
2949 initialized: false,
2950 enabled: true,
2951 format: function(state) { return 'json'; },
@@ -2897,8 +2959,8 @@
2959 initialize: NETDATA.googleInitialize,
2960 create: NETDATA.googleChartCreate,
2961 update: NETDATA.googleChartUpdate,
2900 - setSelection: null,
2901 - clearSelection: null,
2962 + setSelection: function(t) { return true; },
2963 + clearSelection: function() { return true; },
2964 initialized: false,
2965 enabled: true,
2966 format: function(state) { return 'datatable'; },
@@ -2912,8 +2974,8 @@
2974 initialize: NETDATA.raphaelInitialize,
2975 create: NETDATA.raphaelChartCreate,
2976 update: NETDATA.raphaelChartUpdate,
2915 - setSelection: null,
2916 - clearSelection: null,
2977 + setSelection: function(t) { return true; },
2978 + clearSelection: function() { return true; },
2979 initialized: false,
2980 enabled: true,
2981 format: function(state) { return 'json'; },
@@ -2927,8 +2989,8 @@
2989 initialize: NETDATA.easypiechartInitialize,
2990 create: NETDATA.easypiechartChartCreate,
2991 update: NETDATA.easypiechartChartUpdate,
2930 - setSelection: null,
2931 - clearSelection: null,
2992 + setSelection: function(t) { return true; },
2993 + clearSelection: function() { return true; },
2994 initialized: false,
2995 enabled: true,
2996 format: function(state) { return 'json'; },
web/dashboard_full.html new
+352
@@ -0,0 +1,352 @@
1 +<!DOCTYPE html>
2 +<html lang="en">
3 +<head>
4 + <title>NetData Dashboard</title>
5 +
6 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7 + <meta charset="utf-8">
8 + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
9 + <meta name="viewport" content="width=device-width, initial-scale=1">
10 + <meta name="apple-mobile-web-app-capable" content="yes">
11 + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
12 + <meta name="author" content="costa@tsaousis.gr">
13 +</head>
14 +<body>
15 +
16 +<div class="container-fluid">
17 + <div id="charts_div" class="container-fluid"></div>
18 +</div>
19 +
20 +</body>
21 +</html>
22 + <!-- you can set your netdata server globally, by ucommenting this -->
23 + <!-- you can also give a different server per chart, with the attribute: data-host="http://netdata.server:19999" -->
24 + <!-- <script> netdataServer = "http://box:19999"; </script> -->
25 +
26 + <!-- load the dashboard manager - it will do the rest -->
27 + <script type="text/javascript" src="dashboard.js"></script>
28 +
29 +<script>
30 +var options = {
31 + data: null,
32 + hostnane: 'netdata_server',
33 + categories: new Array(),
34 + categories_idx: {},
35 + families: new Array(),
36 + families_idx: {},
37 + chartsPerRow: 0,
38 + chartsMinWidth: 450,
39 + chartsHeight: 200,
40 + sparklinesHeight: 60
41 +}
42 +
43 +function screenWidth() {
44 + return (($(window).width() * 0.95) - 50);
45 +}
46 +
47 +function chartsPerRow(total) {
48 + if(options.chartsPerRow === 0) {
49 + width = Math.floor(total / options.chartsMinWidth);
50 + if(width === 0) width = 1;
51 + return width;
52 + }
53 + else return options.chartsPerRow;
54 +}
55 +
56 +function chartsPrioritySort(a, b) {
57 + if(a.priority === b.priority) {
58 + if(a.name < b.name) return -1;
59 + }
60 + else if(a.priority < b.priority) return -1;
61 + return 1;
62 +}
63 +
64 +function uniq(array, find_key, get_result) {
65 + if(typeof get_result === 'undefined' || get_result === null)
66 + get_result = find_key;
67 +
68 + var idx = {};
69 + var result = new Array();
70 +
71 + $.each(array, function(i, c) {
72 + key = find_key(c);
73 + if(typeof idx[key] === 'undefined') {
74 + idx[key] = true;
75 + result.push(get_result(c));
76 + }
77 + });
78 + return result;
79 +}
80 +
81 +function uniq_with_list(array, find_key_function) {
82 + var idx = {};
83 + var result = new Array();
84 +
85 + $.each(array, function(i, c) {
86 + key = find_key_function(c);
87 + if(typeof idx[key] === 'undefined') {
88 + idx[key] = new Array();
89 + result.push( { name: key, values: idx[key] } );
90 + }
91 + idx[key].push(c);
92 + });
93 + result.sort(function(a, b) {
94 + if(a.name < b.name) return -1;
95 + return 1;
96 + });
97 + return result;
98 +}
99 +
100 +function prepareScreen(data) {
101 + console.log('NETDATA is paused - ready to prepare the screen');
102 + console.log(data);
103 +
104 + options.data = data;
105 + options.hostname = data.hostname;
106 + var charts = data.charts;
107 +
108 + $.each(charts, function(i, c) {
109 + if(c.enabled === true) {
110 +
111 + //if(c.family.length > 4 && c.family.substring(c.family.length - 4, c.family.length) === '-ifb')
112 + // c.family = c.family.split('-')[0];
113 +
114 + // find the category of the chart
115 + c.category = c.type.split('.')[0];
116 +
117 + var tmp = c.category.split('_')[0];
118 + if(tmp === 'net' || tmp === 'disk')
119 + c.category = tmp;
120 +
121 + switch(c.category) {
122 + case 'system':
123 + c.category_priority = 10;
124 + c.category_title = 'System';
125 + c.glyphicon = "glyphicon-dashboard";
126 + break;
127 +
128 + case 'tc':
129 + c.category_priority = 20;
130 + c.category_title = 'Quality Of Service';
131 + c.glyphicon = "glyphicon-random";
132 + break;
133 +
134 + case 'net':
135 + c.category_priority = 30;
136 + c.category_title = 'Network Interfaces';
137 + c.glyphicon = "glyphicon-transfer";
138 + break;
139 +
140 + case 'apps':
141 + c.category_priority = 40;
142 + c.category_title = 'Applications Monitoring';
143 + c.glyphicon = "glyphicon-tasks";
144 + break;
145 +
146 + case 'ipvs':
147 + c.category_priority = 50;
148 + c.category_title = 'IP Virtual Server';
149 + c.glyphicon = "glyphicon-transfer";
150 + break;
151 +
152 + case 'netfilter':
153 + c.category_priority = 60;
154 + c.category_title = 'Netfilter';
155 + c.glyphicon = "glyphicon-cloud";
156 + break;
157 +
158 + case 'ipv4':
159 + c.category_priority = 70;
160 + c.category_title = 'IPv4';
161 + c.glyphicon = "glyphicon-globe";
162 + break;
163 +
164 + case 'mem':
165 + c.category_priority = 80;
166 + c.category_title = 'Memory';
167 + c.glyphicon = "glyphicon-dashboard";
168 + break;
169 +
170 + case 'cpu':
171 + c.category_priority = 90;
172 + c.category_title = 'CPUs';
173 + c.glyphicon = "glyphicon-dashboard";
174 + break;
175 +
176 + case 'disk':
177 + c.category_priority = 100;
178 + c.category_title = 'Disks';
179 + c.glyphicon = "glyphicon-hdd";
180 + break;
181 +
182 + case 'nfsd':
183 + c.category_priority = 110;
184 + c.category_title = 'NFS Server';
185 + c.glyphicon = "glyphicon-hdd";
186 + break;
187 +
188 + case 'squid':
189 + c.category_priority = 140;
190 + c.category_title = 'Proxy Server';
191 + c.glyphicon = "glyphicon-link";
192 + break;
193 +
194 + case 'netdata':
195 + c.category_priority = 150;
196 + c.category_title = 'Netdata Monitoring';
197 + c.glyphicon = "glyphicon-thumbs-up";
198 + break;
199 +
200 + case 'example':
201 + c.category_priority = 100000;
202 + c.category_title = 'Example Plugins';
203 + c.glyphicon = "glyphicon-search";
204 + break;
205 +
206 + default:
207 + c.category_priority = 150;
208 + c.category_title = c.type;
209 + c.glyphicon = "glyphicon-dashboard";
210 + break;
211 + }
212 +
213 + // find the unique categories
214 + if(typeof options.categories_idx[c.category] === 'undefined') {
215 + options.categories_idx[c.category] = {
216 + charts: new Array()
217 + }
218 + options.categories.push({
219 + name: c.category,
220 + title: c.category_title,
221 + priority: c.category_priority,
222 + glyphicon: c.glyphicon,
223 + charts: options.categories_idx[c.category].charts
224 + });
225 + }
226 + options.categories_idx[c.category].charts.push(c);
227 +
228 + // find the unique families
229 + if(typeof options.families_idx[c.family] === 'undefined') {
230 + options.families_idx[c.family] = {
231 + charts: new Array()
232 + };
233 + options.families.push({
234 + name: c.family,
235 + title: c.family,
236 + priority: c.category_priority,
237 + glyphicon: c.glyphicon,
238 + charts: options.categories_idx[c.category].charts
239 + });
240 + }
241 + options.families_idx[c.family].charts.push(c);
242 + }
243 + });
244 +
245 + function prioritySort(a, b) {
246 + if(a.priority < b.priority) return -1;
247 + return 1;
248 + }
249 +
250 + // sort all of them
251 + options.categories.sort(prioritySort);
252 + options.families.sort(prioritySort);
253 + $.each(options.families, function(i, c) { c.charts.sort(prioritySort); });
254 +
255 + var div = document.getElementById('charts_div');
256 + var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
257 +
258 + // find the proper duration for per-second updates
259 + var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
260 + console.log(duration);
261 + html = '';
262 + $.each(options.categories, function(i, t) {
263 + t.charts.sort(prioritySort);
264 +
265 + html += '<div class="container-fluid row clearfix"><h2>' + t.title + '</h2>';
266 +
267 + if(t.name === 'net') {
268 + var interfaces = uniq_with_list(t.charts, function(c) { return c.family; });
269 + console.log(interfaces);
270 +
271 + $.each(interfaces, function(i, c) {
272 + html += '<div class="netdata-group-container" id="interface_' + c.name + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 class="netdata-chart-alignment">' + c.name + '</h2>';
273 + $.each(c.values, function(x, f) {
274 + if(f.type === 'net') {
275 + html += '<div data-netdata="' + f.id + '"'
276 + + ' data-width="100%"'
277 + + ' data-height="' + options.chartsHeight.toString() + 'px"'
278 + + ' data-before="0"'
279 + + ' data-after="-' + duration.toString() + '"'
280 + + '></div>';
281 + }
282 + else {
283 + html += '<div data-netdata="' + f.id + '"'
284 + + ' data-width="100%"'
285 + + ' data-height="' + (options.chartsHeight / 2).toString() + 'px"'
286 + + ' data-before="0"'
287 + + ' data-after="-' + duration.toString() + '"'
288 + + '></div>';
289 + }
290 + });
291 + html += '</div>';
292 + });
293 + }
294 + else if(t.name === 'disk') {
295 + var disks = uniq_with_list(t.charts, function(c) { return c.family; });
296 + console.log(disks);
297 +
298 + $.each(disks, function(i, c) {
299 + html += '<div class="netdata-group-container" id="disk_' + c.name + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 class="netdata-chart-alignment">' + c.name + '</h2>';
300 + $.each(c.values, function(x, f) {
301 + var c = null;
302 + var h = options.chartsHeight / 2;
303 + switch(f.type) {
304 + case 'disk' : h = options.chartsHeight; break;
305 + case 'disk_backlog': c = '#DD4477'; break;
306 + case 'disk_util' : c = '#109618'; break;
307 + case 'disk_qops' : c = '#8B0707'; break;
308 + }
309 +
310 + html += '<div data-netdata="' + f.id + '"'
311 + + ' data-width="100%"'
312 + + ' data-height="' + h.toString() + 'px"'
313 + + ' data-before="0"'
314 + + ' data-after="-' + duration.toString() + '"'
315 + + ' data-colors="' + c + '"'
316 + + '></div>';
317 + });
318 + html += '</div>';
319 + });
320 + }
321 + else {
322 + $.each(t.charts, function(x, c) {
323 + html += '<div data-netdata="' + c.id + '"'
324 + + ' data-width="' + pcent_width.toString() + '%"'
325 + + ' data-height="' + options.chartsHeight.toString() + 'px"'
326 + + ' data-before="0"'
327 + + ' data-after="-' + duration.toString() + '"'
328 + + '></div>';
329 + });
330 + }
331 +
332 + html += '</div>';
333 + });
334 +
335 + html += '<br/>'
336 + $.each(NETDATA.colors, function(i, c){
337 + html += '<div style="display: inline-block;"><div style="display: inline-block; width: 100px; height: 100px; background: ' + c + ';"></div><br/>' + c + '</div>';
338 + });
339 +
340 + div.innerHTML = html;
341 + NETDATA.unpause();
342 +}
343 +
344 +NETDATA.ready(function() {
345 + NETDATA.chartRegistry.downloadAll(NETDATA.serverDefault, function(data) {
346 + NETDATA.pause(function () {
347 + prepareScreen(data);
348 + });
349 + });
350 +});
351 +
352 +</script>