]> arthur.barton.de Git - netdata.git/commitdiff
finalized new main chart with adaptive explorer on the chart
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 23 Apr 2014 23:18:21 +0000 (02:18 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 23 Apr 2014 23:18:21 +0000 (02:18 +0300)
netdata.c
web/index.html

index f4fc50aaef20415d887f8e0c96ae6631d7031069..e3aaf9815c6f5e18be9ddbb90fcbec7b2a8b0b5b 100755 (executable)
--- a/netdata.c
+++ b/netdata.c
@@ -1103,14 +1103,12 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        if(group_count < 1) group_count = 1;
        // if(group_count > st->entries / 20) group_count = st->entries / 20;
 
-       size_t printed = 0;                     // the lines of JSON data we have generated so far
+       long printed = 0;                       // the lines of JSON data we have generated so far
 
-       size_t current_entry = st->current_entry;
-       size_t t, lt;                           // t = the current entry, lt = the lest entry of data
+       long stop_entry, current_entry = st->current_entry;
 
-       RRD_DIMENSION *rd;
-       size_t c = 0;                           // counter for dimension loops
-       size_t dimensions = 0;          // the total number of dimensions present
+       int c = 0;                                      // counter for dimension loops
+       int dimensions = 0;                     // the total number of dimensions present
 
        unsigned long long usec = 0;// usec between the entries
        char dtm[201];                          // temp variable for storing dates
@@ -1118,6 +1116,7 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        int we_need_totals = 0;         // if set, we should calculate totals for all dimensions
 
        // find how many dimensions we have
+       RRD_DIMENSION *rd;
        for( rd = st->dimensions ; rd ; rd = rd->next) {
                dimensions++;
                if(rd->type == RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL || rd->type == RRD_DIMENSION_PCENT_OVER_ROW_TOTAL) we_need_totals++;
@@ -1141,11 +1140,14 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
                group_values[c] = group_counts[c] = 0;
 
+       // print the labels
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "{\n       %scols%s:\n     [\n", kq, kq);
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "          {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "          {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "          {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
 
+       // print the header for each dimension
+       // and update the print_hidden array for the dimensions that should be hidden
        for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
                if(rd->hidden)
                        print_hidden[c] = 1;
@@ -1155,6 +1157,7 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
                }
        }
 
+       // print the begin of row data
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "\n        ],\n    %srows%s:\n     [\n", kq, kq);
 
        // make sure current_entry is within limits
@@ -1162,14 +1165,10 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        if(before == 0) before = st->times[current_entry].tv_sec;
 
        // find the oldest entry of the round-robin
-       t = rrd_stats_first_entry(st);
-       lt = t;
-       size_t stop_entry = t;
+       stop_entry = rrd_stats_first_entry(st);
 
        // skip the oldest, to have incremental data
-       t++;
-       if(t >= st->entries) t = 0;
-       if(after == 0) after = st->times[t].tv_sec;
+       if(after == 0) after = st->times[stop_entry].tv_sec;
 
        // the minimum line length we expect
        int line_size = 4096 + (dimensions * 200);
@@ -1182,43 +1181,31 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        int normal_annotation_len = snprintf(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
        normal_annotation[200] = '\0';
 
-       // count down of the entries examined so far
-       long count = ((before - after) / update_every) + 1;
-
        // to allow grouping on the same values, we need a pad
        long pad = before % group_count;
 
        // checks for debuging
        if(before < after)
-               error("WARNING: The newest value in the database (%lu) is earlier than the oldest (%lu)", before, after);
+               debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%lu) is earlier than the oldest (%lu)", st->name, before, after);
 
        if((before - after) > st->entries * update_every)
-               error("WARNING: The time difference between the oldest and the newest entries (%lu) is higher than the capacity of the database (%lu)", before - after, st->entries * update_every);
-
-       // we need these at the end for debugging
-       long count_start = count;
-       size_t t_start = t;
+               debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%lu) is higher than the capacity of the database (%lu)", st->name, before - after, st->entries * update_every);
 
-       // the loop in dimension data
+       // loop in dimension data
        int annotate_reset = 0;
-       for ( ; t != stop_entry && count >= 0 ; lt = t++) {
+       long t = current_entry, lt = current_entry - 1, count; // t = the current entry, lt = the last entry of data
+       if(lt < 0) lt = st->entries - 1;
+       for (count = printed = 0; t != stop_entry ; t = lt--) {
                int print_this = 0;
 
-               if(t >= st->entries) t = 0;
+               if(lt < 0) lt = st->entries - 1;
 
                // make sure we return data in the proper time range
                if(st->times[t].tv_sec < after || st->times[t].tv_sec > before) continue;
-               count--;
-
-               // check if we may exceed the buffer provided
-               web_buffer_increase(wb, line_size);
-
-               // prefer the most recent last entries
-               if(((count - pad) / group_count) >= entries_to_show) continue;
+               count++;
 
                // ok. we will use this entry!
                // find how much usec since the previous entry
-
                usec = usecdiff(&st->times[t], &st->times[lt]);
 
                if(((count - pad) % group_count) == 0) {
@@ -1227,6 +1214,9 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
                                break;
                        }
 
+                       // check if we may exceed the buffer provided
+                       web_buffer_increase(wb, line_size);
+
                        // generate the local date time
                        struct tm *tm = localtime(&st->times[t].tv_sec);
                        if(!tm) { error("localtime() failed."); continue; }
@@ -1327,8 +1317,7 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, siz
        if(printed) wb->bytes += sprintf(&wb->buffer[wb->bytes], "]}");
        wb->bytes += sprintf(&wb->buffer[wb->bytes], "\n        ]\n}\n");
 
-       if(!printed)
-               error("WARNING: NOTHING PRINTED: %s, after = %lu, before = %lu, diff = %lu, pad = %ld, count = %ld, entries_to_show = %lu, group_count = %lu, current_entry = %lu, t = %lu", st->name, after, before, before - after, pad, count_start, entries_to_show, group_count, current_entry, t_start);
+       debug(D_RRD_STATS, "RRD_STATS_JSON: %s Generated %ld rows, total %ld bytes", st->name, printed, wb->bytes);
 
        pthread_mutex_unlock(&st->mutex);
        return last_timestamp;
index 9006c60a835bcac027f458b71729acbfd0b1bd05..817589d30c773a62f10cd13674eaa9dd5f78a358 100644 (file)
        // Set a callback to run when the Google Visualization API is loaded.
        google.setOnLoadCallback(initCharts);
        
-       var TARGET_THUMB_GRAPH_WIDTH = 500; // chart width will range from 0.5 to 1.5 of that
-       var MINIMUM_THUMB_GRAPH_WIDTH = 400; // chart will generally try to be wider than that
-       var TARGET_THUMB_GRAPH_HEIGHT = 220;
-       var MAINCHART_MAX_TIME_TO_SHOW = 600;
-       var THUMBS_MAX_TIME_TO_SHOW = 600;
-       var MAINCHART_CONTROL_HEIGHT = 100;
-       var MAINCHART_CONTROL_DIVISOR = 1;
-       var MAINCHART_POINTS_DIVISOR = 5;
+       var TARGET_THUMB_GRAPH_WIDTH = 500;             // thumb charts width will range from 0.5 to 1.5 of that
+       var MINIMUM_THUMB_GRAPH_WIDTH = 400;    // thumb chart will generally try to be wider than that
+       var TARGET_THUMB_GRAPH_HEIGHT = 220;    // the height of the thumb charts
+       var THUMBS_MAX_TIME_TO_SHOW = 600;              // how much time the thumb charts will present?
+
+       var MAINCHART_MAX_TIME_TO_SHOW = 600;   // how much time the main chart will present by default?
+       var MAINCHART_POINTS_DIVISOR = 10;              // how much detailed will the main chart be by default? 1 = finest, higher is faster
+       var MAINCHART_CONTROL_HEIGHT = 75;              // how tall the control chart will be
+       var MAINCHART_CONTROL_DIVISOR = 2;              // how much detailed will the control chart be? 1 = finest, higher is faster
 
        var MODE_THUMBS = 1;
        var MODE_MAIN = 2;
        // ------------------------------------------------------------------------
        // common HTML generation
 
-       function chartIsLoadingHTML(width, height) { return "<table><tr><td align=\"center\" width=\"" + width + "\" height=\"" + height + "\" style=\"vertical-align:middle\"><h4><span class=\"glyphicon glyphicon-refresh\"></span><br/><br/>loading chart<br/><br/><span class=\"label label-default\">Please wait...</span></h4></td></tr></table>"; }
+       function chartIsLoadingHTML(name, width, height) { return "<table><tr><td align=\"center\" width=\"" + width + "\" height=\"" + height + "\" style=\"vertical-align:middle\"><h4><span class=\"glyphicon glyphicon-refresh\"></span><br/><br/>loading " + name + "<br/><br/><span class=\"label label-default\">Please wait...</span></h4></td></tr></table>"; }
 
-       function showChartIsLoading(id, width, height) {
+       function showChartIsLoading(id, name, width, height) {
                //mylog('adding loading chart html in div with id ' + id);
-               document.getElementById(id).innerHTML = chartIsLoadingHTML(width, height);
+               document.getElementById(id).innerHTML = chartIsLoadingHTML(name, width, height);
        }
 
        function thumbChartActions(i, c, nogroup) {
                var dt = before - after;
                if(dt > c.entries * c.update_every) dt = c.entries * c.update_every;
 
-               if(maxtime && dt > maxtime) dt = maxtime;
+               if(maxtime) dt = maxtime;
 
-               var data_points = dt / c.update_every;
+               var data_points = Math.round(dt / c.update_every);
                var screen_points = Math.round(c.chartOptions.width / divisor);
+               //mylog('screen = ' + screen_points + ', data = ' + data_points + ', divisor = ' + divisor);
 
-               if(!group) {
+               if(group <= 0) {
                        if(screen_points > data_points) {
                                c.group = 1;
                                c.points_to_show = data_points;
+                               //mylog("rendering at full detail");
                        }
                        else {
                                c.group = Math.round(data_points / screen_points);
 
-                                    if(c.group > 60) c.group = 60;
-                               else if(c.group > 45) c.group = 45;
-                               else if(c.group > 30) c.group = 30;
-                               else if(c.group > 20) c.group = 20;
-                               else if(c.group > 15) c.group = 15;
-                               else if(c.group > 10) c.group = 10;
-                               else if(c.group > 5) c.group = 5;
-                               else if(c.group > 2) c.group = 2;
-                               else c.group = 1;
+                               if(group >= 0) {
+                                            if(c.group > 60) c.group = 60;
+                                       else if(c.group > 45) c.group = 45;
+                                       else if(c.group > 30) c.group = 30;
+                                       else if(c.group > 20) c.group = 20;
+                                       else if(c.group > 15) c.group = 15;
+                                       else if(c.group > 10) c.group = 10;
+                                       else if(c.group > 5) c.group = 5;
+                                       else if(c.group > 2) c.group = 2;
+                                       else c.group = 1;
+                               }
 
                                c.points_to_show = Math.round(data_points / c.group);
+                               //mylog("rendering adaptive");
                        }
                }
                else {
                        c.group = group;
                        c.points_to_show = Math.round(data_points / group);
+                       //mylog("rendering with given group");
                }
+               //mylog('group = ' + c.group + ', points = ' + c.points_to_show);
 
                if(mainchart.chartType == 'LineChart') {
                        if(mainchart.group <= 2) mainchart.chartOptions.lineWidth = 1;
                if(mainchart) cleanThisChart(mainchart);
 
                mainchart = $.extend(true, {}, c);
-               var tempchart = $.extend(true, {}, mainchart);
-
+               mainchart.refreshCount = 0;
+               mainchart.last_updated = 0;
+               mainchart.chartOptions.explorer = null;
                mainchart.chart = null;
+
                mainchart.chartOptions.width = screenWidth();
                mainchart.chartOptions.height = $(window).height() - 150 - MAINCHART_CONTROL_HEIGHT;
                if(mainchart.chartOptions.height < 300) mainchart.chartOptions.height = 300;
+               //mainchart.chartOptions.chartArea = {'width': '80%'};
 
                mainchart.div = 'maingraph';
-               mainchart.last_updated = 0;
-
                calculateChartGroup(mainchart, MAINCHART_POINTS_DIVISOR, MAINCHART_MAX_TIME_TO_SHOW, 0);
-               mainchart.chartOptions.explorer = null;
+
+               // copy it to the hidden chart
+               mainchart.hiddenchart = $.extend(true, {}, mainchart);
+               mainchart.hiddenchart.chartOptions.height = MAINCHART_CONTROL_HEIGHT;
+               mainchart.hiddenchart.div = 'maingraph_control';
 
                // initialize the div
-               showChartIsLoading(mainchart.div, mainchart.chartOptions.width, mainchart.chartOptions.height);
+               showChartIsLoading(mainchart.div, mainchart.name, mainchart.chartOptions.width, mainchart.chartOptions.height);
 
                // set the radio buttons
                setMainChartGroupMethod(mainchart.group_method, 'no-refresh');
                $('#group' + mainchart.group).trigger('click');
                setMainChartGroup(mainchart.group, 'no-refresh');
 
-               var controlopts = $.extend(true, {}, mainchart.chartOptions, {
-                       lineWidth: 1,
-                       height: MAINCHART_CONTROL_HEIGHT,
-                       chartArea: {'width': '90%'},
-                       hAxis: {'baselineColor': 'none'},
-                       vAxis: {'title': null},
-               });
+               switchToMainGraph();
+       }
 
-               mainchart.dashboard = new google.visualization.Dashboard(document.getElementById('maingraph_dashboard'));
-               mainchart.control = new google.visualization.ControlWrapper({
-                       controlType: 'ChartRangeFilter',
-                       containerId: 'maingraph_control',
-                       options: {
-                               filterColumnIndex: 0,
-                               ui: {
-                                       chartType: mainchart.chartType,
-                                       chartOptions: controlopts,
-                                       minRangeSize: MAINCHART_MAX_TIME_TO_SHOW * mainchart.update_every / MAINCHART_POINTS_DIVISOR * 1000,
-                               }
-                       },
-               });
-               mainchart.hiddenchart = new google.visualization.ChartWrapper({
-                       chartType: mainchart.chartType,
-                       containerId: 'maingraph_hidden',
-                       options: {
-                               isStacked: mainchart.chartOptions.isStacked,
-                               width: mainchart.chartOptions.width,
-                               height: mainchart.chartOptions.height,
-                               //chartArea: {'height': '80%', 'width': '100%'},
-                               //hAxis: {'slantedText': false},
-                               //legend: {'position': 'none'}
-                       },
-               });
+       function refreshHiddenChart(doNext) {
+               if(refresh_mode == REFRESH_PAUSED && mainchart.hiddenchart.last_updated != 0) {
+                       if(typeof doNext == "function") doNext();
+                       return;
+               }
+
+               // is it too soon for a refresh?
+               var now = new Date().getTime();
+               if((now - mainchart.hiddenchart.last_updated) < (mainchart.hiddenchart.group * mainchart.hiddenchart.update_every * 1000)) {
+                       if(typeof doNext == "function") doNext();
+                       return;
+               }
 
-               // load the data for the control and the hidden chart
-               calculateChartGroup(tempchart, MAINCHART_CONTROL_DIVISOR, 0, 0);
-               // alert("diff = " + (tempchart.before - tempchart.after) + ", group = " + tempchart.group);
+               if(mainchart.dashboard && mainchart.hiddenchart.refreshCount > 50) {
+                       mainchart.dashboard.clear();
+                       mainchart.control_wrapper.clear();
+                       mainchart.hidden_wrapper.clear();
 
-               switchToMainGraph();
+                       mainchart.dashboard = null;
+                       mainchart.control_wrapper = null;
+                       mainchart.hidden_wrapper = null;
+               }
+
+               if(!mainchart.dashboard) {
+                       var controlopts = $.extend(true, {}, mainchart.chartOptions, {
+                               lineWidth: 1,
+                               height: mainchart.hiddenchart.chartOptions.height,
+                               chartArea: {'width': '98%'},
+                               hAxis: {'baselineColor': 'none'},
+                               vAxis: {'title': null},
+                       });
+
+                       mainchart.dashboard = new google.visualization.Dashboard(document.getElementById('maingraph_dashboard'));
+                       mainchart.control_wrapper = new google.visualization.ControlWrapper({
+                               controlType: 'ChartRangeFilter',
+                               containerId: 'maingraph_control',
+                               options: {
+                                       filterColumnIndex: 0,
+                                       ui: {
+                                               chartType: mainchart.chartType,
+                                               chartOptions: controlopts,
+                                               minRangeSize: (MAINCHART_MAX_TIME_TO_SHOW * 1000 * mainchart.update_every) / MAINCHART_POINTS_DIVISOR,
+                                       }
+                               },
+                       });
+                       mainchart.hidden_wrapper = new google.visualization.ChartWrapper({
+                               chartType: mainchart.chartType,
+                               containerId: 'maingraph_hidden',
+                               options: {
+                                       isStacked: mainchart.chartOptions.isStacked,
+                                       width: mainchart.hiddenchart.chartOptions.width,
+                                       height: mainchart.hiddenchart.chartOptions.height,
+                                       //chartArea: {'height': '80%', 'width': '100%'},
+                                       //hAxis: {'slantedText': false},
+                                       //legend: {'position': 'none'}
+                               },
+                       });
+
+                       mainchart.hiddenchart.refreshCount = 0;
+               }
+
+               // load the data for the control and the hidden wrappers
+               // calculate the group and points to show for the control chart
+               calculateChartGroup(mainchart.hiddenchart, MAINCHART_CONTROL_DIVISOR, 0, -1);
 
                $.ajax({
-                       url: generateChartURL(tempchart),
+                       url: generateChartURL(mainchart.hiddenchart),
                        dataType:"json",
                        cache: false
                })
 
                        mainchart.control_data = new google.visualization.DataTable(jsondata);
 
-                       google.visualization.events.addListener(mainchart.control, 'ready', mainchartControlReadyEvent);
-                       mainchart.dashboard.bind(mainchart.control, mainchart.hiddenchart);
+                       if(mainchart.hiddenchart.last_updated == 0) {
+                               google.visualization.events.addListener(mainchart.control_wrapper, 'ready', mainchartControlReadyEvent);
+                               mainchart.dashboard.bind(mainchart.control_wrapper, mainchart.hidden_wrapper);
+                       }
+                       if(refresh_mode != REFRESH_PAUSED)
+                               mainchart.control_wrapper.setState({range: {
+                                       start: new Date((Math.round(new Date().getTime() / 1000) - MAINCHART_MAX_TIME_TO_SHOW) * 1000),
+                                       end: new Date()
+                               }});
+
                        mainchart.dashboard.draw(mainchart.control_data);
+                       mainchart.hiddenchart.last_updated = new Date().getTime();
+                       mainchart.hiddenchart.refreshCount++;
+               })
+               .always(function() {
+                       if(typeof doNext == "function") doNext();
                });
        }
 
        function mainchartControlReadyEvent() {
-               google.visualization.events.addListener(mainchart.control, 'statechange', mainchartControlStateHandler);
+               google.visualization.events.addListener(mainchart.control_wrapper, 'statechange', mainchartControlStateHandler);
+               //mylog(mainchart);
        }
 
        function mainchartControlStateHandler() {
                // setMainChartPlay('pause');
 
-               var state = mainchart.control.getState();
+               var state = mainchart.control_wrapper.getState();
                mainchart.after = Math.round(state.range.start.getTime() / 1000);
                mainchart.before = Math.round(state.range.end.getTime() / 1000);
 
                calculateChartGroup(mainchart, MAINCHART_POINTS_DIVISOR, 0, 0);
-               console.log('group = ' + mainchart.group + ', points_to_show = ' + mainchart.points_to_show + ', dt = ' + (mainchart.before - mainchart.after));
+               //mylog('group = ' + mainchart.group + ', points_to_show = ' + mainchart.points_to_show + ', dt = ' + (mainchart.before - mainchart.after));
 
                $('#group' + mainchart.group).trigger('click');
                mainchart.last_updated = 0;
 
-               if(refresh_mode != REFRESH_ZOOMED) zoomGraphs();
+               if(refresh_mode != REFRESH_PAUSED) pauseGraphs();
        }
 
        function initMainChartIndex(i) {
 
                if(!norefresh) {
                        mainchart.last_updated = 0;
-                       if(refresh_mode == REFRESH_PAUSED) playGraphs();
                }
        }
 
 
                if(!norefresh) {
                        mainchart.last_updated = 0;
-                       if(refresh_mode == REFRESH_PAUSED) playGraphs();
                }
 
                last_main_chart_avg = g;
 
                if(mainchart) {
                        mainchart.chartOptions.width = width;
-                       mainchart.chartOptions.height = $(window).height() - 200;
+                       mainchart.chartOptions.height = $(window).height() - 150 - MAINCHART_CONTROL_HEIGHT;
                        mainchart.last_updated = 0;
+
+                       mainchart.hidden_wrapper.setOption('width', width);
+                       mainchart.control_wrapper.setOption('ui.chartOptions.width', width);
+                       mainchart.hiddenchart.chartOptions.width = width;
+                       mainchart.hiddenchart.last_updated = 0;
                }
 
                width = thumbWidth();
                        if(c.enabled && c.chartOptions.width != width) {
                                cleanThisChart(c);
                                c.chartOptions.width = width;
-                               showChartIsLoading(c.div, c.chartOptions.width, c.chartOptions.height);
+                               showChartIsLoading(c.div, c.name, c.chartOptions.width, c.chartOptions.height);
                                c.last_updated = 0;
                        }
                });
                                cleanThisChart(c);
                                c.chartOptions.width = sizes.width;
                                c.chartOptions.height = sizes.height;
-                               showChartIsLoading(c.div, c.chartOptions.width, c.chartOptions.height);
+                               showChartIsLoading(c.div, c.name, c.chartOptions.width, c.chartOptions.height);
                                c.last_updated = 0;
                        }
                });
 
        var REFRESH_PAUSED = 0;
        var REFRESH_ALWAYS = 1;
-       var REFRESH_ZOOMED = 2;
 
        var refresh_mode = REFRESH_PAUSED;
        var last_refresh = 0;
        function playGraphs() {
-               if(refresh_mode != REFRESH_PAUSED) {
-                       // check if the thread died due to a javascript error
-                       var now = new Date().getTime();
-                       if((now - last_refresh) > 5000) {
-                               // it died!
-                               //mylog('It seems the refresh thread died. Restarting it.');
-                               chartsRefresh();
-                       }
-                       return;
-               }
+               if(refresh_mode == REFRESH_ALWAYS) return;
 
+               //mylog('PlayGraphs()');
                refresh_mode = REFRESH_ALWAYS;
                $('.mainchart_play_button').button('play');
-               chartsRefresh();
+
+               // check if the thread died due to a javascript error
+               var now = new Date().getTime();
+               if((now - last_refresh) > 5000) {
+                       // it died or never started
+                       //mylog('It seems the refresh thread died. Restarting it.');
+                       chartsRefresh();
+               }
        }
 
        function pauseGraphs() {
                if(refresh_mode == REFRESH_PAUSED) return;
 
+               //mylog('PauseGraphs()');
                refresh_mode = REFRESH_PAUSED;
                $('.mainchart_play_button').button('pause');
        }
 
-       function zoomGraphs() {
-               if(refresh_mode == REFRESH_ZOOMED) return;
-
-               refresh_mode = REFRESH_ZOOMED;
-               $('.mainchart_play_button').button('pause');
-       }
-
        // refresh the proper chart
        // this is an internal function.
        // never call it directly, or new javascript threads will be spawn
        var timeout;
-       var refresh_count = 0;
        function chartsRefresh() {
-               refresh_count++;
-
                if(resize_request) {
                        resizeCharts();
                        resize_request = false;
-                       refresh_mode = REFRESH_ALWAYS;
+                       // refresh_mode = REFRESH_ALWAYS;
                }
 
+               last_refresh = new Date().getTime();
+
                if(refresh_mode == REFRESH_PAUSED) {
-                       //mylog("stop refresh");
+                       if(mode == MODE_MAIN && mainchart.last_updated == 0)
+                               mainChartRefresh();
+                       else
+                               timeout = setTimeout(chartsRefresh, 500);
+
                        return;
                }
 
-               last_refresh = new Date().getTime();
-
                     if(mode == MODE_THUMBS)            thumbChartsRefresh();
                else if(mode == MODE_GROUP_THUMBS)  groupChartsRefresh();
                else if(mode == MODE_MAIN)              mainChartRefresh();
        // this is an internal function.
        // never call it directly, or new javascript threads will be spawn
        function triggerRefresh() {
-               //mylog('triggerRefresh(' + refresh_count + ')');
+               //mylog('triggerRefresh()');
 
                // cleanup has to take place when the charts are not refreshed
                // since the refreshing thread is in this function, it means
                        return;
                }
 
-               if(!refreshChart(mainchart, triggerRefresh))
-                       triggerRefresh();
+               if(!refreshChart(mainchart, hiddenChartRefresh))
+                       hiddenChartRefresh();
+       }
+
+       function hiddenChartRefresh() {
+               refreshHiddenChart(triggerRefresh);
        }
 
        function roundRobinRefresh(charts, startat) {
        function cleanThisChart(chart, emptydivs) {
                //mylog('cleanThisChart(' + chart.name + ', ' + emptydivs +')');
 
+               if(chart.dashboard) {
+                       chart.dashboard.clear();
+                       chart.dashboard = null;
+
+                       if(chart.control_wrapper) {
+                               chart.control_wrapper.clear();
+                               chart.control_wrapper = null;
+                       }
+
+                       if(chart.hidden_wrapper) {
+                               chart.hidden_wrapper.clear();
+                               chart.hidden_wrapper = null;
+                       }
+
+                       chart.control_data = null;
+               }
+
                if(chart.chart) chart.chart.clearChart();
                chart.chart = null;
 
                        c.chartOptions.width = sizes.width;
                        c.chartOptions.height = sizes.height;
 
-                       groupbody += "<div class=\"thumbgraph\" id=\"" + c.div + "_parent\"><table><tr><td><div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.chartOptions.width, c.chartOptions.height) + "</div></td></tr><tr><td align=\"center\">" + thumbChartActions(i, c, 'nogroup') + "</td></tr></table></div>";
+                       groupbody += "<div class=\"thumbgraph\" id=\"" + c.div + "_parent\"><table><tr><td><div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.name, c.chartOptions.width, c.chartOptions.height) + "</div></td></tr><tr><td align=\"center\">" + thumbChartActions(i, c, 'nogroup') + "</td></tr></table></div>";
                });
                groupbody += "";
 
 
                                if(c.enabled) {
                                        var j;
-                                       var h = "<div class=\"thumbgraph\" id=\"" + c.div + "_parent\"><table><tr><td><div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.chartOptions.width, c.chartOptions.height) + "</div></td></tr><tr><td align=\"center\">"
+                                       var h = "<div class=\"thumbgraph\" id=\"" + c.div + "_parent\"><table><tr><td><div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.name, c.chartOptions.width, c.chartOptions.height) + "</div></td></tr><tr><td align=\"center\">"
                                        + thumbChartActions(i, c)
                                        +       "</td></tr></table></div>";