/* vim: set expandtab sw=4 ts=4 sts=4: */ /** * @fileoverview functions used in server status pages * @name Server Status * * @requires jQuery * @requires jQueryUI * @requires jQueryCookie * @requires jQueryTablesorter * @requires Highcharts * @requires canvg * @requires js/functions.js * */ // Add a tablesorter parser to properly handle thousands seperated numbers and SI prefixes $(function() { jQuery.tablesorter.addParser({ id: "fancyNumber", is: function(s) { return /^[0-9]?[0-9,\.]*\s?(k|M|G|T|%)?$/.test(s); }, format: function(s) { var num = jQuery.tablesorter.formatFloat( s.replace(PMA_messages['strThousandsSeperator'],'') .replace(PMA_messages['strDecimalSeperator'],'.') ); var factor = 1; switch (s.charAt(s.length - 1)) { case '%': factor = -2; break; // Todo: Complete this list (as well as in the regexp a few lines up) case 'k': factor = 3; break; case 'M': factor = 6; break; case 'G': factor = 9; break; case 'T': factor = 12; break; } return num * Math.pow(10,factor); }, type: "numeric" }); }); $(function() { // Filters for status variables var textFilter=null; var alertFilter = false; var categoryFilter=''; var odd_row=false; var text=''; // Holds filter text var queryPieChart = null; /* Chart configuration */ // Defines what the tabs are currently displaying (realtime or data) var tabStatus = new Object(); // Holds the current chart instances for each tab var tabChart = new Object(); $.ajaxSetup({ cache:false }); // Add tabs $('#serverStatusTabs').tabs({ // Tab persistence cookie: { name: 'pma_serverStatusTabs', expires: 1 }, // Fixes line break in the menu bar when the page overflows and scrollbar appears show: function() { menuResize(); } }); // Fixes wrong tab height with floated elements. See also http://bugs.jqueryui.com/ticket/5601 $(".ui-widget-content:not(.ui-tabs):not(.ui-helper-clearfix)").addClass("ui-helper-clearfix"); // Initialize each tab $('div.ui-tabs-panel').each(function() { initTab($(this),null); tabStatus[$(this).attr('id')] = 'static'; }); // Handles refresh rate changing $('.statuslinks select').change(function() { var chart=tabChart[$(this).parents('div.ui-tabs-panel').attr('id')]; chart.options.realtime.refreshRate = 1000*parseInt(this.value); chart.xAxis[0].setExtremes( new Date().getTime() - chart.options.realtime.numMaxPoints * chart.options.realtime.refreshRate, new Date().getTime() + chart.options.realtime.refreshRate / 4, true ); // Clear current timeout and set timeout with the new refresh rate clearTimeout(chart_activeTimeouts[chart.options.chart.renderTo]); chart_activeTimeouts[chart.options.chart.renderTo] = setTimeout( chart.options.realtime.timeoutCallBack, chart.options.realtime.refreshRate ); }); // Ajax refresh of variables (always the first element in each tab) $('.statuslinks a.tabRefresh').click(function() { // ui-tabs-panel class is added by the jquery tabs feature var tab=$(this).parents('div.ui-tabs-panel'); var that = this; // Show ajax load icon $(this).find('img').show(); $.get($(this).attr('href'),{ajax_request:1},function(data) { $(that).find('img').hide(); initTab(tab,data); }); tabStatus[tab.attr('id')]='data'; return false; }); /** Realtime charting of variables **/ // Live traffic charting $('.statuslinks a.livetrafficLink').click(function() { // ui-tabs-panel class is added by the jquery tabs feature var $tab=$(this).parents('div.ui-tabs-panel'); var tabstat = tabStatus[$tab.attr('id')]; if(tabstat=='static' || tabstat=='liveconnections') { var settings = { series: [ { name: PMA_messages['strChartKBSent'], data: [] }, { name: PMA_messages['strChartKBReceived'], data: [] } ], title: { text: PMA_messages['strChartServerTraffic'] }, realtime: { url:'server_status.php?' + url_query, type: 'traffic', callback: function(chartObj, curVal, lastVal, numLoadedPoints) { if(lastVal==null) return; chartObj.series[0].addPoint( { x: curVal.x, y: (curVal.y_sent - lastVal.y_sent) / 1024 }, false, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); chartObj.series[1].addPoint( { x: curVal.x, y: (curVal.y_received - lastVal.y_received) / 1024 }, true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); } } } setupLiveChart($tab,this,settings); if(tabstat == 'liveconnections') $tab.find('.statuslinks a.liveconnectionsLink').html(PMA_messages['strLiveConnChart']); tabStatus[$tab.attr('id')]='livetraffic'; } else { $(this).html(PMA_messages['strLiveTrafficChart']); setupLiveChart($tab,this,null); } return false; }); // Live connection/process charting $('.statuslinks a.liveconnectionsLink').click(function() { var $tab=$(this).parents('div.ui-tabs-panel'); var tabstat = tabStatus[$tab.attr('id')]; if(tabstat == 'static' || tabstat == 'livetraffic') { var settings = { series: [ { name: PMA_messages['strChartConnections'], data: [] }, { name: PMA_messages['strChartProcesses'], data: [] } ], title: { text: PMA_messages['strChartConnectionsTitle'] }, realtime: { url:'server_status.php?'+url_query, type: 'proc', callback: function(chartObj, curVal, lastVal,numLoadedPoints) { if(lastVal==null) return; chartObj.series[0].addPoint( { x: curVal.x, y: curVal.y_conn - lastVal.y_conn }, false, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); chartObj.series[1].addPoint( { x: curVal.x, y: curVal.y_proc }, true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); } } }; setupLiveChart($tab,this,settings); if(tabstat == 'livetraffic') $tab.find('.statuslinks a.livetrafficLink').html(PMA_messages['strLiveTrafficChart']); tabStatus[$tab.attr('id')]='liveconnections'; } else { $(this).html(PMA_messages['strLiveConnChart']); setupLiveChart($tab,this,null); } return false; }); // Live query statistics $('.statuslinks a.livequeriesLink').click(function() { var $tab = $(this).parents('div.ui-tabs-panel'); var settings = null; if(tabStatus[$tab.attr('id')] == 'static') { settings = { series: [ { name: PMA_messages['strChartIssuedQueries'], data: [] } ], title: { text: PMA_messages['strChartIssuedQueriesTitle'] }, tooltip: { formatter:function() { return this.point.name; } }, realtime: { url:'server_status.php?'+url_query, type: 'queries', callback: function(chartObj, curVal, lastVal,numLoadedPoints) { if(lastVal == null) return; chartObj.series[0].addPoint( { x: curVal.x, y: curVal.y - lastVal.y, name: sortedQueriesPointInfo(curVal,lastVal) }, true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); } } }; } else { $(this).html(PMA_messages['strLiveQueryChart']); } setupLiveChart($tab,this,settings); tabStatus[$tab.attr('id')] = 'livequeries'; return false; }); function setupLiveChart($tab,link,settings) { if(settings != null) { // Loading a chart with existing chart => remove old chart first if(tabStatus[$tab.attr('id')] != 'static') { clearTimeout(chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"]); chart_activeTimeouts[$tab.attr('id')+"_chart_cnt"] = null; tabChart[$tab.attr('id')].destroy(); // Also reset the select list $tab.find('.statuslinks select').get(0).selectedIndex = 2; } if(! settings.chart) settings.chart = {}; settings.chart.renderTo = $tab.attr('id') + "_chart_cnt"; $tab.find('.tabInnerContent') .hide() .after('
'); tabChart[$tab.attr('id')] = PMA_createChart(settings); $(link).html(PMA_messages['strStaticData']); $tab.find('.statuslinks a.tabRefresh').hide(); $tab.find('.statuslinks .refreshList').show(); } else { clearTimeout(chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"]); chart_activeTimeouts[$tab.attr('id') + "_chart_cnt"]=null; $tab.find('.tabInnerContent').show(); $tab.find('div#'+$tab.attr('id') + '_chart_cnt').remove(); tabStatus[$tab.attr('id')]='static'; tabChart[$tab.attr('id')].destroy(); $tab.find('.statuslinks a.tabRefresh').show(); $tab.find('.statuslinks select').get(0).selectedIndex=2; $tab.find('.statuslinks .refreshList').hide(); } } /* 3 Filtering functions */ $('#filterAlert').change(function() { alertFilter = this.checked; filterVariables(); }); $('#filterText').keyup(function(e) { if($(this).val().length == 0) textFilter = null; else textFilter = new RegExp("(^|_)" + $(this).val(),'i'); text = $(this).val(); filterVariables(); }); $('#filterCategory').change(function() { categoryFilter = $(this).val(); filterVariables(); }); /* Adjust DOM / Add handlers to the tabs */ function initTab(tab,data) { switch(tab.attr('id')) { case 'statustabs_traffic': if(data != null) tab.find('.tabInnerContent').html(data); initTooltips(); break; case 'statustabs_queries': if(data != null) { queryPieChart.destroy(); tab.find('.tabInnerContent').html(data); } // Build query statistics chart var cdata = new Array(); $.each(jQuery.parseJSON($('#serverstatusquerieschart').html()),function(key,value) { cdata.push([key,parseInt(value)]); }); queryPieChart = PMA_createChart({ chart: { renderTo: 'serverstatusquerieschart' }, title: { text:'', margin:0 }, series: [{ type:'pie', name: PMA_messages['strChartQueryPie'], data: cdata }], plotOptions: { pie: { allowPointSelect: true, cursor: 'pointer', dataLabels: { enabled: true, formatter: function() { return ''+ this.point.name +'
' + Highcharts.numberFormat(this.percentage, 2) + ' %'; } } } }, tooltip: { formatter: function() { return '' + this.point.name + '
' + Highcharts.numberFormat(this.y, 2) + '
(' + Highcharts.numberFormat(this.percentage, 2) + ' %)'; } } }); break; case 'statustabs_allvars': if(data != null) { tab.find('.tabInnerContent').html(data); filterVariables(); } break; } initTableSorter(tab.attr('id')); } function initTableSorter(tabid) { switch(tabid) { case 'statustabs_queries': $('#serverstatusqueriesdetails').tablesorter({ sortList: [[3,1]], widgets: ['zebra'], headers: { 1: { sorter: 'fancyNumber' }, 2: { sorter: 'fancyNumber' } } }); $('#serverstatusqueriesdetails tr:first th') .append(''); break; case 'statustabs_allvars': $('#serverstatusvariables').tablesorter({ sortList: [[0,0]], widgets: ['zebra'], headers: { 1: { sorter: 'fancyNumber' } } }); $('#serverstatusvariables tr:first th') .append(''); break; } } /* Filters the status variables by name/category/alert in the variables tab */ function filterVariables() { var useful_links = 0; var section = text; if(categoryFilter.length > 0) section = categoryFilter; if(section.length > 1) { $('#linkSuggestions span').each(function() { if($(this).attr('class').indexOf('status_'+section) != -1) { useful_links++; $(this).css('display',''); } else { $(this).css('display','none'); } }); } if(useful_links > 0) $('#linkSuggestions').css('display',''); else $('#linkSuggestions').css('display','none'); odd_row=false; $('#serverstatusvariables th.name').each(function() { if((textFilter == null || textFilter.exec($(this).text())) && (! alertFilter || $(this).next().find('span.attention').length>0) && (categoryFilter.length == 0 || $(this).parent().hasClass('s_'+categoryFilter))) { odd_row = ! odd_row; $(this).parent().css('display',''); if(odd_row) { $(this).parent().addClass('odd'); $(this).parent().removeClass('even'); } else { $(this).parent().addClass('even'); $(this).parent().removeClass('odd'); } } else { $(this).parent().css('display','none'); } }); } // Provides a nicely formatted and sorted tooltip of each datapoint of the query statistics function sortedQueriesPointInfo(queries, lastQueries){ var max, maxIdx, num=0; var queryKeys = new Array(); var queryValues = new Array(); var sumOther=0; var sumTotal=0; // Separate keys and values, then sort them $.each(queries.pointInfo, function(key,value) { if(value-lastQueries.pointInfo[key] > 0) { queryKeys.push(key); queryValues.push(value-lastQueries.pointInfo[key]); sumTotal += value-lastQueries.pointInfo[key]; } }); var numQueries = queryKeys.length; var pointInfo = '' + PMA_messages['strTotal'] + ': ' + sumTotal + '
'; while(queryKeys.length > 0) { max = 0; for(var i=0; i < queryKeys.length; i++) { if(queryValues[i] > max) { max = queryValues[i]; maxIdx = i; } } if(numQueries > 8 && num >= 6) sumOther += queryValues[maxIdx]; else pointInfo += queryKeys[maxIdx].substr(4).replace('_',' ') + ': ' + queryValues[maxIdx] + '
'; queryKeys.splice(maxIdx,1); queryValues.splice(maxIdx,1); num++; } if(sumOther>0) pointInfo += PMA_messages['strOther'] + ': ' + sumOther; return pointInfo; } });