phpmyadmin/js/server_status.js
2011-07-13 16:57:20 +03:00

1598 lines
63 KiB
JavaScript

/* 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"
});
// Popup behaviour
$('a[rel="popupLink"]').click( function() {
var $link = $(this);
$('.' + $link.attr('href').substr(1))
.show()
.offset({ top: $link.offset().top + $link.height() + 5, left: $link.offset().left })
.addClass('openedPopup');
return false;
});
$(document).click( function(event) {
$('.openedPopup').each(function() {
var $cnt = $(this);
var pos = $(this).offset();
// Hide if the mouseclick is outside the popupcontent
if(event.pageX < pos.left || event.pageY < pos.top || event.pageX > pos.left + $cnt.outerWidth() || event.pageY > pos.top + $cnt.outerHeight())
$cnt.hide().removeClass('openedPopup');
});
});
});
$(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();
/*** Table sort tooltip ***/
var $tableSortHint = $('<div class="dHint" style="display:none;">' + 'Click to sort' + '</div>');
$('body').append($tableSortHint);
$('table.sortable thead th').live('mouseover mouseout',function(e) {
if(e.type == 'mouseover') {
$tableSortHint
.stop(true, true)
.css({
top: e.clientY + 15,
left: e.clientX + 15
})
.show('fast')
.data('shown',true);
} else {
$tableSortHint
.stop(true, true)
.hide(300,function() {
$(this).data('shown',false);
});
}
});
$(document).mousemove(function(e) {
if($tableSortHint.data('shown') == true)
$tableSortHint.css({
top: e.clientY + 15,
left: e.clientX + 15
})
});
// Tell highcarts not to use UTC dates (global setting)
Highcharts.setOptions({
global: {
useUTC: false
}
});
$.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';
});
// Display button links
$('div.buttonlinks').show();
// Handles refresh rate changing
$('.buttonlinks select').change(function() {
var chart=tabChart[$(this).parents('div.ui-tabs-panel').attr('id')];
// Clear current timeout and set timeout with the new refresh rate
clearTimeout(chart_activeTimeouts[chart.options.chart.renderTo]);
if(chart.options.realtime.postRequest)
chart.options.realtime.postRequest.abort();
chart.options.realtime.refreshRate = 1000*parseInt(this.value);
chart.xAxis[0].setExtremes(
new Date().getTime() - server_time_diff - chart.options.realtime.numMaxPoints * chart.options.realtime.refreshRate,
new Date().getTime() - server_time_diff,
true
);
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)
$('.buttonlinks 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
$('.buttonlinks 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('.buttonlinks 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
$('.buttonlinks 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('.buttonlinks 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
$('.buttonlinks 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('.buttonlinks select').get(0).selectedIndex = 2;
}
if(! settings.chart) settings.chart = {};
settings.chart.renderTo = $tab.attr('id') + "_chart_cnt";
$tab.find('.tabInnerContent')
.hide()
.after('<div class="liveChart" id="' + $tab.attr('id') + '_chart_cnt"></div>');
tabChart[$tab.attr('id')] = PMA_createChart(settings);
$(link).html(PMA_messages['strStaticData']);
$tab.find('.buttonlinks a.tabRefresh').hide();
$tab.find('.buttonlinks .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('.buttonlinks a.tabRefresh').show();
$tab.find('.buttonlinks select').get(0).selectedIndex=2;
$tab.find('.buttonlinks .refreshList').hide();
}
}
/* 3 Filtering functions */
$('#filterAlert').change(function() {
alertFilter = this.checked;
filterVariables();
});
$('#filterText').keyup(function(e) {
word = $(this).val().replace('_',' ');
if(word.length == 0) textFilter = null;
else textFilter = new RegExp("(^|_)" + word,'i');
text = word;
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 span').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 '<b>'+ this.point.name +'</b><br/> ' + Highcharts.numberFormat(this.percentage, 2) + ' %';
}
}
}
},
tooltip: {
formatter: function() {
return '<b>' + this.point.name + '</b><br/>' + Highcharts.numberFormat(this.y, 2) + '<br/>(' + 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('<img class="sortableIcon" src="' + pmaThemeImage + 'cleardot.gif" alt="">');
break;
case 'statustabs_allvars':
$('#serverstatusvariables').tablesorter({
sortList: [[0,0]],
widgets: ['zebra'],
headers: {
1: { sorter: 'fancyNumber' }
}
});
$('#serverstatusvariables tr:first th')
.append('<img class="sortableIcon" src="' + pmaThemeImage + 'cleardot.gif" alt="">');
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 = '<b>' + PMA_messages['strTotal'] + ': ' + sumTotal + '</b><br>';
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] + '<br>';
queryKeys.splice(maxIdx,1);
queryValues.splice(maxIdx,1);
num++;
}
if(sumOther>0)
pointInfo += PMA_messages['strOther'] + ': ' + sumOther;
return pointInfo;
}
/**** Monitor charting implementation ****/
/* Saves the previous ajax response for differential values */
var oldChartData = null;
// Holds about to created chart
var newChart = null;
var chartSpacing;
// Runtime parameter of the monitor
var runtime = {
// Holds all visible charts in the grid
charts: null,
// Current max points per chart (needed for auto calculation)
gridMaxPoints: 20,
// displayed time frame
xmin: -1,
xmax: -1,
// Stores the timeout handler so it can be cleared
refreshTimeout: null,
// Stores the GET request to refresh the charts
refreshRequest: null,
// Chart auto increment
chartAI: 0,
// To play/pause the monitor
redrawCharts: false,
// Object that contains a list of nodes that need to be retrieved from the server for chart updates
dataList: []
}
var monitorSettings = null;
var defaultMonitorSettings = {
columns: 4,
chartSize: { width: 295, height: 250 },
// Max points in each chart. Settings it to 'auto' sets gridMaxPoints to (chartwidth - 40) / 12
gridMaxPoints: 'auto',
/* Refresh rate of all grid charts in ms */
gridRefresh: 5000
}
// Allows drag and drop rearrange and print/edit icons on charts
var editMode = false;
var presetCharts = {
'cpu-WINNT': {
title: PMA_messages['strSystemCPUUsage'],
nodes: [{ dataType: 'cpu', name: 'loadavg', unit: '%'}]
},
'memory-WINNT': {
title: PMA_messages['strSystemMemory'],
nodes: [
{ dataType: 'memory', name: 'MemTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
]
},
'swap-WINNT': {
title: PMA_messages['strSystemSwap'],
nodes: [
{ dataType: 'memory', name: 'SwapTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
]
},
'cpu-Linux': {
title: PMA_messages['strSystemCPUUsage'],
nodes: [
{ dataType: 'cpu',
name: PMA_messages['strAverageLoad'],
unit: '%',
transformFn: function(cur, prev) {
if(prev == null) return undefined;
var diff_total = cur.busy + cur.idle - (prev.busy + prev.idle);
var diff_idle = cur.idle - prev.idle;
return 100*(diff_total - diff_idle) / diff_total;
}
}
]
},
'memory-Linux': {
title: PMA_messages['strSystemMemory'],
nodes: [
{ dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'Cached', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'Buffers', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'MemFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
],
settings: {
chart: {
type: 'area',
animation: false
},
plotOptions: {
area: {
stacking: 'percent'
}
}
}
},
'swap-Linux': {
title: PMA_messages['strSystemSwap'],
nodes: [
{ dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'SwapCached', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
{ dataType: 'memory', name: 'SwapFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] },
],
settings: {
chart: {
type: 'area',
animation: false
},
plotOptions: {
area: {
stacking: 'percent'
}
}
}
}
}
// Default setting
defaultChartGrid = {
'c0': { title: PMA_messages['strQuestions'],
nodes: [{ dataType: 'statusvar', name: 'Questions', display: 'differential' }]
},
'c1': {
title: PMA_messages['strChartConnectionsTitle'],
nodes: [ { dataType: 'statusvar', name: 'Connections', display: 'differential' },
{ dataType: 'proc', name: 'Processes'} ]
},
'c2': {
title: PMA_messages['strTraffic'],
nodes: [
{ dataType: 'statusvar', name: 'Bytes_sent', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] },
{ dataType: 'statusvar', name: 'Bytes_received', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] }
]
}
};
// Server is localhost => We can add cpu/memory/swap
if(server_db_isLocal) {
defaultChartGrid['c3'] = presetCharts['cpu-' + server_os];
defaultChartGrid['c4'] = presetCharts['memory-' + server_os];
defaultChartGrid['c5'] = presetCharts['swap-' + server_os];
}
var gridbuttons = {
cogButton: {
//enabled: true,
symbol: 'url(' + pmaThemeImage + 's_cog.png)',
x: -36,
symbolFill: '#B5C9DF',
hoverSymbolFill: '#779ABF',
_titleKey: 'settings',
menuName: 'gridsettings',
menuItems: [{
textKey: 'editChart',
onclick: function() {
alert('tbi');
}
}, {
textKey: 'removeChart',
onclick: function() {
removeChart(this);
}
}]
}
};
Highcharts.setOptions({
lang: {
settings: PMA_messages['strSettings'],
removeChart: PMA_messages['strRemoveChart'],
editChart: PMA_messages['strEditChart']
}
});
$('a[href="#rearrangeCharts"], a[href="#endChartEditMode"]').click(function() {
editMode = !editMode;
if($(this).attr('href') == '#endChartEditMode') editMode = false;
// Icon graphics have zIndex 19,20 and 21. Let's just hope nothing else has the same zIndex
$('table#chartGrid div svg').find('*[zIndex=20], *[zIndex=21], *[zIndex=19]').toggle(editMode)
$('a[href="#endChartEditMode"]').toggle(editMode);
if(editMode) {
// Close the settings popup
$('#statustabs_charting .popupContent').hide().removeClass('openedPopup');
$("#chartGrid").sortableTable({
ignoreRect: {
top: 8,
left: chartSize().width - 63,
width: 54,
height: 24
},
events: {
start: function() {
// console.log('start.');
},
// Drop event. The drag child element is moved into the drop element
// and vice versa. So the parameters are switched.
drop: function(drag, drop, pos) {
var dragKey, dropKey, dropRender;
var dragRender = $(drag).children().first().attr('id');
if($(drop).children().length > 0)
dropRender = $(drop).children().first().attr('id');
// Find the charts in the array
$.each(runtime.charts, function(key, value) {
if(value.chart.options.chart.renderTo == dragRender)
dragKey = key;
if(dropRender && value.chart.options.chart.renderTo == dropRender)
dropKey = key;
});
// Case 1: drag and drop are charts -> Switch keys
if(dropKey) {
if(dragKey) {
dragChart = runtime.charts[dragKey];
runtime.charts[dragKey] = runtime.charts[dropKey];
runtime.charts[dropKey] = dragChart;
} else {
// Case 2: drop is a empty cell => just completely rebuild the ids
var keys = [];
var dropKeyNum = parseInt(dropKey.substr(1));
var insertBefore = pos.col + pos.row * monitorSettings.columns;
var values = [];
var newChartList = {};
var c = 0;
$.each(runtime.charts, function(key, value) {
if(key != dropKey)
keys.push(key);
});
keys.sort();
// Rebuilds all ids, with the dragged chart correctly inserted
for(var i=0; i<keys.length; i++) {
if(keys[i] == insertBefore) {
newChartList['c' + (c++)] = runtime.charts[dropKey];
insertBefore = -1; // Insert ok
}
newChartList['c' + (c++)] = runtime.charts[keys[i]];
}
// Not inserted => put at the end
if(insertBefore != -1)
newChartList['c' + (c++)] = runtime.charts[dropKey];
runtime.charts = newChartList;
}
saveMonitor();
}
}
}
});
} else {
$("#chartGrid").sortableTable('destroy');
saveMonitor(); // Save settings
}
return false;
});
// global settings
$('div#statustabs_charting div.popupContent select[name="chartColumns"]').change(function() {
monitorSettings.columns = parseInt(this.value);
var newSize = chartSize();
// Empty cells should keep their size so you can drop onto them
$('table#chartGrid tr td').css('width',newSize.width + 'px');
/* Reorder all charts that it fills all column cells */
var numColumns;
var $tr = $('table#chartGrid tr:first');
var row=0;
while($tr.length != 0) {
numColumns = 1;
// To many cells in one row => put into next row
$tr.find('td').each(function() {
if(numColumns > monitorSettings.columns) {
if($tr.next().length == 0) $tr.after('<tr></tr>');
$tr.next().prepend($(this));
}
numColumns++;
});
// To little cells in one row => for each cell to little, move all cells backwards by 1
if($tr.next().length > 0) {
var cnt = monitorSettings.columns - $tr.find('td').length;
for(var i=0; i < cnt; i++) {
$tr.append($tr.next().find('td:first'));
$tr.nextAll().each(function() {
if($(this).next().length != 0)
$(this).append($(this).next().find('td:first'));
});
}
}
$tr = $tr.next();
row++;
}
/* Apply new chart size to all charts */
$.each(runtime.charts, function(key, value) {
value.chart.setSize(
newSize.width,
newSize.height,
false
);
});
if(monitorSettings.gridMaxPoints == 'auto')
runtime.gridMaxPoints = Math.round((newSize.width - 40) / 12);
runtime.xmin = new Date().getTime() - server_time_diff - runtime.gridMaxPoints * monitorSettings.gridRefresh;
runtime.xmax = new Date().getTime() - server_time_diff + monitorSettings.gridRefresh;
if(editMode)
$("#chartGrid").sortableTable('refresh');
saveMonitor(); // Save settings
});
$('div#statustabs_charting div.popupContent select[name="gridChartRefresh"]').change(function() {
monitorSettings.gridRefresh = parseInt(this.value) * 1000;
clearTimeout(runtime.refreshTimeout);
if(runtime.refreshRequest)
runtime.refreshRequest.abort();
runtime.xmin = new Date().getTime() - server_time_diff - runtime.gridMaxPoints * monitorSettings.gridRefresh;
runtime.xmax = new Date().getTime() - server_time_diff + monitorSettings.gridRefresh;
$.each(runtime.charts, function(key, value) {
value.chart.xAxis[0].setExtremes(runtime.xmin, runtime.xmax, false);
});
runtime.refreshTimeout = setTimeout(refreshChartGrid, monitorSettings.gridRefresh);
saveMonitor(); // Save settings
});
$('a[href="#addNewChart"]').click(function() {
var dlgButtons = { };
dlgButtons[PMA_messages['strAddChart']] = function() {
var type = $('input[name="chartType"]:checked').val();
if(type == 'cpu' || type == 'memory' || type=='swap')
newChart = presetCharts[type + '-' + server_os];
else {
if(! newChart || ! newChart.nodes || newChart.nodes.length == 0) {
alert(PMA_messages['strAddOneSeriesWarning']);
return;
}
}
newChart.title = $('input[name="chartTitle"]').attr('value');
// Add a cloned object to the chart grid
addChart($.extend(true, {}, newChart));
newChart = null;
saveMonitor(); // Save settings
$(this).dialog("close");
}
dlgButtons[PMA_messages['strClose']] = function() {
newChart = null;
$('span#clearSeriesLink').hide();
$('#seriesPreview').html('');
$(this).dialog("close");
}
$('div#addChartDialog').dialog({
width:'auto',
height:'auto',
buttons: dlgButtons
});
$('div#addChartDialog #seriesPreview').html('<i>' + PMA_messages['strNone'] + '</i>');
return false;
});
$('a[href="#pauseCharts"]').click(function() {
runtime.redrawCharts = ! runtime.redrawCharts;
if(! runtime.redrawCharts)
$(this).html('<img src="' + pmaThemeImage + 'play.png" alt="" /> ' + PMA_messages['strResumeMonitor']);
else {
$(this).html('<img src="' + pmaThemeImage + 'pause.png" alt="" /> ' + PMA_messages['strPauseMonitor']);
if(runtime.charts == null) {
initGrid();
$('a[href="#settingsPopup"]').show();
}
}
return false;
});
$('a[href="#monitorInstructionsDialog"]').click(function() {
var $dialog = $('div#monitorInstructionsDialog');
$dialog.dialog({
width: 595,
height: 'auto'
}).find('img.ajaxIcon').show();
var loadLogVars = function(getvars) {
vars = { ajax_request: true, logging_vars: true };
if(getvars) $.extend(vars,getvars);
$.get('server_status.php?' + url_query, vars,
function(data) {
var logVars = $.parseJSON(data),
icon = 's_success.png', msg='', str='';
if(logVars['general_log'] == 'ON') {
if(logVars['slow_query_log'] == 'ON')
msg = PMA_messages['strBothLogOn'];
else
msg = PMA_messages['strGenLogOn'];
}
if(msg.length == 0 && logVars['slow_query_log'] == 'ON') {
msg = PMA_messages['strSlowLogOn'];
}
if(msg.length == 0) {
icon = 's_error.png';
msg = PMA_messages['strBothLogOff'];
}
str = '<b>' + PMA_messages['strCurrentSettings'] + '</b><br><div class="smallIndent">';
str += '<img src="' + pmaThemeImage + icon + '" alt=""/> ' + msg + '<br />';
if(logVars['log_output'] != 'TABLE')
str += '<img src="' + pmaThemeImage + 's_error.png" alt=""/> ' + PMA_messages['strLogOutNotTable'] + '<br />';
else
str += '<img src="' + pmaThemeImage + 's_success.png" alt=""/> ' + PMA_messages['strLogOutIsTable'] + '<br />';
if(logVars['slow_query_log'] == 'ON') {
if(logVars['long_query_time'] > 2)
str += '<img src="' + pmaThemeImage + 's_attention.png" alt=""/> '
+ $.sprintf(PMA_messages['strSmallerLongQueryTimeAdvice'], logVars['long_query_time'])
+ '<br />';
if(logVars['long_query_time'] < 2)
str += '<img src="' + pmaThemeImage + 's_success.png" alt=""/> '
+ $.sprintf(PMA_messages['strLongQueryTimeSet'], logVars['long_query_time'])
+ '<br />';
}
str += '</div>';
if(is_superuser) {
str += '<p></p><b>Change settings</b>';
str += '<div class="smallIndent">';
str += PMA_messages['strSettingsAppliedGlobal'] + '<br/>';
var varValue = 'TABLE';
if(logVars['log_output'] == 'TABLE') varValue = 'FILE';
str += '- <a class="set" href="#log_output-' + varValue + '">'
+ $.sprintf(PMA_messages['strSetLogOutput'], varValue)
+ ' </a><br />';
if(logVars['general_log'] != 'ON')
str += '- <a class="set" href="#general_log-ON">'
+ $.sprintf(PMA_messages['strEnableVar'], 'general_log')
+ ' </a><br />';
else
str += '- <a class="set" href="#general_log-OFF">'
+ $.sprintf(PMA_messages['strDisableVar'], 'general_log')
+ ' </a><br />';
if(logVars['slow_query_log'] != 'ON')
str += '- <a class="set" href="#slow_query_log-ON">'
+ $.sprintf(PMA_messages['strEnableVar'], 'slow_query_log')
+ ' </a><br />';
else
str += '- <a class="set" href="#slow_query_log-OFF">'
+ $.sprintf(PMA_messages['strDisableVar'], 'slow_query_log')
+ ' </a><br />';
varValue = 5;
if(logVars['long_query_time'] > 2) varValue = 1;
str += '- <a class="set" href="#long_query_time-' + varValue + '">'
+ $.sprintf(PMA_messages['setSetLongQueryTime'], varValue)
+ ' </a><br />';
} else
str += PMA_messages['strNoSuperUser'] + '<br/>';
str += '</div>';
$dialog.find('div.monitorUse').toggle(
logVars['log_output'] == 'TABLE' && (logVars['slow_query_log'] == 'ON' || logVars['general_log'] == 'ON')
);
$dialog.find('div.ajaxContent').html(str);
$dialog.find('img.ajaxIcon').hide();
$dialog.find('a.set').click(function() {
var nameValue = $(this).attr('href').split('-');
loadLogVars({ varName: nameValue[0].substr(1), varValue: nameValue[1]});
$dialog.find('img.ajaxIcon').show();
});
}
);
}
loadLogVars();
return false;
});
$('input[name="chartType"]').change(function() {
$('#chartVariableSettings').toggle(this.checked && this.value == 'variable');
var title = $('input[name="chartTitle"]').attr('value');
if(title == PMA_messages['strChartTitle'] || title == $('label[for="'+$('input[name="chartTitle"]').data('lastRadio')+'"]').text()) {
$('input[name="chartTitle"]').data('lastRadio',$(this).attr('id'));
$('input[name="chartTitle"]').attr('value',$('label[for="'+$(this).attr('id')+'"]').text());
}
});
$('input[name="useDivisor"]').change(function() {
$('span.divisorInput').toggle(this.checked);
});
$('input[name="useUnit"]').change(function() {
$('span.unitInput').toggle(this.checked);
});
$('select[name="varChartList"]').change(function () {
if(this.selectedIndex!=0)
$('#variableInput').attr('value',this.value);
});
$('a[href="#kibDivisor"]').click(function() {
$('input[name="valueDivisor"]').attr('value',1024);
$('input[name="valueUnit"]').attr('value',PMA_messages['strKiB']);
$('span.unitInput').toggle(true);
$('input[name="useUnit"]').prop('checked',true);
return false;
});
$('a[href="#mibDivisor"]').click(function() {
$('input[name="valueDivisor"]').attr('value',1024*1024);
$('input[name="valueUnit"]').attr('value',PMA_messages['strMiB']);
$('span.unitInput').toggle(true);
$('input[name="useUnit"]').prop('checked',true);
return false;
});
$('a[href="#submitClearSeries"]').click(function() {
$('#seriesPreview').html('<i>' + PMA_messages['strNone'] + '</i>');
newChart = null;
$('span#clearSeriesLink').hide();
});
$('a[href="#submitAddSeries"]').click(function() {
if($('input#variableInput').attr('value').length == 0) return false;
if(newChart == null) {
$('#seriesPreview').html('');
newChart = {
title: $('input[name="chartTitle"]').attr('value'),
nodes: []
}
}
var serie = {
dataType:'statusvar',
name: $('input#variableInput').attr('value'),
display: $('input[name="differentialValue"]').attr('checked') ? 'differential' : '',
};
if(serie.name == 'Processes') serie.dataType='proc';
if($('input[name="useDivisor"]').attr('checked'))
serie.valueDivisor = parseInt($('input[name="valueDivisor"]').attr('value'));
if($('input[name="useUnit"]').attr('checked'))
serie.unit = $('input[name="valueUnit"]').attr('value');
var str = serie.display == 'differential' ? ', ' + PMA_messages['strDifferential'] : '';
str += serie.valueDivisor ? (', ' + $.sprintf(PMA_messages['strDividedBy'], serie.valueDivisor)) : '';
$('#seriesPreview').append('- ' + serie.name + str + '<br>');
newChart.nodes.push(serie);
$('input#variableInput').attr('value','');
$('input[name="differentialValue"]').attr('checked',true);
$('input[name="useDivisor"]').attr('checked',false);
$('input[name="useUnit"]').attr('checked',false);
$('input[name="useDivisor"]').trigger('change');
$('input[name="useUnit"]').trigger('change');
$('select[name="varChartList"]').get(0).selectedIndex=0;
$('span#clearSeriesLink').show();
return false;
});
$("input#variableInput").autocomplete({
source: variableNames
});
function initGrid() {
var settings;
var series;
/* Apply default values & config */
if(window.localStorage) {
if(window.localStorage['monitorCharts'])
runtime.charts = $.parseJSON(window.localStorage['monitorCharts']);
if(window.localStorage['monitorSettings'])
monitorSettings = $.parseJSON(window.localStorage['monitorSettings']);
$('a[href="#clearMonitorConfig"]').toggle(runtime.charts != null);
}
if(runtime.charts == null)
runtime.charts = defaultChartGrid;
if(monitorSettings == null)
monitorSettings = defaultMonitorSettings;
$('select[name="gridChartRefresh"]').attr('value',monitorSettings.gridRefresh / 1000);
$('select[name="chartColumns"]').attr('value',monitorSettings.columns);
if(monitorSettings.gridMaxPoints == 'auto')
runtime.gridMaxPoints = Math.round((monitorSettings.chartSize.width - 40) / 12);
else
runtime.gridMaxPoints = monitorSettings.gridMaxPoints;
runtime.xmin = new Date().getTime() - server_time_diff - runtime.gridMaxPoints * monitorSettings.gridRefresh;
runtime.xmax = new Date().getTime() - server_time_diff + monitorSettings.gridRefresh;
/* Calculate how much spacing there is between each chart */
$('table#chartGrid').html('<tr><td></td><td></td></tr><tr><td></td><td></td></tr>');
chartSpacing = {
width: $('table#chartGrid td:nth-child(2)').offset().left - $('table#chartGrid td:nth-child(1)').offset().left,
height: $('table#chartGrid tr:nth-child(2) td:nth-child(2)').offset().top - $('table#chartGrid tr:nth-child(1) td:nth-child(1)').offset().top
}
$('table#chartGrid').html('');
/* Add all charts - in correct order */
var keys = [];
$.each(runtime.charts, function(key, value) {
keys.push(key);
});
keys.sort();
for(var i=0; i<keys.length; i++)
addChart(runtime.charts[keys[i]],true);
/* Fill in missing cells */
var numCharts = $('table#chartGrid .monitorChart').length;
var numMissingCells = (monitorSettings.columns - numCharts % monitorSettings.columns) % monitorSettings.columns;
for(var i=0; i < numMissingCells; i++) {
$('table#chartGrid tr:last').append('<td></td>');
}
// Empty cells should keep their size so you can drop onto them
$('table#chartGrid tr td').css('width',chartSize().width + 'px');
buildRequiredDataList();
refreshChartGrid();
}
function chartSize() {
var wdt = $('div#logTable').innerWidth() / monitorSettings.columns - (monitorSettings.columns - 1) * chartSpacing.width;
return {
width: wdt,
height: 0.75 * wdt
}
}
function addChart(chartObj, initialize) {
series = [];
for(var j=0; j<chartObj.nodes.length; j++)
series.push(chartObj.nodes[j]);
settings = {
chart: {
renderTo: 'gridchart' + runtime.chartAI,
width: chartSize().width,
height: chartSize().height,
marginRight: 5,
zoomType: 'x',
events: {
selection: function(event) {
if(editMode) return false;
var extremesObject = event.xAxis[0],
min = extremesObject.min,
max = extremesObject.max;
$('#logAnalyseDialog').html(
'<p>' + PMA_messages['strSelectedTimeRange']
+ Highcharts.dateFormat('%H:%M:%S',new Date(min)) + ' - '
+ Highcharts.dateFormat('%H:%M:%S',new Date(max)) + '</p>'
+ '<input type="checkbox" id="groupInserts" value="1" checked="checked" />'
+ '<label for="groupData">' + PMA_messages['strGroupInserts'] + '</label>'
+ PMA_messages['strLogAnalyseInfo']
);
var dlgBtns = { };
dlgBtns[PMA_messages['strFromSlowLog']] = function() {
loadLogStatistics(
{ src: 'slow', start: min, end: max, groupInserts: $('input#groupInserts').attr('checked') }
);
$(this).dialog("close");
}
dlgBtns[PMA_messages['strFromGeneralLog']] = function() {
loadLogStatistics(
{ src: 'general', start: min, end: max, groupInserts: $('input#groupInserts').attr('checked') }
);
$(this).dialog("close");
}
$('#logAnalyseDialog').dialog({
width: 'auto',
height: 'auto',
buttons: dlgBtns
});
return false;
}
}
},
xAxis: {
min: runtime.xmin,
max: runtime.xmax
},
yAxis: {
title: {
text: ''
}
},
tooltip: {
formatter: function() {
var s = '<b>'+Highcharts.dateFormat('%H:%M:%S', this.x)+'</b>';
$.each(this.points, function(i, point) {
s += '<br/><span style="color:'+point.series.color+'">'+ point.series.name +':</span> '+
((parseInt(point.y) == point.y) ? point.y : Highcharts.numberFormat(this.y, 2)) + ' ' + (point.series.options.unit || '');
});
return s;
},
shared: true
},
legend: {
enabled: false
},
series: series,
buttons: gridbuttons,
title: { text: chartObj.title },
};
if(chartObj.settings)
$.extend(true,settings,chartObj.settings);
if($('#'+settings.chart.renderTo).length==0) {
var numCharts = $('table#chartGrid .monitorChart').length;
if(numCharts == 0 || !( numCharts % monitorSettings.columns))
$('table#chartGrid').append('<tr></tr>');
$('table#chartGrid tr:last').append('<td><div class="ui-state-default monitorChart" id="'+settings.chart.renderTo+'"></div></td>');
}
chartObj.chart = PMA_createChart(settings);
chartObj.numPoints = 0;
if(initialize != true) {
runtime.charts['c'+runtime.chartAI] = chartObj;
buildRequiredDataList();
}
// Edit,Print icon only in edit mode
$('table#chartGrid div svg').find('*[zIndex=20], *[zIndex=21], *[zIndex=19]').toggle(editMode)
runtime.chartAI++;
}
function removeChart(chartObj) {
var htmlnode = chartObj.options.chart.renderTo;
if(! htmlnode ) return;
$.each(runtime.charts, function(key, value) {
if(value.chart.options.chart.renderTo == htmlnode) {
delete runtime.charts[key];
return false;
}
});
buildRequiredDataList();
// Using settimeout() because clicking the remove link fires an onclick event
// which throws an error when the chart is destroyed
setTimeout(function() {
chartObj.destroy();
$('li#' + htmlnode).remove();
},10);
saveMonitor(); // Save settings
}
function refreshChartGrid() {
/* Send to server */
runtime.refreshRequest = $.post('server_status.php?'+url_query, { ajax_request: true, chart_data: 1, type: 'chartgrid', requiredData: $.toJSON(runtime.dataList) },function(data) {
var chartData = $.parseJSON(data);
var value, i=0;
var diff;
/* Update values in each graph */
$.each(runtime.charts, function(orderKey, elem) {
var key = elem.chartID;
// If newly added chart, we have no data for it yet
if(! chartData[key]) return;
// Draw all points
for(var j=0; j < elem.nodes.length; j++) {
value = chartData[key][j].y;
if(i==0 && j==0) {
if(oldChartData==null) diff = chartData.x - runtime.xmax;
else diff = parseInt(chartData.x - oldChartData.x);
runtime.xmin+= diff;
runtime.xmax+= diff;
}
elem.chart.xAxis[0].setExtremes(runtime.xmin, runtime.xmax, false);
if(elem.nodes[j].display == 'differential') {
if(oldChartData == null || oldChartData[key] == null) continue;
value -= oldChartData[key][j].y;
}
if(elem.nodes[j].valueDivisor)
value = value / elem.nodes[j].valueDivisor;
if(elem.nodes[j].transformFn) {
value = elem.nodes[j].transformFn(
chartData[key][j],
(oldChartData == null) ? null : oldChartData[key][j]
);
}
if(value != undefined)
elem.chart.series[j].addPoint(
{ x: chartData.x, y: value },
false,
elem.numPoints >= runtime.gridMaxPoints
);
}
i++;
runtime.charts[orderKey].numPoints++;
if(runtime.redrawCharts)
elem.chart.redraw();
});
oldChartData = chartData;
runtime.refreshTimeout = setTimeout(refreshChartGrid, monitorSettings.gridRefresh);
});
}
/* Build list of nodes that need to be retrieved */
function buildRequiredDataList() {
runtime.dataList = {};
// Store an own id, because the property name is subject of reordering, thus destroying our mapping with runtime.charts <=> runtime.dataList
var chartID = 0;
$.each(runtime.charts, function(key, chart) {
runtime.dataList[chartID] = chart.nodes;
runtime.charts[key].chartID = chartID;
chartID++;
});
}
function loadLogStatistics(opts) {
var tableStr = '';
var logRequest = null;
var groupInsert = false;
if(opts.groupInserts)
groupInserts = true;
$('#loadingLogsDialog').html(PMA_messages['strAnalysingLogs'] + ' <img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">');
$('#loadingLogsDialog').dialog({
width: 'auto',
height: 'auto',
buttons: {
'Cancel request': function() {
if(logRequest != null)
logRequest.abort();
$(this).dialog("close");
}
}
});
var formatValue = function(name, value) {
switch(name) {
case 'user_host':
return value.replace(/(\[.*?\])+/g,'');
}
return value;
}
logRequest = $.get('server_status.php?'+url_query,
{ ajax_request: true, log_data: 1, type: opts.src, time_start: Math.round(opts.start / 1000), time_end: Math.round(opts.end / 1000), groupInserts: groupInserts },
function(data) {
var data = $.parseJSON(data);
var rows = data.rows;
var cols = new Array();
if(rows.length != 0) {
tableStr = '<table border="0" class="sortable">';
for(var i=0; i < rows.length; i++) {
if(i == 0) {
tableStr += '<thead>';
$.each(rows[0],function(key, value) {
cols.push(key);
});
tableStr += '<tr><th class="nowrap">' + cols.join('</th><th class="nowrap">') + '</th></tr>';
tableStr += '</thead><tbody>';
}
tableStr += '<tr>';
for(var j=0; j < cols.length; j++)
tableStr += '<td>' + formatValue(cols[j], rows[i][cols[j]]) + '</td>';
tableStr += '</tr>';
}
tableStr+='</tbody></table>';
$('#logTable').html(tableStr);
// Append a tooltip to the count column, if there exist one
if($('#logTable th:last').html() == '#') {
$('#logTable th:last').append('&nbsp;<img class="qroupedQueryInfoIcon" src="' + pmaThemeImage + 'b_docs.png" alt="" />');
var qtipContent = PMA_messages['strCountColumnExplanation'];
if(groupInserts) qtipContent += '<p>' + PMA_messages['strMoreCountColumnExplanation'] + '</p>';
$('img.qroupedQueryInfoIcon').qtip({
content: qtipContent,
position: {
corner: {
target: 'bottomMiddle',
tooltip: 'topRight'
}
},
hide: { delay: 1000 }
})
}
$('div#logTable table').tablesorter({
sortList: [[0,1]],
widgets: ['zebra']
});
$('div#logTable table thead th')
.append('<img class="sortableIcon" src="' + pmaThemeImage + 'cleardot.gif" alt="">');
$('#loadingLogsDialog').html('<p>' + PMA_messages['strLogDataLoaded'] + '</p>');
$.each(data.sum, function(key, value) {
key = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
if(key == 'Total') key = '<b>' + key + '</b>';
$('#loadingLogsDialog').append(key + ': ' + value + '<br/>');
});
var dlgBtns = {};
dlgBtns[PMA_messages['strJumpToTable']] = function() {
$(this).dialog("close");
$(document).scrollTop($('div#logTable').offset().top);
}
$('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns);
} else {
$('#loadingLogsDialog').html('<p>' + PMA_messages['strNoDataFound'] + '</p>');
var dlgBtns = {};
dlgBtns[PMA_messages['strClose']] = function() {
$(this).dialog("close");
}
$('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns );
}
}
);
}
function saveMonitor() {
var gridCopy = {};
$.each(runtime.charts, function(key, elem) {
gridCopy[key] = {};
gridCopy[key].nodes = elem.nodes;
gridCopy[key].settings = elem.settings;
gridCopy[key].title = elem.title;
});
if(window.localStorage) {
window.localStorage['monitorCharts'] = $.toJSON(gridCopy);
window.localStorage['monitorSettings'] = $.toJSON(monitorSettings);
}
$('a[href="#clearMonitorConfig"]').show();
}
$('a[href="#clearMonitorConfig"]').click(function() {
window.localStorage.removeItem('monitorCharts');
window.localStorage.removeItem('monitorSettings');
$(this).hide();
});
});