/* vim: set expandtab sw=4 ts=4 sts=4: */ $(function() { // Show tab links $('div#statustabs_charting div.tabLinks').show(); $('div#statustabs_charting img#loadingMonitorIcon').remove(); // Codemirror is loaded on demand so we might need to initialize it if (! codemirror_editor) { var $elm = $('#sqlquery'); if ($elm.length > 0 && typeof CodeMirror != 'undefined') { codemirror_editor = CodeMirror.fromTextArea( $elm[0], { lineNumbers: true, matchBrackets: true, indentUnit: 4, mode: "text/x-mysql" } ); } } // Timepicker is loaded on demand so we need to initialize datetime fields from the 'load log' dialog $('div#logAnalyseDialog .datetimefield').each(function() { PMA_addDatepicker($(this)); }); /**** Monitor charting implementation ****/ /* Saves the previous ajax response for differential values */ var oldChartData = null; // Holds about to created chart var newChart = null; var chartSpacing; // Whenever the monitor object (runtime.charts) or the settings object (monitorSettings) // changes in a way incompatible to the previous version, increase this number // It will reset the users monitor and settings object in his localStorage to the default configuration var monitorProtocolVersion = '1.0'; // Runtime parameter of the monitor, is being fully set in initGrid() var runtime = { // Holds all visible charts in the grid charts: null, // 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: [], // Current max points per chart (needed for auto calculation) gridMaxPoints: 20, // displayed time frame xmin: -1, xmax: -1 }; var monitorSettings = null; var defaultMonitorSettings = { columns: 3, 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; /* List of preconfigured charts that the user may select */ var presetCharts = { // Query cache efficiency 'qce': { title: PMA_messages['strQueryCacheEfficiency'], series: [ { label: PMA_messages['strQueryCacheEfficiency'] } ], nodes: [ { dataPoints: [{type: 'statusvar', name: 'Qcache_hits'}, {type: 'statusvar', name: 'Com_select'}], transformFn: 'qce' } ], maxYLabel: [] }, // Query cache usage 'qcu': { title: PMA_messages['strQueryCacheUsage'], series: [ { label: PMA_messages['strQueryCacheUsed'] } ], nodes: [ { dataPoints: [{type: 'statusvar', name: 'Qcache_free_memory'}, {type: 'servervar', name: 'query_cache_size'}], transformFn: 'qcu' } ], maxYLabel: [] } }; /* Add OS specific system info charts to the preset chart list */ switch(server_os) { case 'WINNT': $.extend(presetCharts, { 'cpu': { title: PMA_messages['strSystemCPUUsage'], series: [ { label: PMA_messages['strAverageLoad'] } ], nodes: [ { dataPoints: [{ type: 'cpu', name: 'loadavg'}] } ], maxYLabel: [] }, 'memory': { title: PMA_messages['strSystemMemory'], series: [ { label: PMA_messages['strTotalMemory'], fill:true, stackSeries: true }, { dataType: 'memory', label: PMA_messages['strUsedMemory'], fill:true, stackSeries: true } ], nodes: [{ dataPoints: [{ type: 'memory', name: 'MemTotal' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 } ], maxYLabel: [] }, 'swap': { title: PMA_messages['strSystemSwap'], series: [ { label: PMA_messages['strTotalSwap'], fill:true, stackSeries: true }, { label: PMA_messages['strUsedSwap'], fill:true, stackSeries: true } ], nodes: [{ dataPoints: [{ type: 'memory', name: 'SwapTotal' }]}, { dataPoints: [{ type: 'memory', name: 'SwapUsed' }]} ], maxYLabel: [] } }); break; case 'Linux': $.extend(presetCharts, { 'cpu': { title: PMA_messages['strSystemCPUUsage'], series: [ { label: PMA_messages['strAverageLoad'] } ], nodes: [{ dataPoints: [{ type: 'cpu', name: 'irrelevant' }], transformFn: 'cpu-linux'}], maxYLabel: [] }, 'memory': { title: PMA_messages['strSystemMemory'], series: [ { label: PMA_messages['strUsedMemory'], fill:true, stackSeries: true}, { label: PMA_messages['strCachedMemory'], fill:true, stackSeries: true}, { label: PMA_messages['strBufferedMemory'], fill:true, stackSeries: true}, { label: PMA_messages['strFreeMemory'], fill:true, stackSeries: true} ], nodes: [ { dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'Cached' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'Buffers' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemFree' }], valueDivisor: 1024 } ], maxYLabel: [] }, 'swap': { title: PMA_messages['strSystemSwap'], series: [ { label: PMA_messages['strUsedSwap'], fill:true, stackSeries: true}, { label: PMA_messages['strCachedSwap'], fill:true, stackSeries: true}, { label: PMA_messages['strFreeSwap'], fill:true, stackSeries: true} ], nodes: [ { dataPoints: [{ type: 'memory', name: 'SwapUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapCached' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapFree' }], valueDivisor: 1024 } ], maxYLabel: [] } }); break; case 'SunOS': $.extend(presetCharts, { 'cpu': { title: PMA_messages['strSystemCPUUsage'], series: [ { label: PMA_messages['strAverageLoad'] } ], nodes: [ { dataPoints: [{ type: 'cpu', name: 'loadavg'}] } ], maxYLabel: [] }, 'memory': { title: PMA_messages['strSystemMemory'], series: [ { label: PMA_messages['strUsedMemory'], fill:true, stackSeries: true}, { label: PMA_messages['strFreeMemory'], fill:true, stackSeries: true} ], nodes: [ { dataPoints: [{ type: 'memory', name: 'MemUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'MemFree' }], valueDivisor: 1024 } ], maxYLabel: [] }, 'swap': { title: PMA_messages['strSystemSwap'], series: [ { label: PMA_messages['strUsedSwap'], fill:true, stackSeries: true}, { label: PMA_messages['strFreeSwap'], fill:true, stackSeries: true} ], nodes: [ { dataPoints: [{ type: 'memory', name: 'SwapUsed' }], valueDivisor: 1024 }, { dataPoints: [{ type: 'memory', name: 'SwapFree' }], valueDivisor: 1024 } ], maxYLabel: [] } }); break; } // Default setting for the chart grid defaultChartGrid = { 'c0': { title: PMA_messages['strQuestions'], series: [{label: PMA_messages['strQuestions']}], nodes: [{dataPoints: [{ type: 'statusvar', name: 'Questions' }], display: 'differential' }], maxYLabel: [] }, 'c1': { title: PMA_messages['strChartConnectionsTitle'], series: [ { label: PMA_messages['strConnections']}, { label: PMA_messages['strProcesses']} ], nodes: [ { dataPoints: [{ type: 'statusvar', name: 'Connections' }], display: 'differential' }, { dataPoints: [{ type: 'proc', name: 'processes' }] } ], maxYLabel: [] }, 'c2': { title: PMA_messages['strTraffic'], series: [ { label: PMA_messages['strBytesSent']}, { label: PMA_messages['strBytesReceived']} ], nodes: [ { dataPoints: [{ type: 'statusvar', name: 'Bytes_sent' }], display: 'differential', valueDivisor: 1024 }, { dataPoints: [{ type: 'statusvar', name: 'Bytes_received' }], display: 'differential', valueDivisor: 1024 } ], maxYLabel: [] } }; // Server is localhost => We can add cpu/memory/swap to the default chart if (server_db_isLocal) { defaultChartGrid['c3'] = presetCharts['cpu']; defaultChartGrid['c4'] = presetCharts['memory']; defaultChartGrid['c5'] = presetCharts['swap']; } /* Buttons that are on the top right corner of each chart */ 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() { editChart(this); } }, { 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: { // 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 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.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['axes']['xaxis']['max'] = runtime.xmax; value.chart['axes']['xaxis']['min'] = runtime.xmin; }); 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 == 'preset') { newChart = presetCharts[$('div#addChartDialog select[name="presetCharts"]').prop('value')]; } else { // If user builds his own chart, it's being set/updated each time he adds a series // So here we only warn if he didn't add a series yet if (! newChart || ! newChart.nodes || newChart.nodes.length == 0) { alert(PMA_messages['strAddOneSeriesWarning']); return; } } newChart.title = $('input[name="chartTitle"]').val(); // 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"); }; var $presetList = $('div#addChartDialog select[name="presetCharts"]'); if ($presetList.html().length == 0) { $.each(presetCharts, function(key, value) { $presetList.append(''); }); $presetList.change(function() { $('input#chartPreset').trigger('click'); $('input[name="chartTitle"]').val(presetCharts[$(this).val()].title); }); } $('div#addChartDialog').dialog({ width: 'auto', height: 'auto', buttons: dlgButtons }); $('div#addChartDialog #seriesPreview').html('' + PMA_messages['strNone'] + ''); return false; }); $('a[href="#exportMonitorConfig"]').click(function() { 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; }); var exportData = { monitorCharts: gridCopy, monitorSettings: monitorSettings }; var $form; $('body').append($form = $('
')); $form.append(''); $form.submit(); $form.remove(); }); $('a[href="#importMonitorConfig"]').click(function() { $('div#emptyDialog').dialog({title: PMA_messages['strImportDialogTitle']}); $('div#emptyDialog').html(PMA_messages['strImportDialogMessage'] + ':
' + '
'); var dlgBtns = {}; dlgBtns[PMA_messages['strImport']] = function() { var $iframe, $form; $('body').append($iframe = $('')); var d = $iframe[0].contentWindow.document; d.open(); d.close(); mew = d; $iframe.load(function() { var json; // Try loading config try { var data = $('body', $('iframe#monitorConfigUpload')[0].contentWindow.document).html(); // Chrome wraps around '
' to any text content -.-
                    json = $.secureEvalJSON(data.substring(data.indexOf("{"), data.lastIndexOf("}") + 1));
                } catch (err) {
                    alert(PMA_messages['strFailedParsingConfig']);
                    $('div#emptyDialog').dialog('close');
                    return;
                }

                // Basic check, is this a monitor config json?
                if (!json || ! json.monitorCharts || ! json.monitorCharts) {
                    alert(PMA_messages['strFailedParsingConfig']);
                    $('div#emptyDialog').dialog('close');
                    return;
                }

                // If json ok, try applying config
                try {
                    window.localStorage['monitorCharts'] = $.toJSON(json.monitorCharts);
                    window.localStorage['monitorSettings'] = $.toJSON(json.monitorSettings);
                    rebuildGrid();
                } catch(err) {
                    alert(PMA_messages['strFailedBuildingGrid']);
                    // If an exception is thrown, load default again
                    window.localStorage.removeItem('monitorCharts');
                    window.localStorage.removeItem('monitorSettings');
                    rebuildGrid();
                }

                $('div#emptyDialog').dialog('close');
            });

            $("body", d).append($form = $('div#emptyDialog').find('form'));
            $form.submit();
            $('div#emptyDialog').append('');
        };

        dlgBtns[PMA_messages['strCancel']] = function() {
            $(this).dialog('close');
        };


        $('div#emptyDialog').dialog({
            width: 'auto',
            height: 'auto',
            buttons: dlgBtns
        });
    });

    $('a[href="#clearMonitorConfig"]').click(function() {
        window.localStorage.removeItem('monitorCharts');
        window.localStorage.removeItem('monitorSettings');
        window.localStorage.removeItem('monitorVersion');
        $(this).hide();
        rebuildGrid();
    });

    $('a[href="#pauseCharts"]').click(function() {
        runtime.redrawCharts = ! runtime.redrawCharts;
        if (! runtime.redrawCharts) {
            $(this).html(PMA_getImage('play.png') + ' ' + PMA_messages['strResumeMonitor']);
        } else {
            $(this).html(PMA_getImage('pause.png') + ' ' + PMA_messages['strPauseMonitor']);
            if (! runtime.charts) {
                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.message),
                        icon = PMA_getImage('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 = PMA_getImage('s_error.png');
                        msg = PMA_messages['strBothLogOff'];
                    }

                    str = '' + PMA_messages['strCurrentSettings'] + '
'; str += icon + msg + '
'; if (logVars['log_output'] != 'TABLE') { str += PMA_getImage('s_error.png') + ' ' + PMA_messages['strLogOutNotTable'] + '
'; } else { str += PMA_getImage('s_success.png') + ' ' + PMA_messages['strLogOutIsTable'] + '
'; } if (logVars['slow_query_log'] == 'ON') { if (logVars['long_query_time'] > 2) { str += PMA_getImage('s_attention.png') + ' ' + $.sprintf(PMA_messages['strSmallerLongQueryTimeAdvice'], logVars['long_query_time']) + '
'; } if (logVars['long_query_time'] < 2) { str += PMA_getImage('s_success.png') + ' ' + $.sprintf(PMA_messages['strLongQueryTimeSet'], logVars['long_query_time']) + '
'; } } str += '
'; if (is_superuser) { str += '

' + PMA_messages['strChangeSettings'] + ''; str += '
'; str += PMA_messages['strSettingsAppliedGlobal'] + '
'; var varValue = 'TABLE'; if (logVars['log_output'] == 'TABLE') { varValue = 'FILE'; } str += '- ' + $.sprintf(PMA_messages['strSetLogOutput'], varValue) + '
'; if (logVars['general_log'] != 'ON') { str += '- ' + $.sprintf(PMA_messages['strEnableVar'], 'general_log') + '
'; } else { str += '- ' + $.sprintf(PMA_messages['strDisableVar'], 'general_log') + '
'; } if (logVars['slow_query_log'] != 'ON') { str += '- ' + $.sprintf(PMA_messages['strEnableVar'], 'slow_query_log') + '
'; } else { str += '- ' + $.sprintf(PMA_messages['strDisableVar'], 'slow_query_log') + '
'; } varValue = 5; if (logVars['long_query_time'] > 2) { varValue = 1; } str += '- ' + $.sprintf(PMA_messages['setSetLongQueryTime'], varValue) + '
'; } else { str += PMA_messages['strNoSuperUser'] + '
'; } str += '
'; $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"]').val(); if (title == PMA_messages['strChartTitle'] || title == $('label[for="' + $('input[name="chartTitle"]').data('lastRadio') + '"]').text() ) { $('input[name="chartTitle"]') .data('lastRadio', $(this).attr('id')) .val($('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').val(this.value); } }); $('a[href="#kibDivisor"]').click(function() { $('input[name="valueDivisor"]').val(1024); $('input[name="valueUnit"]').val(PMA_messages['strKiB']); $('span.unitInput').toggle(true); $('input[name="useUnit"]').prop('checked', true); return false; }); $('a[href="#mibDivisor"]').click(function() { $('input[name="valueDivisor"]').val(1024*1024); $('input[name="valueUnit"]').val(PMA_messages['strMiB']); $('span.unitInput').toggle(true); $('input[name="useUnit"]').prop('checked', true); return false; }); $('a[href="#submitClearSeries"]').click(function() { $('#seriesPreview').html('' + PMA_messages['strNone'] + ''); newChart = null; $('span#clearSeriesLink').hide(); }); $('a[href="#submitAddSeries"]').click(function() { if ($('input#variableInput').val() == "") { return false; } if (newChart == null) { $('#seriesPreview').html(''); newChart = { title: $('input[name="chartTitle"]').val(), nodes: [] }; } var serie = { dataPoints: [{ type: 'statusvar', name: $('input#variableInput').val() }], name: $('input#variableInput').val(), display: $('input[name="differentialValue"]').prop('checked') ? 'differential' : '' }; if (serie.dataPoint == 'Processes') { serie.dataType='proc'; } if ($('input[name="useDivisor"]').prop('checked')) { serie.valueDivisor = parseInt($('input[name="valueDivisor"]').val()); } if ($('input[name="useUnit"]').prop('checked')) { serie.unit = $('input[name="valueUnit"]').val(); } var str = serie.display == 'differential' ? ', ' + PMA_messages['strDifferential'] : ''; str += serie.valueDivisor ? (', ' + $.sprintf(PMA_messages['strDividedBy'], serie.valueDivisor)) : ''; str += serie.unit ? (', ' + PMA_messages['strUnit'] + ': ' + serie.unit) : ''; $('#seriesPreview').append('- ' + serie.name + str + '
'); newChart.nodes.push(serie); $('input#variableInput').val(''); $('input[name="differentialValue"]').prop('checked', true); $('input[name="useDivisor"]').prop('checked', false); $('input[name="useUnit"]').prop('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 }); /* Initializes the monitor, called only once */ function initGrid() { /* 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 && monitorProtocolVersion != window.localStorage['monitorVersion']) { $('div#emptyDialog').dialog({title: PMA_messages['strIncompatibleMonitorConfig']}); $('div#emptyDialog').html(PMA_messages['strIncompatibleMonitorConfigDescription']); var dlgBtns = {}; dlgBtns[PMA_messages['strClose']] = function() { $(this).dialog('close'); }; $('div#emptyDialog').dialog({ width: 400, buttons: dlgBtns }); } } if (runtime.charts == null) { runtime.charts = defaultChartGrid; } if (monitorSettings == null) { monitorSettings = defaultMonitorSettings; } $('select[name="gridChartRefresh"]').val(monitorSettings.gridRefresh / 1000); $('select[name="chartColumns"]').val(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(''); 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(''); } // Empty cells should keep their size so you can drop onto them $('table#chartGrid tr td').css('width', chartSize().width + 'px'); buildRequiredDataList(); refreshChartGrid(); } /* Destroys all monitor related resources */ function destroyGrid() { if (runtime.charts) { $.each(runtime.charts, function(key, value) { try { value.chart.destroy(); } catch(err) {} }); } try { runtime.refreshRequest.abort(); } catch(err) {} try { clearTimeout(runtime.refreshTimeout); } catch(err) {} $('table#chartGrid').html(''); runtime.charts = null; runtime.chartAI = 0; monitorSettings = null; } /* Calls destroyGrid() and initGrid(), but before doing so it saves the chart * data from each chart and restores it after the monitor is initialized again */ function rebuildGrid() { var oldData = null; if (runtime.charts) { oldData = {}; $.each(runtime.charts, function(key, chartObj) { for (var i = 0, l = chartObj.nodes.length; i < l; i++) { oldData[chartObj.nodes[i].dataPoint] = []; for (var j = 0, ll = chartObj.chart.series[i].data.length; j < ll; j++) { oldData[chartObj.nodes[i].dataPoint].push([chartObj.chart.series[i].data[j].x, chartObj.chart.series[i].data[j].y]); } } }); } destroyGrid(); initGrid(); if (oldData) { $.each(runtime.charts, function(key, chartObj) { for (var j = 0, l = chartObj.nodes.length; j < l; j++) { if (oldData[chartObj.nodes[j].dataPoint]) { chartObj.chart.series[j].setData(oldData[chartObj.nodes[j].dataPoint]); } } }); } } /* Calculactes the dynamic chart size that depends on the column width */ function chartSize() { var wdt = $('div#logTable').innerWidth() / monitorSettings.columns - (monitorSettings.columns - 1) * chartSpacing.width; return { width: wdt, height: 0.75 * wdt }; } /* Adds a chart to the chart grid */ function addChart(chartObj, initialize) { /* series = []; for (var j = 0; j'; $.each(this.points, function(i, point) { s += '
' + point.series.name + ': ' + ((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 } }; */ var settings = { title: chartObj.title, grid: { drawBorder: false, shadow: false, background: 'rgba(0,0,0,0)' }, axes: { xaxis: { renderer: $.jqplot.DateAxisRenderer, tickOptions: { formatString: '%H:%M:%S', showGridline: false }, min: runtime.xmin, max: runtime.xmax }, yaxis: { min:0, max:100, tickInterval: 20 } }, seriesDefaults: { rendererOptions: { smooth: true } }, highlighter: { show: true, showTooltip: false } }; settings.series = chartObj.series; if ($('#' + 'gridchart' + runtime.chartAI).length == 0) { var numCharts = $('table#chartGrid .monitorChart').length; if (numCharts == 0 || !( numCharts % monitorSettings.columns)) { $('table#chartGrid').append(''); } $('table#chartGrid tr:last').append('
'); } var series = []; for(i in chartObj.series){ series.push([[,]]); } chartObj.chart = $.jqplot('gridchart' + runtime.chartAI, series, 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++; } /* Opens a dialog that allows one to edit the title and series labels of the supplied chart */ function editChart(chartObj) { var htmlnode = chartObj.options.chart.renderTo; if (! htmlnode ) { return; } var chart = null; var chartKey = null; $.each(runtime.charts, function(key, value) { if (value.chart.options.chart.renderTo == htmlnode) { chart = value; chartKey = key; return false; } }); if (chart == null) { return; } var htmlStr = '

' + PMA_messages['strChartTitle'] + ':
'; htmlStr += '

' + PMA_messages['strSeries'] + ':

    '; for (var i = 0; i
    '; } dlgBtns = {}; dlgBtns[PMA_messages['strSave']] = function() { runtime.charts[chartKey].title = $('div#emptyDialog input[name="chartTitle"]').val(); runtime.charts[chartKey].chart.setTitle({ text: runtime.charts[chartKey].title }); $('div#emptyDialog input[name*="chartSerie"]').each(function() { var $t = $(this); var idx = $t.attr('name').split('-')[1]; runtime.charts[chartKey].nodes[idx].name = $t.val(); runtime.charts[chartKey].chart.series[idx].name = $t.val(); }); $(this).dialog('close'); saveMonitor(); }; dlgBtns[PMA_messages['strCancel']] = function() { $(this).dialog('close'); }; $('div#emptyDialog').html(htmlStr + '
'); $('div#emptyDialog').dialog({ title: PMA_messages['strChartEdit'], width: 'auto', height: 'auto', buttons: dlgBtns }); } /* Removes a chart from the grid */ 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(); $('div#' + htmlnode).remove(); }, 10); saveMonitor(); // Save settings } /* Called in regular intervalls, this function updates the values of each chart in the grid */ 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; try { chartData = $.parseJSON(data.message); } catch(err) { return serverResponseError(); } var value, i = 0; var diff; /* Update values in each graph */ $.each(runtime.charts, function(orderKey, elem) { var key = elem.chartID; var maxVal = 1; // If newly added chart, we have no data for it yet if (! chartData[key]) { return; } // Draw all series for (var j = 0; j < elem.nodes.length; j++) { // Update x-axis 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); /* Calculate y value */ // If transform function given, use it if (elem.nodes[j].transformFn) { value = chartValueTransform( elem.nodes[j].transformFn, chartData[key][j], // Check if first iteration (oldChartData==null), or if newly added chart oldChartData[key]==null (oldChartData == null || oldChartData[key] == null ? null : oldChartData[key][j]) ); // Otherwise use original value and apply differential and divisor if given, // in this case we have only one data point per series - located at chartData[key][j][0] } else { value = parseFloat(chartData[key][j][0].value); if (elem.nodes[j].display == 'differential') { if (oldChartData == null || oldChartData[key] == null) { continue; } value -= oldChartData[key][j][0].value; } if (elem.nodes[j].valueDivisor) { value = value / elem.nodes[j].valueDivisor; } } // Set y value, if defined if (value != undefined) { elem.chart.series[j].data.push([chartData.x, value]); maxVal = (maxVal > value) ? maxVal : value; } } if(elem.maxYLabel.length == 0) { elem.maxYLabel.push([runtime.xmax, 1]); } if(maxVal > elem.maxYLabel[elem.maxYLabel.length - 1][1]) { elem.maxYLabel.push([chartData.x, (Math.ceil(maxVal*1.2))]); } else if(maxVal > elem.maxYLabel[0][1]) { elem.maxYLabel.splice(1,0,[chartData.x, (Math.ceil(maxVal*1.2))]); } if(elem.maxYLabel.length > 1 && elem.maxYLabel[elem.maxYLabel.length - 1][0] < runtime.xmin ) { elem.maxYLabel.pop(); elem.maxYLabel.sort(function(a,b){return a[1]-b[1]}); } // update chart options elem.chart['axes']['xaxis']['max'] = runtime.xmax; elem.chart['axes']['xaxis']['min'] = runtime.xmin; elem.chart['axes']['yaxis']['max'] = elem.maxYLabel[elem.maxYLabel.length - 1][1]; elem.chart['axes']['yaxis']['tickInterval'] = elem.maxYLabel[elem.maxYLabel.length - 1][1]/5; i++; runtime.charts[orderKey].numPoints++; if (runtime.redrawCharts) { elem.chart.replot(); } }); oldChartData = chartData; runtime.refreshTimeout = setTimeout(refreshChartGrid, monitorSettings.gridRefresh); }); } /* Function that supplies special value transform functions for chart values */ function chartValueTransform(name, cur, prev) { switch(name) { case 'cpu-linux': if (prev == null) { return undefined; } // cur and prev are datapoint arrays, but containing only 1 element for cpu-linux cur = cur[0]; prev = prev[0]; 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; // Query cache efficiency (%) case 'qce': if (prev == null) { return undefined; } // cur[0].value is Qcache_hits, cur[1].value is Com_select var diffQHits = cur[0].value - prev[0].value; // No NaN please :-) if (cur[1].value - prev[1].value == 0) { return 0; } return diffQHits / (cur[1].value - prev[1].value + diffQHits) * 100; // Query cache usage (%) case 'qcu': if (cur[1].value == 0) { return 0; } // cur[0].value is Qcache_free_memory, cur[1].value is query_cache_size return 100 - cur[0].value / cur[1].value * 100; } return undefined; } /* Build list of nodes that need to be retrieved from server. * It creates something like a stripped down version of the runtime.charts object. */ 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] = []; for(var i=0, l=chart.nodes.length; i < l; i++) { runtime.dataList[chartID][i] = chart.nodes[i].dataPoints; } runtime.charts[key].chartID = chartID; chartID++; }); } /* Loads the log table data, generates the table and handles the filters */ function loadLogStatistics(opts) { var tableStr = ''; var logRequest = null; if (! opts.removeVariables) { opts.removeVariables = false; } if (! opts.limitTypes) { opts.limitTypes = false; } $('#emptyDialog').dialog({title: PMA_messages['strAnalysingLogsTitle']}); $('#emptyDialog').html(PMA_messages['strAnalysingLogs'] + ' '); var dlgBtns = {}; dlgBtns[PMA_messages['strCancelRequest']] = function() { if (logRequest != null) { logRequest.abort(); } $(this).dialog("close"); }; $('#emptyDialog').dialog({ width: 'auto', height: 'auto', buttons: dlgBtns }); 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), removeVariables: opts.removeVariables, limitTypes: opts.limitTypes }, function(data) { var logData; try { logData = $.parseJSON(data.message); } catch(err) { return serverResponseError(); } if (logData.rows.length != 0) { runtime.logDataCols = buildLogTable(logData); /* Show some stats in the dialog */ $('#emptyDialog').dialog({title: PMA_messages['strLoadingLogs']}); $('#emptyDialog').html('

' + PMA_messages['strLogDataLoaded'] + '

'); $.each(logData.sum, function(key, value) { key = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(); if (key == 'Total') { key = '' + key + ''; } $('#emptyDialog').append(key + ': ' + value + '
'); }); /* Add filter options if more than a bunch of rows there to filter */ if (logData.numRows > 12) { $('div#logTable').prepend( '
' + ' ' + PMA_messages['strFiltersForLogTable'] + '' + '
' + ' ' + ' ' + '
' + ((logData.numRows > 250) ? '
' : '') + '
' + ' ' + ' ' + ' ' ); $('div#logTable input#noWHEREData').change(function() { filterQueries(true); }); if (logData.numRows > 250) { $('div#logTable button#startFilterQueryText').click(filterQueries); } else { $('div#logTable input#filterQueryText').keyup(filterQueries); } } var dlgBtns = {}; dlgBtns[PMA_messages['strJumpToTable']] = function() { $(this).dialog("close"); $(document).scrollTop($('div#logTable').offset().top); }; $('#emptyDialog').dialog( "option", "buttons", dlgBtns); } else { $('#emptyDialog').dialog({title: PMA_messages['strNoDataFoundTitle']}); $('#emptyDialog').html('

' + PMA_messages['strNoDataFound'] + '

'); var dlgBtns = {}; dlgBtns[PMA_messages['strClose']] = function() { $(this).dialog("close"); }; $('#emptyDialog').dialog( "option", "buttons", dlgBtns ); } } ); /* Handles the actions performed when the user uses any of the log table filters * which are the filter by name and grouping with ignoring data in WHERE clauses * * @param boolean Should be true when the users enabled or disabled to group queries ignoring data in WHERE clauses */ function filterQueries(varFilterChange) { var odd_row = false, cell, textFilter; var val = $('div#logTable input#filterQueryText').val(); if (val.length == 0) { textFilter = null; } else { textFilter = new RegExp(val, 'i'); } var rowSum = 0, totalSum = 0, i = 0, q; var noVars = $('div#logTable input#noWHEREData').prop('checked'); var equalsFilter = /([^=]+)=(\d+|((\'|"|).*?[^\\])\4((\s+)|$))/gi; var functionFilter = /([a-z0-9_]+)\(.+?\)/gi; var filteredQueries = {}, filteredQueriesLines = {}; var hide = false, rowData; var queryColumnName = runtime.logDataCols[runtime.logDataCols.length - 2]; var sumColumnName = runtime.logDataCols[runtime.logDataCols.length - 1]; var isSlowLog = opts.src == 'slow'; var columnSums = {}; // For the slow log we have to count many columns (query_time, lock_time, rows_examined, rows_sent, etc.) var countRow = function(query, row) { var cells = row.match(/(.*?)<\/td>/gi); if (!columnSums[query]) { columnSums[query] = [0, 0, 0, 0]; } // lock_time and query_time and displayed in timespan format columnSums[query][0] += timeToSec(cells[2].replace(/(|<\/td>)/gi, '')); columnSums[query][1] += timeToSec(cells[3].replace(/(|<\/td>)/gi, '')); // rows_examind and rows_sent are just numbers columnSums[query][2] += parseInt(cells[4].replace(/(|<\/td>)/gi, '')); columnSums[query][3] += parseInt(cells[5].replace(/(|<\/td>)/gi, '')); }; // We just assume the sql text is always in the second last column, and that the total count is right of it $('div#logTable table tbody tr td:nth-child(' + (runtime.logDataCols.length - 1) + ')').each(function() { var $t = $(this); // If query is a SELECT and user enabled or disabled to group queries ignoring data in where statements, we // need to re-calculate the sums of each row if (varFilterChange && $t.html().match(/^SELECT/i)) { if (noVars) { // Group on => Sum up identical columns, and hide all but 1 q = $t.text().replace(equalsFilter, '$1=...$6').trim(); q = q.replace(functionFilter, ' $1(...)'); // Js does not specify a limit on property name length, so we can abuse it as index :-) if (filteredQueries[q]) { filteredQueries[q] += parseInt($t.next().text()); totalSum += parseInt($t.next().text()); hide = true; } else { filteredQueries[q] = parseInt($t.next().text()); filteredQueriesLines[q] = i; $t.text(q); } if (isSlowLog) { countRow(q, $t.parent().html()); } } else { // Group off: Restore original columns rowData = $t.parent().data('query'); // Restore SQL text $t.text(rowData[queryColumnName]); // Restore total count $t.next().text(rowData[sumColumnName]); // Restore slow log columns if (isSlowLog) { $t.parent().children('td:nth-child(3)').text(rowData['query_time']); $t.parent().children('td:nth-child(4)').text(rowData['lock_time']); $t.parent().children('td:nth-child(5)').text(rowData['rows_sent']); $t.parent().children('td:nth-child(6)').text(rowData['rows_examined']); } } } // If not required to be hidden, do we need to hide because of a not matching text filter? if (! hide && (textFilter != null && ! textFilter.exec($t.text()))) { hide = true; } // Now display or hide this column if (hide) { $t.parent().css('display', 'none'); } else { totalSum += parseInt($t.next().text()); rowSum++; odd_row = ! odd_row; $t.parent().css('display', ''); if (odd_row) { $t.parent().addClass('odd'); $t.parent().removeClass('even'); } else { $t.parent().addClass('even'); $t.parent().removeClass('odd'); } } hide = false; i++; }); // We finished summarizing counts => Update count values of all grouped entries if (varFilterChange) { if (noVars) { var numCol, row, $table = $('div#logTable table tbody'); $.each(filteredQueriesLines, function(key, value) { if (filteredQueries[key] <= 1) { return; } row = $table.children('tr:nth-child(' + (value + 1) + ')'); numCol = row.children(':nth-child(' + (runtime.logDataCols.length) + ')'); numCol.text(filteredQueries[key]); if (isSlowLog) { row.children('td:nth-child(3)').text(secToTime(columnSums[key][0])); row.children('td:nth-child(4)').text(secToTime(columnSums[key][1])); row.children('td:nth-child(5)').text(columnSums[key][2]); row.children('td:nth-child(6)').text(columnSums[key][3]); } }); } $('div#logTable table').trigger("update"); setTimeout(function() { $('div#logTable table').trigger('sorton', [[[runtime.logDataCols.length - 1, 1]]]); }, 0); } // Display some stats at the bottom of the table $('div#logTable table tfoot tr') .html('' + PMA_messages['strSumRows'] + ' ' + rowSum + '' + PMA_messages['strTotal'] + '' + totalSum + ''); } } /* Turns a timespan (12:12:12) into a number */ function timeToSec(timeStr) { var time = timeStr.split(':'); return parseInt(time[0]*3600) + parseInt(time[1]*60) + parseInt(time[2]); } /* Turns a number into a timespan (100 into 00:01:40) */ function secToTime(timeInt) { var hours = Math.floor(timeInt / 3600); timeInt -= hours*3600; var minutes = Math.floor(timeInt / 60); timeInt -= minutes*60; if (hours < 10) { hours = '0' + hours; } if (minutes < 10) { minutes = '0' + minutes; } if (timeInt < 10) { timeInt = '0' + timeInt; } return hours + ':' + minutes + ':' + timeInt; } /* Constructs the log table out of the retrieved server data */ function buildLogTable(data) { var rows = data.rows; var cols = new Array(); var $table = $('
'); var $tBody, $tRow, $tCell; $('#logTable').html($table); var formatValue = function(name, value) { switch(name) { case 'user_host': return value.replace(/(\[.*?\])+/g, ''); } return value; }; for (var i = 0, l = rows.length; i < l; i++) { if (i == 0) { $.each(rows[0], function(key, value) { cols.push(key); }); $table.append( '' + '' + cols.join('') + '' + ''); $table.append($tBody = $('')); } $tBody.append($tRow = $('')); var cl = ''; for (var j = 0, ll = cols.length; j < ll; j++) { // Assuming the query column is the second last if (j == cols.length - 2 && rows[i][cols[j]].match(/^SELECT/i)) { $tRow.append($tCell = $('' + formatValue(cols[j], rows[i][cols[j]]) + '')); $tCell.click(openQueryAnalyzer); } else { $tRow.append('' + formatValue(cols[j], rows[i][cols[j]]) + ''); } $tRow.data('query', rows[i]); } } $table.append('' + '' + PMA_messages['strSumRows'] + ' ' + data.numRows + '' + PMA_messages['strTotal'] + '' + data.sum.TOTAL + ''); // Append a tooltip to the count column, if there exist one if ($('#logTable th:last').html() == '#') { $('#logTable th:last').append(' ' + PMA_getImage('b_docs.png', '', {'class': 'qroupedQueryInfoIcon'})); var qtipContent = PMA_messages['strCountColumnExplanation']; if (groupInserts) { qtipContent += '

' + PMA_messages['strMoreCountColumnExplanation'] + '

'; } $('img.qroupedQueryInfoIcon').qtip({ content: qtipContent, position: { corner: { target: 'bottomMiddle', tooltip: 'topRight' } }, hide: { delay: 1000 } }); } $('div#logTable table').tablesorter({ sortList: [[cols.length - 1, 1]], widgets: ['fast-zebra'] }); $('div#logTable table thead th') .append(''); return cols; } /* Opens the query analyzer dialog */ function openQueryAnalyzer() { var rowData = $(this).parent().data('query'); var query = rowData.argument || rowData.sql_text; if (codemirror_editor) { query = PMA_SQLPrettyPrint(query); codemirror_editor.setValue(query); // Codemirror is bugged, it doesn't refresh properly sometimes. Following lines seem to fix that setTimeout(function() { codemirror_editor.refresh(); },50); } else { $('#sqlquery').val(query); } var profilingChart = null; var dlgBtns = {}; dlgBtns[PMA_messages['strAnalyzeQuery']] = function() { loadQueryAnalysis(rowData); }; dlgBtns[PMA_messages['strClose']] = function() { if (profilingChart != null) { profilingChart.destroy(); } $('div#queryAnalyzerDialog div.placeHolder').html(''); if (codemirror_editor) { codemirror_editor.setValue(''); } else { $('#sqlquery').val(''); } $(this).dialog("close"); }; $('div#queryAnalyzerDialog').dialog({ width: 'auto', height: 'auto', resizable: false, buttons: dlgBtns }); } /* Loads and displays the analyzed query data */ function loadQueryAnalysis(rowData) { var db = rowData.db || ''; $('div#queryAnalyzerDialog div.placeHolder').html( PMA_messages['strAnalyzing'] + ' '); $.post('server_status.php?' + url_query, { ajax_request: true, query_analyzer: true, query: codemirror_editor ? codemirror_editor.getValue() : $('#sqlquery').val(), database: db }, function(data) { data = $.parseJSON(data.message); var totalTime = 0; if (data.error) { $('div#queryAnalyzerDialog div.placeHolder').html('
' + data.error + '
'); return; } // Float sux, I'll use table :( $('div#queryAnalyzerDialog div.placeHolder') .html('
'); var explain = '' + PMA_messages['strExplainOutput'] + ' ' + explain_docu; if (data.explain.length > 1) { explain += ' ('; for (var i = 0; i < data.explain.length; i++) { if (i > 0) { explain += ', '; } explain += '' + i + ''; } explain += ')'; } explain += '

'; for (var i = 0, l = data.explain.length; i < l; i++) { explain += '
0? 'style="display:none;"' : '' ) + '>'; $.each(data.explain[i], function(key, value) { value = (value == null)?'null':value; if (key == 'type' && value.toLowerCase() == 'all') { value = '' + value + ''; } if (key == 'Extra') { value = value.replace(/(using (temporary|filesort))/gi, '$1'); } explain += key + ': ' + value + '
'; }); explain += '
'; } explain += '

' + PMA_messages['strAffectedRows'] + ' ' + data.affectedRows; $('div#queryAnalyzerDialog div.placeHolder td.explain').append(explain); $('div#queryAnalyzerDialog div.placeHolder a[href*="#showExplain"]').click(function() { var id = $(this).attr('href').split('-')[1]; $(this).parent().find('div[class*="explain"]').hide(); $(this).parent().find('div[class*="explain-' + id + '"]').show(); }); if (data.profiling) { var chartData = []; var numberTable = ''; var duration; for (var i = 0, l = data.profiling.length; i < l; i++) { duration = parseFloat(data.profiling[i].duration); chartData.push([data.profiling[i].state, duration]); totalTime += duration; numberTable += ''; } numberTable += ''; numberTable += '
' + PMA_messages['strStatus'] + '' + PMA_messages['strTime'] + '
' + data.profiling[i].state + ' ' + PMA_prettyProfilingNum(duration, 2) + '
' + PMA_messages['strTotalTime'] + '' + PMA_prettyProfilingNum(totalTime, 2) + '
'; $('div#queryAnalyzerDialog div.placeHolder td.chart').append( '' + PMA_messages['strProfilingResults'] + ' ' + profiling_docu + ' ' + '(' + PMA_messages['strTable'] + ', ' + PMA_messages['strChart'] + ')
' + numberTable + '

'); $('div#queryAnalyzerDialog div.placeHolder a[href="#showNums"]').click(function() { $('div#queryAnalyzerDialog div#queryProfiling').hide(); $('div#queryAnalyzerDialog table.queryNums').show(); return false; }); $('div#queryAnalyzerDialog div.placeHolder a[href="#showChart"]').click(function() { $('div#queryAnalyzerDialog div#queryProfiling').show(); $('div#queryAnalyzerDialog table.queryNums').hide(); return false; }); profilingChart = PMA_createProfilingChart(chartData, { chart: { renderTo: 'queryProfiling' }, plotOptions: { pie: { size: '50%' } } }); $('div#queryProfiling').resizable(); } }); } /* Saves the monitor to localstorage */ 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); window.localStorage['monitorVersion'] = monitorProtocolVersion; } $('a[href="#clearMonitorConfig"]').show(); } }); // Run the monitor once loaded $(function() { $('a[href="#pauseCharts"]').trigger('click'); });