/**
* Used in or for console
*
* @package phpMyAdmin-Console
*/
/* global debugSQLInfo */ // libraries/classes/Footer.php
/**
* Console object
*/
var Console = {
/**
* @var {JQuery}, jQuery object, selector is '#pma_console>.content'
* @access private
*/
$consoleContent: null,
/**
* @var {Jquery}, jQuery object, selector is '#pma_console .content',
* used for resizer
* @access private
*/
$consoleAllContents: null,
/**
* @var {JQuery}, jQuery object, selector is '#pma_console .toolbar'
* @access private
*/
$consoleToolbar: null,
/**
* @var {JQuery}, jQuery object, selector is '#pma_console .template'
* @access private
*/
$consoleTemplates: null,
/**
* @var {JQuery}, jQuery object, form for submit
* @access private
*/
$requestForm: null,
/**
* @var {object}, contain console config
* @access private
*/
config: null,
/**
* @var {boolean}, if console element exist, it'll be true
* @access public
*/
isEnabled: false,
/**
* @var {boolean}, make sure console events bind only once
* @access private
*/
isInitialized: false,
/**
* Used for console initialize, reinit is ok, just some variable assignment
*
* @return {void}
*/
initialize: function () {
if ($('#pma_console').length === 0) {
return;
}
Functions.configGet('Console', false, data => {
Console.config = data;
Console.setupAfterInit();
}, () => {
Console.config = {}; // Avoid null pointers in setupAfterInit()
// Fetching data failed, still perform the console init
Console.setupAfterInit();
});
},
/**
* Setup the console after the config has been set at initialize stage
*/
setupAfterInit: function () {
Console.isEnabled = true;
// Vars init
Console.$consoleToolbar = $('#pma_console').find('>.toolbar');
Console.$consoleContent = $('#pma_console').find('>.content');
Console.$consoleAllContents = $('#pma_console').find('.content');
Console.$consoleTemplates = $('#pma_console').find('>.templates');
// Generate a form for post
Console.$requestForm = $('
');
Console.$requestForm.children('[name=token]').val(CommonParams.get('token'));
Console.$requestForm.on('submit', AJAX.requestHandler);
// Event binds shouldn't run again
if (Console.isInitialized === false) {
// Load config first
if (Console.config.AlwaysExpand === true) {
$('#pma_console_options input[name=always_expand]').prop('checked', true);
}
if (Console.config.StartHistory === true) {
$('#pma_console_options').find('input[name=start_history]').prop('checked', true);
}
if (Console.config.CurrentQuery === true) {
$('#pma_console_options').find('input[name=current_query]').prop('checked', true);
}
if (Console.config.EnterExecutes === true) {
$('#pma_console_options').find('input[name=enter_executes]').prop('checked', true);
}
if (Console.config.DarkTheme === true) {
$('#pma_console_options').find('input[name=dark_theme]').prop('checked', true);
$('#pma_console').find('>.content').addClass('console_dark_theme');
}
ConsoleResizer.initialize();
ConsoleInput.initialize();
ConsoleMessages.initialize();
ConsoleBookmarks.initialize();
ConsoleDebug.initialize();
Console.$consoleToolbar.children('.console_switch').on('click', Console.toggle);
$('#pma_console').find('.toolbar').children().on('mousedown', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
});
$('#pma_console').find('.button.clear').on('click', function () {
ConsoleMessages.clear();
});
$('#pma_console').find('.button.history').on('click', function () {
ConsoleMessages.showHistory();
});
$('#pma_console').find('.button.options').on('click', function () {
Console.showCard('#pma_console_options');
});
$('#pma_console').find('.button.debug').on('click', function () {
Console.showCard('#debug_console');
});
Console.$consoleContent.on('click', function (event) {
if (event.target === this) {
ConsoleInput.focus();
}
});
$('#pma_console').find('.mid_layer').on('click', function () {
Console.hideCard($(this).parent().children('.card'));
});
$('#debug_console').find('.switch_button').on('click', function () {
Console.hideCard($(this).closest('.card'));
});
$('#pma_bookmarks').find('.switch_button').on('click', function () {
Console.hideCard($(this).closest('.card'));
});
$('#pma_console_options').find('.switch_button').on('click', function () {
Console.hideCard($(this).closest('.card'));
});
$('#pma_console_options').find('input[type=checkbox]').on('change', function () {
Console.updateConfig();
});
$('#pma_console_options').find('.button.default').on('click', function () {
$('#pma_console_options input[name=always_expand]').prop('checked', false);
$('#pma_console_options').find('input[name=start_history]').prop('checked', false);
$('#pma_console_options').find('input[name=current_query]').prop('checked', true);
$('#pma_console_options').find('input[name=enter_executes]').prop('checked', false);
$('#pma_console_options').find('input[name=dark_theme]').prop('checked', false);
Console.updateConfig();
});
$('#pma_console_options').find('input[name=enter_executes]').on('change', function () {
ConsoleMessages.showInstructions(Console.config.EnterExecutes);
});
$(document).on('ajaxComplete', function (event, xhr, ajaxOptions) {
// Not a json body, then skip
if (ajaxOptions.dataType && ajaxOptions.dataType.indexOf('json') === -1) {
return;
}
if (xhr.status !== 200) {
return;
}
try {
var data = JSON.parse(xhr.responseText);
Console.ajaxCallback(data);
} catch (e) {
// eslint-disable-next-line no-console, compat/compat
console.trace();
// eslint-disable-next-line no-console
console.log('Failed to parse JSON: ' + e.message);
}
});
Console.isInitialized = true;
}
// Change console mode from cookie
switch (Console.config.Mode) {
case 'collapse':
Console.collapse();
break;
case 'info':
Console.info();
break;
case 'show':
Console.show(true);
Console.scrollBottom();
break;
default:
Console.setConfig('Mode', 'info');
Console.info();
}
},
/**
* Execute query and show results in console
*
* @param {string} queryString
* @param {object} options
*
* @return {void}
*/
execute: function (queryString, options) {
if (typeof queryString !== 'string' || !/[a-z]|[A-Z]/.test(queryString)) {
return;
}
Console.$requestForm.children('textarea').val(queryString);
Console.$requestForm.children('[name=server]').attr('value', CommonParams.get('server'));
if (options && options.db) {
Console.$requestForm.children('[name=db]').val(options.db);
if (options.table) {
Console.$requestForm.children('[name=table]').val(options.table);
} else {
Console.$requestForm.children('[name=table]').val('');
}
} else {
Console.$requestForm.children('[name=db]').val(CommonParams.get('db').length > 0 ? CommonParams.get('db') : '');
}
Console.$requestForm.find('[name=profiling]').remove();
if (options && options.profiling === true) {
Console.$requestForm.append('');
}
if (!Functions.confirmQuery(Console.$requestForm[0], Console.$requestForm.children('textarea')[0].value)) {
return;
}
Console.$requestForm.children('[name=console_message_id]').val(ConsoleMessages.appendQuery({
'sql_query': queryString
}).message_id);
Console.$requestForm.trigger('submit');
ConsoleInput.clear();
Navigation.reload();
},
ajaxCallback: function (data) {
if (data && data.console_message_id) {
ConsoleMessages.updateQuery(data.console_message_id, data.success, data.reloadQuerywindow ? data.reloadQuerywindow : false);
} else if (data && data.reloadQuerywindow) {
if (data.reloadQuerywindow.sql_query.length > 0) {
ConsoleMessages.appendQuery(data.reloadQuerywindow, 'successed').$message.addClass(Console.config.CurrentQuery ? '' : 'hide');
}
}
},
/**
* Change console to collapse mode
*
* @return {void}
*/
collapse: function () {
Console.setConfig('Mode', 'collapse');
var pmaConsoleHeight = Math.max(92, Console.config.Height);
Console.$consoleToolbar.addClass('collapsed');
Console.$consoleAllContents.height(pmaConsoleHeight);
Console.$consoleContent.stop();
Console.$consoleContent.animate({
'margin-bottom': -1 * Console.$consoleContent.outerHeight() + 'px'
}, 'fast', 'easeOutQuart', function () {
Console.$consoleContent.css({
display: 'none'
});
$(window).trigger('resize');
});
Console.hideCard();
},
/**
* Show console
*
* @param {boolean} inputFocus If true, focus the input line after show()
* @return {void}
*/
show: function (inputFocus) {
Console.setConfig('Mode', 'show');
var pmaConsoleHeight = Math.max(92, Console.config.Height);
// eslint-disable-next-line compat/compat
pmaConsoleHeight = Math.min(Console.config.Height, (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - 25);
Console.$consoleContent.css({
display: 'block'
});
if (Console.$consoleToolbar.hasClass('collapsed')) {
Console.$consoleToolbar.removeClass('collapsed');
}
Console.$consoleAllContents.height(pmaConsoleHeight);
Console.$consoleContent.stop();
Console.$consoleContent.animate({
'margin-bottom': 0
}, 'fast', 'easeOutQuart', function () {
$(window).trigger('resize');
if (inputFocus) {
ConsoleInput.focus();
}
});
},
/**
* Change console to SQL information mode
* this mode shows current SQL query
* This mode is the default mode
*
* @return {void}
*/
info: function () {
// Under construction
Console.collapse();
},
/**
* Toggle console mode between collapse/show
* Used for toggle buttons and shortcuts
*
* @return {void}
*/
toggle: function () {
switch (Console.config.Mode) {
case 'collapse':
case 'info':
Console.show(true);
break;
case 'show':
Console.collapse();
break;
}
},
/**
* Scroll console to bottom
*
* @return {void}
*/
scrollBottom: function () {
Console.$consoleContent.scrollTop(Console.$consoleContent.prop('scrollHeight'));
},
/**
* Show card
*
* @param {string | JQuery} cardSelector Selector, select string will be "#pma_console " + cardSelector
* this param also can be JQuery object, if you need.
*
* @return {void}
*/
showCard: function (cardSelector) {
var $card = null;
if (typeof cardSelector !== 'string') {
if (cardSelector.length > 0) {
$card = cardSelector;
} else {
return;
}
} else {
$card = $('#pma_console ' + cardSelector);
}
if ($card.length === 0) {
return;
}
$card.parent().children('.mid_layer').show().fadeTo(0, 0.15);
$card.addClass('show');
ConsoleInput.blur();
if ($card.parents('.card').length > 0) {
Console.showCard($card.parents('.card'));
}
},
/**
* Scroll console to bottom
*
* @param {object} $targetCard Target card JQuery object, if it's empty, function will hide all cards
* @return {void}
*/
hideCard: function ($targetCard) {
if (!$targetCard) {
$('#pma_console').find('.mid_layer').fadeOut(140);
$('#pma_console').find('.card').removeClass('show');
} else if ($targetCard.length > 0) {
$targetCard.parent().find('.mid_layer').fadeOut(140);
$targetCard.find('.card').removeClass('show');
$targetCard.removeClass('show');
}
},
/**
* Used for update console config
*
* @return {void}
*/
updateConfig: function () {
Console.setConfig('AlwaysExpand', $('#pma_console_options input[name=always_expand]').prop('checked'));
Console.setConfig('StartHistory', $('#pma_console_options').find('input[name=start_history]').prop('checked'));
Console.setConfig('CurrentQuery', $('#pma_console_options').find('input[name=current_query]').prop('checked'));
Console.setConfig('EnterExecutes', $('#pma_console_options').find('input[name=enter_executes]').prop('checked'));
Console.setConfig('DarkTheme', $('#pma_console_options').find('input[name=dark_theme]').prop('checked'));
/* Setting the dark theme of the console*/
if (Console.config.DarkTheme) {
$('#pma_console').find('>.content').addClass('console_dark_theme');
} else {
$('#pma_console').find('>.content').removeClass('console_dark_theme');
}
},
setConfig: function (key, value) {
Console.config[key] = value;
Functions.configSet('Console/' + key, value);
},
isSelect: function (queryString) {
var regExp = /^SELECT\s+/i;
return regExp.test(queryString);
}
};
/**
* Resizer object
* Careful: this object UI logics highly related with functions under Console
* Resizing min-height is 32, if small than it, console will collapse
*/
var ConsoleResizer = {
posY: 0,
height: 0,
resultHeight: 0,
/**
* Mousedown event handler for bind to resizer
*
* @param {MouseEvent} event
*
* @return {void}
*/
mouseDown: function (event) {
if (Console.config.Mode !== 'show') {
return;
}
ConsoleResizer.posY = event.pageY;
ConsoleResizer.height = Console.$consoleContent.height();
$(document).on('mousemove', ConsoleResizer.mouseMove);
$(document).on('mouseup', ConsoleResizer.mouseUp);
// Disable text selection while resizing
$(document).on('selectstart', function () {
return false;
});
},
/**
* Mousemove event handler for bind to resizer
*
* @param {MouseEvent} event
*
* @return {void}
*/
mouseMove: function (event) {
if (event.pageY < 35) {
event.pageY = 35;
}
ConsoleResizer.resultHeight = ConsoleResizer.height + (ConsoleResizer.posY - event.pageY);
// Content min-height is 32, if adjusting height small than it we'll move it out of the page
if (ConsoleResizer.resultHeight <= 32) {
Console.$consoleAllContents.height(32);
Console.$consoleContent.css('margin-bottom', ConsoleResizer.resultHeight - 32);
} else {
// Logic below makes viewable area always at bottom when adjusting height and content already at bottom
if (Console.$consoleContent.scrollTop() + Console.$consoleContent.innerHeight() + 16 >= Console.$consoleContent.prop('scrollHeight')) {
Console.$consoleAllContents.height(ConsoleResizer.resultHeight);
Console.scrollBottom();
} else {
Console.$consoleAllContents.height(ConsoleResizer.resultHeight);
}
}
},
/**
* Mouseup event handler for bind to resizer
*
* @return {void}
*/
mouseUp: function () {
Console.setConfig('Height', ConsoleResizer.resultHeight);
Console.show();
$(document).off('mousemove');
$(document).off('mouseup');
$(document).off('selectstart');
},
/**
* Used for console resizer initialize
*
* @return {void}
*/
initialize: function () {
$('#pma_console').find('.toolbar').off('mousedown');
$('#pma_console').find('.toolbar').on('mousedown', ConsoleResizer.mouseDown);
}
};
/**
* Console input object
*/
var ConsoleInput = {
/**
* @var array, contains Codemirror objects or input jQuery objects
* @access private
*/
inputs: null,
/**
* @var {boolean}, if codemirror enabled
* @access private
*/
codeMirror: false,
/**
* @var {number}, count for history navigation, 0 for current input
* @access private
*/
historyCount: 0,
/**
* @var {string}, current input when navigating through history
* @access private
*/
historyPreserveCurrent: null,
/**
* Used for console input initialize
*
* @return {void}
*/
initialize: function () {
// _cm object can't be reinitialize
if (ConsoleInput.inputs !== null) {
return;
}
if (typeof CodeMirror !== 'undefined') {
ConsoleInput.codeMirror = true;
}
ConsoleInput.inputs = [];
if (ConsoleInput.codeMirror) {
// eslint-disable-next-line new-cap
ConsoleInput.inputs.console = CodeMirror($('#pma_console').find('.console_query_input')[0], {
// style: cm-s-pma
theme: 'pma',
mode: 'text/x-sql',
lineWrapping: true,
extraKeys: {
'Ctrl-Space': 'autocomplete'
},
hintOptions: {
'completeSingle': false,
'completeOnSingleClick': true
},
gutters: ['CodeMirror-lint-markers'],
lint: {
'getAnnotations': CodeMirror.sqlLint,
'async': true
}
});
ConsoleInput.inputs.console.on('inputRead', Functions.codeMirrorAutoCompleteOnInputRead);
ConsoleInput.inputs.console.on('keydown', function (instance, event) {
ConsoleInput.historyNavigate(event);
});
if ($('#pma_bookmarks').length !== 0) {
// eslint-disable-next-line new-cap
ConsoleInput.inputs.bookmark = CodeMirror($('#pma_console').find('.bookmark_add_input')[0], {
// style: cm-s-pma
theme: 'pma',
mode: 'text/x-sql',
lineWrapping: true,
extraKeys: {
'Ctrl-Space': 'autocomplete'
},
hintOptions: {
'completeSingle': false,
'completeOnSingleClick': true
},
gutters: ['CodeMirror-lint-markers'],
lint: {
'getAnnotations': CodeMirror.sqlLint,
'async': true
}
});
ConsoleInput.inputs.bookmark.on('inputRead', Functions.codeMirrorAutoCompleteOnInputRead);
}
} else {
ConsoleInput.inputs.console = $('