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