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