]> arthur.barton.de Git - netdata.git/commitdiff
more optimizations; a working example of a full dashboard
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 13 Dec 2015 18:19:56 +0000 (20:19 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 13 Dec 2015 18:19:56 +0000 (20:19 +0200)
web/Makefile.am
web/dashboard.css
web/dashboard.js
web/dashboard_full.html [new file with mode: 0755]

index d963e8c1fd04b5177fbe737f441dc124d250053f..c25dec13137b7493a618426da3df672f3dc0d609 100644 (file)
@@ -10,6 +10,7 @@ dist_web_DATA = \
         netdata.js \
         robots.txt \
         theme.css \
+        dashboard_full.html \
         dashboard.html \
         dashboard.js \
         dashboard.css \
index 5c95f75a3402cf397a44ead3b2ebe00ab9c351b5..d7a4ccd1736a6d4f330a97ded4acd0250fe57a7b 100755 (executable)
@@ -2,6 +2,10 @@ html {
     font-family: sans-serif;\r
 }\r
 \r
+.netdata-chart-alignment {\r
+       margin-left: 55px;\r
+}\r
+\r
 .netdata-container {\r
        display: -webkit-flex; /* Safari */\r
        -webkit-flex-wrap: wrap; /* Safari 6.1+ */\r
index 8e4d037df138014c43a84d4748f50a667caca270..58becde437a3017fd6a4ed1afd94217e7c891de0 100755 (executable)
 
                pause: false,                                   // when enabled we don't auto-refresh the charts
 
-               targets: null,                                  // an array of all the DOM elements that are
-                                                                               // currently visible (independently of their
+               targets: null,                                  // an array of all the state objects that are
+                                                                               // currently active (independently of their
                                                                                // viewport visibility)
 
                updated_dom: true,                              // when true, the DOM has been updated with
        };
 
        window.onscroll = function(event) {
-               // FIXME targets should be states not DOM elements
-               for(i = 0; i < NETDATA.options.targets.length ;i++) {
-                       var state = NETDATA.chartState(NETDATA.options.targets[i])
-                       state.isVisible();
-               }
+               // when the user scrolls he sees that we have
+               // hidden all the not-visible charts
+               // using this little function we try to switch
+               // the charts back to visible quickly
+               var targets = NETDATA.options.targets;
+               var len = targets.length;
+               while(len--) targets[len].isVisible();
        }
 
        // ----------------------------------------------------------------------------------------------------------------
                        library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
                        library: null,                  // object - the chart library used
 
+                       colors: null,
+
                        element: element,               // the element already created by the user
                        element_message: null,
                        element_loading: null,
        };
 
        // prevent to global selection sync for some time
-       chartState.prototype.globalSelectionSyncDelay = function() {
+       chartState.prototype.globalSelectionSyncDelay = function(ms) {
                if(!NETDATA.options.current.sync_selection) return;
-               NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
+               if(typeof ms === 'number')
+                       NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
+               else
+                       NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
        }
 
        // can we globally apply selection sync?
                return true;
        }
 
+       chartState.prototype.globalSelectionSyncIsMaster = function() {
+               if(NETDATA.globalSelectionSync.state === this)
+                       return true;
+               else
+                       return false;
+       }
+
        // this chart is the master of the global selection sync
        chartState.prototype.globalSelectionSyncBeMaster = function() {
                // am I the master?
-               if(NETDATA.globalSelectionSync.state === this) {
+               if(this.globalSelectionSyncIsMaster()) {
                        if(this.debug) this.log('sync: I am the master already.');
                        return;
                }
                NETDATA.globalSelectionSync.state = this;
 
                // find the all slaves
-               var self = this;
-               // FIXME targets should be states not DOM elements
-               $.each(NETDATA.options.targets, function(i, target) {
-                       var st = NETDATA.chartState(target);
-                       if(st === self) {
-                               if(self.debug) st.log('sync: not adding me to sync');
+               var targets = NETDATA.options.targets;
+               var len = targets.length;
+               while(len--) {
+                       st = targets[len];
+
+                       if(st === this) {
+                               if(this.debug) st.log('sync: not adding me to sync');
                        }
                        else if(st.globalSelectionSyncIsEligible()) {
-                               if(self.debug) st.log('sync: adding to sync as slave');
+                               if(this.debug) st.log('sync: adding to sync as slave');
                                st.globalSelectionSyncBeSlave();
                        }
-               });
+               }
+
+               this.globalSelectionSyncDelay(100);
        }
 
        // can the chart participate to the global selection sync as a slave?
                        return;
                }
 
-               if(this.debug) this.log('sync: trying to be sync master.');
-               this.globalSelectionSyncBeMaster();
+               if(!this.globalSelectionSyncIsMaster()) {
+                       if(this.debug) this.log('sync: trying to be sync master.');
+                       this.globalSelectionSyncBeMaster();
 
-               var self = this;
-               $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
-                       if(st === self) {
-                               // since we are the sync master, we should not call state.setSelection()
-                               // the chart library is taking care of visualizing our selection.
-                               if(self.debug) st.log('sync: ignoring me from set selection');
-                       }
-                       else {
-                               if(self.debug) st.log('sync: showing master selection');
-                               st.setSelection(t);
+                       if(!this.globalSelectionSyncAbility()) {
+                               if(this.debug) this.log('sync: cannot sync (yet?).');
+                               return;
                        }
+               }
+
+               // FIXME
+               // var start = new Date().getTime();
+
+               $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
+                       st.setSelection(t);
                });
+
+               // FIXME
+               // var end = new Date().getTime();
+               // console.log(end - start);
        }
 
        // stop syncing all charts to the given time
        }
 
        chartState.prototype.legendFormatValue = function(value) {
-               if(typeof value !== 'number' || value === null) return '';
+               // FIXME
+               // return '';
+               // return value;
+               if(value === null) return '';
+               if(typeof value !== 'number') return value;
 
                var abs = Math.abs(value);
                if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
                return (Math.round(value * 10000) / 10000).toLocaleString();
        }
 
-       chartState.prototype.legendSetLabelValue = function(label, string) {
-               if(typeof this.element_legend_childs.series[label] === 'undefined')
+       chartState.prototype.legendSetLabelValue = function(label, value) {
+               var series = this.element_legend_childs.series[label];
+
+               if(typeof series === 'undefined' || series.last === value)
                        return;
 
-               if(this.element_legend_childs.series[label].value !== null)
-                       this.element_legend_childs.series[label].value.innerHTML = string;
+               series.last = value;
+               value = this.legendFormatValue(value);
 
-               if(this.element_legend_childs.series[label].user !== null)
-                       this.element_legend_childs.series[label].user.innerHTML = string;
+               if(series.value !== null) series.value.innerHTML = value;
+               if(series.user !== null) series.user.innerHTML = value;
        }
 
        chartState.prototype.legendSetDate = function(ms) {
                else
                        this.legendUndefined();
 
-               for(var i = 0; i < this.current.data.dimension_names.length; i++) {
-                       if(typeof this.current.data.dimension_names[i] === 'undefined')
-                               continue;
+               var labels = this.current.data.dimension_names;
+               var i = labels.length;
+               while(i--) {
+                       var label = labels[i];
 
-                       if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] === 'undefined')
-                               continue;
+                       if(typeof label === 'undefined') continue;
+                       if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
 
                        if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every)
-                               this.legendSetLabelValue(this.current.data.dimension_names[i], this.legendFormatValue(this.current.data.result_latest_values[i]));
+                               this.legendSetLabelValue(label, this.current.data.result_latest_values[i]);
                        else
-                               this.legendSetLabelValue(this.current.data.dimension_names[i], '');
+                               this.legendSetLabelValue(label, null);
                }
        }
 
                this.legendShowLatestValues();
        }
 
+       chartState.prototype.chartColors = function() {
+               if(this.colors !== null) return this.colors;
+
+               this.colors = $(this.element).data('colors');
+               if(typeof this.colors === 'undefined' || this.colors === null)
+                       this.colors = NETDATA.colors;
+               else {
+                       // console.log(this.colors);
+                       var s = this.colors;
+                       if(typeof s === 'string') s = s.split(' ');
+
+                       this.colors = new Array();
+                       var self = this;
+                       $.each(s, function(i, c) {
+                               self.colors.push(c);
+                       });
+
+                       // push the default colors too
+                       var self = this;
+                       $.each(NETDATA.colors, function(i, c) {
+                               self.colors.push(c);
+                       });
+               }
+
+               return this.colors;
+       }
+
        chartState.prototype.legendUpdateDOM = function() {
                if(!this.hasLegend()) return;
 
                }
                else {
                        // this.log('checking existing legend');
-                       for(var i = 0; i < this.current.data.dimension_names.length; i++) {
-                               if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] === 'undefined') {
-                                       // this.log('legend is incosistent - missing dimension:' + this.current.data.dimension_names[i]);
+                       var labels = this.current.data.dimension_names;
+                       var i = labels.length;
+                       while(i--) {
+                               var name = labels[i];
+                               if(typeof this.element_legend_childs.series[name] === 'undefined') {
+                                       // this.log('legend is incosistent - missing dimension:' + name);
                                        needed = true;
                                        break;
                                }
                                else if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every) {
-                                       // this.log('setting legend of ' + this.current.data.dimension_names[i] + ' to ' + this.current.data.latest_values[i]);
-                                       this.legendSetLabelValue(this.current.data.dimension_names[i], this.legendFormatValue(this.current.data.latest_values[i]));
+                                       // this.log('setting legend of ' + name + ' to ' + this.current.data.latest_values[i]);
+                                       this.legendSetLabelValue(name, this.current.data.latest_values[i]);
                                }
                        }
                }
 
                self = $(this);
                var genLabel = function(state, parent, name, count) {
-                       var c = count % NETDATA.colors.length;
+                       var c = count % state.chartColors().length;
 
                        var user_element = null;
                        var user_id = self.data('show-value-of-' + name + '-at') || null;
                        state.element_legend_childs.series[name] = {
                                name: document.createElement('span'),
                                value: document.createElement('span'),
-                               user: user_element
+                               user: user_element,
+                               last: null
                        };
 
                        var label = state.element_legend_childs.series[name];
                        label.name.title = name;
                        label.value.title = name;
 
-                       var rgb = NETDATA.colorHex2Rgb(NETDATA.colors[c]);
+                       var rgb = NETDATA.colorHex2Rgb(state.chartColors()[c]);
                        label.name.innerHTML = '<table class="netdata-legend-name-table-'
                                + state.chart.chart_type
                                + '" style="background-color: '
                        var text = document.createTextNode(' ' + name);
                        label.name.appendChild(text);
 
-                       label.name.style.color = NETDATA.colors[c];
-                       label.value.style.color = NETDATA.colors[c];
+                       label.name.style.color = state.chartColors()[c];
+                       label.value.style.color = state.chartColors()[c];
 
                        if(count > 0)
                                parent.appendChild(document.createElement('br'));
        // ----------------------------------------------------------------------------------------------------------------
 
        NETDATA.chartRefresherNoParallel = function(index) {
-               // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
+               if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
 
                if(NETDATA.options.updated_dom) {
                        // the dom has been updated
                        NETDATA.getDomCharts(NETDATA.chartRefresher);
                        return;
                }
-               // FIXME targets should be states not DOM elements
-               var target = NETDATA.options.targets.get(index);
-               if(typeof target === 'undefined') {
+               if(index >= NETDATA.options.targets.length) {
                        if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
                                NETDATA.options.auto_refresher_fast_weight = 0;
 
                                }, NETDATA.options.current.idle_between_loops);
                        }
                else {
-                       var state = NETDATA.chartState(target);
+                       var state = NETDATA.options.targets[index];
 
                        if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
                                if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
                NETDATA.options.parallel = new Array();
                NETDATA.options.sequencial = new Array();
 
-               // FIXME targets should be states not DOM elements
-               for(var i = 0; i < NETDATA.options.targets.length ; i++) {
-                       var target = NETDATA.options.targets.get(i);
-                       var state = NETDATA.chartState(target);
+               var targets = NETDATA.options.targets;
+               var len = targets.length;
+               while(len--) {
+                       var state = targets[len];
 
                        if(!state.library.initialized)
                                NETDATA.options.sequencial.push(state);
        NETDATA.getDomCharts = function(callback) {
                NETDATA.options.updated_dom = false;
 
-               // FIXME targets should be states not DOM elements
-               NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
+               var targets = $('div[data-netdata]').filter(':visible');
 
                if(NETDATA.options.debug.main_loop)
-                       console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
+                       console.log('DOM updated - there are ' + targets.length + ' charts on page.');
 
-               // we need to re-size all the charts quickly
-               // before making any external calls
-               // FIXME targets should be states not DOM elements
-               $.each(NETDATA.options.targets, function(i, target) {
+               NETDATA.options.targets = new Array();
+               var len = targets.length;
+               while(len--) {
                        // the initialization will take care of sizing
                        // and the "loading..." message
-                       var state = NETDATA.chartState(target);
-               });
+                       NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
+               }
 
                if(typeof callback === 'function') callback();
        }
        NETDATA.sparklineChartCreate = function(state, data) {
                var self = $(state.element);
                var type = self.data('sparkline-type') || 'line';
-               var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
+               var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
                var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?'#FFF':NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
                var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
                var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
        NETDATA.dygraphSetSelection = function(state, t) {
                if(typeof state.dygraph_instance !== 'undefined') {
                        var r = state.calculateRowForTime(t);
-                       if(r !== -1) {
+                       if(r !== -1)
                                state.dygraph_instance.setSelection(r);
-                               return true;
-                       }
-                       else {
+                       else
                                state.dygraph_instance.clearSelection();
-                               return false;
-                       }
                }
+
+               return true;
        }
 
        NETDATA.dygraphClearSelection = function(state, t) {
 
        NETDATA.dygraphChartUpdate = function(state, data) {
                var dygraph = state.dygraph_instance;
+               
+               // when the chart is not visible, and hidden
+               // if there is a window resize, dygraph detects
+               // its element size as 0x0.
+               // this will make it re-appear properly
+               dygraph.resize();
 
                if(state.current.name === 'pan') {
                        if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() loose update');
                var self = $(state.element);
 
                state.dygraph_options = {
-                       colors: self.data('dygraph-colors') || NETDATA.colors,
+                       colors: self.data('dygraph-colors') || state.chartColors(),
                        
                        // leave a few pixels empty on the right of the chart
                        rightGap: self.data('dygraph-rightgap') || 5,
                                        pixelsPerLabel: 15,
                                        valueFormatter: function (x) {
                                                // return (Math.round(x*100) / 100).toLocaleString();
-                                               return state.legendFormatValue(x);
+                                               //return state.legendFormatValue(x);
+                                               //FIXME
+                                               return x;
                                        }
                                }
                        },
                                var elements = state.element_legend_childs;
 
                                // if the hidden div is not there
-                               // state is not managing the legend
+                               // we are not managing the legend
                                if(elements.hidden === null) return;
 
                                if (typeof data.x === 'undefined') {
                                }
                                else {
                                        state.legendSetDate(data.x);
-                                       for (var i = 0; i < data.series.length; i++) {
+                                       var i = data.series.length;
+                                       while(i--) {
                                                var series = data.series[i];
                                                if(!series.isVisible) continue;
-                                               state.legendSetLabelValue(series.label, series.yHTML);
-                                               // elements.series[series.label].value.innerHTML = series.yHTML;
+                                               state.legendSetLabelValue(series.label, series.y);
                                        }
                                }
 
                        initialize: NETDATA.sparklineInitialize,
                        create: NETDATA.sparklineChartCreate,
                        update: NETDATA.sparklineChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                        initialize: NETDATA.peityInitialize,
                        create: NETDATA.peityChartCreate,
                        update: NETDATA.peityChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'ssvcomma'; },
                        initialize: NETDATA.morrisInitialize,
                        create: NETDATA.morrisChartCreate,
                        update: NETDATA.morrisChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        initialize: NETDATA.googleInitialize,
                        create: NETDATA.googleChartCreate,
                        update: NETDATA.googleChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'datatable'; },
                        initialize: NETDATA.raphaelInitialize,
                        create: NETDATA.raphaelChartCreate,
                        update: NETDATA.raphaelChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        initialize: NETDATA.easypiechartInitialize,
                        create: NETDATA.easypiechartChartCreate,
                        update: NETDATA.easypiechartChartUpdate,
-                       setSelection: null,
-                       clearSelection: null,
+                       setSelection: function(t) { return true; },
+                       clearSelection: function() { return true; },
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
diff --git a/web/dashboard_full.html b/web/dashboard_full.html
new file mode 100755 (executable)
index 0000000..203f75a
--- /dev/null
@@ -0,0 +1,352 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+       <title>NetData Dashboard</title>
+
+       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+       <meta name="viewport" content="width=device-width, initial-scale=1">
+       <meta name="apple-mobile-web-app-capable" content="yes">
+       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+       <meta name="author" content="costa@tsaousis.gr">
+</head>
+<body>
+
+<div class="container-fluid">
+       <div id="charts_div" class="container-fluid"></div>
+</div>
+
+</body>
+</html>
+       <!-- you can set your netdata server globally, by ucommenting this -->
+       <!-- you can also give a different server per chart, with the attribute: data-host="http://netdata.server:19999" -->
+       <!-- <script> netdataServer = "http://box:19999"; </script> -->
+
+       <!-- load the dashboard manager - it will do the rest -->
+       <script type="text/javascript" src="dashboard.js"></script>
+
+<script>
+var options = {
+       data: null,
+       hostnane: 'netdata_server',
+       categories: new Array(),
+       categories_idx: {},
+       families: new Array(),
+       families_idx: {},
+       chartsPerRow: 0,
+       chartsMinWidth: 450,
+       chartsHeight: 200,
+       sparklinesHeight: 60
+}
+
+function screenWidth() {
+       return (($(window).width() * 0.95) - 50);
+}
+
+function chartsPerRow(total) {
+       if(options.chartsPerRow === 0) {
+               width = Math.floor(total / options.chartsMinWidth);
+               if(width === 0) width = 1;
+               return width;
+       }
+       else return options.chartsPerRow;
+}
+
+function chartsPrioritySort(a, b) {
+       if(a.priority === b.priority) {
+               if(a.name < b.name) return -1;
+       }
+       else if(a.priority < b.priority) return -1;
+       return 1;
+}
+
+function uniq(array, find_key, get_result) {
+       if(typeof get_result === 'undefined' || get_result === null)
+               get_result = find_key;
+
+       var idx = {};
+       var result = new Array();
+
+       $.each(array, function(i, c) {
+               key = find_key(c);
+               if(typeof idx[key] === 'undefined') {
+                       idx[key] = true;
+                       result.push(get_result(c));
+               }
+       });
+       return result;
+}
+
+function uniq_with_list(array, find_key_function) {
+       var idx = {};
+       var result = new Array();
+
+       $.each(array, function(i, c) {
+               key = find_key_function(c);
+               if(typeof idx[key] === 'undefined') {
+                       idx[key] = new Array();
+                       result.push( { name: key, values: idx[key] } );
+               }
+               idx[key].push(c);
+       });
+       result.sort(function(a, b) {
+               if(a.name < b.name) return -1;
+               return 1;
+       });
+       return result;
+}
+
+function prepareScreen(data) {
+       console.log('NETDATA is paused - ready to prepare the screen');
+       console.log(data);
+
+       options.data = data;
+       options.hostname = data.hostname;
+       var charts = data.charts;
+
+       $.each(charts, function(i, c) {
+               if(c.enabled === true) {
+
+                       //if(c.family.length > 4 && c.family.substring(c.family.length - 4, c.family.length) === '-ifb')
+                       //      c.family = c.family.split('-')[0];
+
+                       // find the category of the chart
+                       c.category = c.type.split('.')[0];
+
+                       var tmp = c.category.split('_')[0];
+                       if(tmp === 'net' || tmp === 'disk')
+                               c.category = tmp;
+
+                       switch(c.category) {
+                               case 'system':
+                                       c.category_priority = 10;
+                                       c.category_title = 'System';
+                                       c.glyphicon = "glyphicon-dashboard";
+                                       break;
+
+                               case 'tc':
+                                       c.category_priority = 20;
+                                       c.category_title = 'Quality Of Service';
+                                       c.glyphicon = "glyphicon-random";
+                                       break;
+
+                               case 'net':
+                                       c.category_priority = 30;
+                                       c.category_title = 'Network Interfaces';
+                                       c.glyphicon = "glyphicon-transfer";
+                                       break;
+
+                               case 'apps':
+                                       c.category_priority = 40;
+                                       c.category_title = 'Applications Monitoring';
+                                       c.glyphicon = "glyphicon-tasks";
+                                       break;
+
+                               case 'ipvs':
+                                       c.category_priority = 50;
+                                       c.category_title = 'IP Virtual Server';
+                                       c.glyphicon = "glyphicon-transfer";
+                                       break;
+
+                               case 'netfilter':
+                                       c.category_priority = 60;
+                                       c.category_title = 'Netfilter';
+                                       c.glyphicon = "glyphicon-cloud";
+                                       break;
+
+                               case 'ipv4':
+                                       c.category_priority = 70;
+                                       c.category_title = 'IPv4';
+                                       c.glyphicon = "glyphicon-globe";
+                                       break;
+
+                               case 'mem':
+                                       c.category_priority = 80;
+                                       c.category_title = 'Memory';
+                                       c.glyphicon = "glyphicon-dashboard";
+                                       break;
+
+                               case 'cpu':
+                                       c.category_priority = 90;
+                                       c.category_title = 'CPUs';
+                                       c.glyphicon = "glyphicon-dashboard";
+                                       break;
+
+                               case 'disk':
+                                       c.category_priority = 100;
+                                       c.category_title = 'Disks';
+                                       c.glyphicon = "glyphicon-hdd";
+                                       break;
+
+                               case 'nfsd':
+                                       c.category_priority = 110;
+                                       c.category_title = 'NFS Server';
+                                       c.glyphicon = "glyphicon-hdd";
+                                       break;
+
+                               case 'squid':
+                                       c.category_priority = 140;
+                                       c.category_title = 'Proxy Server';
+                                       c.glyphicon = "glyphicon-link";
+                                       break;
+
+                               case 'netdata':
+                                       c.category_priority = 150;
+                                       c.category_title = 'Netdata Monitoring';
+                                       c.glyphicon = "glyphicon-thumbs-up";
+                                       break;
+
+                               case 'example':
+                                       c.category_priority = 100000;
+                                       c.category_title = 'Example Plugins';
+                                       c.glyphicon = "glyphicon-search";
+                                       break;
+
+                               default:
+                                       c.category_priority = 150;
+                                       c.category_title = c.type;
+                                       c.glyphicon = "glyphicon-dashboard";
+                                       break;
+                       }
+
+                       // find the unique categories
+                       if(typeof options.categories_idx[c.category] === 'undefined') {
+                               options.categories_idx[c.category] = {
+                                       charts: new Array()
+                               }
+                               options.categories.push({
+                                       name: c.category,
+                                       title: c.category_title,
+                                       priority: c.category_priority,
+                                       glyphicon: c.glyphicon,
+                                       charts: options.categories_idx[c.category].charts
+                               });
+                       }
+                       options.categories_idx[c.category].charts.push(c);
+
+                       // find the unique families
+                       if(typeof options.families_idx[c.family] === 'undefined') {
+                               options.families_idx[c.family] = {
+                                       charts: new Array()
+                               };
+                               options.families.push({
+                                       name: c.family,
+                                       title: c.family,
+                                       priority: c.category_priority,
+                                       glyphicon: c.glyphicon,
+                                       charts: options.categories_idx[c.category].charts
+                               });
+                       }
+                       options.families_idx[c.family].charts.push(c);
+               }
+       });
+
+       function prioritySort(a, b) {
+               if(a.priority < b.priority) return -1;
+               return 1;
+       }
+
+       // sort all of them
+       options.categories.sort(prioritySort);
+       options.families.sort(prioritySort);
+       $.each(options.families,   function(i, c) { c.charts.sort(prioritySort); });
+
+       var div = document.getElementById('charts_div');
+       var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
+
+       // find the proper duration for per-second updates
+       var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
+       console.log(duration);
+       html = '';
+       $.each(options.categories, function(i, t) {
+               t.charts.sort(prioritySort);
+
+               html += '<div class="container-fluid row clearfix"><h2>' + t.title + '</h2>';
+
+               if(t.name === 'net') {
+                       var interfaces = uniq_with_list(t.charts, function(c) { return c.family; });
+                       console.log(interfaces);
+
+                       $.each(interfaces, function(i, c) {
+                               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>';
+                               $.each(c.values, function(x, f) {
+                                       if(f.type === 'net') {
+                                               html += '<div data-netdata="' + f.id + '"'
+                                                       + ' data-width="100%"'
+                                                       + ' data-height="' + options.chartsHeight.toString() + 'px"'
+                                                       + ' data-before="0"'
+                                                       + ' data-after="-' + duration.toString() + '"'
+                                                       + '></div>';
+                                       }
+                                       else {
+                                               html += '<div data-netdata="' + f.id + '"'
+                                                       + ' data-width="100%"'
+                                                       + ' data-height="' + (options.chartsHeight / 2).toString() + 'px"'
+                                                       + ' data-before="0"'
+                                                       + ' data-after="-' + duration.toString() + '"'
+                                                       + '></div>';
+                                       }
+                               });
+                               html += '</div>';
+                       });
+               }
+               else if(t.name === 'disk') {
+                       var disks = uniq_with_list(t.charts, function(c) { return c.family; });
+                       console.log(disks);
+
+                       $.each(disks, function(i, c) {
+                               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>';
+                               $.each(c.values, function(x, f) {
+                                       var c = null;
+                                       var h = options.chartsHeight / 2;
+                                       switch(f.type) {
+                                               case 'disk'        : h = options.chartsHeight; break;
+                                               case 'disk_backlog': c = '#DD4477'; break;
+                                               case 'disk_util'   : c = '#109618'; break;
+                                               case 'disk_qops'   : c = '#8B0707'; break;
+                                       }
+
+                                       html += '<div data-netdata="' + f.id + '"'
+                                               + ' data-width="100%"'
+                                               + ' data-height="' + h.toString() + 'px"'
+                                               + ' data-before="0"'
+                                               + ' data-after="-' + duration.toString() + '"'
+                                               + ' data-colors="' + c + '"'
+                                               + '></div>';
+                               });
+                               html += '</div>';
+                       });
+               }
+               else {
+                       $.each(t.charts, function(x, c) {
+                               html += '<div data-netdata="' + c.id + '"'
+                                       + ' data-width="' + pcent_width.toString() + '%"'
+                                       + ' data-height="' + options.chartsHeight.toString() + 'px"'
+                                       + ' data-before="0"'
+                                       + ' data-after="-' + duration.toString() + '"'
+                                       + '></div>';
+                       });
+               }
+
+               html += '</div>';
+       });
+
+       html += '<br/>'
+       $.each(NETDATA.colors, function(i, c){
+               html += '<div style="display: inline-block;"><div style="display: inline-block; width: 100px; height: 100px; background: ' + c + ';"></div><br/>' + c + '</div>';
+       });
+
+       div.innerHTML = html;
+       NETDATA.unpause();
+}
+
+NETDATA.ready(function() {
+       NETDATA.chartRegistry.downloadAll(NETDATA.serverDefault, function(data) {
+               NETDATA.pause(function () {
+                       prepareScreen(data);
+               });
+       });
+});
+
+</script>