Remove unneeded variables from config.default.php
[phpmyadmin.git] / js / server_status.js
blob870f9b120553225b75724bc93dc612ce2f82c969
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * @fileoverview    functions used in server status pages
4  * @name            Server Status
5  *
6  * @requires    jQuery
7  * @requires    jQueryUI
8  * @requires    jQueryCookie
9  * @requires    jQueryTablesorter
10  * @requires    Highcharts
11  * @requires    canvg
12  * @requires    js/functions.js
13  *
14  */
16 // Add a tablesorter parser to properly handle thousands seperated numbers and SI prefixes
17 $(function() {
18     jQuery.tablesorter.addParser({
19         id: "fancyNumber",
20         is: function(s) {
21             return /^[0-9]?[0-9,\.]*\s?(k|M|G|T|%)?$/.test(s);
22         },
23         format: function(s) {
24             var num = jQuery.tablesorter.formatFloat(
25                 s.replace(PMA_messages['strThousandsSeperator'], '')
26                  .replace(PMA_messages['strDecimalSeperator'], '.')
27             );
29             var factor = 1;
30             switch (s.charAt(s.length - 1)) {
31                 case '%': factor = -2; break;
32                 // Todo: Complete this list (as well as in the regexp a few lines up)
33                 case 'k': factor = 3; break;
34                 case 'M': factor = 6; break;
35                 case 'G': factor = 9; break;
36                 case 'T': factor = 12; break;
37             }
39             return num * Math.pow(10, factor);
40         },
41         type: "numeric"
42     });
45     // Popup behaviour
46     $('a[rel="popupLink"]').click( function() {
47         var $link = $(this);
49         $('.' + $link.attr('href').substr(1))
50             .show()
51             .offset({ top: $link.offset().top + $link.height() + 5, left: $link.offset().left })
52             .addClass('openedPopup');
54         return false;
55     });
57     $(document).click( function(event) {
58         $('.openedPopup').each(function() {
59             var $cnt = $(this);
60             var pos = $(this).offset();
62             // Hide if the mouseclick is outside the popupcontent
63             if (event.pageX < pos.left 
64                || event.pageY < pos.top 
65                || event.pageX > pos.left + $cnt.outerWidth() 
66                || event.pageY > pos.top + $cnt.outerHeight()
67             ) {
68                 $cnt.hide().removeClass('openedPopup');
69             }
70         });
71     });
72 });
74 $(function() {
75     // Filters for status variables
76     var textFilter = null;
77     var alertFilter = false;
78     var categoryFilter = '';
79     var odd_row = false;
80     var text = ''; // Holds filter text
81     var queryPieChart = null;
82     var monitorLoaded = false;
84     /* Chart configuration */
85     // Defines what the tabs are currently displaying (realtime or data)
86     var tabStatus = new Object();
87     // Holds the current chart instances for each tab
88     var tabChart = new Object();
90     /*** Table sort tooltip ***/
91     PMA_createqTip($('table.sortable thead th'), PMA_messages['strSortHint']);
93     // Tell highcarts not to use UTC dates (global setting)
94     Highcharts.setOptions({
95         global: {
96             useUTC: false
97         }
98     });
100     $.ajaxSetup({
101         cache: false
102     });
104     // Add tabs
105     $('#serverStatusTabs').tabs({
106         // Tab persistence
107         cookie: { name: 'pma_serverStatusTabs', expires: 1 },
108         show: function(event, ui) { 
109             // Fixes line break in the menu bar when the page overflows and scrollbar appears
110             menuResize(); 
111             // Load Server status monitor
112             if (ui.tab.hash == '#statustabs_charting' && ! monitorLoaded) {
113                 $('div#statustabs_charting').append(
114                     '<img class="ajaxIcon" id="loadingMonitorIcon" src="' +
115                     pmaThemeImage + 'ajax_clock_small.gif" alt="">'
116                 );
117                 // Delay loading a bit so the tab loads and the user gets to see a ajax loading icon
118                 setTimeout(function() {
119                     loadJavascript(['js/jquery/timepicker.js', 'js/jquery/jquery.json-2.2.js',
120                                     'js/jquery/jquery.sprintf.js', 'js/jquery/jquery.sortableTable.js',
121                                     'js/codemirror/lib/codemirror.js', 'js/codemirror/mode/mysql/mysql.js',
122                                     'js/server_status_monitor.js']);
123                 }, 50);
124                 
125                 monitorLoaded = true;
126             }
128             // Run the advisor immediately when the user clicks the tab, but only when this is the first time
129             if (ui.tab.hash == '#statustabs_advisor' && $('table#rulesFired').length == 0) {
130                 // Start with a small delay because the click event hasn't been setup yet
131                 setTimeout(function() {
132                     $('a[href="#startAnalyzer"]').trigger('click');
133                 }, 25);
134             }
135         }
136     });
138     // Fixes wrong tab height with floated elements. See also http://bugs.jqueryui.com/ticket/5601
139     $(".ui-widget-content:not(.ui-tabs):not(.ui-helper-clearfix)").addClass("ui-helper-clearfix");
141     // Initialize each tab
142     $('div.ui-tabs-panel').each(function() {
143         initTab($(this), null);
144         tabStatus[$(this).attr('id')] = 'static';
145     });
147     // Display button links
148     $('div.buttonlinks').show();
150     // Handles refresh rate changing
151     $('.buttonlinks select').change(function() {
152         var chart = tabChart[$(this).parents('div.ui-tabs-panel').attr('id')];
154         // Clear current timeout and set timeout with the new refresh rate
155         clearTimeout(chart_activeTimeouts[chart.options.chart.renderTo]);
156         if (chart.options.realtime.postRequest) {
157             chart.options.realtime.postRequest.abort();
158         }
160         chart.options.realtime.refreshRate = 1000*parseInt(this.value);
162         chart.xAxis[0].setExtremes(
163             new Date().getTime() - server_time_diff - chart.options.realtime.numMaxPoints * chart.options.realtime.refreshRate,
164             new Date().getTime() - server_time_diff,
165             true
166         );
168         chart_activeTimeouts[chart.options.chart.renderTo] = setTimeout(
169             chart.options.realtime.timeoutCallBack,
170             chart.options.realtime.refreshRate
171         );
172     });
174     // Ajax refresh of variables (always the first element in each tab)
175     $('.buttonlinks a.tabRefresh').click(function() {
176         // ui-tabs-panel class is added by the jquery tabs feature
177         var tab = $(this).parents('div.ui-tabs-panel');
178         var that = this;
180         // Show ajax load icon
181         $(this).find('img').show();
183         $.get($(this).attr('href'), { ajax_request: 1 }, function(data) {
184             $(that).find('img').hide();
185             initTab(tab, data);
186         });
188         tabStatus[tab.attr('id')] = 'data';
190         return false;
191     });
194     /** Realtime charting of variables **/
196     // Live traffic charting
197     $('.buttonlinks a.livetrafficLink').click(function() {
198         // ui-tabs-panel class is added by the jquery tabs feature
199         var $tab = $(this).parents('div.ui-tabs-panel');
200         var tabstat = tabStatus[$tab.attr('id')];
202         if (tabstat == 'static' || tabstat == 'liveconnections') {
203             var settings = {
204                 series: [
205                     { name: PMA_messages['strChartKBSent'], data: [] },
206                     { name: PMA_messages['strChartKBReceived'], data: [] }
207                 ],
208                 title: { text: PMA_messages['strChartServerTraffic'] },
209                 realtime: { url: 'server_status.php?' + url_query,
210                            type: 'traffic',
211                            callback: function(chartObj, curVal, lastVal, numLoadedPoints) {
212                                if (lastVal == null) {
213                                    return;
214                                 }
215                                 chartObj.series[0].addPoint(
216                                     { x: curVal.x, y: (curVal.y_sent - lastVal.y_sent) / 1024 },
217                                     false,
218                                     numLoadedPoints >= chartObj.options.realtime.numMaxPoints
219                                 );
220                                 chartObj.series[1].addPoint(
221                                     { x: curVal.x, y: (curVal.y_received - lastVal.y_received) / 1024 },
222                                     true,
223                                     numLoadedPoints >= chartObj.options.realtime.numMaxPoints
224                                 );
225                             },
226                             error: function() { serverResponseError(); }
227                          }
228             }
230             setupLiveChart($tab, this, settings);
231             if (tabstat == 'liveconnections') {
232                 $tab.find('.buttonlinks a.liveconnectionsLink').html(PMA_messages['strLiveConnChart']);
233             }
234             tabStatus[$tab.attr('id')] = 'livetraffic';
235         } else {
236             $(this).html(PMA_messages['strLiveTrafficChart']);
237             setupLiveChart($tab, this, null);
238         }
240         return false;
241     });
243     // Live connection/process charting
244     $('.buttonlinks a.liveconnectionsLink').click(function() {
245         var $tab = $(this).parents('div.ui-tabs-panel');
246         var tabstat = tabStatus[$tab.attr('id')];
248         if (tabstat == 'static' || tabstat == 'livetraffic') {
249             var settings = {
250                 series: [
251                     { name: PMA_messages['strChartConnections'], data: [] },
252                     { name: PMA_messages['strChartProcesses'], data: [] }
253                 ],
254                 title: { text: PMA_messages['strChartConnectionsTitle'] },
255                 realtime: { url: 'server_status.php?' + url_query,
256                            type: 'proc',
257                            callback: function(chartObj, curVal, lastVal, numLoadedPoints) {
258                                 if (lastVal == null) {
259                                     return;
260                                 }
261                                 chartObj.series[0].addPoint(
262                                     { x: curVal.x, y: curVal.y_conn - lastVal.y_conn },
263                                     false,
264                                     numLoadedPoints >= chartObj.options.realtime.numMaxPoints
265                                 );
266                                 chartObj.series[1].addPoint(
267                                     { x: curVal.x, y: curVal.y_proc },
268                                     true,
269                                     numLoadedPoints >= chartObj.options.realtime.numMaxPoints
270                                 );
271                             },
272                             error: function() { serverResponseError(); }
273                          }
274             };
276             setupLiveChart($tab, this, settings);
277             if (tabstat == 'livetraffic') {
278                 $tab.find('.buttonlinks a.livetrafficLink').html(PMA_messages['strLiveTrafficChart']);
279             }
280             tabStatus[$tab.attr('id')] = 'liveconnections';
281         } else {
282             $(this).html(PMA_messages['strLiveConnChart']);
283             setupLiveChart($tab, this, null);
284         }
286         return false;
287     });
289     // Live query statistics
290     $('.buttonlinks a.livequeriesLink').click(function() {
291         var $tab = $(this).parents('div.ui-tabs-panel');
292         var settings = null;
294         if (tabStatus[$tab.attr('id')] == 'static') {
295             settings = {
296                 series: [ { name: PMA_messages['strChartIssuedQueries'], data: [] } ],
297                 title: { text: PMA_messages['strChartIssuedQueriesTitle'] },
298                 tooltip: { formatter: function() { return this.point.name; } },
299                 realtime: { url: 'server_status.php?' + url_query,
300                           type: 'queries',
301                           callback: function(chartObj, curVal, lastVal, numLoadedPoints) {
302                                 if (lastVal == null) { return; }
303                                 chartObj.series[0].addPoint({
304                                         x: curVal.x,
305                                         y: curVal.y - lastVal.y,
306                                         name: sortedQueriesPointInfo(curVal, lastVal)
307                                     },
308                                     true,
309                                     numLoadedPoints >= chartObj.options.realtime.numMaxPoints
310                                 );
311                             },
312                             error: function() { serverResponseError(); }
313                          }
314             };
315         } else {
316             $(this).html(PMA_messages['strLiveQueryChart']);
317         }
319         setupLiveChart($tab, this, settings);
320         tabStatus[$tab.attr('id')] = 'livequeries';
321         return false;
322     });
324     function setupLiveChart($tab, link, settings) {
325         if (settings != null) {
326             // Loading a chart with existing chart => remove old chart first
327             if (tabStatus[$tab.attr('id')] != 'static') {
328                 clearTimeout(chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"]);
329                 chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"] = null;
330                 tabChart[$tab.attr('id')].destroy();
331                 // Also reset the select list
332                 $tab.find('.buttonlinks select').get(0).selectedIndex = 2;
333             }
335             if (! settings.chart) settings.chart = {};
336             settings.chart.renderTo = $tab.attr('id') + "_chart_cnt";
338             $tab.find('.tabInnerContent')
339                 .hide()
340                 .after('<div class="liveChart" id="' + $tab.attr('id') + '_chart_cnt"></div>');
341             tabChart[$tab.attr('id')] = PMA_createChart(settings);
342             $(link).html(PMA_messages['strStaticData']);
343             $tab.find('.buttonlinks a.tabRefresh').hide();
344             $tab.find('.buttonlinks .refreshList').show();
345         } else {
346             clearTimeout(chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"]);
347             chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"] = null;
348             $tab.find('.tabInnerContent').show();
349             $tab.find('div#' + $tab.attr('id') + '_chart_cnt').remove();
350             tabStatus[$tab.attr('id')] = 'static';
351             tabChart[$tab.attr('id')].destroy();
352             $tab.find('.buttonlinks a.tabRefresh').show();
353             $tab.find('.buttonlinks select').get(0).selectedIndex = 2;
354             $tab.find('.buttonlinks .refreshList').hide();
355         }
356     }
358     /* 3 Filtering functions */
359     $('#filterAlert').change(function() {
360         alertFilter = this.checked;
361         filterVariables();
362     });
364     $('#filterText').keyup(function(e) {
365         word = $(this).val().replace(/_/g, ' ');
367         if (word.length == 0) {
368             textFilter = null;
369         }
370         else textFilter = new RegExp("(^|_)" + word, 'i');
372         text = word;
374         filterVariables();
375     });
377     $('#filterCategory').change(function() {
378         categoryFilter = $(this).val();
379         filterVariables();
380     });
382     $('input#dontFormat').change(function() {
383         // Hiding the table while changing values speeds up the process a lot
384         $('#serverstatusvariables').hide();
385         $('#serverstatusvariables td.value span.original').toggle(this.checked);
386         $('#serverstatusvariables td.value span.formatted').toggle(! this.checked);
387         $('#serverstatusvariables').show();
388     });
390     /* Adjust DOM / Add handlers to the tabs */
391     function initTab(tab, data) {
392         switch(tab.attr('id')) {
393             case 'statustabs_traffic':
394                 if (data != null) {
395                     tab.find('.tabInnerContent').html(data);
396                 }
397                 PMA_convertFootnotesToTooltips();
398                 break;
399             case 'statustabs_queries':
400                 if (data != null) {
401                     queryPieChart.destroy();
402                     tab.find('.tabInnerContent').html(data);
403                 }
405                 // Build query statistics chart
406                 var cdata = new Array();
407                 $.each(jQuery.parseJSON($('#serverstatusquerieschart span').html()), function(key, value) {
408                     cdata.push([key, parseInt(value)]);
409                 });
411                 queryPieChart = PMA_createChart({
412                     chart: {
413                         renderTo: 'serverstatusquerieschart'
414                     },
415                     title: {
416                         text: '',
417                         margin: 0
418                     },
419                     series: [{
420                         type: 'pie',
421                         name: PMA_messages['strChartQueryPie'],
422                         data: cdata
423                     }],
424                     plotOptions: {
425                         pie: {
426                             allowPointSelect: true,
427                             cursor: 'pointer',
428                             dataLabels: {
429                                 enabled: true,
430                                 formatter: function() {
431                                     return '<b>' + this.point.name +'</b><br/> ' + 
432                                             Highcharts.numberFormat(this.percentage, 2) + ' %';
433                                }
434                             }
435                         }
436                     },
437                     tooltip: {
438                         formatter: function() {
439                             return '<b>' + this.point.name + '</b><br/>' + 
440                                     Highcharts.numberFormat(this.y, 2) + '<br/>(' + 
441                                     Highcharts.numberFormat(this.percentage, 2) + ' %)';
442                         }
443                     }
444                 });
445                 break;
447             case 'statustabs_allvars':
448                 if (data != null) {
449                     tab.find('.tabInnerContent').html(data);
450                     filterVariables();
451                 }
452                 break;
453         }
455         initTableSorter(tab.attr('id'));
456     }
458     function initTableSorter(tabid) {
459         switch(tabid) {
460             case 'statustabs_queries':
461                 $('#serverstatusqueriesdetails').tablesorter({
462                         sortList: [[3, 1]],
463                         widgets: ['zebra'],
464                         headers: {
465                             1: { sorter: 'fancyNumber' },
466                             2: { sorter: 'fancyNumber' }
467                         }
468                     });
470                 $('#serverstatusqueriesdetails tr:first th')
471                     .append('<img class="icon sortableIcon" src="themes/dot.gif" alt="">');
473                 break;
475             case 'statustabs_allvars':
476                 $('#serverstatusvariables').tablesorter({
477                         sortList: [[0, 0]],
478                         widgets: ['zebra'],
479                         headers: {
480                             1: { sorter: 'fancyNumber' }
481                         }
482                     });
484                 $('#serverstatusvariables tr:first th')
485                     .append('<img class="icon sortableIcon" src="themes/dot.gif" alt="">');
487                 break;
488         }
489     }
491     /* Filters the status variables by name/category/alert in the variables tab */
492     function filterVariables() {
493         var useful_links = 0;
494         var section = text;
496         if (categoryFilter.length > 0) {
497             section = categoryFilter;
498         }
500         if (section.length > 1) {
501             $('#linkSuggestions span').each(function() {
502                 if ($(this).attr('class').indexOf('status_' + section) != -1) {
503                     useful_links++;
504                     $(this).css('display', '');
505                 } else {
506                     $(this).css('display', 'none');
507                 }
510             });
511         }
513         if (useful_links > 0) {
514             $('#linkSuggestions').css('display', '');
515         } else {
516             $('#linkSuggestions').css('display', 'none');
517         }
519         odd_row = false;
520         $('#serverstatusvariables th.name').each(function() {
521             if ((textFilter == null || textFilter.exec($(this).text()))
522                 && (! alertFilter || $(this).next().find('span.attention').length>0)
523                 && (categoryFilter.length == 0 || $(this).parent().hasClass('s_' + categoryFilter))
524             ) {
525                 odd_row = ! odd_row;
526                 $(this).parent().css('display', '');
527                 if (odd_row) {
528                     $(this).parent().addClass('odd');
529                     $(this).parent().removeClass('even');
530                 } else {
531                     $(this).parent().addClass('even');
532                     $(this).parent().removeClass('odd');
533                 }
534             } else {
535                 $(this).parent().css('display', 'none');
536             }
537         });
538     }
540     // Provides a nicely formatted and sorted tooltip of each datapoint of the query statistics
541     function sortedQueriesPointInfo(queries, lastQueries){
542         var max, maxIdx, num = 0;
543         var queryKeys = new Array();
544         var queryValues = new Array();
545         var sumOther = 0;
546         var sumTotal = 0;
548         // Separate keys and values, then  sort them
549         $.each(queries.pointInfo, function(key, value) {
550             if (value-lastQueries.pointInfo[key] > 0) {
551                 queryKeys.push(key);
552                 queryValues.push(value-lastQueries.pointInfo[key]);
553                 sumTotal += value-lastQueries.pointInfo[key];
554             }
555         });
556         var numQueries = queryKeys.length;
557         var pointInfo = '<b>' + PMA_messages['strTotal'] + ': ' + sumTotal + '</b><br>';
559         while(queryKeys.length > 0) {
560             max = 0;
561             for (var i = 0; i < queryKeys.length; i++) {
562                 if (queryValues[i] > max) {
563                     max = queryValues[i];
564                     maxIdx = i;
565                 }
566             }
567             if (numQueries > 8 && num >= 6) {
568                 sumOther += queryValues[maxIdx];
569             } else {
570                 pointInfo += queryKeys[maxIdx].substr(4).replace('_', ' ') + ': ' + queryValues[maxIdx] + '<br>';
571             }
573             queryKeys.splice(maxIdx, 1);
574             queryValues.splice(maxIdx, 1);
575             num++;
576         }
578         if (sumOther>0) {
579             pointInfo += PMA_messages['strOther'] + ': ' + sumOther;
580         }
581         
582         return pointInfo;
583     }
585     /**** Server config advisor ****/
587     $('a[href="#openAdvisorInstructions"]').click(function() {
588         var dlgBtns = {};
589         
590         dlgBtns[PMA_messages['strClose']] = function() {
591             $(this).dialog('close');
592         }
593         
594         $('#advisorInstructionsDialog').attr('title', PMA_messages['strAdvisorSystem']);
595         $('#advisorInstructionsDialog').dialog({
596             width: 700,
597             buttons: dlgBtns 
598         });
599     });
601     $('a[href="#startAnalyzer"]').click(function() {
602         var $cnt = $('#statustabs_advisor .tabInnerContent');
603         $cnt.html('<img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">');
604         
605         $.get('server_status.php?' + url_query, { ajax_request: true, advisor: true }, function(data) {
606             var $tbody, $tr, str, even = true;
608             data = $.parseJSON(data);
609             
610             $cnt.html('');
611             
612             if (data.parse.errors.length > 0) {
613                 $cnt.append('<b>Rules file not well formed, following errors were found:</b><br />- ');
614                 $cnt.append(data.parse.errors.join('<br/>- '));
615                 $cnt.append('<p></p>');
616             }
617             
618             if (data.run.errors.length > 0) {
619                 $cnt.append('<b>Errors occured while executing rule expressions:</b><br />- ');
620                 $cnt.append(data.run.errors.join('<br/>- '));
621                 $cnt.append('<p></p>');
622             }
623             
624             if (data.run.fired.length > 0) {
625                 $cnt.append('<p><b>' + PMA_messages['strPerformanceIssues'] + '</b></p>');
626                 $cnt.append('<table class="data" id="rulesFired" border="0"><thead><tr>' +
627                             '<th>' + PMA_messages['strIssuse'] + '</th><th>' + PMA_messages['strRecommendation'] + 
628                             '</th></tr></thead><tbody></tbody></table>'); 
629                 $tbody = $cnt.find('table#rulesFired');
630                 
631                 var rc_stripped;
632                 
633                 $.each(data.run.fired, function(key, value) {
634                     // recommendation may contain links, don't show those in overview table (clicking on them redirects the user)
635                     rc_stripped = $.trim($('<div>').html(value.recommendation).text())
636                     $tbody.append($tr = $('<tr class="linkElem noclick ' + (even ? 'even' : 'odd') + '"><td>' + 
637                                             value.issue + '</td><td>' + rc_stripped + ' </td></tr>')); 
638                     even = !even;
639                     $tr.data('rule', value);
640                     
641                     $tr.click(function() {
642                         var rule = $(this).data('rule');
643                         $('div#emptyDialog').attr('title', PMA_messages['strRuleDetails']);
644                         $('div#emptyDialog').html(
645                             '<p><b>' + PMA_messages['strIssuse'] + ':</b><br />' + rule.issue + '</p>' +
646                             '<p><b>' + PMA_messages['strRecommendation'] + ':</b><br />' + rule.recommendation + '</p>' +
647                             '<p><b>' + PMA_messages['strJustification'] + ':</b><br />' + rule.justification + '</p>' +
648                             '<p><b>' + PMA_messages['strFormula'] + ':</b><br />' + rule.formula + '</p>' +
649                             '<p><b>' + PMA_messages['strTest'] + ':</b><br />' + rule.test + '</p>'
650                         );
651                         
652                         var dlgBtns = {};
653                         dlgBtns[PMA_messages['strClose']] = function() { 
654                             $(this).dialog('close'); 
655                         };
656                         
657                         $('div#emptyDialog').dialog({ width: 600, buttons: dlgBtns });
658                     });
659                 });
660             }
661         });
662                 
663         return false;
664     });
668 // Needs to be global as server_status_monitor.js uses it too
669 function serverResponseError() {
670     var btns = {};
671     btns[PMA_messages['strReloadPage']] = function() {
672         window.location.reload();
673     };
674     $('#emptyDialog').attr('title', PMA_messages['strRefreshFailed']);
675     $('#emptyDialog').html(
676         '<img class="icon ic_s_attention" src="themes/dot.gif" alt=""> ' + 
677         PMA_messages['strInvalidResponseExplanation']
678     );
679     $('#emptyDialog').dialog({ buttons: btns });