diff --git a/js/src/functions/Sql/SqlEditor.js b/js/src/functions/Sql/SqlEditor.js
new file mode 100644
index 0000000000..519143d5c7
--- /dev/null
+++ b/js/src/functions/Sql/SqlEditor.js
@@ -0,0 +1,117 @@
+import { sqlQueryOptions } from '../../utils/sql';
+import CommonParams from '../../variables/common_params';
+import { AJAX } from '../../ajax';
+import { codemirrorAutocompleteOnInputRead } from '../../utils/sql';
+
+import CodeMirror from 'codemirror';
+import 'codemirror/mode/sql/sql.js';
+import 'codemirror/addon/runmode/runmode.js';
+import 'codemirror/addon/hint/show-hint.js';
+import 'codemirror/addon/hint/sql-hint.js';
+import 'codemirror/addon/lint/lint.js';
+import '../../plugins/codemirror/sql-lint';
+
+function catchKeypressesFromSqlInlineEdit (event) {
+ // ctrl-enter is 10 in chrome and ie, but 13 in ff
+ if ((event.ctrlKey || event.metaKey) && (event.keyCode === 13 || event.keyCode === 10)) {
+ $('#sql_query_edit_save').trigger('click');
+ }
+}
+
+/**
+ * Creates an SQL editor which supports auto completing etc.
+ *
+ * @param $textarea jQuery object wrapping the textarea to be made the editor
+ * @param options optional options for CodeMirror
+ * @param resize optional resizing ('vertical', 'horizontal', 'both')
+ * @param lintOptions additional options for lint
+ */
+
+export function PMA_getSQLEditor ($textarea, options, resize, lintOptions) {
+ if ($textarea.length > 0 && CommonParams.get('CodemirrorEnable') === true) {
+ // merge options for CodeMirror
+ var defaults = {
+ lineNumbers: true,
+ matchBrackets: true,
+ extraKeys: { 'Ctrl-Space': 'autocomplete' },
+ hintOptions: { 'completeSingle': false, 'completeOnSingleClick': true },
+ indentUnit: 4,
+ mode: 'text/x-mysql',
+ lineWrapping: true
+ };
+
+ if (CommonParams.get('LintEnable')) {
+ $.extend(defaults, {
+ gutters: ['CodeMirror-lint-markers'],
+ lint: {
+ 'getAnnotations': CodeMirror.sqlLint,
+ 'async': true,
+ 'lintOptions': lintOptions
+ }
+ });
+ }
+
+ $.extend(true, defaults, options);
+
+ // create CodeMirror editor
+ var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults);
+ // allow resizing
+ if (! resize) {
+ resize = 'vertical';
+ }
+ var handles = '';
+ if (resize === 'vertical') {
+ handles = 's';
+ }
+ if (resize === 'both') {
+ handles = 'all';
+ }
+ if (resize === 'horizontal') {
+ handles = 'e, w';
+ }
+ $(codemirrorEditor.getWrapperElement())
+ .css('resize', resize)
+ .resizable({
+ handles: handles,
+ resize: function () {
+ codemirrorEditor.setSize($(this).width(), $(this).height());
+ }
+ });
+ // enable autocomplete
+ codemirrorEditor.on('inputRead', codemirrorAutocompleteOnInputRead);
+
+ // page locking
+ codemirrorEditor.on('change', function (e) {
+ e.data = {
+ value: 3,
+ content: codemirrorEditor.isClean(),
+ };
+ AJAX.lockPageHandler(e);
+ });
+
+ return codemirrorEditor;
+ }
+ return null;
+}
+
+/**
+ * Binds the CodeMirror to the text area used to inline edit a query.
+ */
+export function bindCodeMirrorToInlineEditor () {
+ var $inline_editor = $('#sql_query_edit');
+ if ($inline_editor.length > 0) {
+ if (CommonParams.get('CodemirrorEnable') === true) {
+ var height = $inline_editor.css('height');
+ sqlQueryOptions.codemirror_inline_editor = PMA_getSQLEditor($inline_editor);
+ sqlQueryOptions.codemirror_inline_editor.getWrapperElement().style.height = height;
+ sqlQueryOptions.codemirror_inline_editor.refresh();
+ sqlQueryOptions.codemirror_inline_editor.focus();
+ $(sqlQueryOptions.codemirror_inline_editor.getWrapperElement())
+ .on('keydown', catchKeypressesFromSqlInlineEdit);
+ } else {
+ $inline_editor
+ .focus()
+ .on('keydown', catchKeypressesFromSqlInlineEdit);
+ }
+ }
+}
diff --git a/js/src/functions/Sql/SqlProfiling.js b/js/src/functions/Sql/SqlProfiling.js
new file mode 100644
index 0000000000..3fe91495ab
--- /dev/null
+++ b/js/src/functions/Sql/SqlProfiling.js
@@ -0,0 +1,57 @@
+import { createProfilingChart } from '../chart';
+
+/*
+ * Profiling Chart
+ */
+export function makeProfilingChart () {
+ if ($('#profilingchart').length === 0 ||
+ $('#profilingchart').html().length !== 0 ||
+ !$.jqplot || !$.jqplot.Highlighter || !$.jqplot.PieRenderer
+ ) {
+ return;
+ }
+
+ var data = [];
+ $.each(JSON.parse($('#profilingChartData').html()), function (key, value) {
+ data.push([key, parseFloat(value)]);
+ });
+
+ // Remove chart and data divs contents
+ $('#profilingchart').html('').show();
+ $('#profilingChartData').html('');
+
+ createProfilingChart('profilingchart', data);
+}
+
+/*
+ * initialize profiling data tables
+ */
+export function initProfilingTables () {
+ if (!$.tablesorter) {
+ return;
+ }
+
+ $('#profiletable').tablesorter({
+ widgets: ['zebra'],
+ sortList: [[0, 0]],
+ textExtraction: function (node) {
+ if (node.children.length > 0) {
+ return node.children[0].innerHTML;
+ } else {
+ return node.innerHTML;
+ }
+ }
+ });
+
+ $('#profilesummarytable').tablesorter({
+ widgets: ['zebra'],
+ sortList: [[1, 1]],
+ textExtraction: function (node) {
+ if (node.children.length > 0) {
+ return node.children[0].innerHTML;
+ } else {
+ return node.innerHTML;
+ }
+ }
+ });
+}
diff --git a/js/src/functions/Sql/SqlQuery.js b/js/src/functions/Sql/SqlQuery.js
new file mode 100644
index 0000000000..4d80e80521
--- /dev/null
+++ b/js/src/functions/Sql/SqlQuery.js
@@ -0,0 +1,325 @@
+import { isStorageSupported } from '../config';
+import Cookies from 'js-cookie';
+import { sqlQueryOptions } from '../../utils/sql';
+import { GlobalVariables, PMA_Messages as PMA_messages } from '../../variables/export_variables';
+import { PMA_ajaxShowMessage } from '../../utils/show_ajax_messages';
+import { PMA_sprintf } from '../../utils/sprintf';
+
+/**
+ * Handles 'Simulate query' button on SQL query box.
+ *
+ * @return void
+ */
+export function PMA_handleSimulateQueryButton () {
+ var update_re = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i');
+ var delete_re = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
+ var query = '';
+
+ if (sqlQueryOptions.codemirror_editor) {
+ query = sqlQueryOptions.codemirror_editor.getValue();
+ } else {
+ query = $('#sqlquery').val();
+ }
+
+ var $simulateDml = $('#simulate_dml');
+ if (update_re.test(query) || delete_re.test(query)) {
+ if (! $simulateDml.length) {
+ $('#button_submit_query')
+ .before('');
+ }
+ } else {
+ if ($simulateDml.length) {
+ $simulateDml.remove();
+ }
+ }
+}
+
+/**
+ * Sets current value for query box.
+ */
+function setQuery (query) {
+ if (sqlQueryOptions.codemirror_editor) {
+ sqlQueryOptions.codemirror_editor.setValue(query);
+ sqlQueryOptions.codemirror_editor.focus();
+ } else if (document.sqlform) {
+ document.sqlform.sql_query.value = query;
+ document.sqlform.sql_query.focus();
+ }
+}
+
+/**
+ * Create quick sql statements.
+ *
+ */
+export function insertQuery (queryType) {
+ if (queryType === 'clear') {
+ setQuery('');
+ return;
+ } else if (queryType === 'format') {
+ if (sqlQueryOptions.codemirror_editor) {
+ $('#querymessage').html(PMA_messages.strFormatting +
+ '
');
+ var href = 'db_sql_format.php';
+ var params = {
+ 'ajax_request': true,
+ 'sql': sqlQueryOptions.codemirror_editor.getValue()
+ };
+ $.ajax({
+ type: 'POST',
+ url: href,
+ data: params,
+ success: function (data) {
+ if (data.success) {
+ sqlQueryOptions.codemirror_editor.setValue(data.sql);
+ }
+ $('#querymessage').html('');
+ }
+ });
+ }
+ return;
+ } else if (queryType === 'saved') {
+ if (isStorageSupported('localStorage')
+ && typeof window.localStorage.auto_saved_sql !== 'undefined'
+ ) {
+ setQuery(window.localStorage.auto_saved_sql);
+ } else if (Cookies.get('auto_saved_sql')) {
+ setQuery(Cookies.get('auto_saved_sql'));
+ } else {
+ PMA_ajaxShowMessage(PMA_messages.strNoAutoSavedQuery);
+ }
+ return;
+ }
+
+ var query = '';
+ var myListBox = document.sqlform.dummy;
+ var table = document.sqlform.table.value;
+
+ if (myListBox.options.length > 0) {
+ sql_box_locked = true;
+ var columnsList = '';
+ var valDis = '';
+ var editDis = '';
+ var NbSelect = 0;
+ for (var i = 0; i < myListBox.options.length; i++) {
+ NbSelect++;
+ if (NbSelect > 1) {
+ columnsList += ', ';
+ valDis += ',';
+ editDis += ',';
+ }
+ columnsList += myListBox.options[i].value;
+ valDis += '[value-' + NbSelect + ']';
+ editDis += myListBox.options[i].value + '=[value-' + NbSelect + ']';
+ }
+ if (queryType === 'selectall') {
+ query = 'SELECT * FROM `' + table + '` WHERE 1';
+ } else if (queryType === 'select') {
+ query = 'SELECT ' + columnsList + ' FROM `' + table + '` WHERE 1';
+ } else if (queryType === 'insert') {
+ query = 'INSERT INTO `' + table + '`(' + columnsList + ') VALUES (' + valDis + ')';
+ } else if (queryType === 'update') {
+ query = 'UPDATE `' + table + '` SET ' + editDis + ' WHERE 1';
+ } else if (queryType === 'delete') {
+ query = 'DELETE FROM `' + table + '` WHERE 0';
+ }
+ setQuery(query);
+ sql_box_locked = false;
+ }
+}
+
+/**
+ * Confirms a "DROP/DELETE/ALTER" query before
+ * submitting it if required.
+ * This function is called by the 'checkSqlQuery()' js function.
+ *
+ * @param theForm1 object the form
+ * @param sqlQuery1 string the sql query string
+ *
+ * @return boolean whether to run the query or not
+ *
+ * @see checkSqlQuery()
+ */
+function confirmQuery (theForm1, sqlQuery1) {
+ // Confirmation is not required in the configuration file
+ if (PMA_messages.strDoYouReally === '') {
+ return true;
+ }
+
+ // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
+ //
+ // TODO: find a way (if possible) to use the parser-analyser
+ // for this kind of verification
+ // For now, I just added a ^ to check for the statement at
+ // beginning of expression
+
+ var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i');
+ var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
+ var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
+ var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
+
+ if (do_confirm_re_0.test(sqlQuery1) ||
+ do_confirm_re_1.test(sqlQuery1) ||
+ do_confirm_re_2.test(sqlQuery1) ||
+ do_confirm_re_3.test(sqlQuery1)) {
+ var message;
+ if (sqlQuery1.length > 100) {
+ message = sqlQuery1.substr(0, 100) + '\n ...';
+ } else {
+ message = sqlQuery1;
+ }
+ var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, message));
+ // statement is confirmed -> update the
+ // "is_js_confirmed" form field so the confirm test won't be
+ // run on the server side and allows to submit the form
+ if (is_confirmed) {
+ theForm1.elements.is_js_confirmed.value = 1;
+ return true;
+ } else {
+ // statement is rejected -> do not submit the form
+ window.focus();
+ return false;
+ } // end if (handle confirm box result)
+ } // end if (display confirm box)
+
+ return true;
+} // end of the 'confirmQuery()' function
+
+/**
+ * Displays an error message if the user submitted the sql query form with no
+ * sql query, else checks for "DROP/DELETE/ALTER" statements
+ *
+ * @param theForm object the form
+ *
+ * @return boolean always false
+ *
+ * @see confirmQuery()
+ */
+export function checkSqlQuery (theForm) {
+ // get the textarea element containing the query
+ var sqlQuery;
+ if (sqlQueryOptions.codemirror_editor) {
+ sqlQueryOptions.codemirror_editor.save();
+ sqlQuery = sqlQueryOptions.codemirror_editor.getValue();
+ } else {
+ sqlQuery = theForm.elements.sql_query.value;
+ }
+ var space_re = new RegExp('\\s+');
+ if (typeof(theForm.elements.sql_file) !== 'undefined' &&
+ theForm.elements.sql_file.value.replace(space_re, '') !== '') {
+ return true;
+ }
+ if (typeof(theForm.elements.id_bookmark) !== 'undefined' &&
+ (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') &&
+ theForm.elements.id_bookmark.selectedIndex !== 0) {
+ return true;
+ }
+ var result = false;
+ // Checks for "DROP/DELETE/ALTER" statements
+ if (sqlQuery.replace(space_re, '') !== '') {
+ result = confirmQuery(theForm, sqlQuery);
+ } else {
+ alert(PMA_messages.strFormEmpty);
+ }
+
+ if (sqlQueryOptions.codemirror_editor) {
+ sqlQueryOptions.codemirror_editor.focus();
+ } else if (sqlQueryOptions.codemirror_inline_editor) {
+ sqlQueryOptions.codemirror_inline_editor.focus();
+ }
+ return result;
+} // end of the 'checkSqlQuery()' function
+
+export function checkSavedQuery () {
+ if (isStorageSupported('localStorage')
+ && window.localStorage.auto_saved_sql !== undefined
+ ) {
+ PMA_ajaxShowMessage(PMA_messages.strPreviousSaveQuery);
+ }
+}
+
+/**
+ * Set query to codemirror if show this query is
+ * checked and query for the db and table pair exists
+ */
+export function setShowThisQuery () {
+ var db = $('input[name="db"]').val();
+ var table = $('input[name="table"]').val();
+ if (isStorageSupported('localStorage')) {
+ if (window.localStorage.show_this_query_object !== undefined) {
+ var stored_db = JSON.parse(window.localStorage.show_this_query_object).db;
+ var stored_table = JSON.parse(window.localStorage.show_this_query_object).table;
+ var stored_query = JSON.parse(window.localStorage.show_this_query_object).query;
+ }
+ if (window.localStorage.show_this_query !== undefined
+ && window.localStorage.show_this_query === '1') {
+ $('input[name="show_query"]').prop('checked', true);
+ if ((db === stored_db && table === stored_table) || (db === undefined && table === undefined)) {
+ if (sqlQueryOptions.codemirror_editor) {
+ sqlQueryOptions.codemirror_editor.setValue(stored_query);
+ } else if (document.sqlform) {
+ document.sqlform.sql_query.value = stored_query;
+ }
+ }
+ } else {
+ $('input[name="show_query"]').prop('checked', false);
+ }
+ }
+}
+
+
+/**
+ * Saves SQL query in local storage or cookie
+ *
+ * @param string database name
+ * @param string table name
+ * @param string SQL query
+ * @return void
+ */
+export function PMA_showThisQuery (db, table, query) {
+ var show_this_query_object = {
+ 'db': db,
+ 'table': table,
+ 'query': query
+ };
+ if (isStorageSupported('localStorage')) {
+ window.localStorage.show_this_query = 1;
+ window.localStorage.show_this_query_object = JSON.stringify(show_this_query_object);
+ } else {
+ Cookies.set('show_this_quey', 1);
+ Cookies.set('show_this_query_object', JSON.stringify(show_this_query_object));
+ }
+}
+
+/**
+ * Saves SQL query in local storage or cookie
+ *
+ * @param string SQL query
+ * @return void
+ */
+export function PMA_autosaveSQL (query) {
+ if (isStorageSupported('localStorage')) {
+ window.localStorage.auto_saved_sql = query;
+ } else {
+ Cookies.set('auto_saved_sql', query);
+ }
+}
+
+/**
+ * Saves SQL query with sort in local storage or cookie
+ *
+ * @param string SQL query
+ * @return void
+ */
+export function PMA_autosaveSQLSort (query) {
+ if (query) {
+ if (isStorageSupported('localStorage')) {
+ window.localStorage.auto_saved_sql_sort = query;
+ } else {
+ Cookies.set('auto_saved_sql_sort', query);
+ }
+ }
+}