]> arthur.barton.de Git - netdata.git/blob - web/old/index.js
Merge pull request #2022 from l2isbad/dns_query_time_fixes
[netdata.git] / web / old / index.js
1 var page_is_visible = 1;
2
3 var TARGET_THUMB_GRAPH_WIDTH = 500;             // thumb charts width will range from 0.5 to 1.5 of that
4 var MINIMUM_THUMB_GRAPH_WIDTH = 400;    // thumb chart will generally try to be wider than that
5 var TARGET_THUMB_GRAPH_HEIGHT = 160;    // the height of the thumb charts
6 var TARGET_GROUP_GRAPH_HEIGHT = 160;
7
8 var THUMBS_MAX_TIME_TO_SHOW = 240;              // how much time the thumb charts will present?
9 var THUMBS_POINTS_DIVISOR = 3;
10 var THUMBS_STACKED_POINTS_DIVISOR = 4;
11
12 var GROUPS_MAX_TIME_TO_SHOW = 600;              // how much time the group charts will present?
13 var GROUPS_POINTS_DIVISOR = 2;
14 var GROUPS_STACKED_POINTS_DIVISOR = 3;
15
16 var MAINCHART_MIN_TIME_TO_SHOW = 1200;  // how much time the main chart will present by default?
17 var MAINCHART_POINTS_DIVISOR = 2;               // how much detailed will the main chart be by default? 1 = finest, higher is faster
18 var MAINCHART_STACKED_POINTS_DIVISOR = 3;               // how much detailed will the main chart be by default? 1 = finest, higher is faster
19
20 var MAINCHART_CONTROL_HEIGHT = 75;              // how tall the control chart will be
21 var MAINCHART_CONTROL_DIVISOR = 5;              // how much detailed will the control chart be? 1 = finest, higher is faster
22 var MAINCHART_INITIAL_SELECTOR= 20;             // 1/20th of the width, this overrides MAINCHART_MIN_TIME_TO_SHOW
23
24 var CHARTS_REFRESH_LOOP = 50;                   // delay between chart refreshes
25 var CHARTS_REFRESH_IDLE = 500;                  // delay between chart refreshes when no chart was ready for refresh the last time
26 var CHARTS_CHECK_NO_FOCUS = 500;                // delay to check for visibility when the page has no focus
27 var CHARTS_SCROLL_IDLE = 100;                   // delay to wait after a page scroll
28
29 var ENABLE_CURVE = 1;
30
31 var resize_request = false;
32
33 function setPresentationNormal(ui) {
34         THUMBS_POINTS_DIVISOR                           = 3;
35         THUMBS_STACKED_POINTS_DIVISOR           = Math.round(THUMBS_POINTS_DIVISOR * 1.5);
36         GROUPS_POINTS_DIVISOR                           = 2;
37         GROUPS_STACKED_POINTS_DIVISOR           = Math.round(GROUPS_POINTS_DIVISOR * 1.5);
38         MAINCHART_POINTS_DIVISOR                        = 2;
39         MAINCHART_STACKED_POINTS_DIVISOR        = Math.round(MAINCHART_POINTS_DIVISOR * 1.5);
40         ENABLE_CURVE = 1;
41         CHARTS_REFRESH_LOOP = 50;
42         CHARTS_SCROLL_IDLE = 50;
43         resize_request = true;
44         if(ui) $('#presentation_normal').trigger('click');
45         playGraphs();
46 }
47 function setPresentationSpeedy(ui) {
48         THUMBS_POINTS_DIVISOR                           = 10;
49         THUMBS_STACKED_POINTS_DIVISOR           = Math.round(THUMBS_POINTS_DIVISOR * 1.5);
50         GROUPS_POINTS_DIVISOR                           = 8;
51         GROUPS_STACKED_POINTS_DIVISOR           = Math.round(GROUPS_POINTS_DIVISOR * 1.5);
52         MAINCHART_POINTS_DIVISOR                        = 5;
53         MAINCHART_STACKED_POINTS_DIVISOR        = Math.round(MAINCHART_POINTS_DIVISOR * 1.5);
54         ENABLE_CURVE = 0;
55         CHARTS_REFRESH_LOOP = 50;
56         CHARTS_SCROLL_IDLE = 100;
57         resize_request = true;
58         if(ui) $('#presentation_speedy').trigger('click');
59         playGraphs();
60 }
61 function setPresentationDetailed(ui) {
62         THUMBS_POINTS_DIVISOR                           = 1;
63         THUMBS_STACKED_POINTS_DIVISOR           = 1;
64         GROUPS_POINTS_DIVISOR                           = 1;
65         GROUPS_STACKED_POINTS_DIVISOR           = 1;
66         MAINCHART_POINTS_DIVISOR                        = 1;
67         MAINCHART_STACKED_POINTS_DIVISOR        = 1;
68         ENABLE_CURVE = 1;
69         CHARTS_REFRESH_LOOP = 50;
70         CHARTS_SCROLL_IDLE = 50;
71         resize_request = true;
72         if(ui) $('#presentation_detailed').trigger('click');
73         playGraphs();
74 }
75
76 function isIE() {
77   userAgent = navigator.userAgent;
78   return userAgent.indexOf("MSIE ") > -1 || userAgent.indexOf("Trident/") > -1;
79 }
80
81 if(isIE()){
82         // do stuff with ie-users
83         CHARTS_REFRESH_LOOP=250;
84         CHARTS_SCROLL_IDLE=500;
85 }
86
87 var MODE_THUMBS = 1;
88 var MODE_MAIN = 2;
89 var MODE_GROUP_THUMBS = 3;
90 var mode; // one of the MODE_* values
91
92 var allCharts = new Array();
93 var mainchart;
94
95 // html for the main menu
96 var mainmenu = "";
97 var categoriesmainmenu = "";
98 var familiesmainmenu = "";
99 var chartsmainmenu = "";
100
101
102 // ------------------------------------------------------------------------
103 // common HTML generation
104
105 function thumbChartActions(i, c, nogroup) {
106         var name = c.name;
107         if(!nogroup) name = c.family;
108
109         var refinfo = "the chart is drawing ";
110         if(c.group == 1) refinfo += "every single point collected (" + c.update_every + "s each).";
111         else refinfo += ((c.group_method == "average")?"the average":"the max") + " value for every " + (c.group * c.update_every) + " seconds of data";
112
113         var html = "<div class=\"btn-group btn-group\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + refinfo + "\">"
114         +               "<button type=\"button\" class=\"btn btn-default\" onclick=\"javascript: return;\"><span class=\"glyphicon glyphicon-info-sign\"></span></button>"
115         +       "</div>"
116         +       "<div class=\"btn-group btn-group\"><button type=\"button\" class=\"btn btn-default disabled\"><small>&nbsp;&nbsp; " + name + "</small></button>";
117
118         if(!nogroup) {
119                 var ingroup = 0;
120                 var ingroup_detail = 0;
121
122                 $.each(allCharts, function(i, d) {
123                         if(d.family == c.family) {
124                                 ingroup++;
125                                 if(d.isdetail) ingroup_detail++;
126                         }
127                 });
128
129                 var hidden = "";
130                 if(ingroup_detail) hidden = ", including " + ingroup_detail + " charts not shown now";
131
132                 html += "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"Show all " + ingroup + " charts in group '" + c.family + "'" + hidden + "\" class=\"btn btn-default\" onclick=\"initGroupGraphs('" + c.family +"');\"><span class=\"glyphicon glyphicon-th-large\"></span></button>";
133         }
134
135         html += "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"show chart '" + c.name + "' in fullscreen\" class=\"btn btn-default\" onclick=\"initMainChartIndex(" + i +");\"><span class=\"glyphicon glyphicon-resize-full\"></span></button>"
136         +               "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"set options for chart '" + c.name + "'\" class=\"btn btn-default disabled\" onclick=\"alert('Not implemented yet!');\"><span class=\"glyphicon glyphicon-cog\"></span></button>"
137         +               "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"ignore chart '" + c.name + "'\" class=\"btn btn-default\" onclick=\"disableChart(" + i + ");\"><span class=\"glyphicon glyphicon-trash\"></span></button>"
138         +       "</div>";
139
140         return html;
141 }
142
143 function groupChartActions(i, c) {
144         var name = c.name;
145
146         var refinfo = "the chart is drawing ";
147         if(c.group == 1) refinfo += "every single point collected (" + c.update_every + "s each).";
148         else refinfo += ((c.group_method == "average")?"the average":"the max") + " value for every " + (c.group * c.update_every) + " seconds of data";
149
150         var html = "<div class=\"btn-group btn-group\" data-toggle=\"tooltip\" data-placement=\"left\" title=\"" + refinfo + "\">"
151         +               "<button type=\"button\" class=\"btn btn-default\" onclick=\"javascript: return;\"><span class=\"glyphicon glyphicon-info-sign\"></span></button>"
152         +       "</div>";
153
154         html += "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"left\" title=\"show chart '" + c.name + "' in fullscreen\" class=\"btn btn-default\" onclick=\"initMainChartIndex(" + i +");\"><span class=\"glyphicon glyphicon-resize-full\"></span></button>"
155         +               "<button type=\"button\" data-toggle=\"tooltip\" data-placement=\"left\" title=\"ignore chart '" + c.name + "'\" class=\"btn btn-default\" onclick=\"disableChart(" + i + ");\"><span class=\"glyphicon glyphicon-trash\"></span></button>"
156         +       "</div>";
157
158         return html;
159 }
160
161 function mylog(txt) {
162         console.log(txt);
163         $('#logline').html(txt);
164 }
165
166 function chartssort(a, b) {
167         if(a.priority == b.priority) {
168                 if(a.name < b.name) return -1;
169         }
170         else if(a.priority < b.priority) return -1;
171
172         return 1;
173 }
174
175
176 // ------------------------------------------------------------------------
177 // MAINGRAPH = fullscreen view of 1 graph
178
179 // copy the chart c to mainchart
180 // switch to main graphs screen
181 function initMainChart(c) {
182         if(mainchart) cleanThisChart(mainchart);
183
184         mainchart = $.extend(true, {}, c);
185         mainchart.enabled = true;
186         mainchart.refreshCount = 0;
187         mainchart.last_updated = 0;
188         mainchart.chartOptions.explorer = null;
189         mainchart.chart = null;
190
191         mainchart.before = 0;
192         mainchart.after = 0;
193
194         mainchart.chartOptions.width = screenWidth();
195         mainchart.chartOptions.height = $(window).height() - 150 - MAINCHART_CONTROL_HEIGHT;
196         if(mainchart.chartOptions.height < 300) mainchart.chartOptions.height = 300;
197
198         mainchart.div = 'maingraph';
199         mainchart.max_time_to_show = (mainchart.last_entry_t - mainchart.first_entry_t) / ( MAINCHART_INITIAL_SELECTOR * mainchart.update_every );
200         if(mainchart.max_time_to_show < MAINCHART_MIN_TIME_TO_SHOW) mainchart.max_time_to_show = MAINCHART_MIN_TIME_TO_SHOW;
201         calculateChartPointsToShow(mainchart, mainchart.chartOptions.isStacked?MAINCHART_STACKED_POINTS_DIVISOR:MAINCHART_POINTS_DIVISOR, mainchart.max_time_to_show, 0, ENABLE_CURVE);
202
203         // copy it to the hidden chart
204         mainchart.hiddenchart = $.extend(true, {}, mainchart);
205         mainchart.hiddenchart.chartOptions.height = MAINCHART_CONTROL_HEIGHT;
206         mainchart.hiddenchart.div = 'maingraph_control';
207         mainchart.hiddenchart.non_zero = 0;
208
209         // initialize the div
210         showChartIsLoading(mainchart.div, mainchart.name, mainchart.chartOptions.width, mainchart.chartOptions.height);
211         document.getElementById(mainchart.hiddenchart.div).innerHTML = "<table><tr><td align=\"center\" width=\"" + mainchart.hiddenchart.chartOptions.width + "\" height=\"" + mainchart.hiddenchart.chartOptions.height + "\" style=\"vertical-align:middle\"><h4><span class=\"label label-default\">Please wait...</span></h4></td></tr></table>";
212         //showChartIsLoading(mainchart.hiddenchart.div, mainchart.hiddenchart.name, mainchart.hiddenchart.chartOptions.width, mainchart.hiddenchart.chartOptions.height);
213
214         // set the radio buttons
215         setMainChartGroupMethod(mainchart.group_method, 'no-refresh');
216         setMainChartMax('normal');
217
218         $('#group' + mainchart.group).trigger('click');
219         setMainChartGroup(mainchart.group, 'no-refresh');
220
221         switchToMainGraph();
222 }
223
224 function refreshHiddenChart(doNext) {
225         if(refresh_mode == REFRESH_PAUSED && mainchart.hiddenchart.last_updated != 0) {
226                 if(typeof doNext == "function") doNext();
227                 return;
228         }
229
230         // is it too soon for a refresh?
231         var now = Date.now();
232         if((now - mainchart.hiddenchart.last_updated) < (mainchart.update_every * 10 * 1000) || (now - mainchart.hiddenchart.last_updated) < (mainchart.hiddenchart.group * mainchart.hiddenchart.update_every * 1000)) {
233                 if(typeof doNext == "function") doNext();
234                 return;
235         }
236
237         if(mainchart.dashboard && mainchart.hiddenchart.refreshCount > 50) {
238                 mainchart.dashboard.clear();
239                 mainchart.control_wrapper.clear();
240                 mainchart.hidden_wrapper.clear();
241
242                 mainchart.dashboard = null;
243                 mainchart.control_wrapper = null;
244                 mainchart.hidden_wrapper = null;
245                 mainchart.hiddenchart.last_updated = 0;
246         }
247
248         if(!mainchart.dashboard) {
249                 var controlopts = $.extend(true, {}, mainchart.chartOptions, {
250                         lineWidth: 1,
251                         height: mainchart.hiddenchart.chartOptions.height,
252                         chartArea: {'width': '98%'},
253                         hAxis: {'baselineColor': 'none', viewWindowMode: 'maximized', gridlines: { count: 0 } },
254                         vAxis: {'title': null, gridlines: { count: 0 } },
255                 });
256
257                 mainchart.dashboard = new google.visualization.Dashboard(document.getElementById('maingraph_dashboard'));
258                 mainchart.control_wrapper = new google.visualization.ControlWrapper({
259                         controlType: 'ChartRangeFilter',
260                         containerId: 'maingraph_control',
261                         options: {
262                                 filterColumnIndex: 0,
263                                 ui: {
264                                         chartType: mainchart.chartType,
265                                         chartOptions: controlopts,
266                                         minRangeSize: (mainchart.max_time_to_show * 1000) / MAINCHART_POINTS_DIVISOR,
267                                 }
268                         },
269                 });
270                 mainchart.hidden_wrapper = new google.visualization.ChartWrapper({
271                         chartType: mainchart.chartType,
272                         containerId: 'maingraph_hidden',
273                         options: {
274                                 isStacked: mainchart.chartOptions.isStacked,
275                                 width: mainchart.hiddenchart.chartOptions.width,
276                                 height: mainchart.hiddenchart.chartOptions.height,
277                                 //chartArea: {'height': '80%', 'width': '100%'},
278                                 //hAxis: {'slantedText': false},
279                                 //legend: {'position': 'none'}
280                         },
281                 });
282
283                 mainchart.hiddenchart.refreshCount = 0;
284         }
285
286         // load the data for the control and the hidden wrappers
287         // calculate the group and points to show for the control chart
288         calculateChartPointsToShow(mainchart.hiddenchart, MAINCHART_CONTROL_DIVISOR, 0, -1, ENABLE_CURVE);
289
290         $.ajax({
291                 url: generateChartURL(mainchart.hiddenchart),
292                 dataType:"json",
293                 cache: false
294         })
295         .done(function(jsondata) {
296                 if(!jsondata || jsondata.length == 0) return;
297
298                 mainchart.control_data = new google.visualization.DataTable(jsondata);
299
300                 if(mainchart.hiddenchart.last_updated == 0) {
301                         google.visualization.events.addListener(mainchart.control_wrapper, 'ready', mainchartControlReadyEvent);
302                         mainchart.dashboard.bind(mainchart.control_wrapper, mainchart.hidden_wrapper);
303                 }
304                 if(refresh_mode != REFRESH_PAUSED) {
305                         // console.log('mainchart.points_to_show: ' + mainchart.points_to_show + ', mainchart.group: ' + mainchart.group + ', mainchart.update_every: ' + mainchart.update_every);
306
307                         var start = now - (mainchart.points_to_show * mainchart.group * mainchart.update_every * 1000);
308                         var end = now;
309                         var min = MAINCHART_MIN_TIME_TO_SHOW * 1000;
310                         if(end - start < min) start = end - min;
311
312                         mainchart.control_wrapper.setState({range: {
313                                 start: new Date(start),
314                                 end: new Date(end)
315                         },
316                         ui: {
317                                 minRangeSize: min
318                         }});
319                 }
320
321                 mainchart.dashboard.draw(mainchart.control_data);
322                 mainchart.hiddenchart.last_updated = Date.now();
323                 mainchart.hiddenchart.refreshCount++;
324         })
325         .always(function() {
326                 if(typeof doNext == "function") doNext();
327         });
328 }
329
330 function mainchartControlReadyEvent() {
331         google.visualization.events.addListener(mainchart.control_wrapper, 'statechange', mainchartControlStateHandler);
332         //mylog(mainchart);
333 }
334
335 function mainchartControlStateHandler() {
336         // setMainChartPlay('pause');
337
338         var state = mainchart.control_wrapper.getState();
339         mainchart.after = Math.round(state.range.start.getTime() / 1000);
340         mainchart.before = Math.round(state.range.end.getTime() / 1000);
341
342         calculateChartPointsToShow(mainchart, mainchart.chartOptions.isStacked?MAINCHART_STACKED_POINTS_DIVISOR:MAINCHART_POINTS_DIVISOR, mainchart.before - mainchart.after, 0, ENABLE_CURVE);
343         //mylog('group = ' + mainchart.group + ', points_to_show = ' + mainchart.points_to_show + ', dt = ' + (mainchart.before - mainchart.after));
344
345         $('#group' + mainchart.group).trigger('click');
346         mainchart.last_updated = 0;
347
348         if(refresh_mode != REFRESH_PAUSED) pauseGraphs();
349 }
350
351 function initMainChartIndex(i) {
352         if(mode == MODE_GROUP_THUMBS)
353                 initMainChart(groupCharts[i]);
354
355         else if(mode == MODE_THUMBS)
356                 initMainChart(allCharts[i]);
357
358         else
359                 initMainChart(allCharts[i]);
360 }
361
362 function initMainChartIndexOfMyCharts(i) {
363         initMainChart(allCharts[i]);
364 }
365
366 var last_main_chart_max='normal';
367 function setMainChartMax(m) {
368         if(!mainchart) return;
369
370         if(m == 'toggle') {
371                 if(last_main_chart_max == 'maximized') m = 'normal';
372                 else m = 'maximized';
373         }
374
375         if(m == "maximized") {
376                 mainchart.chartOptions.theme = 'maximized';
377                 //mainchart.chartOptions.axisTitlesPosition = 'in';
378                 //mainchart.chartOptions.legend = {position: 'none'};
379                 mainchart.chartOptions.hAxis.title = null;
380                 mainchart.chartOptions.hAxis.viewWindowMode = 'maximized';
381                 mainchart.chartOptions.vAxis.viewWindowMode = 'maximized';
382                 mainchart.chartOptions.chartArea = {'width': '98%', 'height': '100%'};
383         }
384         else {
385                 mainchart.chartOptions.hAxis.title = null;
386                 mainchart.chartOptions.theme = null;
387                 mainchart.chartOptions.hAxis.viewWindowMode = null;
388                 mainchart.chartOptions.vAxis.viewWindowMode = null;
389                 mainchart.chartOptions.chartArea = {'width': '80%', 'height': '90%'};
390         }
391         $('.mainchart_max_button').button(m);
392         last_main_chart_max = m;
393         mainchart.last_updated = 0;
394 }
395
396 function setMainChartGroup(g, norefresh) {
397         if(!mainchart) return;
398
399         mainchart.group = g;
400
401         if(!mainchart.before && !mainchart.after)
402                 calculateChartPointsToShow(mainchart, mainchart.chartOptions.isStacked?MAINCHART_STACKED_POINTS_DIVISOR:MAINCHART_POINTS_DIVISOR, mainchart.max_time_to_show, mainchart.group, ENABLE_CURVE);
403         else
404                 calculateChartPointsToShow(mainchart, mainchart.chartOptions.isStacked?MAINCHART_STACKED_POINTS_DIVISOR:MAINCHART_POINTS_DIVISOR, 0, mainchart.group, ENABLE_CURVE);
405
406         if(!norefresh) {
407                 mainchart.last_updated = 0;
408         }
409 }
410
411 var last_main_chart_avg = null;
412 function setMainChartGroupMethod(g, norefresh) {
413         if(!mainchart) return;
414
415         if(g == 'toggle') {
416                 if(last_main_chart_avg == 'max') g = 'average';
417                 else g = 'max';
418         }
419
420         mainchart.group_method = g;
421
422         $('.mainchart_avg_button').button(g);
423
424         if(!norefresh) {
425                 mainchart.last_updated = 0;
426         }
427
428         last_main_chart_avg = g;
429 }
430
431 function setMainChartPlay(p) {
432         if(!mainchart) return;
433
434         if(p == 'toggle') {
435                 if(refresh_mode != REFRESH_ALWAYS) p = 'play';
436                 else p = 'pause';
437         }
438
439         if(p == 'play') {
440                 //mainchart.chartOptions.explorer = null;
441                 mainchart.after = 0;
442                 mainchart.before = 0;
443                 calculateChartPointsToShow(mainchart, mainchart.chartOptions.isStacked?MAINCHART_STACKED_POINTS_DIVISOR:MAINCHART_POINTS_DIVISOR, mainchart.max_time_to_show, 0, ENABLE_CURVE);
444                 $('#group' + mainchart.group).trigger('click');
445                 mainchart.last_updated = 0;
446                 mainchart.hiddenchart.last_updated = 0;
447                 playGraphs();
448         }
449         else {
450                 //mainchart.chartOptions.explorer = {
451                 //      'axis': 'horizontal',
452                 //      'maxZoomOut': 1,
453                 //};
454                 //mainchart.last_updated = 0;
455
456                 //if(!renderChart(mainchart, pauseGraphs))
457                 pauseGraphs();
458         }
459 }
460
461 function buttonGlobalPlayPause(p) {
462         if(mode == MODE_MAIN) {
463                 setMainChartPlay(p);
464                 return;
465         }
466
467         if(p == 'toggle') {
468                 if(refresh_mode != REFRESH_ALWAYS) p = 'play';
469                 else p = 'pause';
470         }
471
472         if(p == 'play') playGraphs();
473         else pauseGraphs();
474 }
475
476
477 // ------------------------------------------------------------------------
478 // Chart resizing
479
480 function screenWidth() {
481         return (($(window).width() * 0.95) - 50);
482 }
483
484 // calculate the proper width for the thumb charts
485 function thumbWidth() {
486         var cwidth = screenWidth();
487         var items = Math.round(cwidth / TARGET_THUMB_GRAPH_WIDTH);
488         if(items < 1) items = 1;
489
490         if(items > 1 && (cwidth / items) < MINIMUM_THUMB_GRAPH_WIDTH) items--;
491
492         return Math.round(cwidth / items) - 1;
493 }
494
495 function groupChartSizes() {
496         var s = { width: screenWidth(), height: TARGET_GROUP_GRAPH_HEIGHT };
497
498         var count = 0;
499         if(groupCharts) $.each(groupCharts, function(i, c) {
500                 if(c.enabled) count++;
501         });
502
503         if(count == 0) {
504                 s.width = TARGET_GROUP_GRAPH_HEIGHT;
505                 s.height = TARGET_GROUP_GRAPH_HEIGHT;
506         }
507         else {
508                 if(s.width < MINIMUM_THUMB_GRAPH_WIDTH) s.width = screenWidth();
509                 s.height = ($(window).height() - 130) / count - 10;
510         }
511
512         if(s.height < TARGET_GROUP_GRAPH_HEIGHT)
513                 s.height = TARGET_GROUP_GRAPH_HEIGHT;
514
515         return s;
516 }
517
518 // resize all charts
519 // if the thumb charts need resize in their width, reset them
520 function resizeCharts() {
521         var width = screenWidth();
522
523         if(mainchart) {
524                 mainchart.chartOptions.width = width;
525                 mainchart.chartOptions.height = $(window).height() - 150 - MAINCHART_CONTROL_HEIGHT;
526                 mainchart.last_updated = 0;
527
528                 mainchart.hidden_wrapper.setOption('width', width);
529                 mainchart.control_wrapper.setOption('ui.chartOptions.width', width);
530                 mainchart.hiddenchart.chartOptions.width = width;
531                 mainchart.hiddenchart.last_updated = 0;
532         }
533
534         width = thumbWidth();
535         $.each(allCharts, function(i, c) {
536                 if(c.enabled) {
537                         cleanThisChart(c);
538                         c.chartOptions.width = width;
539                         calculateChartPointsToShow(c, c.chartOptions.isStacked?THUMBS_STACKED_POINTS_DIVISOR:THUMBS_POINTS_DIVISOR, THUMBS_MAX_TIME_TO_SHOW, -1, ENABLE_CURVE);
540                         showChartIsLoading(c.div, c.name, c.chartOptions.width, c.chartOptions.height);
541                         document.getElementById(c.id + '_thumb_actions_div').innerHTML = thumbChartActions(i, c);
542                         c.last_updated = 0;
543                 }
544         });
545
546         if(groupCharts) $.each(groupCharts, function(i, c) {
547                 var sizes = groupChartSizes();
548
549                 if(c.enabled) {
550                         cleanThisChart(c);
551                         c.chartOptions.width = sizes.width;
552                         c.chartOptions.height = sizes.height;
553                         calculateChartPointsToShow(c, c.chartOptions.isStacked?GROUPS_STACKED_POINTS_DIVISOR:GROUPS_POINTS_DIVISOR, GROUPS_MAX_TIME_TO_SHOW, -1, ENABLE_CURVE);
554                         showChartIsLoading(c.div, c.name, c.chartOptions.width, c.chartOptions.height);
555                         document.getElementById(c.id + '_group_actions_div').innerHTML = groupChartActions(i, c);
556                         c.last_updated = 0;
557                 }
558         });
559
560         updateUI();
561 }
562
563 window.onresize = function(event) {
564         resize_request = true;
565 };
566
567
568 // ------------------------------------------------------------------------
569 // Core of the thread refreshing the charts
570
571 var REFRESH_PAUSED = 0;
572 var REFRESH_ALWAYS = 1;
573
574 var refresh_mode = REFRESH_PAUSED;
575 var last_refresh = 0;
576 function playGraphs() {
577         mylog('playGraphs()');
578         if(refresh_mode == REFRESH_ALWAYS) return;
579
580         //mylog('PlayGraphs()');
581         refresh_mode = REFRESH_ALWAYS;
582         $('.mainchart_play_button').button('play');
583         $('.global_play_button').button('play');
584
585         // check if the thread died due to a javascript error
586         var now = Date.now();
587         if((now - last_refresh) > 60000) {
588                 // it died or never started
589                 //mylog('It seems the refresh thread died. Restarting it.');
590                 renderChartCallback();
591         }
592 }
593
594 function pauseGraphs() {
595         mylog('pauseGraphs()');
596         if(refresh_mode == REFRESH_PAUSED) return;
597
598         refresh_mode = REFRESH_PAUSED;
599         $('.mainchart_play_button').button('pause');
600         $('.global_play_button').button('pause');
601 }
602
603 var interval = null;
604 function checkRefreshThread() {
605         if(interval == null) {
606                 interval = setInterval(checkRefreshThread, 2000);
607                 return;
608         }
609
610         var now = Date.now();
611         if(now - last_refresh > 60000) {
612                 mylog('Refresh thread died. Restarting it.');
613                 renderChartCallback();
614         }
615 }
616
617 // refresh the proper chart
618 // this is an internal function.
619 // never call it directly, or new javascript threads will be spawn
620 var timeout = null;
621 function renderChartCallback() {
622         last_refresh = Date.now();
623
624         if(!page_is_visible) {
625                 timeout = setTimeout(triggerRefresh, CHARTS_CHECK_NO_FOCUS);
626                 return;
627         }
628
629         if(resize_request) {
630                 mylog('renderChartCallback() resize_request is set');
631                 cleanupCharts();
632                 resizeCharts();
633                 resize_request = false;
634                 // refresh_mode = REFRESH_ALWAYS;
635         }
636
637         if(last_user_scroll) {
638                 var now = Date.now();
639                 if((now - last_user_scroll) >= CHARTS_SCROLL_IDLE) {
640                         last_user_scroll = 0;
641                         mylog('Scrolling: resuming refresh...');
642                 }
643                 else {
644                         mylog('Scrolling: pausing refresh for ' + (CHARTS_SCROLL_IDLE - (now - last_user_scroll)) + ' ms...');
645                         timeout = setTimeout(triggerRefresh, CHARTS_SCROLL_IDLE - (now - last_user_scroll));
646                         return;
647                 }
648         }
649
650         if(refresh_mode == REFRESH_PAUSED) {
651                 if(mode == MODE_MAIN && mainchart.last_updated == 0) {
652                         mainChartRefresh();
653                         return;
654                 }
655
656                 if(mode != MODE_MAIN) {
657                         timeout = setTimeout(triggerRefresh, CHARTS_REFRESH_IDLE);
658                         return;
659                 }
660         }
661
662              if(mode == MODE_THUMBS)            timeout = setTimeout(thumbChartsRefreshNext, CHARTS_REFRESH_LOOP);
663         else if(mode == MODE_GROUP_THUMBS)  timeout = setTimeout(groupChartsRefreshNext, CHARTS_REFRESH_LOOP);
664         else if(mode == MODE_MAIN)              timeout = setTimeout(mainChartRefresh, CHARTS_REFRESH_LOOP);
665         else                                    timeout = setTimeout(triggerRefresh, CHARTS_REFRESH_IDLE);
666 }
667
668 // callback for refreshing the charts later
669 // this is an internal function.
670 // never call it directly, or new javascript threads will be spawn
671 function triggerRefresh() {
672         //mylog('triggerRefresh()');
673
674         if(!page_is_visible || (refresh_mode == REFRESH_PAUSED && mode != MODE_MAIN)) {
675                 last_refresh = Date.now();
676                 timeout = setTimeout(triggerRefresh, CHARTS_REFRESH_IDLE);
677                 return;
678         }
679
680              if(mode == MODE_THUMBS)            timeout = setTimeout(renderChartCallback, CHARTS_REFRESH_IDLE);
681         else if(mode == MODE_GROUP_THUMBS)      timeout = setTimeout(renderChartCallback, CHARTS_REFRESH_IDLE);
682         else if(mode == MODE_MAIN)              timeout = setTimeout(renderChartCallback, CHARTS_REFRESH_IDLE);
683         else                                    timeout = setTimeout(triggerRefresh, CHARTS_REFRESH_IDLE);
684 }
685
686 // refresh the main chart
687 // make sure we don't loose the refreshing thread
688 function mainChartRefresh() {
689         //mylog('mainChartRefresh()');
690
691         if(mode != MODE_MAIN || !mainchart) {
692                 triggerRefresh();
693                 return;
694         }
695
696         if(refresh_mode == REFRESH_PAUSED && mainchart.last_updated != 0) {
697                 hiddenChartRefresh();
698                 return;
699         }
700
701         if(!renderChart(mainchart, hiddenChartRefresh))
702                 hiddenChartRefresh();
703 }
704
705 function hiddenChartRefresh() {
706         refreshHiddenChart(triggerRefresh);
707 }
708
709 function roundRobinRenderChart(charts, startat) {
710         var refreshed = false;
711
712         // find a chart to refresh
713         var all = charts.length;
714         var cur = startat + 1;
715         var count = 0;
716
717         for(count = 0; count < all ; count++, cur++) {
718                 if(cur >= all) cur = 0;
719
720                 if(charts[cur].enabled) {
721                         refreshed = renderChart(charts[cur], renderChartCallback);
722                         if(refreshed) {
723                                 mylog('Refreshed: ' + charts[cur].name);
724                                 break;
725                         }
726                 }
727         }
728
729         if(!refreshed) triggerRefresh();
730         return cur;
731 }
732
733 // refresh the thumb charts
734 // make sure we don't loose the refreshing thread
735 var last_thumb_updated = 0;
736 function thumbChartsRefreshNext() {
737         //mylog('thumbChartsRefreshNext()');
738
739         if(allCharts.length == 0 || mode != MODE_THUMBS) {
740                 triggerRefresh();
741                 return;
742         }
743
744         last_thumb_updated = roundRobinRenderChart(allCharts, last_thumb_updated);
745 }
746
747 // refresh the group charts
748 // make sure we don't loose the refreshing thread
749 var last_group_updated = 0;
750 function groupChartsRefreshNext() {
751         //mylog('groupChartsRefreshNext()');
752
753         if(!groupCharts || groupCharts.length == 0 || mode != MODE_GROUP_THUMBS) {
754                 //mylog('cannot refresh charts');
755                 triggerRefresh();
756                 return;
757         }
758
759         last_group_updated = roundRobinRenderChart(groupCharts, last_group_updated);
760 }
761
762
763 // ------------------------------------------------------------------------
764 // switch the screen between views
765 // these should be called only from initXXXX()
766
767 function disableChart(i) {
768         mylog('disableChart(' + i + ')');
769
770         var chart = null;
771
772         var count = 0;
773         if(mode == MODE_GROUP_THUMBS && groupCharts) {
774                 $.each(groupCharts, function(i, c) {
775                         if(c.enabled) count++;
776                 });
777
778                 if(i < groupCharts.length) chart = groupCharts[i];
779         }
780         else if(mode == MODE_THUMBS) {
781                 $.each(allCharts, function(i, c) {
782                         if(c.enabled) count++;
783                 });
784
785                 if(i < allCharts.length) chart = allCharts[i];
786         }
787
788         if(!chart) return;
789
790         if(count <= 1) {
791                 alert('Cannot close the last chart shown.');
792                 return;
793         }
794
795         if(chart) {
796                 mylog("request to disable chart " + chart.name);
797                 chart.disablethisplease = true;
798                 resize_request = true;
799         }
800         else
801                 mylog("no chart to disable");
802 }
803
804 function cleanThisChart(chart, emptydivs) {
805         //mylog('cleanThisChart(' + chart.name + ', ' + emptydivs +')');
806
807         if(chart.dashboard) {
808                 chart.dashboard.clear();
809                 chart.dashboard = null;
810
811                 if(chart.control_wrapper) {
812                         chart.control_wrapper.clear();
813                         chart.control_wrapper = null;
814                 }
815
816                 if(chart.hidden_wrapper) {
817                         chart.hidden_wrapper.clear();
818                         chart.hidden_wrapper = null;
819                 }
820
821                 chart.control_data = null;
822         }
823
824         if(chart.chart) chart.chart.clearChart();
825         chart.chart = null;
826
827         if(emptydivs) {
828                 var div = document.getElementById(chart.div);
829                 if(div) {
830                         div.style.display = 'none';
831                         div.innerHTML = "";
832                 }
833
834                 div = document.getElementById(chart.div + "_parent");
835                 if(div) {
836                         div.style.display = 'none';
837                         div.innerHTML = "";
838                 }
839         }
840
841         //mylog("chart " + chart.name + " cleaned with option " + emptydivs);
842 }
843
844 // cleanup the previously shown charts
845 function cleanupCharts() {
846         // mylog('cleanupCharts()');
847
848         if(mode != MODE_MAIN && mainchart) {
849                 if(mainchart.chart) cleanThisChart(mainchart);
850                 mainchart = null;
851         }
852
853         if(mode != MODE_GROUP_THUMBS && groupCharts) {
854                 clearGroupGraphs();
855         }
856
857         // cleanup the disabled charts
858         $.each(allCharts, function(i, c) {
859                 if(c.disablethisplease && c.enabled) {
860                         cleanThisChart(c, 'emptydivs');
861                         c.disablethisplease = false;
862                         c.enabled = false;
863                         resize_request = true;
864                         mylog("removed thumb chart " + c.name + " removed");
865                 }
866         });
867
868         if(groupCharts) $.each(groupCharts, function(i, c) {
869                 if(c.disablethisplease && c.enabled) {
870                         cleanThisChart(c, 'emptydivs');
871                         c.disablethisplease = false;
872                         c.enabled = false;
873                         resize_request = true;
874                         mylog("removed group chart " + c.name + " removed");
875                 }
876         });
877
878         // we never cleanup the main chart
879 }
880
881 function updateUI() {
882         $('[data-toggle="tooltip"]').tooltip({'container': 'body', 'html': true});
883
884         $('[data-spy="scroll"]').each(function () {
885                 var $spy = $(this).scrollspy('refresh')
886         })
887 }
888
889 var thumbsScrollPosition = null;
890 function switchToMainGraph() {
891         //mylog('switchToMainGraph()');
892
893         if(!mainchart) return;
894
895         if(!groupCharts) thumbsScrollPosition = window.pageYOffset;
896
897         document.getElementById('maingraph_container').style.display = 'block';
898         document.getElementById('thumbgraphs_container').style.display = 'none';
899         document.getElementById('groupgraphs_container').style.display = 'none';
900         document.getElementById('splash_container').style.display = 'none';
901
902         document.getElementById("main_menu_div").innerHTML = "<ul class=\"nav navbar-nav\"><li><a href=\"javascript:switchToThumbGraphs();\"><span class=\"glyphicon glyphicon-circle-arrow-left\"></span> Back to Dashboard</a></li><li class=\"active\"><a href=\"#\">" + mainchart.name + "</a></li>" + familiesmainmenu + chartsmainmenu + "</ul>" ;
903
904         window.scrollTo(0, 0);
905
906         mode = MODE_MAIN;
907         playGraphs();
908         updateUI();
909 }
910
911 function switchToThumbGraphs() {
912         //mylog('switchToThumbGraphs()');
913
914         document.getElementById('maingraph_container').style.display = 'none';
915         document.getElementById('thumbgraphs_container').style.display = 'block';
916         document.getElementById('groupgraphs_container').style.display = 'none';
917         document.getElementById('splash_container').style.display = 'none';
918
919         document.getElementById("main_menu_div").innerHTML = mainmenu;
920
921         if(thumbsScrollPosition) window.scrollTo(0, thumbsScrollPosition);
922
923         // switch mode
924         mode = MODE_THUMBS;
925         playGraphs();
926         updateUI();
927 }
928
929 function switchToGroupGraphs() {
930         //mylog('switchToGroupGraphs()');
931
932         if(!groupCharts) return;
933
934         if(!mainchart) thumbsScrollPosition = window.pageYOffset;
935
936         document.getElementById('maingraph_container').style.display = 'none';
937         document.getElementById('thumbgraphs_container').style.display = 'none';
938         document.getElementById('groupgraphs_container').style.display = 'block';
939         document.getElementById('splash_container').style.display = 'none';
940
941         document.getElementById("main_menu_div").innerHTML = "<ul class=\"nav navbar-nav\"><li><a href=\"javascript:switchToThumbGraphs();\"><span class=\"glyphicon glyphicon-circle-arrow-left\"></span> Back to Dashboard</a></li><li class=\"active\"><a href=\"#\">" + groupCharts[0].family + "</a></li>" + familiesmainmenu + chartsmainmenu + "</ul>";
942
943         window.scrollTo(0, 0);
944
945         mode = MODE_GROUP_THUMBS;
946         playGraphs();
947         updateUI();
948 }
949
950
951 // ------------------------------------------------------------------------
952 // Group Charts
953
954 var groupCharts = null;
955 function initGroupGraphs(group) {
956         var count = 0;
957
958         if(groupCharts) clearGroupGraphs();
959         groupCharts = new Array();
960
961         var groupbody = "";
962         $.each(allCharts, function(i, c) {
963                 if(c.family == group) {
964                         groupCharts[count] = [];
965                         groupCharts[count] = $.extend(true, {}, c);
966                         groupCharts[count].div += "_group";
967                         groupCharts[count].enabled = true;
968                         groupCharts[count].chart = null;
969                         groupCharts[count].last_updated = 0;
970                         count++;
971                 }
972         });
973         groupCharts.sort(chartssort);
974
975         var sizes = groupChartSizes();
976
977         var groupbody = "";
978         $.each(groupCharts, function(i, c) {
979                 c.chartOptions.width = sizes.width;
980                 c.chartOptions.height = sizes.height;
981                 c.chartOptions.chartArea.width = '85%';
982                 c.chartOptions.chartArea.height = '90%';
983                 c.chartOptions.hAxis.textPosition = 'in';
984                 c.chartOptions.hAxis.viewWindowMode = 'maximized';
985                 c.chartOptions.hAxis.textStyle = { "fontSize": 9 };
986                 c.chartOptions.vAxis.textStyle = { "fontSize": 9 };
987                 c.chartOptions.fontSize = 11;
988                 c.chartOptions.titlePosition = 'in';
989                 c.chartOptions.tooltip = { "textStyle": { "fontSize": 9 } };
990                 c.chartOptions.legend = { "textStyle": { "fontSize": 9 } };
991
992                 calculateChartPointsToShow(c, c.chartOptions.isStacked?GROUPS_STACKED_POINTS_DIVISOR:GROUPS_POINTS_DIVISOR, GROUPS_MAX_TIME_TO_SHOW, -1, ENABLE_CURVE);
993
994                 groupbody += "<div class=\"thumbgraph\" id=\"" + c.div + "_parent\"><table><tr><td width='" + sizes.width + "'><div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.name, c.chartOptions.width, c.chartOptions.height) + "</div></td><td id=\"" + c.id + "_group_actions_div\" align=\"center\">" + groupChartActions(i, c) + "</td></tr><tr><td width='15'></td></tr></table></div>";
995                 //groupbody += "<div class=\"thumbgraph\" id=\"" + c.div + "\">" + chartIsLoadingHTML(c.name, c.chartOptions.width, c.chartOptions.height) + "</div>";
996         });
997         groupbody += "";
998
999         document.getElementById("groupgraphs").innerHTML = groupbody;
1000         switchToGroupGraphs();
1001 }
1002
1003 function clearGroupGraphs() {
1004         if(groupCharts && groupCharts.length) {
1005                 $.each(groupCharts, function(i, c) {
1006                         cleanThisChart(c, 'emptydivs');
1007                 });
1008
1009                 groupCharts = null;
1010         }
1011
1012         document.getElementById("groupgraphs").innerHTML = "";
1013 }
1014
1015
1016 // ------------------------------------------------------------------------
1017 // Global entry point
1018 // initialize the thumb charts
1019
1020 var last_user_scroll = 0;
1021
1022 // load the charts from the server
1023 // generate html for the thumbgraphs to support them
1024 function initCharts() {
1025         setPresentationNormal(1);
1026
1027         var width = thumbWidth();
1028         var height = TARGET_THUMB_GRAPH_HEIGHT;
1029
1030         window.onscroll = function (e) {
1031                 last_user_scroll = Date.now();
1032                 mylog('Scrolling: detected');
1033         }
1034
1035         loadCharts(null, function(all) {
1036                 allCharts = all.charts;
1037
1038                 if(allCharts == null || allCharts.length == 0) {
1039                         alert("Cannot load data from server.");
1040                         return;
1041                 }
1042
1043                 var thumbsContainer = document.getElementById("thumbgraphs");
1044                 if(!thumbsContainer) {
1045                         alert("Cannot find the thumbsContainer");
1046                         return;
1047                 }
1048
1049                 allCharts.sort(chartssort);
1050
1051                 document.getElementById('hostname_id').innerHTML = all.hostname;
1052                 document.title = all.hostname;
1053
1054                 // create an array for grouping all same-type graphs together
1055                 var dimensions = 0;
1056                 var categories = new Array();
1057                 var families = new Array();
1058                 var chartslist = new Array();
1059                 $.each(allCharts, function(i, c) {
1060                         var j;
1061
1062                         chartslist.push({name: c.name, type: c.type, id: i});
1063
1064                         dimensions += c.dimensions.length;
1065                         c.chartOptions.width = width;
1066                         c.chartOptions.height = height;
1067
1068                         // calculate how many point to show for each chart
1069                         //c.points_to_show = Math.round(c.entries / c.group) - 1;
1070                         // show max 10 mins of data
1071                         //if(c.points_to_show * c.group > THUMBS_MAX_TIME_TO_SHOW) c.points_to_show = THUMBS_MAX_TIME_TO_SHOW / c.group;
1072                         calculateChartPointsToShow(c, c.chartOptions.isStacked?THUMBS_STACKED_POINTS_DIVISOR:THUMBS_POINTS_DIVISOR, THUMBS_MAX_TIME_TO_SHOW, -1, ENABLE_CURVE);
1073
1074                         if(c.enabled) {
1075                                 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 id=\"" + c.id + "_thumb_actions_div\" align=\"center\">"
1076                                 + thumbChartActions(i, c)
1077                                 +       "</td></tr><tr><td height='15'></td></tr></table></div>";
1078
1079                                 // find the categories object for this type
1080                                 for(j = 0; j < categories.length ;j++) {
1081                                         if(categories[j].name == c.type) {
1082                                                 categories[j].html += h;
1083                                                 categories[j].count++;
1084                                                 break;
1085                                         }
1086                                 }
1087
1088                                 if(j == categories.length)
1089                                         categories.push({name: c.type, title: c.category, description: '', priority: c.categoryPriority, count: 1, glyphicon: c.glyphicon, html: h});
1090                         }
1091
1092                         // find the families object for this type
1093                         for(j = 0; j < families.length ;j++) {
1094                                 if(families[j].name == c.family) {
1095                                         families[j].count++;
1096                                         break;
1097                                 }
1098                         }
1099
1100                         if(j == families.length)
1101                                 families.push({name: c.family, count: 1});
1102                 });
1103
1104                 document.getElementById('server_summary_id').innerHTML = "<small>NetData server at <b>" + all.hostname + "</b> is maintaining <b>" + allCharts.length + "</b> charts, having <b>" + dimensions + "</b> dimensions (by default with <b>" + all.history + "</b> entries each), which are updated every <b>" + all.update_every + "s</b>, using a total of <b>" + (Math.round(all.memory * 10 / 1024 / 1024) / 10) + " MB</b> for the round robin database.</small>";
1105
1106                 $.each(categories, function(i, a) {
1107                         a.html = "<tr><td id=\"" + a.name + "\"><ol class=\"breadcrumb graphs\"><li class=\"active\"><span class=\"glyphicon " + a.glyphicon + "\"></span> &nbsp; <a id=\"" + a.name + "\" href=\"#" + a.name + "\"><b>" + a.title + "</b> " + a.description + " </a></li></ol></td></tr><tr><td><div class=\"thumbgraphs\">" + a.html + "</td></tr>";
1108                 });
1109
1110                 function categoriessort(a, b) {
1111                         if(a.priority < b.priority) return -1;
1112                         return 1;
1113                 }
1114                 categories.sort(categoriessort);
1115
1116                 function familiessort(a, b) {
1117                         if(a.name < b.name) return -1;
1118                         return 1;
1119                 }
1120                 families.sort(familiessort);
1121
1122                 function chartslistsort(a, b) {
1123                         if(a.name < b.name) return -1;
1124                         return 1;
1125                 }
1126                 chartslist.sort(chartslistsort);
1127
1128                 // combine all the htmls into one
1129                 var allcategories = "<table width=\"100%\">";
1130                 mainmenu = '<ul class="nav navbar-nav">';
1131
1132                 categoriesmainmenu = '<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class=\"glyphicon glyphicon-fire\"></span> Dashboard Sections <b class="caret"></b></a><ul class="dropdown-menu">';
1133                 $.each(categories, function(i, a) {
1134                         allcategories += a.html;
1135                         categoriesmainmenu += "<li><a href=\"#" + a.name + "\">" + a.title + "</a></li>";
1136                 });
1137                 categoriesmainmenu += "</ul></li>";
1138                 allcategories += "</table>";
1139
1140                 familiesmainmenu = '<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class=\"glyphicon glyphicon-th-large\"></span> Chart Families <b class="caret"></b></a><ul class="dropdown-menu">';
1141                 $.each(families, function(i, a) {
1142                         familiesmainmenu += "<li><a href=\"javascript:initGroupGraphs('" + a.name + "');\">" + a.name + " <span class=\"badge pull-right\">" + a.count + "</span></a></li>";
1143                 });
1144                 familiesmainmenu += "</ul></li>";
1145
1146                 chartsmainmenu = '<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class=\"glyphicon glyphicon-resize-full\"></span> All Charts <b class="caret"></b></a><ul class="dropdown-menu">';
1147                 $.each(chartslist, function(i, a) {
1148                         chartsmainmenu += "<li><a href=\"javascript:initMainChartIndexOfMyCharts('" + a.id + "');\">" + a.name + "</a></li>";
1149                 });
1150                 chartsmainmenu += "</ul></li>";
1151
1152                 mainmenu += categoriesmainmenu;
1153                 mainmenu += familiesmainmenu;
1154                 mainmenu += chartsmainmenu;
1155                 mainmenu += '<li role="presentation" class="disabled" style="display: none;"><a href="#" id="logline"></a></li></ul>';
1156
1157                 thumbsContainer.innerHTML = allcategories;
1158                 switchToThumbGraphs();
1159                 checkRefreshThread();
1160         });
1161 }
1162
1163 $(window).blur(function() {
1164         page_is_visible = 0;
1165         mylog('Lost Focus!');
1166 });
1167
1168 $(window).focus(function() {
1169         page_is_visible = 1;
1170         mylog('Focus restored!');
1171 });
1172
1173 // Set a callback to run when the Google Visualization API is loaded.
1174 google.setOnLoadCallback(initCharts);