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>