aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/js/table
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/js/table')
-rw-r--r--srcs/phpmyadmin/js/table/change.js724
-rw-r--r--srcs/phpmyadmin/js/table/chart.js425
-rw-r--r--srcs/phpmyadmin/js/table/find_replace.js46
-rw-r--r--srcs/phpmyadmin/js/table/gis_visualization.js368
-rw-r--r--srcs/phpmyadmin/js/table/operations.js321
-rw-r--r--srcs/phpmyadmin/js/table/relation.js248
-rw-r--r--srcs/phpmyadmin/js/table/select.js409
-rw-r--r--srcs/phpmyadmin/js/table/structure.js505
-rw-r--r--srcs/phpmyadmin/js/table/tracking.js106
-rw-r--r--srcs/phpmyadmin/js/table/zoom_plot_jqplot.js607
10 files changed, 3759 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/js/table/change.js b/srcs/phpmyadmin/js/table/change.js
new file mode 100644
index 0000000..bd7d4ba
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/change.js
@@ -0,0 +1,724 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * @fileoverview function used in table data manipulation pages
+ *
+ * @requires jQuery
+ * @requires jQueryUI
+ * @requires js/functions.js
+ *
+ */
+
+/* global extendingValidatorMessages */ // js/messages.php
+/* global openGISEditor, gisEditorLoaded, loadJSAndGISEditor, loadGISEditor */ // js/gis_data_editor.js
+
+/**
+ * Modify form controls when the "NULL" checkbox is checked
+ *
+ * @param theType string the MySQL field type
+ * @param urlField string the urlencoded field name - OBSOLETE
+ * @param md5Field string the md5 hashed field name
+ * @param multiEdit string the multi_edit row sequence number
+ *
+ * @return boolean always true
+ */
+function nullify (theType, urlField, md5Field, multiEdit) {
+ var rowForm = document.forms.insertForm;
+
+ if (typeof(rowForm.elements['funcs' + multiEdit + '[' + md5Field + ']']) !== 'undefined') {
+ rowForm.elements['funcs' + multiEdit + '[' + md5Field + ']'].selectedIndex = -1;
+ }
+
+ // "ENUM" field with more than 20 characters
+ if (Number(theType) === 1) {
+ rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'][1].selectedIndex = -1;
+ // Other "ENUM" field
+ } else if (Number(theType) === 2) {
+ var elts = rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'];
+ // when there is just one option in ENUM:
+ if (elts.checked) {
+ elts.checked = false;
+ } else {
+ var eltsCnt = elts.length;
+ for (var i = 0; i < eltsCnt; i++) {
+ elts[i].checked = false;
+ } // end for
+ } // end if
+ // "SET" field
+ } else if (Number(theType) === 3) {
+ rowForm.elements['fields' + multiEdit + '[' + md5Field + '][]'].selectedIndex = -1;
+ // Foreign key field (drop-down)
+ } else if (Number(theType) === 4) {
+ rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].selectedIndex = -1;
+ // foreign key field (with browsing icon for foreign values)
+ } else if (Number(theType) === 6) {
+ rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].value = '';
+ // Other field types
+ } else /* if (theType === 5)*/ {
+ rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].value = '';
+ } // end if... else if... else
+
+ return true;
+} // end of the 'nullify()' function
+
+
+/**
+ * javascript DateTime format validation.
+ * its used to prevent adding default (0000-00-00 00:00:00) to database when user enter wrong values
+ * Start of validation part
+ */
+// function checks the number of days in febuary
+function daysInFebruary (year) {
+ return (((year % 4 === 0) && (((year % 100 !== 0)) || (year % 400 === 0))) ? 29 : 28);
+}
+// function to convert single digit to double digit
+function fractionReplace (number) {
+ var num = parseInt(number, 10);
+ return num >= 1 && num <= 9 ? '0' + num : '00';
+}
+
+/* function to check the validity of date
+* The following patterns are accepted in this validation (accepted in mysql as well)
+* 1) 2001-12-23
+* 2) 2001-1-2
+* 3) 02-12-23
+* 4) And instead of using '-' the following punctuations can be used (+,.,*,^,@,/) All these are accepted by mysql as well. Therefore no issues
+*/
+function isDate (val, tmstmp) {
+ var value = val.replace(/[.|*|^|+|//|@]/g, '-');
+ var arrayVal = value.split('-');
+ for (var a = 0; a < arrayVal.length; a++) {
+ if (arrayVal[a].length === 1) {
+ arrayVal[a] = fractionReplace(arrayVal[a]);
+ }
+ }
+ value = arrayVal.join('-');
+ var pos = 2;
+ var dtexp = new RegExp(/^([0-9]{4})-(((01|03|05|07|08|10|12)-((0[0-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)-((0[0-9])|([1-2][0-9])|30))|((00)-(00)))$/);
+ if (value.length === 8) {
+ pos = 0;
+ }
+ if (dtexp.test(value)) {
+ var month = parseInt(value.substring(pos + 3, pos + 5), 10);
+ var day = parseInt(value.substring(pos + 6, pos + 8), 10);
+ var year = parseInt(value.substring(0, pos + 2), 10);
+ if (month === 2 && day > daysInFebruary(year)) {
+ return false;
+ }
+ if (value.substring(0, pos + 2).length === 2) {
+ year = parseInt('20' + value.substring(0, pos + 2), 10);
+ }
+ if (tmstmp === true) {
+ if (year < 1978) {
+ return false;
+ }
+ if (year > 2038 || (year > 2037 && day > 19 && month >= 1) || (year > 2037 && month > 1)) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/* function to check the validity of time
+* The following patterns are accepted in this validation (accepted in mysql as well)
+* 1) 2:3:4
+* 2) 2:23:43
+* 3) 2:23:43.123456
+*/
+function isTime (val) {
+ var arrayVal = val.split(':');
+ for (var a = 0, l = arrayVal.length; a < l; a++) {
+ if (arrayVal[a].length === 1) {
+ arrayVal[a] = fractionReplace(arrayVal[a]);
+ }
+ }
+ var newVal = arrayVal.join(':');
+ var tmexp = new RegExp(/^(-)?(([0-7]?[0-9][0-9])|(8[0-2][0-9])|(83[0-8])):((0[0-9])|([1-5][0-9])):((0[0-9])|([1-5][0-9]))(\.[0-9]{1,6}){0,1}$/);
+ return tmexp.test(newVal);
+}
+
+/**
+ * To check whether insert section is ignored or not
+ */
+function checkForCheckbox (multiEdit) {
+ if ($('#insert_ignore_' + multiEdit).length) {
+ return $('#insert_ignore_' + multiEdit).is(':unchecked');
+ }
+ return true;
+}
+
+function verificationsAfterFieldChange (urlField, multiEdit, theType) {
+ var evt = window.event || arguments.callee.caller.arguments[0];
+ var target = evt.target || evt.srcElement;
+ var $thisInput = $(':input[name^=\'fields[multi_edit][' + multiEdit + '][' +
+ urlField + ']\']');
+ // the function drop-down that corresponds to this input field
+ var $thisFunction = $('select[name=\'funcs[multi_edit][' + multiEdit + '][' +
+ urlField + ']\']');
+ var functionSelected = false;
+ if (typeof $thisFunction.val() !== 'undefined' &&
+ $thisFunction.val() !== null &&
+ $thisFunction.val().length > 0
+ ) {
+ functionSelected = true;
+ }
+
+ // To generate the textbox that can take the salt
+ var newSaltBox = '<br><input type=text name=salt[multi_edit][' + multiEdit + '][' + urlField + ']' +
+ ' id=salt_' + target.id + ' placeholder=\'' + Messages.strEncryptionKey + '\'>';
+
+ // If encrypting or decrypting functions that take salt as input is selected append the new textbox for salt
+ if (target.value === 'AES_ENCRYPT' ||
+ target.value === 'AES_DECRYPT' ||
+ target.value === 'DES_ENCRYPT' ||
+ target.value === 'DES_DECRYPT' ||
+ target.value === 'ENCRYPT') {
+ if (!($('#salt_' + target.id).length)) {
+ $thisInput.after(newSaltBox);
+ }
+ } else {
+ // Remove the textbox for salt
+ $('#salt_' + target.id).prev('br').remove();
+ $('#salt_' + target.id).remove();
+ }
+
+ if (target.value === 'AES_DECRYPT'
+ || target.value === 'AES_ENCRYPT'
+ || target.value === 'MD5') {
+ $('#' + target.id).rules('add', {
+ validationFunctionForFuns: {
+ param: $thisInput,
+ depends: function () {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ });
+ }
+
+ // Unchecks the corresponding "NULL" control
+ $('input[name=\'fields_null[multi_edit][' + multiEdit + '][' + urlField + ']\']').prop('checked', false);
+
+ // Unchecks the Ignore checkbox for the current row
+ $('input[name=\'insert_ignore_' + multiEdit + '\']').prop('checked', false);
+
+ var charExceptionHandling;
+ if (theType.substring(0,4) === 'char') {
+ charExceptionHandling = theType.substring(5,6);
+ } else if (theType.substring(0,7) === 'varchar') {
+ charExceptionHandling = theType.substring(8,9);
+ }
+ if (functionSelected) {
+ $thisInput.removeAttr('min');
+ $thisInput.removeAttr('max');
+ // @todo: put back attributes if corresponding function is deselected
+ }
+
+ if ($thisInput.data('rulesadded') === null && ! functionSelected) {
+ // call validate before adding rules
+ $($thisInput[0].form).validate();
+ // validate for date time
+ if (theType === 'datetime' || theType === 'time' || theType === 'date' || theType === 'timestamp') {
+ $thisInput.rules('add', {
+ validationFunctionForDateTime: {
+ param: theType,
+ depends: function () {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ });
+ }
+ // validation for integer type
+ if ($thisInput.data('type') === 'INT') {
+ var mini = parseInt($thisInput.attr('min'));
+ var maxi = parseInt($thisInput.attr('max'));
+ $thisInput.rules('add', {
+ number: {
+ param : true,
+ depends: function () {
+ return checkForCheckbox(multiEdit);
+ }
+ },
+ min: {
+ param: mini,
+ depends: function () {
+ if (isNaN($thisInput.val())) {
+ return false;
+ } else {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ },
+ max: {
+ param: maxi,
+ depends: function () {
+ if (isNaN($thisInput.val())) {
+ return false;
+ } else {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ }
+ });
+ // validation for CHAR types
+ } else if ($thisInput.data('type') === 'CHAR') {
+ var maxlen = $thisInput.data('maxlength');
+ if (typeof maxlen !== 'undefined') {
+ if (maxlen <= 4) {
+ maxlen = charExceptionHandling;
+ }
+ $thisInput.rules('add', {
+ maxlength: {
+ param: maxlen,
+ depends: function () {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ });
+ }
+ // validate binary & blob types
+ } else if ($thisInput.data('type') === 'HEX') {
+ $thisInput.rules('add', {
+ validationFunctionForHex: {
+ param: true,
+ depends: function () {
+ return checkForCheckbox(multiEdit);
+ }
+ }
+ });
+ }
+ $thisInput.data('rulesadded', true);
+ } else if ($thisInput.data('rulesadded') === true && functionSelected) {
+ // remove any rules added
+ $thisInput.rules('remove');
+ // remove any error messages
+ $thisInput
+ .removeClass('error')
+ .removeAttr('aria-invalid')
+ .siblings('.error')
+ .remove();
+ $thisInput.data('rulesadded', null);
+ }
+}
+/* End of fields validation*/
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/change.js', function () {
+ $(document).off('click', 'span.open_gis_editor');
+ $(document).off('click', 'input[name^=\'insert_ignore_\']');
+ $(document).off('click', 'input[name=\'gis_data[save]\']');
+ $(document).off('click', 'input.checkbox_null');
+ $('select[name="submit_type"]').off('change');
+ $(document).off('change', '#insert_rows');
+});
+
+/**
+ * Ajax handlers for Change Table page
+ *
+ * Actions Ajaxified here:
+ * Submit Data to be inserted into the table.
+ * Restart insertion with 'N' rows.
+ */
+AJAX.registerOnload('table/change.js', function () {
+ if ($('#insertForm').length) {
+ // validate the comment form when it is submitted
+ $('#insertForm').validate();
+ jQuery.validator.addMethod('validationFunctionForHex', function (value) {
+ return value.match(/^[a-f0-9]*$/i) !== null;
+ });
+
+ jQuery.validator.addMethod('validationFunctionForFuns', function (value, element, options) {
+ if (value.substring(0, 3) === 'AES' && options.data('type') !== 'HEX') {
+ return false;
+ }
+
+ return !(value.substring(0, 3) === 'MD5' &&
+ typeof options.data('maxlength') !== 'undefined' &&
+ options.data('maxlength') < 32);
+ });
+
+ jQuery.validator.addMethod('validationFunctionForDateTime', function (value, element, options) {
+ var dtValue = value;
+ var theType = options;
+ if (theType === 'date') {
+ return isDate(dtValue);
+ } else if (theType === 'time') {
+ return isTime(dtValue);
+ } else if (theType === 'datetime' || theType === 'timestamp') {
+ var tmstmp = false;
+ dtValue = dtValue.trim();
+ if (dtValue === 'CURRENT_TIMESTAMP' || dtValue === 'current_timestamp()') {
+ return true;
+ }
+ if (theType === 'timestamp') {
+ tmstmp = true;
+ }
+ if (dtValue === '0000-00-00 00:00:00') {
+ return true;
+ }
+ var dv = dtValue.indexOf(' ');
+ if (dv === -1) { // Only the date component, which is valid
+ return isDate(dtValue, tmstmp);
+ }
+
+ return isDate(dtValue.substring(0, dv), tmstmp) &&
+ isTime(dtValue.substring(dv + 1));
+ }
+ });
+ /*
+ * message extending script must be run
+ * after initiation of functions
+ */
+ extendingValidatorMessages();
+ }
+
+ $.datepicker.initialized = false;
+
+ $(document).on('click', 'span.open_gis_editor', function (event) {
+ event.preventDefault();
+
+ var $span = $(this);
+ // Current value
+ var value = $span.parent('td').children('input[type=\'text\']').val();
+ // Field name
+ var field = $span.parents('tr').children('td:first').find('input[type=\'hidden\']').val();
+ // Column type
+ var type = $span.parents('tr').find('span.column_type').text();
+ // Names of input field and null checkbox
+ var inputName = $span.parent('td').children('input[type=\'text\']').attr('name');
+
+ openGISEditor();
+ if (!gisEditorLoaded) {
+ loadJSAndGISEditor(value, field, type, inputName);
+ } else {
+ loadGISEditor(value, field, type, inputName);
+ }
+ });
+
+ /**
+ * Forced validation check of fields
+ */
+ $(document).on('click','input[name^=\'insert_ignore_\']', function () {
+ $('#insertForm').valid();
+ });
+
+ /**
+ * Uncheck the null checkbox as geometry data is placed on the input field
+ */
+ $(document).on('click', 'input[name=\'gis_data[save]\']', function () {
+ var inputName = $('form#gis_data_editor_form').find('input[name=\'input_name\']').val();
+ var $nullCheckbox = $('input[name=\'' + inputName + '\']').parents('tr').find('.checkbox_null');
+ $nullCheckbox.prop('checked', false);
+ });
+
+ /**
+ * Handles all current checkboxes for Null; this only takes care of the
+ * checkboxes on currently displayed rows as the rows generated by
+ * "Continue insertion" are handled in the "Continue insertion" code
+ *
+ */
+ $(document).on('click', 'input.checkbox_null', function () {
+ nullify(
+ // use hidden fields populated by tbl_change.php
+ $(this).siblings('.nullify_code').val(),
+ $(this).closest('tr').find('input:hidden').first().val(),
+ $(this).siblings('.hashed_field').val(),
+ $(this).siblings('.multi_edit').val()
+ );
+ });
+
+ /**
+ * Reset the auto_increment column to 0 when selecting any of the
+ * insert options in submit_type-dropdown. Only perform the reset
+ * when we are in edit-mode, and not in insert-mode(no previous value
+ * available).
+ */
+ $('select[name="submit_type"]').on('change', function () {
+ var thisElemSubmitTypeVal = $(this).val();
+ var $table = $('table.insertRowTable');
+ var autoIncrementColumn = $table.find('input[name^="auto_increment"]');
+ autoIncrementColumn.each(function () {
+ var $thisElemAIField = $(this);
+ var thisElemName = $thisElemAIField.attr('name');
+
+ var prevValueField = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields_prev') + '"]');
+ var valueField = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields') + '"]');
+ var previousValue = $(prevValueField).val();
+ if (previousValue !== undefined) {
+ if (thisElemSubmitTypeVal === 'insert'
+ || thisElemSubmitTypeVal === 'insertignore'
+ || thisElemSubmitTypeVal === 'showinsert'
+ ) {
+ $(valueField).val(0);
+ } else {
+ $(valueField).val(previousValue);
+ }
+ }
+ });
+ });
+
+ /**
+ * Handle ENTER key when press on Continue insert with field
+ */
+ $('#insert_rows').on('keypress', function (e) {
+ var key = e.which;
+ if (key === 13) {
+ addNewContinueInsertionFiels(e);
+ }
+ });
+
+ /**
+ * Continue Insertion form
+ */
+ $(document).on('change', '#insert_rows', addNewContinueInsertionFiels);
+});
+
+function addNewContinueInsertionFiels (event) {
+ event.preventDefault();
+ /**
+ * @var columnCount Number of number of columns table has.
+ */
+ var columnCount = $('table.insertRowTable:first').find('tr').has('input[name*=\'fields_name\']').length;
+ /**
+ * @var curr_rows Number of current insert rows already on page
+ */
+ var currRows = $('table.insertRowTable').length;
+ /**
+ * @var target_rows Number of rows the user wants
+ */
+ var targetRows = $('#insert_rows').val();
+
+ // remove all datepickers
+ $('input.datefield, input.datetimefield').each(function () {
+ $(this).datepicker('destroy');
+ });
+
+ if (currRows < targetRows) {
+ var tempIncrementIndex = function () {
+ var $thisElement = $(this);
+ /**
+ * Extract the index from the name attribute for all input/select fields and increment it
+ * name is of format funcs[multi_edit][10][<long random string of alphanum chars>]
+ */
+
+ /**
+ * @var this_name String containing name of the input/select elements
+ */
+ var thisName = $thisElement.attr('name');
+ /** split {@link thisName} at [10], so we have the parts that can be concatenated later */
+ var nameParts = thisName.split(/\[\d+\]/);
+ /** extract the [10] from {@link nameParts} */
+ var oldRowIndexString = thisName.match(/\[\d+\]/)[0];
+ /** extract 10 - had to split into two steps to accomodate double digits */
+ var oldRowIndex = parseInt(oldRowIndexString.match(/\d+/)[0], 10);
+
+ /** calculate next index i.e. 11 */
+ newRowIndex = oldRowIndex + 1;
+ /** generate the new name i.e. funcs[multi_edit][11][foobarbaz] */
+ var newName = nameParts[0] + '[' + newRowIndex + ']' + nameParts[1];
+
+ var hashedField = nameParts[1].match(/\[(.+)\]/)[1];
+ $thisElement.attr('name', newName);
+
+ /** If element is select[name*='funcs'], update id */
+ if ($thisElement.is('select[name*=\'funcs\']')) {
+ var thisId = $thisElement.attr('id');
+ var idParts = thisId.split(/_/);
+ var oldIdIndex = idParts[1];
+ var prevSelectedValue = $('#field_' + oldIdIndex + '_1').val();
+ var newIdIndex = parseInt(oldIdIndex) + columnCount;
+ var newId = 'field_' + newIdIndex + '_1';
+ $thisElement.attr('id', newId);
+ $thisElement.find('option').filter(function () {
+ return $(this).text() === prevSelectedValue;
+ }).attr('selected','selected');
+
+ // If salt field is there then update its id.
+ var nextSaltInput = $thisElement.parent().next('td').next('td').find('input[name*=\'salt\']');
+ if (nextSaltInput.length !== 0) {
+ nextSaltInput.attr('id', 'salt_' + newId);
+ }
+ }
+
+ // handle input text fields and textareas
+ if ($thisElement.is('.textfield') || $thisElement.is('.char') || $thisElement.is('textarea')) {
+ // do not remove the 'value' attribute for ENUM columns
+ // special handling for radio fields after updating ids to unique - see below
+ if ($thisElement.closest('tr').find('span.column_type').html() !== 'enum') {
+ $thisElement.val($thisElement.closest('tr').find('span.default_value').html());
+ }
+ $thisElement
+ .off('change')
+ // Remove onchange attribute that was placed
+ // by tbl_change.php; it refers to the wrong row index
+ .attr('onchange', null)
+ // Keep these values to be used when the element
+ // will change
+ .data('hashed_field', hashedField)
+ .data('new_row_index', newRowIndex)
+ .on('change', function () {
+ var $changedElement = $(this);
+ verificationsAfterFieldChange(
+ $changedElement.data('hashed_field'),
+ $changedElement.data('new_row_index'),
+ $changedElement.closest('tr').find('span.column_type').html()
+ );
+ });
+ }
+
+ if ($thisElement.is('.checkbox_null')) {
+ $thisElement
+ // this event was bound earlier by jQuery but
+ // to the original row, not the cloned one, so unbind()
+ .off('click')
+ // Keep these values to be used when the element
+ // will be clicked
+ .data('hashed_field', hashedField)
+ .data('new_row_index', newRowIndex)
+ .on('click', function () {
+ var $changedElement = $(this);
+ nullify(
+ $changedElement.siblings('.nullify_code').val(),
+ $thisElement.closest('tr').find('input:hidden').first().val(),
+ $changedElement.data('hashed_field'),
+ '[multi_edit][' + $changedElement.data('new_row_index') + ']'
+ );
+ });
+ }
+ };
+
+ var tempReplaceAnchor = function () {
+ var $anchor = $(this);
+ var newValue = 'rownumber=' + newRowIndex;
+ // needs improvement in case something else inside
+ // the href contains this pattern
+ var newHref = $anchor.attr('href').replace(/rownumber=\d+/, newValue);
+ $anchor.attr('href', newHref);
+ };
+
+ while (currRows < targetRows) {
+ /**
+ * @var $last_row Object referring to the last row
+ */
+ var $lastRow = $('#insertForm').find('.insertRowTable:last');
+
+ // need to access this at more than one level
+ // (also needs improvement because it should be calculated
+ // just once per cloned row, not once per column)
+ var newRowIndex = 0;
+
+ // Clone the insert tables
+ $lastRow
+ .clone(true, true)
+ .insertBefore('#actions_panel')
+ .find('input[name*=multi_edit],select[name*=multi_edit],textarea[name*=multi_edit]')
+ .each(tempIncrementIndex)
+ .end()
+ .find('.foreign_values_anchor')
+ .each(tempReplaceAnchor);
+
+ // Insert/Clone the ignore checkboxes
+ if (currRows === 1) {
+ $('<input id="insert_ignore_1" type="checkbox" name="insert_ignore_1" checked="checked">')
+ .insertBefore('table.insertRowTable:last')
+ .after('<label for="insert_ignore_1">' + Messages.strIgnore + '</label>');
+ } else {
+ /**
+ * @var $last_checkbox Object reference to the last checkbox in #insertForm
+ */
+ var $lastCheckbox = $('#insertForm').children('input:checkbox:last');
+
+ /** name of {@link $lastCheckbox} */
+ var lastCheckboxName = $lastCheckbox.attr('name');
+ /** index of {@link $lastCheckbox} */
+ var lastCheckboxIndex = parseInt(lastCheckboxName.match(/\d+/), 10);
+ /** name of new {@link $lastCheckbox} */
+ var newName = lastCheckboxName.replace(/\d+/, lastCheckboxIndex + 1);
+
+ $('<br><div class="clearfloat"></div>')
+ .insertBefore('table.insertRowTable:last');
+
+ $lastCheckbox
+ .clone()
+ .attr({ 'id': newName, 'name': newName })
+ .prop('checked', true)
+ .insertBefore('table.insertRowTable:last');
+
+ $('label[for^=insert_ignore]:last')
+ .clone()
+ .attr('for', newName)
+ .insertBefore('table.insertRowTable:last');
+
+ $('<br>')
+ .insertBefore('table.insertRowTable:last');
+ }
+ currRows++;
+ }
+ // recompute tabindex for text fields and other controls at footer;
+ // IMO it's not really important to handle the tabindex for
+ // function and Null
+ var tabIndex = 0;
+ $('.textfield, .char, textarea')
+ .each(function () {
+ tabIndex++;
+ $(this).attr('tabindex', tabIndex);
+ // update the IDs of textfields to ensure that they are unique
+ $(this).attr('id', 'field_' + tabIndex + '_3');
+
+ // special handling for radio fields after updating ids to unique
+ if ($(this).closest('tr').find('span.column_type').html() === 'enum') {
+ if ($(this).val() === $(this).closest('tr').find('span.default_value').html()) {
+ $(this).prop('checked', true);
+ } else {
+ $(this).prop('checked', false);
+ }
+ }
+ });
+ $('.control_at_footer')
+ .each(function () {
+ tabIndex++;
+ $(this).attr('tabindex', tabIndex);
+ });
+ } else if (currRows > targetRows) {
+ /**
+ * Displays alert if data loss possible on decrease
+ * of rows.
+ */
+ var checkLock = jQuery.isEmptyObject(AJAX.lockedTargets);
+ if (checkLock || confirm(Messages.strConfirmRowChange) === true) {
+ while (currRows > targetRows) {
+ $('input[id^=insert_ignore]:last')
+ .nextUntil('fieldset')
+ .addBack()
+ .remove();
+ currRows--;
+ }
+ } else {
+ document.getElementById('insert_rows').value = currRows;
+ }
+ }
+ // Add all the required datepickers back
+ Functions.addDateTimePicker();
+}
+
+// eslint-disable-next-line no-unused-vars
+function changeValueFieldType (elem, searchIndex) {
+ var fieldsValue = $('select#fieldID_' + searchIndex);
+ if (0 === fieldsValue.size()) {
+ return;
+ }
+
+ var type = $(elem).val();
+ if ('IN (...)' === type ||
+ 'NOT IN (...)' === type ||
+ 'BETWEEN' === type ||
+ 'NOT BETWEEN' === type
+ ) {
+ $('#fieldID_' + searchIndex).attr('multiple', '');
+ } else {
+ $('#fieldID_' + searchIndex).removeAttr('multiple');
+ }
+}
diff --git a/srcs/phpmyadmin/js/table/chart.js b/srcs/phpmyadmin/js/table/chart.js
new file mode 100644
index 0000000..2a6f43f
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/chart.js
@@ -0,0 +1,425 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+
+/* global ColumnType, DataTable, JQPlotChartFactory */ // js/chart.js
+/* global codeMirrorEditor */ // js/functions.js
+
+var chartData = {};
+var tempChartTitle;
+
+var currentChart = null;
+var currentSettings = null;
+
+var dateTimeCols = [];
+var numericCols = [];
+
+function extractDate (dateString) {
+ var matches;
+ var match;
+ var dateTimeRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/;
+ var dateRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
+
+ matches = dateTimeRegExp.exec(dateString);
+ if (matches !== null && matches.length > 0) {
+ match = matches[0];
+ return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2), match.substr(11, 2), match.substr(14, 2), match.substr(17, 2));
+ } else {
+ matches = dateRegExp.exec(dateString);
+ if (matches !== null && matches.length > 0) {
+ match = matches[0];
+ return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2));
+ }
+ }
+ return null;
+}
+
+function queryChart (data, columnNames, settings) {
+ if ($('#querychart').length === 0) {
+ return;
+ }
+
+ var plotSettings = {
+ title : {
+ text : settings.title,
+ escapeHtml: true
+ },
+ grid : {
+ drawBorder : false,
+ shadow : false,
+ background : 'rgba(0,0,0,0)'
+ },
+ legend : {
+ show : true,
+ placement : 'outsideGrid',
+ location : 'e',
+ rendererOptions: {
+ numberColumns: 2
+ }
+ },
+ axes : {
+ xaxis : {
+ label : Functions.escapeHtml(settings.xaxisLabel)
+ },
+ yaxis : {
+ label : settings.yaxisLabel
+ }
+ },
+ stackSeries : settings.stackSeries
+ };
+
+ // create the chart
+ var factory = new JQPlotChartFactory();
+ var chart = factory.createChart(settings.type, 'querychart');
+
+ // create the data table and add columns
+ var dataTable = new DataTable();
+ if (settings.type === 'timeline') {
+ dataTable.addColumn(ColumnType.DATE, columnNames[settings.mainAxis]);
+ } else if (settings.type === 'scatter') {
+ dataTable.addColumn(ColumnType.NUMBER, columnNames[settings.mainAxis]);
+ } else {
+ dataTable.addColumn(ColumnType.STRING, columnNames[settings.mainAxis]);
+ }
+
+ var i;
+ var values = [];
+ if (settings.seriesColumn === null) {
+ $.each(settings.selectedSeries, function (index, element) {
+ dataTable.addColumn(ColumnType.NUMBER, columnNames[element]);
+ });
+
+ // set data to the data table
+ var columnsToExtract = [settings.mainAxis];
+ $.each(settings.selectedSeries, function (index, element) {
+ columnsToExtract.push(element);
+ });
+ var newRow;
+ var row;
+ var col;
+ for (i = 0; i < data.length; i++) {
+ row = data[i];
+ newRow = [];
+ for (var j = 0; j < columnsToExtract.length; j++) {
+ col = columnNames[columnsToExtract[j]];
+ if (j === 0) {
+ if (settings.type === 'timeline') { // first column is date type
+ newRow.push(extractDate(row[col]));
+ } else if (settings.type === 'scatter') {
+ newRow.push(parseFloat(row[col]));
+ } else { // first column is string type
+ newRow.push(row[col]);
+ }
+ } else { // subsequent columns are of type, number
+ newRow.push(parseFloat(row[col]));
+ }
+ }
+ values.push(newRow);
+ }
+ dataTable.setData(values);
+ } else {
+ var seriesNames = {};
+ var seriesNumber = 1;
+ var seriesColumnName = columnNames[settings.seriesColumn];
+ for (i = 0; i < data.length; i++) {
+ if (! seriesNames[data[i][seriesColumnName]]) {
+ seriesNames[data[i][seriesColumnName]] = seriesNumber;
+ seriesNumber++;
+ }
+ }
+
+ $.each(seriesNames, function (seriesName) {
+ dataTable.addColumn(ColumnType.NUMBER, seriesName);
+ });
+
+ var valueMap = {};
+ var xValue;
+ var value;
+ var mainAxisName = columnNames[settings.mainAxis];
+ var valueColumnName = columnNames[settings.valueColumn];
+ for (i = 0; i < data.length; i++) {
+ xValue = data[i][mainAxisName];
+ value = valueMap[xValue];
+ if (! value) {
+ value = [xValue];
+ valueMap[xValue] = value;
+ }
+ seriesNumber = seriesNames[data[i][seriesColumnName]];
+ value[seriesNumber] = parseFloat(data[i][valueColumnName]);
+ }
+
+ $.each(valueMap, function (index, value) {
+ values.push(value);
+ });
+ dataTable.setData(values);
+ }
+
+ // draw the chart and return the chart object
+ chart.draw(dataTable, plotSettings);
+ return chart;
+}
+
+function drawChart () {
+ currentSettings.width = $('#resizer').width() - 20;
+ currentSettings.height = $('#resizer').height() - 20;
+
+ // TODO: a better way using .redraw() ?
+ if (currentChart !== null) {
+ currentChart.destroy();
+ }
+
+ var columnNames = [];
+ $('select[name="chartXAxis"] option').each(function () {
+ columnNames.push(Functions.escapeHtml($(this).text()));
+ });
+ try {
+ currentChart = queryChart(chartData, columnNames, currentSettings);
+ if (currentChart !== null) {
+ $('#saveChart').attr('href', currentChart.toImageString());
+ }
+ } catch (err) {
+ Functions.ajaxShowMessage(err.message, false);
+ }
+}
+
+function getSelectedSeries () {
+ var val = $('select[name="chartSeries"]').val() || [];
+ var ret = [];
+ $.each(val, function (i, v) {
+ ret.push(parseInt(v, 10));
+ });
+ return ret;
+}
+
+function onXAxisChange () {
+ var $xAxisSelect = $('select[name="chartXAxis"]');
+ currentSettings.mainAxis = parseInt($xAxisSelect.val(), 10);
+ if (dateTimeCols.indexOf(currentSettings.mainAxis) !== -1) {
+ $('span.span_timeline').show();
+ } else {
+ $('span.span_timeline').hide();
+ if (currentSettings.type === 'timeline') {
+ $('input#radio_line').prop('checked', true);
+ currentSettings.type = 'line';
+ }
+ }
+ if (numericCols.indexOf(currentSettings.mainAxis) !== -1) {
+ $('span.span_scatter').show();
+ } else {
+ $('span.span_scatter').hide();
+ if (currentSettings.type === 'scatter') {
+ $('input#radio_line').prop('checked', true);
+ currentSettings.type = 'line';
+ }
+ }
+ var xAxisTitle = $xAxisSelect.children('option:selected').text();
+ $('input[name="xaxis_label"]').val(xAxisTitle);
+ currentSettings.xaxisLabel = xAxisTitle;
+}
+
+function onDataSeriesChange () {
+ var $seriesSelect = $('select[name="chartSeries"]');
+ currentSettings.selectedSeries = getSelectedSeries();
+ var yAxisTitle;
+ if (currentSettings.selectedSeries.length === 1) {
+ $('span.span_pie').show();
+ yAxisTitle = $seriesSelect.children('option:selected').text();
+ } else {
+ $('span.span_pie').hide();
+ if (currentSettings.type === 'pie') {
+ $('input#radio_line').prop('checked', true);
+ currentSettings.type = 'line';
+ }
+ yAxisTitle = Messages.strYValues;
+ }
+ $('input[name="yaxis_label"]').val(yAxisTitle);
+ currentSettings.yaxisLabel = yAxisTitle;
+}
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/chart.js', function () {
+ $('input[name="chartType"]').off('click');
+ $('input[name="barStacked"]').off('click');
+ $('input[name="chkAlternative"]').off('click');
+ $('input[name="chartTitle"]').off('focus').off('keyup').off('blur');
+ $('select[name="chartXAxis"]').off('change');
+ $('select[name="chartSeries"]').off('change');
+ $('select[name="chartSeriesColumn"]').off('change');
+ $('select[name="chartValueColumn"]').off('change');
+ $('input[name="xaxis_label"]').off('keyup');
+ $('input[name="yaxis_label"]').off('keyup');
+ $('#resizer').off('resizestop');
+ $('#tblchartform').off('submit');
+});
+
+AJAX.registerOnload('table/chart.js', function () {
+ // handle manual resize
+ $('#resizer').on('resizestop', function () {
+ // make room so that the handle will still appear
+ $('#querychart').height($('#resizer').height() * 0.96);
+ $('#querychart').width($('#resizer').width() * 0.96);
+ if (currentChart !== null) {
+ currentChart.redraw({
+ resetAxes : true
+ });
+ }
+ });
+
+ // handle chart type changes
+ $('input[name="chartType"]').on('click', function () {
+ var type = currentSettings.type = $(this).val();
+ if (type === 'bar' || type === 'column' || type === 'area') {
+ $('span.barStacked').show();
+ } else {
+ $('input[name="barStacked"]').prop('checked', false);
+ $.extend(true, currentSettings, { stackSeries : false });
+ $('span.barStacked').hide();
+ }
+ drawChart();
+ });
+
+ // handle chosing alternative data format
+ $('input[name="chkAlternative"]').on('click', function () {
+ var $seriesColumn = $('select[name="chartSeriesColumn"]');
+ var $valueColumn = $('select[name="chartValueColumn"]');
+ var $chartSeries = $('select[name="chartSeries"]');
+ if ($(this).is(':checked')) {
+ $seriesColumn.prop('disabled', false);
+ $valueColumn.prop('disabled', false);
+ $chartSeries.prop('disabled', true);
+ currentSettings.seriesColumn = parseInt($seriesColumn.val(), 10);
+ currentSettings.valueColumn = parseInt($valueColumn.val(), 10);
+ } else {
+ $seriesColumn.prop('disabled', true);
+ $valueColumn.prop('disabled', true);
+ $chartSeries.prop('disabled', false);
+ currentSettings.seriesColumn = null;
+ currentSettings.valueColumn = null;
+ }
+ drawChart();
+ });
+
+ // handle stacking for bar, column and area charts
+ $('input[name="barStacked"]').on('click', function () {
+ if ($(this).is(':checked')) {
+ $.extend(true, currentSettings, { stackSeries : true });
+ } else {
+ $.extend(true, currentSettings, { stackSeries : false });
+ }
+ drawChart();
+ });
+
+ // handle changes in chart title
+ $('input[name="chartTitle"]')
+ .on('focus', function () {
+ tempChartTitle = $(this).val();
+ })
+ .on('keyup', function () {
+ currentSettings.title = $('input[name="chartTitle"]').val();
+ drawChart();
+ })
+ .on('blur', function () {
+ if ($(this).val() !== tempChartTitle) {
+ drawChart();
+ }
+ });
+
+ // handle changing the x-axis
+ $('select[name="chartXAxis"]').on('change', function () {
+ onXAxisChange();
+ drawChart();
+ });
+
+ // handle changing the selected data series
+ $('select[name="chartSeries"]').on('change', function () {
+ onDataSeriesChange();
+ drawChart();
+ });
+
+ // handle changing the series column
+ $('select[name="chartSeriesColumn"]').on('change', function () {
+ currentSettings.seriesColumn = parseInt($(this).val(), 10);
+ drawChart();
+ });
+
+ // handle changing the value column
+ $('select[name="chartValueColumn"]').on('change', function () {
+ currentSettings.valueColumn = parseInt($(this).val(), 10);
+ drawChart();
+ });
+
+ // handle manual changes to the chart x-axis labels
+ $('input[name="xaxis_label"]').on('keyup', function () {
+ currentSettings.xaxisLabel = $(this).val();
+ drawChart();
+ });
+
+ // handle manual changes to the chart y-axis labels
+ $('input[name="yaxis_label"]').on('keyup', function () {
+ currentSettings.yaxisLabel = $(this).val();
+ drawChart();
+ });
+
+ // handler for ajax form submission
+ $('#tblchartform').submit(function () {
+ var $form = $(this);
+ if (codeMirrorEditor) {
+ $form[0].elements.sql_query.value = codeMirrorEditor.getValue();
+ }
+ if (!Functions.checkSqlQuery($form[0])) {
+ return false;
+ }
+
+ var $msgbox = Functions.ajaxShowMessage();
+ Functions.prepareForAjaxRequest($form);
+ $.post($form.attr('action'), $form.serialize(), function (data) {
+ if (typeof data !== 'undefined' &&
+ data.success === true &&
+ typeof data.chartData !== 'undefined') {
+ chartData = JSON.parse(data.chartData);
+ drawChart();
+ Functions.ajaxRemoveMessage($msgbox);
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }, 'json'); // end $.post()
+
+ return false;
+ });
+
+ // from jQuery UI
+ $('#resizer').resizable({
+ minHeight: 240,
+ minWidth: 300
+ })
+ .width($('#div_view_options').width() - 50)
+ .trigger('resizestop');
+
+ currentSettings = {
+ type : 'line',
+ width : $('#resizer').width() - 20,
+ height : $('#resizer').height() - 20,
+ xaxisLabel : $('input[name="xaxis_label"]').val(),
+ yaxisLabel : $('input[name="yaxis_label"]').val(),
+ title : $('input[name="chartTitle"]').val(),
+ stackSeries : false,
+ mainAxis : parseInt($('select[name="chartXAxis"]').val(), 10),
+ selectedSeries : getSelectedSeries(),
+ seriesColumn : null
+ };
+
+ var vals = $('input[name="dateTimeCols"]').val().split(' ');
+ $.each(vals, function (i, v) {
+ dateTimeCols.push(parseInt(v, 10));
+ });
+
+ vals = $('input[name="numericCols"]').val().split(' ');
+ $.each(vals, function (i, v) {
+ numericCols.push(parseInt(v, 10));
+ });
+
+ onXAxisChange();
+ onDataSeriesChange();
+
+ $('#tblchartform').trigger('submit');
+});
diff --git a/srcs/phpmyadmin/js/table/find_replace.js b/srcs/phpmyadmin/js/table/find_replace.js
new file mode 100644
index 0000000..9470ca7
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/find_replace.js
@@ -0,0 +1,46 @@
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/find_replace.js', function () {
+ $('#find_replace_form').off('submit');
+ $('#toggle_find').off('click');
+});
+
+/**
+ * Bind events
+ */
+AJAX.registerOnload('table/find_replace.js', function () {
+ $('<div id="toggle_find_div"><a id="toggle_find"></a></div>')
+ .insertAfter('#find_replace_form')
+ .hide();
+
+ $('#toggle_find')
+ .html(Messages.strHideFindNReplaceCriteria)
+ .on('click', function () {
+ var $link = $(this);
+ $('#find_replace_form').slideToggle();
+ if ($link.text() === Messages.strHideFindNReplaceCriteria) {
+ $link.text(Messages.strShowFindNReplaceCriteria);
+ } else {
+ $link.text(Messages.strHideFindNReplaceCriteria);
+ }
+ return false;
+ });
+
+ $('#find_replace_form').submit(function (e) {
+ e.preventDefault();
+ var findReplaceForm = $('#find_replace_form');
+ Functions.prepareForAjaxRequest(findReplaceForm);
+ var $msgbox = Functions.ajaxShowMessage();
+ $.post(findReplaceForm.attr('action'), findReplaceForm.serialize(), function (data) {
+ Functions.ajaxRemoveMessage($msgbox);
+ if (data.success === true) {
+ $('#toggle_find_div').show();
+ $('#toggle_find').trigger('click');
+ $('#sqlqueryresultsouter').html(data.preview);
+ } else {
+ $('#sqlqueryresultsouter').html(data.error);
+ }
+ });
+ });
+});
diff --git a/srcs/phpmyadmin/js/table/gis_visualization.js b/srcs/phpmyadmin/js/table/gis_visualization.js
new file mode 100644
index 0000000..da07fb7
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/gis_visualization.js
@@ -0,0 +1,368 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * @fileoverview functions used for visualizing GIS data
+ *
+ * @requires jquery
+ * @requires vendor/jquery/jquery.svg.js
+ * @requires vendor/jquery/jquery.mousewheel.js
+ * @requires vendor/jquery/jquery.event.drag-2.2.js
+ */
+
+/* global drawOpenLayers */ // templates/table/gis_visualization/gis_visualization.twig
+
+// Constants
+var zoomFactor = 1.5;
+var defaultX = 0;
+var defaultY = 0;
+
+// Variables
+var x = 0;
+var y = 0;
+var scale = 1;
+
+var svg;
+
+/**
+ * Zooms and pans the visualization.
+ */
+function zoomAndPan () {
+ var g = svg.getElementById('groupPanel');
+ if (!g) {
+ return;
+ }
+
+ g.setAttribute('transform', 'translate(' + x + ', ' + y + ') scale(' + scale + ')');
+ var id;
+ var circle;
+ $('circle.vector').each(function () {
+ id = $(this).attr('id');
+ circle = svg.getElementById(id);
+ $(svg).on('change', circle, {
+ r : (3 / scale),
+ 'stroke-width' : (2 / scale)
+ });
+ });
+
+ var line;
+ $('polyline.vector').each(function () {
+ id = $(this).attr('id');
+ line = svg.getElementById(id);
+ $(svg).on('change', line, {
+ 'stroke-width' : (2 / scale)
+ });
+ });
+
+ var polygon;
+ $('path.vector').each(function () {
+ id = $(this).attr('id');
+ polygon = svg.getElementById(id);
+ $(svg).on('change', polygon, {
+ 'stroke-width' : (0.5 / scale)
+ });
+ });
+}
+
+/**
+ * Initially loads either SVG or OSM visualization based on the choice.
+ */
+function selectVisualization () {
+ if ($('#choice').prop('checked') !== true) {
+ $('#openlayersmap').hide();
+ } else {
+ $('#placeholder').hide();
+ }
+}
+
+/**
+ * Adds necessary styles to the div that coontains the openStreetMap.
+ */
+function styleOSM () {
+ var $placeholder = $('#placeholder');
+ var cssObj = {
+ 'border' : '1px solid #aaa',
+ 'width' : $placeholder.width(),
+ 'height' : $placeholder.height(),
+ 'float' : 'right'
+ };
+ $('#openlayersmap').css(cssObj);
+}
+
+/**
+ * Loads the SVG element and make a reference to it.
+ */
+function loadSVG () {
+ var $placeholder = $('#placeholder');
+
+ $placeholder.svg({
+ onLoad: function (svgRef) {
+ svg = svgRef;
+ }
+ });
+
+ // Removes the second SVG element unnecessarily added due to the above command
+ $placeholder.find('svg:nth-child(2)').remove();
+}
+
+/**
+ * Adds controllers for zooming and panning.
+ */
+function addZoomPanControllers () {
+ var $placeholder = $('#placeholder');
+ if ($('#placeholder').find('svg').length > 0) {
+ var pmaThemeImage = $('#pmaThemeImage').val();
+ // add panning arrows
+ $('<img class="button" id="left_arrow" src="' + pmaThemeImage + 'west-mini.png">').appendTo($placeholder);
+ $('<img class="button" id="right_arrow" src="' + pmaThemeImage + 'east-mini.png">').appendTo($placeholder);
+ $('<img class="button" id="up_arrow" src="' + pmaThemeImage + 'north-mini.png">').appendTo($placeholder);
+ $('<img class="button" id="down_arrow" src="' + pmaThemeImage + 'south-mini.png">').appendTo($placeholder);
+ // add zooming controls
+ $('<img class="button" id="zoom_in" src="' + pmaThemeImage + 'zoom-plus-mini.png">').appendTo($placeholder);
+ $('<img class="button" id="zoom_world" src="' + pmaThemeImage + 'zoom-world-mini.png">').appendTo($placeholder);
+ $('<img class="button" id="zoom_out" src="' + pmaThemeImage + 'zoom-minus-mini.png">').appendTo($placeholder);
+ }
+}
+
+/**
+ * Resizes the GIS visualization to fit into the space available.
+ */
+function resizeGISVisualization () {
+ var $placeholder = $('#placeholder');
+ var oldWidth = $placeholder.width();
+ var visWidth = $('#div_view_options').width() - 48;
+
+ // Assign new value for width
+ $placeholder.width(visWidth);
+ $('svg').attr('width', visWidth);
+
+ // Assign the offset created due to resizing to defaultX and center the svg.
+ defaultX = (visWidth - oldWidth) / 2;
+ x = defaultX;
+ y = 0;
+ scale = 1;
+}
+
+/**
+ * Initialize the GIS visualization.
+ */
+function initGISVisualization () {
+ // Loads either SVG or OSM visualization based on the choice
+ selectVisualization();
+ // Resizes the GIS visualization to fit into the space available
+ resizeGISVisualization();
+ if (typeof OpenLayers !== 'undefined') {
+ // Configure OpenLayers
+ // eslint-disable-next-line no-underscore-dangle
+ OpenLayers._getScriptLocation = function () {
+ return './js/vendor/openlayers/';
+ };
+ // Adds necessary styles to the div that coontains the openStreetMap
+ styleOSM();
+ // Draws openStreetMap with openLayers
+ drawOpenLayers();
+ }
+ // Loads the SVG element and make a reference to it
+ loadSVG();
+ // Adds controllers for zooming and panning
+ addZoomPanControllers();
+ zoomAndPan();
+}
+
+function getRelativeCoords (e) {
+ var position = $('#placeholder').offset();
+ return {
+ x : e.pageX - position.left,
+ y : e.pageY - position.top
+ };
+}
+
+/**
+ * Ajax handlers for GIS visualization page
+ *
+ * Actions Ajaxified here:
+ *
+ * Zooming in and zooming out on mousewheel movement.
+ * Panning the visualization on dragging.
+ * Zooming in on double clicking.
+ * Zooming out on clicking the zoom out button.
+ * Panning on clicking the arrow buttons.
+ * Displaying tooltips for GIS objects.
+ */
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/gis_visualization.js', function () {
+ $(document).off('click', '#choice');
+ $(document).off('mousewheel', '#placeholder');
+ $(document).off('dragstart', 'svg');
+ $(document).off('mouseup', 'svg');
+ $(document).off('drag', 'svg');
+ $(document).off('dblclick', '#placeholder');
+ $(document).off('click', '#zoom_in');
+ $(document).off('click', '#zoom_world');
+ $(document).off('click', '#zoom_out');
+ $(document).off('click', '#left_arrow');
+ $(document).off('click', '#right_arrow');
+ $(document).off('click', '#up_arrow');
+ $(document).off('click', '#down_arrow');
+ $('.vector').off('mousemove').off('mouseout');
+});
+
+AJAX.registerOnload('table/gis_visualization.js', function () {
+ // If we are in GIS visualization, initialize it
+ if ($('#gis_div').length > 0) {
+ initGISVisualization();
+ }
+
+ if (typeof OpenLayers === 'undefined') {
+ $('#choice, #labelChoice').hide();
+ }
+ $(document).on('click', '#choice', function () {
+ if ($(this).prop('checked') === false) {
+ $('#placeholder').show();
+ $('#openlayersmap').hide();
+ } else {
+ $('#placeholder').hide();
+ $('#openlayersmap').show();
+ }
+ });
+
+ $(document).on('mousewheel', '#placeholder', function (event, delta) {
+ event.preventDefault();
+ var relCoords = getRelativeCoords(event);
+ if (delta > 0) {
+ // zoom in
+ scale *= zoomFactor;
+ // zooming in keeping the position under mouse pointer unmoved.
+ x = relCoords.x - (relCoords.x - x) * zoomFactor;
+ y = relCoords.y - (relCoords.y - y) * zoomFactor;
+ zoomAndPan();
+ } else {
+ // zoom out
+ scale /= zoomFactor;
+ // zooming out keeping the position under mouse pointer unmoved.
+ x = relCoords.x - (relCoords.x - x) / zoomFactor;
+ y = relCoords.y - (relCoords.y - y) / zoomFactor;
+ zoomAndPan();
+ }
+ return true;
+ });
+
+ var dragX = 0;
+ var dragY = 0;
+
+ $(document).on('dragstart', 'svg', function (event, dd) {
+ $('#placeholder').addClass('placeholderDrag');
+ dragX = Math.round(dd.offsetX);
+ dragY = Math.round(dd.offsetY);
+ });
+
+ $(document).on('mouseup', 'svg', function () {
+ $('#placeholder').removeClass('placeholderDrag');
+ });
+
+ $(document).on('drag', 'svg', function (event, dd) {
+ var newX = Math.round(dd.offsetX);
+ x += newX - dragX;
+ dragX = newX;
+ var newY = Math.round(dd.offsetY);
+ y += newY - dragY;
+ dragY = newY;
+ zoomAndPan();
+ });
+
+ $(document).on('dblclick', '#placeholder', function (event) {
+ scale *= zoomFactor;
+ // zooming in keeping the position under mouse pointer unmoved.
+ var relCoords = getRelativeCoords(event);
+ x = relCoords.x - (relCoords.x - x) * zoomFactor;
+ y = relCoords.y - (relCoords.y - y) * zoomFactor;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#zoom_in', function (e) {
+ e.preventDefault();
+ // zoom in
+ scale *= zoomFactor;
+
+ var $placeholder = $('#placeholder').find('svg');
+ var width = $placeholder.attr('width');
+ var height = $placeholder.attr('height');
+ // zooming in keeping the center unmoved.
+ x = width / 2 - (width / 2 - x) * zoomFactor;
+ y = height / 2 - (height / 2 - y) * zoomFactor;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#zoom_world', function (e) {
+ e.preventDefault();
+ scale = 1;
+ x = defaultX;
+ y = defaultY;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#zoom_out', function (e) {
+ e.preventDefault();
+ // zoom out
+ scale /= zoomFactor;
+
+ var $placeholder = $('#placeholder').find('svg');
+ var width = $placeholder.attr('width');
+ var height = $placeholder.attr('height');
+ // zooming out keeping the center unmoved.
+ x = width / 2 - (width / 2 - x) / zoomFactor;
+ y = height / 2 - (height / 2 - y) / zoomFactor;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#left_arrow', function (e) {
+ e.preventDefault();
+ x += 100;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#right_arrow', function (e) {
+ e.preventDefault();
+ x -= 100;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#up_arrow', function (e) {
+ e.preventDefault();
+ y += 100;
+ zoomAndPan();
+ });
+
+ $(document).on('click', '#down_arrow', function (e) {
+ e.preventDefault();
+ y -= 100;
+ zoomAndPan();
+ });
+
+ /**
+ * Detect the mousemove event and show tooltips.
+ */
+ $('.vector').on('mousemove', function (event) {
+ var contents = $.trim(Functions.escapeHtml($(this).attr('name')));
+ $('#tooltip').remove();
+ if (contents !== '') {
+ $('<div id="tooltip">' + contents + '</div>').css({
+ position : 'absolute',
+ top : event.pageY + 10,
+ left : event.pageX + 10,
+ border : '1px solid #fdd',
+ padding : '2px',
+ 'background-color' : '#fee',
+ opacity : 0.90
+ }).appendTo('body').fadeIn(200);
+ }
+ });
+
+ /**
+ * Detect the mouseout event and hide tooltips.
+ */
+ $('.vector').on('mouseout', function () {
+ $('#tooltip').remove();
+ });
+});
diff --git a/srcs/phpmyadmin/js/table/operations.js b/srcs/phpmyadmin/js/table/operations.js
new file mode 100644
index 0000000..9bd2af8
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/operations.js
@@ -0,0 +1,321 @@
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/operations.js', function () {
+ $(document).off('submit', '#copyTable.ajax');
+ $(document).off('submit', '#moveTableForm');
+ $(document).off('submit', '#tableOptionsForm');
+ $(document).off('submit', '#partitionsForm');
+ $(document).off('click', '#tbl_maintenance li a.maintain_action.ajax');
+ $(document).off('click', '#drop_tbl_anchor.ajax');
+ $(document).off('click', '#drop_view_anchor.ajax');
+ $(document).off('click', '#truncate_tbl_anchor.ajax');
+});
+
+/**
+ * jQuery coding for 'Table operations'. Used on tbl_operations.php
+ * Attach Ajax Event handlers for Table operations
+ */
+AJAX.registerOnload('table/operations.js', function () {
+ /**
+ *Ajax action for submitting the "Copy table"
+ **/
+ $(document).on('submit', '#copyTable.ajax', function (event) {
+ event.preventDefault();
+ var $form = $(this);
+ Functions.prepareForAjaxRequest($form);
+ var argsep = CommonParams.get('arg_separator');
+ $.post($form.attr('action'), $form.serialize() + argsep + 'submit_copy=Go', function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ if ($form.find('input[name=\'switch_to_new\']').prop('checked')) {
+ CommonParams.set(
+ 'db',
+ $form.find('select[name=\'target_db\']').val()
+ );
+ CommonParams.set(
+ 'table',
+ $form.find('input[name=\'new_name\']').val()
+ );
+ CommonActions.refreshMain(false, function () {
+ Functions.ajaxShowMessage(data.message);
+ });
+ } else {
+ Functions.ajaxShowMessage(data.message);
+ }
+ // Refresh navigation when the table is copied
+ Navigation.reload();
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ });// end of copyTable ajax submit
+
+ /**
+ *Ajax action for submitting the "Move table"
+ */
+ $(document).on('submit', '#moveTableForm', function (event) {
+ event.preventDefault();
+ var $form = $(this);
+ Functions.prepareForAjaxRequest($form);
+ var argsep = CommonParams.get('arg_separator');
+ $.post($form.attr('action'), $form.serialize() + argsep + 'submit_move=1', function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ CommonParams.set('db', data.params.db);
+ CommonParams.set('table', data.params.table);
+ CommonActions.refreshMain('tbl_sql.php', function () {
+ Functions.ajaxShowMessage(data.message);
+ });
+ // Refresh navigation when the table is copied
+ Navigation.reload();
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ });
+
+ /**
+ * Ajax action for submitting the "Table options"
+ */
+ $(document).on('submit', '#tableOptionsForm', function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+ var $form = $(this);
+ var $tblNameField = $form.find('input[name=new_name]');
+ var $tblCollationField = $form.find('select[name=tbl_collation]');
+ var collationOrigValue = $('select[name="tbl_collation"] option[selected]').val();
+ var $changeAllColumnCollationsCheckBox = $('#checkbox_change_all_collations');
+ var question = Messages.strChangeAllColumnCollationsWarning;
+
+ if ($tblNameField.val() !== $tblNameField[0].defaultValue) {
+ // reload page and navigation if the table has been renamed
+ Functions.prepareForAjaxRequest($form);
+
+ if ($tblCollationField.val() !== collationOrigValue && $changeAllColumnCollationsCheckBox.is(':checked')) {
+ $form.confirm(question, $form.attr('action'), function () {
+ submitOptionsForm();
+ });
+ } else {
+ submitOptionsForm();
+ }
+ } else {
+ if ($tblCollationField.val() !== collationOrigValue && $changeAllColumnCollationsCheckBox.is(':checked')) {
+ $form.confirm(question, $form.attr('action'), function () {
+ $form.removeClass('ajax').trigger('submit').addClass('ajax');
+ });
+ } else {
+ $form.removeClass('ajax').trigger('submit').addClass('ajax');
+ }
+ }
+
+ function submitOptionsForm () {
+ $.post($form.attr('action'), $form.serialize(), function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ CommonParams.set('table', data.params.table);
+ CommonActions.refreshMain(false, function () {
+ $('#page_content').html(data.message);
+ Functions.highlightSql($('#page_content'));
+ });
+ // Refresh navigation when the table is renamed
+ Navigation.reload();
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ }
+ });
+
+ /**
+ *Ajax events for actions in the "Table maintenance"
+ **/
+ $(document).on('click', '#tbl_maintenance li a.maintain_action.ajax', function (event) {
+ event.preventDefault();
+ var $link = $(this);
+
+ if ($('.sqlqueryresults').length !== 0) {
+ $('.sqlqueryresults').remove();
+ }
+ if ($('.result_query').length !== 0) {
+ $('.result_query').remove();
+ }
+ // variables which stores the common attributes
+ var params = $.param({
+ 'ajax_request': 1,
+ 'server': CommonParams.get('server')
+ });
+ var postData = $link.getPostData();
+ if (postData) {
+ params += CommonParams.get('arg_separator') + postData;
+ }
+
+ $.post($link.attr('href'), params, function (data) {
+ function scrollToTop () {
+ $('html, body').animate({ scrollTop: 0 });
+ }
+ var $tempDiv;
+ if (typeof data !== 'undefined' && data.success === true && data.sql_query !== undefined) {
+ Functions.ajaxShowMessage(data.message);
+ $('<div class=\'sqlqueryresults ajax\'></div>').prependTo('#page_content');
+ $('.sqlqueryresults').html(data.sql_query);
+ Functions.highlightSql($('#page_content'));
+ scrollToTop();
+ } else if (typeof data !== 'undefined' && data.success === true) {
+ $tempDiv = $('<div id=\'temp_div\'></div>');
+ $tempDiv.html(data.message);
+ var $success = $tempDiv.find('.result_query .success');
+ Functions.ajaxShowMessage($success);
+ $('<div class=\'sqlqueryresults ajax\'></div>').prependTo('#page_content');
+ $('.sqlqueryresults').html(data.message);
+ Functions.highlightSql($('#page_content'));
+ Functions.initSlider();
+ $('.sqlqueryresults').children('fieldset,br').remove();
+ scrollToTop();
+ } else {
+ $tempDiv = $('<div id=\'temp_div\'></div>');
+ $tempDiv.html(data.error);
+
+ var $error;
+ if ($tempDiv.find('.error code').length !== 0) {
+ $error = $tempDiv.find('.error code').addClass('error');
+ } else {
+ $error = $tempDiv;
+ }
+
+ Functions.ajaxShowMessage($error, false);
+ }
+ }); // end $.post()
+ });// end of table maintenance ajax click
+
+ /**
+ * Ajax action for submitting the "Partition Maintenance"
+ * Also, asks for confirmation when DROP partition is submitted
+ */
+ $(document).on('submit', '#partitionsForm', function (event) {
+ event.preventDefault();
+ var $form = $(this);
+
+ function submitPartitionMaintenance () {
+ var argsep = CommonParams.get('arg_separator');
+ var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
+ Functions.ajaxShowMessage(Messages.strProcessingRequest);
+ AJAX.source = $form;
+ $.post($form.attr('action'), submitData, AJAX.responseHandler);
+ }
+
+ if ($('#partition_operation_DROP').is(':checked')) {
+ $form.confirm(Messages.strDropPartitionWarning, $form.attr('action'), function () {
+ submitPartitionMaintenance();
+ });
+ } else if ($('#partition_operation_TRUNCATE').is(':checked')) {
+ $form.confirm(Messages.strTruncatePartitionWarning, $form.attr('action'), function () {
+ submitPartitionMaintenance();
+ });
+ } else {
+ submitPartitionMaintenance();
+ }
+ });
+
+ $(document).on('click', '#drop_tbl_anchor.ajax', function (event) {
+ event.preventDefault();
+ var $link = $(this);
+ /**
+ * @var question String containing the question to be asked for confirmation
+ */
+ var question = Messages.strDropTableStrongWarning + ' ';
+ question += Functions.sprintf(
+ Messages.strDoYouReally,
+ 'DROP TABLE `' + Functions.escapeHtml(CommonParams.get('db')) + '`.`' + Functions.escapeHtml(CommonParams.get('table') + '`')
+ ) + Functions.getForeignKeyCheckboxLoader();
+
+ $(this).confirm(question, $(this).attr('href'), function (url) {
+ var $msgbox = Functions.ajaxShowMessage(Messages.strProcessingRequest);
+
+ var params = Functions.getJsConfirmCommonParam(this, $link.getPostData());
+
+ $.post(url, params, function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ Functions.ajaxRemoveMessage($msgbox);
+ // Table deleted successfully, refresh both the frames
+ Navigation.reload();
+ CommonParams.set('table', '');
+ CommonActions.refreshMain(
+ CommonParams.get('opendb_url'),
+ function () {
+ Functions.ajaxShowMessage(data.message);
+ }
+ );
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ }, Functions.loadForeignKeyCheckbox);
+ }); // end of Drop Table Ajax action
+
+ $(document).on('click', '#drop_view_anchor.ajax', function (event) {
+ event.preventDefault();
+ var $link = $(this);
+ /**
+ * @var question String containing the question to be asked for confirmation
+ */
+ var question = Messages.strDropTableStrongWarning + ' ';
+ question += Functions.sprintf(
+ Messages.strDoYouReally,
+ 'DROP VIEW `' + Functions.escapeHtml(CommonParams.get('table') + '`')
+ );
+
+ $(this).confirm(question, $(this).attr('href'), function (url) {
+ var $msgbox = Functions.ajaxShowMessage(Messages.strProcessingRequest);
+ var params = Functions.getJsConfirmCommonParam(this, $link.getPostData());
+ $.post(url, params, function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ Functions.ajaxRemoveMessage($msgbox);
+ // Table deleted successfully, refresh both the frames
+ Navigation.reload();
+ CommonParams.set('table', '');
+ CommonActions.refreshMain(
+ CommonParams.get('opendb_url'),
+ function () {
+ Functions.ajaxShowMessage(data.message);
+ }
+ );
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ });
+ }); // end of Drop View Ajax action
+
+ $(document).on('click', '#truncate_tbl_anchor.ajax', function (event) {
+ event.preventDefault();
+ var $link = $(this);
+ /**
+ * @var question String containing the question to be asked for confirmation
+ */
+ var question = Messages.strTruncateTableStrongWarning + ' ';
+ question += Functions.sprintf(
+ Messages.strDoYouReally,
+ 'TRUNCATE `' + Functions.escapeHtml(CommonParams.get('db')) + '`.`' + Functions.escapeHtml(CommonParams.get('table') + '`')
+ ) + Functions.getForeignKeyCheckboxLoader();
+ $(this).confirm(question, $(this).attr('href'), function (url) {
+ Functions.ajaxShowMessage(Messages.strProcessingRequest);
+
+ var params = Functions.getJsConfirmCommonParam(this, $link.getPostData());
+
+ $.post(url, params, function (data) {
+ if ($('.sqlqueryresults').length !== 0) {
+ $('.sqlqueryresults').remove();
+ }
+ if ($('.result_query').length !== 0) {
+ $('.result_query').remove();
+ }
+ if (typeof data !== 'undefined' && data.success === true) {
+ Functions.ajaxShowMessage(data.message);
+ $('<div class=\'sqlqueryresults ajax\'></div>').prependTo('#page_content');
+ $('.sqlqueryresults').html(data.sql_query);
+ Functions.highlightSql($('#page_content'));
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ }, Functions.loadForeignKeyCheckbox);
+ }); // end of Truncate Table Ajax action
+}); // end $(document).ready for 'Table operations'
diff --git a/srcs/phpmyadmin/js/table/relation.js b/srcs/phpmyadmin/js/table/relation.js
new file mode 100644
index 0000000..600dfc1
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/relation.js
@@ -0,0 +1,248 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * for tbl_relation.php
+ *
+ */
+
+var TableRelation = {};
+
+TableRelation.showHideClauses = function ($thisDropdown) {
+ if ($thisDropdown.val() === '') {
+ $thisDropdown.parent().nextAll('span').hide();
+ } else {
+ if ($thisDropdown.is('select[name^="destination_foreign_column"]')) {
+ $thisDropdown.parent().nextAll('span').show();
+ }
+ }
+};
+
+/**
+ * Sets dropdown options to values
+ */
+TableRelation.setDropdownValues = function ($dropdown, values, selectedValue) {
+ $dropdown.empty();
+ var optionsAsString = '';
+ // add an empty string to the beginning for empty selection
+ values.unshift('');
+ $.each(values, function () {
+ optionsAsString += '<option value=\'' + Functions.escapeHtml(this) + '\'' + (selectedValue === Functions.escapeHtml(this) ? ' selected=\'selected\'' : '') + '>' + Functions.escapeHtml(this) + '</option>';
+ });
+ $dropdown.append($(optionsAsString));
+};
+
+/**
+ * Retrieves and populates dropdowns to the left based on the selected value
+ *
+ * @param $dropdown the dropdown whose value got changed
+ */
+TableRelation.getDropdownValues = function ($dropdown) {
+ var foreignDb = null;
+ var foreignTable = null;
+ var $databaseDd;
+ var $tableDd;
+ var $columnDd;
+ var foreign = '';
+ // if the changed dropdown is for foreign key constraints
+ if ($dropdown.is('select[name^="destination_foreign"]')) {
+ $databaseDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_db"]');
+ $tableDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_table"]');
+ $columnDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_column"]');
+ foreign = '_foreign';
+ } else { // internal relations
+ $databaseDd = $dropdown.parent().find('select[name^="destination_db"]');
+ $tableDd = $dropdown.parent().find('select[name^="destination_table"]');
+ $columnDd = $dropdown.parent().find('select[name^="destination_column"]');
+ }
+
+ // if the changed dropdown is a database selector
+ if ($dropdown.is('select[name^="destination' + foreign + '_db"]')) {
+ foreignDb = $dropdown.val();
+ // if no database is selected empty table and column dropdowns
+ if (foreignDb === '') {
+ TableRelation.setDropdownValues($tableDd, []);
+ TableRelation.setDropdownValues($columnDd, []);
+ return;
+ }
+ } else { // if a table selector
+ foreignDb = $databaseDd.val();
+ foreignTable = $dropdown.val();
+ // if no table is selected empty the column dropdown
+ if (foreignTable === '') {
+ TableRelation.setDropdownValues($columnDd, []);
+ return;
+ }
+ }
+ var $msgbox = Functions.ajaxShowMessage();
+ var $form = $dropdown.parents('form');
+ var $db = $form.find('input[name="db"]').val();
+ var $table = $form.find('input[name="table"]').val();
+ var argsep = CommonParams.get('arg_separator');
+ var params = 'getDropdownValues=true' + argsep + 'ajax_request=true' +
+ argsep + 'db=' + encodeURIComponent($db) +
+ argsep + 'table=' + encodeURIComponent($table) +
+ argsep + 'foreign=' + (foreign !== '') +
+ argsep + 'foreignDb=' + encodeURIComponent(foreignDb) +
+ (foreignTable !== null ?
+ argsep + 'foreignTable=' + encodeURIComponent(foreignTable) : ''
+ );
+ var $server = $form.find('input[name="server"]');
+ if ($server.length > 0) {
+ params += argsep + 'server=' + $form.find('input[name="server"]').val();
+ }
+ $.ajax({
+ type: 'POST',
+ url: 'tbl_relation.php',
+ data: params,
+ dataType: 'json',
+ success: function (data) {
+ Functions.ajaxRemoveMessage($msgbox);
+ if (typeof data !== 'undefined' && data.success) {
+ // if the changed dropdown is a database selector
+ if (foreignTable === null) {
+ // set values for table and column dropdowns
+ TableRelation.setDropdownValues($tableDd, data.tables);
+ TableRelation.setDropdownValues($columnDd, []);
+ } else { // if a table selector
+ // set values for the column dropdown
+ var primary = null;
+ if (typeof data.primary !== 'undefined'
+ && 1 === data.primary.length
+ ) {
+ primary = data.primary[0];
+ }
+ TableRelation.setDropdownValues($columnDd.first(), data.columns, primary);
+ TableRelation.setDropdownValues($columnDd.slice(1), data.columns);
+ }
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }
+ });
+};
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/relation.js', function () {
+ $('body').off('change',
+ 'select[name^="destination_db"], ' +
+ 'select[name^="destination_table"], ' +
+ 'select[name^="destination_foreign_db"], ' +
+ 'select[name^="destination_foreign_table"]'
+ );
+ $('body').off('click', 'a.add_foreign_key_field');
+ $('body').off('click', 'a.add_foreign_key');
+ $('a.drop_foreign_key_anchor.ajax').off('click');
+});
+
+AJAX.registerOnload('table/relation.js', function () {
+ /**
+ * Ajax event handler to fetch table/column dropdown values.
+ */
+ $('body').on('change',
+ 'select[name^="destination_db"], ' +
+ 'select[name^="destination_table"], ' +
+ 'select[name^="destination_foreign_db"], ' +
+ 'select[name^="destination_foreign_table"]',
+ function () {
+ TableRelation.getDropdownValues($(this));
+ }
+ );
+
+ /**
+ * Ajax event handler to add a column to a foreign key constraint.
+ */
+ $('body').on('click', 'a.add_foreign_key_field', function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // Add field.
+ $(this)
+ .prev('span')
+ .clone(true, true)
+ .insertBefore($(this))
+ .find('select')
+ .val('');
+
+ // Add foreign field.
+ var $sourceElem = $('select[name^="destination_foreign_column[' +
+ $(this).attr('data-index') + ']"]:last').parent();
+ $sourceElem
+ .clone(true, true)
+ .insertAfter($sourceElem)
+ .find('select')
+ .val('');
+ });
+
+ /**
+ * Ajax event handler to add a foreign key constraint.
+ */
+ $('body').on('click', 'a.add_foreign_key', function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var $prevRow = $(this).closest('tr').prev('tr');
+ var $newRow = $prevRow.clone(true, true);
+
+ // Update serial number.
+ var currIndex = $newRow
+ .find('a.add_foreign_key_field')
+ .attr('data-index');
+ var newIndex = parseInt(currIndex) + 1;
+ $newRow.find('a.add_foreign_key_field').attr('data-index', newIndex);
+
+ // Update form parameter names.
+ $newRow.find('select[name^="foreign_key_fields_name"]:not(:first), ' +
+ 'select[name^="destination_foreign_column"]:not(:first)'
+ ).each(function () {
+ $(this).parent().remove();
+ });
+ $newRow.find('input, select').each(function () {
+ $(this).attr('name',
+ $(this).attr('name').replace(/\d/, newIndex)
+ );
+ });
+ $newRow.find('input[type="text"]').each(function () {
+ $(this).val('');
+ });
+ // Finally add the row.
+ $newRow.insertAfter($prevRow);
+ });
+
+ /**
+ * Ajax Event handler for 'Drop Foreign key'
+ */
+ $('a.drop_foreign_key_anchor.ajax').on('click', function (event) {
+ event.preventDefault();
+ var $anchor = $(this);
+
+ // Object containing reference to the current field's row
+ var $currRow = $anchor.parents('tr');
+
+ var dropQuery = Functions.escapeHtml(
+ $currRow.children('td')
+ .children('.drop_foreign_key_msg')
+ .val()
+ );
+
+ var question = Functions.sprintf(Messages.strDoYouReally, dropQuery);
+
+ $anchor.confirm(question, $anchor.attr('href'), function (url) {
+ var $msg = Functions.ajaxShowMessage(Messages.strDroppingForeignKey, false);
+ var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
+ $.post(url, params, function (data) {
+ if (data.success === true) {
+ Functions.ajaxRemoveMessage($msg);
+ CommonActions.refreshMain(false, function () {
+ // Do nothing
+ });
+ } else {
+ Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
+ }
+ }); // end $.post()
+ });
+ }); // end Drop Foreign key
+
+ var windowWidth = $(window).width();
+ $('.jsresponsive').css('max-width', (windowWidth - 35) + 'px');
+});
diff --git a/srcs/phpmyadmin/js/table/select.js b/srcs/phpmyadmin/js/table/select.js
new file mode 100644
index 0000000..3050e3c
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/select.js
@@ -0,0 +1,409 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * @fileoverview JavaScript functions used on tbl_select.php
+ *
+ * @requires jQuery
+ * @requires js/functions.js
+ */
+
+/* global changeValueFieldType */ // js/table/change.js
+/* global openGISEditor, gisEditorLoaded, loadJSAndGISEditor, loadGISEditor */ // js/gis_data_editor.js
+
+var TableSelect = {};
+
+/**
+ * Checks if given data-type is numeric or date.
+ *
+ * @param {string} dataType Column data-type
+ *
+ * @return {(boolean|string)}
+ */
+TableSelect.checkIfDataTypeNumericOrDate = function (dataType) {
+ // To test for numeric data-types.
+ var numericRegExp = new RegExp(
+ 'TINYINT|SMALLINT|MEDIUMINT|INT|BIGINT|DECIMAL|FLOAT|DOUBLE|REAL',
+ 'i'
+ );
+
+ // To test for date data-types.
+ var dateRegExp = new RegExp(
+ 'DATETIME|DATE|TIMESTAMP|TIME|YEAR',
+ 'i'
+ );
+
+ // Return matched data-type
+ if (numericRegExp.test(dataType)) {
+ return numericRegExp.exec(dataType)[0];
+ }
+
+ if (dateRegExp.test(dataType)) {
+ return dateRegExp.exec(dataType)[0];
+ }
+
+ return false;
+};
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/select.js', function () {
+ $('#togglesearchformlink').off('click');
+ $(document).off('submit', '#tbl_search_form.ajax');
+ $('select.geom_func').off('change');
+ $(document).off('click', 'span.open_search_gis_editor');
+ $('body').off('change', 'select[name*="criteriaColumnOperators"]'); // Fix for bug #13778, changed 'click' to 'change'
+});
+
+AJAX.registerOnload('table/select.js', function () {
+ /**
+ * Prepare a div containing a link, otherwise it's incorrectly displayed
+ * after a couple of clicks
+ */
+ $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>')
+ .insertAfter('#tbl_search_form')
+ // don't show it until we have results on-screen
+ .hide();
+
+ $('#togglesearchformlink')
+ .html(Messages.strShowSearchCriteria)
+ .on('click', function () {
+ var $link = $(this);
+ $('#tbl_search_form').slideToggle();
+ if ($link.text() === Messages.strHideSearchCriteria) {
+ $link.text(Messages.strShowSearchCriteria);
+ } else {
+ $link.text(Messages.strHideSearchCriteria);
+ }
+ // avoid default click action
+ return false;
+ });
+
+ var tableRows = $('#fieldset_table_qbe select');
+ $.each(tableRows, function (index, item) {
+ $(item).on('change', function () {
+ changeValueFieldType(this, index);
+ });
+ });
+
+ /**
+ * Ajax event handler for Table search
+ */
+ $(document).on('submit', '#tbl_search_form.ajax', function (event) {
+ var unaryFunctions = [
+ 'IS NULL',
+ 'IS NOT NULL',
+ '= \'\'',
+ '!= \'\''
+ ];
+
+ var geomUnaryFunctions = [
+ 'IsEmpty',
+ 'IsSimple',
+ 'IsRing',
+ 'IsClosed',
+ ];
+
+ // jQuery object to reuse
+ var $searchForm = $(this);
+ event.preventDefault();
+
+ // empty previous search results while we are waiting for new results
+ $('#sqlqueryresultsouter').empty();
+ var $msgbox = Functions.ajaxShowMessage(Messages.strSearching, false);
+
+ Functions.prepareForAjaxRequest($searchForm);
+
+ var values = {};
+ $searchForm.find(':input').each(function () {
+ var $input = $(this);
+ if ($input.attr('type') === 'checkbox' || $input.attr('type') === 'radio') {
+ if ($input.is(':checked')) {
+ values[this.name] = $input.val();
+ }
+ } else {
+ values[this.name] = $input.val();
+ }
+ });
+ var columnCount = $('select[name="columnsToDisplay[]"] option').length;
+ // Submit values only for the columns that have unary column operator or a search criteria
+ for (var a = 0; a < columnCount; a++) {
+ if ($.inArray(values['criteriaColumnOperators[' + a + ']'], unaryFunctions) >= 0) {
+ continue;
+ }
+
+ if (values['geom_func[' + a + ']'] &&
+ $.isArray(values['geom_func[' + a + ']'], geomUnaryFunctions) >= 0) {
+ continue;
+ }
+
+ if (values['criteriaValues[' + a + ']'] === '' || values['criteriaValues[' + a + ']'] === null) {
+ delete values['criteriaValues[' + a + ']'];
+ delete values['criteriaColumnOperators[' + a + ']'];
+ delete values['criteriaColumnNames[' + a + ']'];
+ delete values['criteriaColumnTypes[' + a + ']'];
+ delete values['criteriaColumnCollations[' + a + ']'];
+ }
+ }
+ // If all columns are selected, use a single parameter to indicate that
+ if (values['columnsToDisplay[]'] !== null) {
+ if (values['columnsToDisplay[]'].length === columnCount) {
+ delete values['columnsToDisplay[]'];
+ values.displayAllColumns = true;
+ }
+ } else {
+ values.displayAllColumns = true;
+ }
+
+ $.post($searchForm.attr('action'), values, function (data) {
+ Functions.ajaxRemoveMessage($msgbox);
+ if (typeof data !== 'undefined' && data.success === true) {
+ if (typeof data.sql_query !== 'undefined') { // zero rows
+ $('#sqlqueryresultsouter').html(data.sql_query);
+ } else { // results found
+ $('#sqlqueryresultsouter').html(data.message);
+ $('.sqlqueryresults').trigger('makegrid').trigger('stickycolumns');
+ }
+ $('#tbl_search_form')
+ // workaround for bug #3168569 - Issue on toggling the "Hide search criteria" in chrome.
+ .slideToggle()
+ .hide();
+ $('#togglesearchformlink')
+ // always start with the Show message
+ .text(Messages.strShowSearchCriteria);
+ $('#togglesearchformdiv')
+ // now it's time to show the div containing the link
+ .show();
+ // needed for the display options slider in the results
+ Functions.initSlider();
+ $('html, body').animate({ scrollTop: 0 }, 'fast');
+ } else {
+ $('#sqlqueryresultsouter').html(data.error);
+ }
+ Functions.highlightSql($('#sqlqueryresultsouter'));
+ }); // end $.post()
+ });
+
+ // Following section is related to the 'function based search' for geometry data types.
+ // Initialy hide all the open_gis_editor spans
+ $('span.open_search_gis_editor').hide();
+
+ $('select.geom_func').on('change', function () {
+ var $geomFuncSelector = $(this);
+
+ var binaryFunctions = [
+ 'Contains',
+ 'Crosses',
+ 'Disjoint',
+ 'Equals',
+ 'Intersects',
+ 'Overlaps',
+ 'Touches',
+ 'Within',
+ 'MBRContains',
+ 'MBRDisjoint',
+ 'MBREquals',
+ 'MBRIntersects',
+ 'MBROverlaps',
+ 'MBRTouches',
+ 'MBRWithin',
+ 'ST_Contains',
+ 'ST_Crosses',
+ 'ST_Disjoint',
+ 'ST_Equals',
+ 'ST_Intersects',
+ 'ST_Overlaps',
+ 'ST_Touches',
+ 'ST_Within'
+ ];
+
+ var tempArray = [
+ 'Envelope',
+ 'EndPoint',
+ 'StartPoint',
+ 'ExteriorRing',
+ 'Centroid',
+ 'PointOnSurface'
+ ];
+ var outputGeomFunctions = binaryFunctions.concat(tempArray);
+
+ // If the chosen function takes two geometry objects as parameters
+ var $operator = $geomFuncSelector.parents('tr').find('td:nth-child(5)').find('select');
+ if ($.inArray($geomFuncSelector.val(), binaryFunctions) >= 0) {
+ $operator.prop('readonly', true);
+ } else {
+ $operator.prop('readonly', false);
+ }
+
+ // if the chosen function's output is a geometry, enable GIS editor
+ var $editorSpan = $geomFuncSelector.parents('tr').find('span.open_search_gis_editor');
+ if ($.inArray($geomFuncSelector.val(), outputGeomFunctions) >= 0) {
+ $editorSpan.show();
+ } else {
+ $editorSpan.hide();
+ }
+ });
+
+ $(document).on('click', 'span.open_search_gis_editor', function (event) {
+ event.preventDefault();
+
+ var $span = $(this);
+ // Current value
+ var value = $span.parent('td').children('input[type=\'text\']').val();
+ // Field name
+ var field = 'Parameter';
+ // Column type
+ var geomFunc = $span.parents('tr').find('.geom_func').val();
+ var type;
+ if (geomFunc === 'Envelope') {
+ type = 'polygon';
+ } else if (geomFunc === 'ExteriorRing') {
+ type = 'linestring';
+ } else {
+ type = 'point';
+ }
+ // Names of input field and null checkbox
+ var inputName = $span.parent('td').children('input[type=\'text\']').attr('name');
+ // Token
+
+ openGISEditor();
+ if (!gisEditorLoaded) {
+ loadJSAndGISEditor(value, field, type, inputName);
+ } else {
+ loadGISEditor(value, field, type, inputName);
+ }
+ });
+
+ /**
+ * Ajax event handler for Range-Search.
+ */
+ $('body').on('change', 'select[name*="criteriaColumnOperators"]', function () { // Fix for bug #13778, changed 'click' to 'change'
+ var $sourceSelect = $(this);
+ // Get the column name.
+ var columnName = $(this)
+ .closest('tr')
+ .find('th:first')
+ .text();
+
+ // Get the data-type of column excluding size.
+ var dataType = $(this)
+ .closest('tr')
+ .find('td[data-type]')
+ .attr('data-type');
+ dataType = TableSelect.checkIfDataTypeNumericOrDate(dataType);
+
+ // Get the operator.
+ var operator = $(this).val();
+
+ if ((operator === 'BETWEEN' || operator === 'NOT BETWEEN') && dataType) {
+ var $msgbox = Functions.ajaxShowMessage();
+ $.ajax({
+ url: 'tbl_select.php',
+ type: 'POST',
+ data: {
+ 'server': CommonParams.get('server'),
+ 'ajax_request': 1,
+ 'db': $('input[name="db"]').val(),
+ 'table': $('input[name="table"]').val(),
+ 'column': columnName,
+ 'range_search': 1
+ },
+ success: function (response) {
+ Functions.ajaxRemoveMessage($msgbox);
+ if (response.success) {
+ // Get the column min value.
+ var min = response.column_data.min
+ ? '(' + Messages.strColumnMin +
+ ' ' + response.column_data.min + ')'
+ : '';
+ // Get the column max value.
+ var max = response.column_data.max
+ ? '(' + Messages.strColumnMax +
+ ' ' + response.column_data.max + ')'
+ : '';
+ var buttonOptions = {};
+ buttonOptions[Messages.strGo] = function () {
+ var minValue = $('#min_value').val();
+ var maxValue = $('#max_value').val();
+ var finalValue = '';
+ if (minValue.length && maxValue.length) {
+ finalValue = minValue + ', ' +
+ maxValue;
+ }
+ var $targetField = $sourceSelect.closest('tr')
+ .find('[name*="criteriaValues"]');
+
+ // If target field is a select list.
+ if ($targetField.is('select')) {
+ $targetField.val(finalValue);
+ var $options = $targetField.find('option');
+ var $closestMin = null;
+ var $closestMax = null;
+ // Find closest min and max value.
+ $options.each(function () {
+ if (
+ $closestMin === null
+ || Math.abs($(this).val() - minValue) < Math.abs($closestMin.val() - minValue)
+ ) {
+ $closestMin = $(this);
+ }
+
+ if (
+ $closestMax === null
+ || Math.abs($(this).val() - maxValue) < Math.abs($closestMax.val() - maxValue)
+ ) {
+ $closestMax = $(this);
+ }
+ });
+
+ $closestMin.attr('selected', 'selected');
+ $closestMax.attr('selected', 'selected');
+ } else {
+ $targetField.val(finalValue);
+ }
+ $(this).dialog('close');
+ };
+ buttonOptions[Messages.strCancel] = function () {
+ $(this).dialog('close');
+ };
+
+ // Display dialog box.
+ $('<div></div>').append(
+ '<fieldset>' +
+ '<legend>' + operator + '</legend>' +
+ '<label for="min_value">' + Messages.strMinValue +
+ '</label>' +
+ '<input type="text" id="min_value">' + '<br>' +
+ '<span class="small_font">' + min + '</span>' + '<br>' +
+ '<label for="max_value">' + Messages.strMaxValue +
+ '</label>' +
+ '<input type="text" id="max_value">' + '<br>' +
+ '<span class="small_font">' + max + '</span>' +
+ '</fieldset>'
+ ).dialog({
+ width: 'auto',
+ maxHeight: 400,
+ modal: true,
+ buttons: buttonOptions,
+ title: Messages.strRangeSearch,
+ open: function () {
+ // Add datepicker wherever required.
+ Functions.addDatepicker($('#min_value'), dataType);
+ Functions.addDatepicker($('#max_value'), dataType);
+ },
+ close: function () {
+ $(this).remove();
+ }
+ });
+ } else {
+ Functions.ajaxShowMessage(response.error);
+ }
+ },
+ error: function () {
+ Functions.ajaxShowMessage(Messages.strErrorProcessingRequest);
+ }
+ });
+ }
+ });
+ var windowWidth = $(window).width();
+ $('.jsresponsive').css('max-width', (windowWidth - 69) + 'px');
+});
diff --git a/srcs/phpmyadmin/js/table/structure.js b/srcs/phpmyadmin/js/table/structure.js
new file mode 100644
index 0000000..8be0671
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/structure.js
@@ -0,0 +1,505 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * @fileoverview functions used on the table structure page
+ * @name Table Structure
+ *
+ * @requires jQuery
+ * @requires jQueryUI
+ * @required js/functions.js
+ */
+
+// eslint-disable-next-line no-unused-vars
+/* global primaryIndexes:writable, indexes:writable, fulltextIndexes:writable, spatialIndexes:writable */ // js/functions.js
+/* global sprintf */ // js/vendor/sprintf.js
+
+/**
+ * AJAX scripts for tbl_structure.php
+ *
+ * Actions ajaxified here:
+ * Drop Column
+ * Add Primary Key
+ * Drop Primary Key/Index
+ *
+ */
+
+/**
+ * Reload fields table
+ */
+function reloadFieldForm () {
+ $.post($('#fieldsForm').attr('action'), $('#fieldsForm').serialize() + CommonParams.get('arg_separator') + 'ajax_request=true', function (formData) {
+ var $tempDiv = $('<div id=\'temp_div\'><div>').append(formData.message);
+ $('#fieldsForm').replaceWith($tempDiv.find('#fieldsForm'));
+ $('#addColumns').replaceWith($tempDiv.find('#addColumns'));
+ $('#move_columns_dialog').find('ul').replaceWith($tempDiv.find('#move_columns_dialog ul'));
+ $('#moveColumns').removeClass('move-active');
+ });
+ $('#page_content').show();
+}
+
+function checkFirst () {
+ if ($('select[name=after_field] option:selected').data('pos') === 'first') {
+ $('input[name=field_where]').val('first');
+ } else {
+ $('input[name=field_where]').val('after');
+ }
+}
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/structure.js', function () {
+ $(document).off('click', 'a.drop_column_anchor.ajax');
+ $(document).off('click', 'a.add_key.ajax');
+ $(document).off('click', '#move_columns_anchor');
+ $(document).off('click', '#printView');
+ $(document).off('submit', '.append_fields_form.ajax');
+ $('body').off('click', '#fieldsForm.ajax button[name="submit_mult"], #fieldsForm.ajax input[name="submit_mult"]');
+ $(document).off('click', 'a[name^=partition_action].ajax');
+ $(document).off('click', '#remove_partitioning.ajax');
+});
+
+AJAX.registerOnload('table/structure.js', function () {
+ // Re-initialize variables.
+ primaryIndexes = [];
+ indexes = [];
+ fulltextIndexes = [];
+ spatialIndexes = [];
+
+ /**
+ *Ajax action for submitting the "Column Change" and "Add Column" form
+ */
+ $('.append_fields_form.ajax').off();
+ $(document).on('submit', '.append_fields_form.ajax', function (event) {
+ event.preventDefault();
+ /**
+ * @var the_form object referring to the export form
+ */
+ var $form = $(this);
+ var fieldCnt = $form.find('input[name=orig_num_fields]').val();
+
+
+ function submitForm () {
+ var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
+ $.post($form.attr('action'), $form.serialize() + CommonParams.get('arg_separator') + 'do_save_data=1', function (data) {
+ if ($('.sqlqueryresults').length !== 0) {
+ $('.sqlqueryresults').remove();
+ } else if ($('.error:not(.tab)').length !== 0) {
+ $('.error:not(.tab)').remove();
+ }
+ if (typeof data.success !== 'undefined' && data.success === true) {
+ $('#page_content')
+ .empty()
+ .append(data.message)
+ .show();
+ Functions.highlightSql($('#page_content'));
+ $('.result_query .notice').remove();
+ reloadFieldForm();
+ $form.remove();
+ Functions.ajaxRemoveMessage($msg);
+ Functions.initSlider();
+ Navigation.reload();
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // end $.post()
+ }
+
+ function checkIfConfirmRequired ($form) {
+ var i = 0;
+ var id;
+ var elm;
+ var val;
+ var nameOrig;
+ var elmOrig;
+ var valOrig;
+ var checkRequired = false;
+ for (i = 0; i < fieldCnt; i++) {
+ id = '#field_' + i + '_5';
+ elm = $(id);
+ val = elm.val();
+
+ nameOrig = 'input[name=field_collation_orig\\[' + i + '\\]]';
+ elmOrig = $form.find(nameOrig);
+ valOrig = elmOrig.val();
+
+ if (val && valOrig && val !== valOrig) {
+ checkRequired = true;
+ break;
+ }
+ }
+ return checkRequired;
+ }
+
+ /*
+ * First validate the form; if there is a problem, avoid submitting it
+ *
+ * Functions.checkTableEditForm() needs a pure element and not a jQuery object,
+ * this is why we pass $form[0] as a parameter (the jQuery object
+ * is actually an array of DOM elements)
+ */
+ if (Functions.checkTableEditForm($form[0], fieldCnt)) {
+ // OK, form passed validation step
+
+ Functions.prepareForAjaxRequest($form);
+ if (Functions.checkReservedWordColumns($form)) {
+ // User wants to submit the form
+
+ // If Collation is changed, Warn and Confirm
+ if (checkIfConfirmRequired($form)) {
+ var question = sprintf(
+ Messages.strChangeColumnCollation, 'https://wiki.phpmyadmin.net/pma/Garbled_data'
+ );
+ $form.confirm(question, $form.attr('action'), function () {
+ submitForm();
+ });
+ } else {
+ submitForm();
+ }
+ }
+ }
+ }); // end change table button "do_save_data"
+
+ /**
+ * Attach Event Handler for 'Drop Column'
+ */
+ $(document).on('click', 'a.drop_column_anchor.ajax', function (event) {
+ event.preventDefault();
+ /**
+ * @var curr_table_name String containing the name of the current table
+ */
+ var currTableName = $(this).closest('form').find('input[name=table]').val();
+ /**
+ * @var curr_row Object reference to the currently selected row (i.e. field in the table)
+ */
+ var $currRow = $(this).parents('tr');
+ /**
+ * @var curr_column_name String containing name of the field referred to by {@link curr_row}
+ */
+ var currColumnName = $currRow.children('th').children('label').text().trim();
+ currColumnName = Functions.escapeHtml(currColumnName);
+ /**
+ * @var $after_field_item Corresponding entry in the 'After' field.
+ */
+ var $afterFieldItem = $('select[name=\'after_field\'] option[value=\'' + currColumnName + '\']');
+ /**
+ * @var question String containing the question to be asked for confirmation
+ */
+ var question = Functions.sprintf(Messages.strDoYouReally, 'ALTER TABLE `' + Functions.escapeHtml(currTableName) + '` DROP `' + Functions.escapeHtml(currColumnName) + '`;');
+ var $thisAnchor = $(this);
+ $thisAnchor.confirm(question, $thisAnchor.attr('href'), function (url) {
+ var $msg = Functions.ajaxShowMessage(Messages.strDroppingColumn, false);
+ var params = Functions.getJsConfirmCommonParam(this, $thisAnchor.getPostData());
+ params += CommonParams.get('arg_separator') + 'ajax_page_request=1';
+ $.post(url, params, function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ Functions.ajaxRemoveMessage($msg);
+ if ($('.result_query').length) {
+ $('.result_query').remove();
+ }
+ if (data.sql_query) {
+ $('<div class="result_query"></div>')
+ .html(data.sql_query)
+ .prependTo('#structure_content');
+ Functions.highlightSql($('#page_content'));
+ }
+ // Adjust the row numbers
+ for (var $row = $currRow.next(); $row.length > 0; $row = $row.next()) {
+ var newVal = parseInt($row.find('td:nth-child(2)').text(), 10) - 1;
+ $row.find('td:nth-child(2)').text(newVal);
+ }
+ $afterFieldItem.remove();
+ $currRow.hide('medium').remove();
+
+ // Remove the dropped column from select menu for 'after field'
+ $('select[name=after_field]').find(
+ '[value="' + currColumnName + '"]'
+ ).remove();
+
+ // by default select the (new) last option to add new column
+ // (in case last column is dropped)
+ $('select[name=after_field] option:last').attr('selected','selected');
+
+ // refresh table stats
+ if (data.tableStat) {
+ $('#tablestatistics').html(data.tableStat);
+ }
+ // refresh the list of indexes (comes from sql.php)
+ $('.index_info').replaceWith(data.indexes_list);
+ Navigation.reload();
+ } else {
+ Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
+ }
+ }); // end $.post()
+ });
+ }); // end of Drop Column Anchor action
+
+ /**
+ * Attach Event Handler for 'Print' link
+ */
+ $(document).on('click', '#printView', function (event) {
+ event.preventDefault();
+
+ // Take to preview mode
+ Functions.printPreview();
+ }); // end of Print View action
+
+ /**
+ * Ajax Event handler for adding keys
+ */
+ $(document).on('click', 'a.add_key.ajax', function (event) {
+ event.preventDefault();
+
+ var $this = $(this);
+ var currTableName = $this.closest('form').find('input[name=table]').val();
+ var currColumnName = $this.parents('tr').children('th').children('label').text().trim();
+
+ var addClause = '';
+ if ($this.is('.add_primary_key_anchor')) {
+ addClause = 'ADD PRIMARY KEY';
+ } else if ($this.is('.add_index_anchor')) {
+ addClause = 'ADD INDEX';
+ } else if ($this.is('.add_unique_anchor')) {
+ addClause = 'ADD UNIQUE';
+ } else if ($this.is('.add_spatial_anchor')) {
+ addClause = 'ADD SPATIAL';
+ } else if ($this.is('.add_fulltext_anchor')) {
+ addClause = 'ADD FULLTEXT';
+ }
+ var question = Functions.sprintf(Messages.strDoYouReally, 'ALTER TABLE `' +
+ Functions.escapeHtml(currTableName) + '` ' + addClause + '(`' + Functions.escapeHtml(currColumnName) + '`);');
+
+ var $thisAnchor = $(this);
+
+ $thisAnchor.confirm(question, $thisAnchor.attr('href'), function (url) {
+ Functions.ajaxShowMessage();
+ AJAX.source = $this;
+
+ var params = Functions.getJsConfirmCommonParam(this, $thisAnchor.getPostData());
+ params += CommonParams.get('arg_separator') + 'ajax_page_request=1';
+ $.post(url, params, AJAX.responseHandler);
+ });
+ }); // end Add key
+
+ /**
+ * Inline move columns
+ **/
+ $(document).on('click', '#move_columns_anchor', function (e) {
+ e.preventDefault();
+
+ if ($(this).hasClass('move-active')) {
+ return;
+ }
+
+ /**
+ * @var button_options Object that stores the options passed to jQueryUI
+ * dialog
+ */
+ var buttonOptions = {};
+
+ buttonOptions[Messages.strGo] = function (event) {
+ event.preventDefault();
+ var $msgbox = Functions.ajaxShowMessage();
+ var $this = $(this);
+ var $form = $this.find('form');
+ var serialized = $form.serialize();
+ // check if any columns were moved at all
+ if (serialized === $form.data('serialized-unmoved')) {
+ Functions.ajaxRemoveMessage($msgbox);
+ $this.dialog('close');
+ return;
+ }
+ $.post($form.prop('action'), serialized + CommonParams.get('arg_separator') + 'ajax_request=true', function (data) {
+ if (data.success === false) {
+ Functions.ajaxRemoveMessage($msgbox);
+ $this
+ .clone()
+ .html(data.error)
+ .dialog({
+ title: $(this).prop('title'),
+ height: 230,
+ width: 900,
+ modal: true,
+ buttons: buttonOptionsError
+ }); // end dialog options
+ } else {
+ // sort the fields table
+ var $fieldsTable = $('table#tablestructure tbody');
+ // remove all existing rows and remember them
+ var $rows = $fieldsTable.find('tr').remove();
+ // loop through the correct order
+ for (var i in data.columns) {
+ var theColumn = data.columns[i];
+ var $theRow = $rows
+ .find('input:checkbox[value=\'' + theColumn + '\']')
+ .closest('tr');
+ // append the row for this column to the table
+ $fieldsTable.append($theRow);
+ }
+ var $firstrow = $fieldsTable.find('tr').eq(0);
+ // Adjust the row numbers and colors
+ for (var $row = $firstrow; $row.length > 0; $row = $row.next()) {
+ $row
+ .find('td:nth-child(2)')
+ .text($row.index() + 1)
+ .end()
+ .removeClass('odd even')
+ .addClass($row.index() % 2 === 0 ? 'odd' : 'even');
+ }
+ Functions.ajaxShowMessage(data.message);
+ $this.dialog('close');
+ }
+ });
+ };
+ buttonOptions[Messages.strPreviewSQL] = function () {
+ // Function for Previewing SQL
+ var $form = $('#move_column_form');
+ Functions.previewSql($form);
+ };
+ buttonOptions[Messages.strCancel] = function () {
+ $(this).dialog('close');
+ };
+
+ var buttonOptionsError = {};
+ buttonOptionsError[Messages.strOK] = function () {
+ $(this).dialog('close').remove();
+ };
+
+ var columns = [];
+
+ $('#tablestructure').find('tbody tr').each(function () {
+ var colName = $(this).find('input:checkbox').eq(0).val();
+ var hiddenInput = $('<input>')
+ .prop({
+ name: 'move_columns[]',
+ type: 'hidden'
+ })
+ .val(colName);
+ columns[columns.length] = $('<li></li>')
+ .addClass('placeholderDrag')
+ .text(colName)
+ .append(hiddenInput);
+ });
+
+ var colList = $('#move_columns_dialog').find('ul')
+ .find('li').remove().end();
+ for (var i in columns) {
+ colList.append(columns[i]);
+ }
+ colList.sortable({
+ axis: 'y',
+ containment: $('#move_columns_dialog').find('div'),
+ tolerance: 'pointer'
+ }).disableSelection();
+ var $form = $('#move_columns_dialog').find('form');
+ $form.data('serialized-unmoved', $form.serialize());
+
+ $('#move_columns_dialog').dialog({
+ modal: true,
+ buttons: buttonOptions,
+ open: function () {
+ if ($('#move_columns_dialog').parents('.ui-dialog').height() > $(window).height()) {
+ $('#move_columns_dialog').dialog('option', 'height', $(window).height());
+ }
+ },
+ beforeClose: function () {
+ $('#move_columns_anchor').removeClass('move-active');
+ }
+ });
+ });
+
+ /**
+ * Handles multi submits in table structure page such as change, browse, drop, primary etc.
+ */
+ $('body').on('click', '#fieldsForm.ajax button[name="submit_mult"], #fieldsForm.ajax input[name="submit_mult"]', function (e) {
+ e.preventDefault();
+ var $button = $(this);
+ var $form = $button.parents('form');
+ var argsep = CommonParams.get('arg_separator');
+ var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
+ Functions.ajaxShowMessage();
+ AJAX.source = $form;
+ $.post($form.attr('action'), submitData, AJAX.responseHandler);
+ });
+
+ /**
+ * Handles clicks on Action links in partition table
+ */
+ $(document).on('click', 'a[name^=partition_action].ajax', function (e) {
+ e.preventDefault();
+ var $link = $(this);
+
+ function submitPartitionAction (url) {
+ var params = 'ajax_request=true&ajax_page_request=true&' + $link.getPostData();
+ Functions.ajaxShowMessage();
+ AJAX.source = $link;
+ $.post(url, params, AJAX.responseHandler);
+ }
+
+ if ($link.is('#partition_action_DROP')) {
+ $link.confirm(Messages.strDropPartitionWarning, $link.attr('href'), function (url) {
+ submitPartitionAction(url);
+ });
+ } else if ($link.is('#partition_action_TRUNCATE')) {
+ $link.confirm(Messages.strTruncatePartitionWarning, $link.attr('href'), function (url) {
+ submitPartitionAction(url);
+ });
+ } else {
+ submitPartitionAction($link.attr('href'));
+ }
+ });
+
+ /**
+ * Handles remove partitioning
+ */
+ $(document).on('click', '#remove_partitioning.ajax', function (e) {
+ e.preventDefault();
+ var $link = $(this);
+ var question = Messages.strRemovePartitioningWarning;
+ $link.confirm(question, $link.attr('href'), function (url) {
+ var params = Functions.getJsConfirmCommonParam({
+ 'ajax_request' : true,
+ 'ajax_page_request' : true
+ }, $link.getPostData());
+ Functions.ajaxShowMessage();
+ AJAX.source = $link;
+ $.post(url, params, AJAX.responseHandler);
+ });
+ });
+
+ $(document).on('change', 'select[name=after_field]', function () {
+ checkFirst();
+ });
+});
+
+/** Handler for "More" dropdown in structure table rows */
+AJAX.registerOnload('table/structure.js', function () {
+ var windowwidth = $(window).width();
+ if (windowwidth > 768) {
+ if (! $('#fieldsForm').hasClass('HideStructureActions')) {
+ $('.table-structure-actions').width(function () {
+ var width = 5;
+ $(this).find('li').each(function () {
+ width += $(this).outerWidth(true);
+ });
+ return width;
+ });
+ }
+ }
+
+ $('.jsresponsive').css('max-width', (windowwidth - 35) + 'px');
+ var tableRows = $('.central_columns');
+ $.each(tableRows, function (index, item) {
+ if ($(item).hasClass('add_button')) {
+ $(item).on('click', function () {
+ $('input:checkbox').prop('checked', false);
+ $('#checkbox_row_' + (index + 1)).prop('checked', true);
+ $('button[value=add_to_central_columns]').trigger('click');
+ });
+ } else {
+ $(item).on('click', function () {
+ $('input:checkbox').prop('checked', false);
+ $('#checkbox_row_' + (index + 1)).prop('checked', true);
+ $('button[value=remove_from_central_columns]').trigger('click');
+ });
+ }
+ });
+});
diff --git a/srcs/phpmyadmin/js/table/tracking.js b/srcs/phpmyadmin/js/table/tracking.js
new file mode 100644
index 0000000..7d2d943
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/tracking.js
@@ -0,0 +1,106 @@
+/**
+ * Unbind all event handlers before tearing down the page
+ */
+AJAX.registerTeardown('table/tracking.js', function () {
+ $('body').off('click', '#versionsForm.ajax button[name="submit_mult"], #versionsForm.ajax input[name="submit_mult"]');
+ $('body').off('click', 'a.delete_version_anchor.ajax');
+ $('body').off('click', 'a.delete_entry_anchor.ajax');
+});
+
+/**
+ * Bind event handlers
+ */
+AJAX.registerOnload('table/tracking.js', function () {
+ $('#versions tr:first th').append($('<div class="sorticon"></div>'));
+ $('#versions').tablesorter({
+ sortList: [[1, 0]],
+ headers: {
+ 0: { sorter: false },
+ 1: { sorter: 'integer' },
+ 5: { sorter: false },
+ 6: { sorter: false }
+ }
+ });
+
+ if ($('#ddl_versions tbody tr').length > 0) {
+ $('#ddl_versions tr:first th').append($('<div class="sorticon"></div>'));
+ $('#ddl_versions').tablesorter({
+ sortList: [[0, 0]],
+ headers: {
+ 0: { sorter: 'integer' },
+ 3: { sorter: false },
+ 4: { sorter: false }
+ }
+ });
+ }
+
+ if ($('#dml_versions tbody tr').length > 0) {
+ $('#dml_versions tr:first th').append($('<div class="sorticon"></div>'));
+ $('#dml_versions').tablesorter({
+ sortList: [[0, 0]],
+ headers: {
+ 0: { sorter: 'integer' },
+ 3: { sorter: false },
+ 4: { sorter: false }
+ }
+ });
+ }
+
+ /**
+ * Handles multi submit for tracking versions
+ */
+ $('body').on('click', '#versionsForm.ajax button[name="submit_mult"], #versionsForm.ajax input[name="submit_mult"]', function (e) {
+ e.preventDefault();
+ var $button = $(this);
+ var $form = $button.parent('form');
+ var argsep = CommonParams.get('arg_separator');
+ var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
+
+ if ($button.val() === 'delete_version') {
+ var question = Messages.strDeleteTrackingVersionMultiple;
+ $button.confirm(question, $form.attr('action'), function (url) {
+ Functions.ajaxShowMessage();
+ AJAX.source = $form;
+ $.post(url, submitData, AJAX.responseHandler);
+ });
+ } else {
+ Functions.ajaxShowMessage();
+ AJAX.source = $form;
+ $.post($form.attr('action'), submitData, AJAX.responseHandler);
+ }
+ });
+
+ /**
+ * Ajax Event handler for 'Delete version'
+ */
+ $('body').on('click', 'a.delete_version_anchor.ajax', function (e) {
+ e.preventDefault();
+ var $anchor = $(this);
+ var question = Messages.strDeleteTrackingVersion;
+ $anchor.confirm(question, $anchor.attr('href'), function (url) {
+ Functions.ajaxShowMessage();
+ AJAX.source = $anchor;
+ var argSep = CommonParams.get('arg_separator');
+ var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
+ params += argSep + 'ajax_page_request=1';
+ $.post(url, params, AJAX.responseHandler);
+ });
+ });
+
+ /**
+ * Ajax Event handler for 'Delete tracking report entry'
+ */
+ $('body').on('click', 'a.delete_entry_anchor.ajax', function (e) {
+ e.preventDefault();
+ var $anchor = $(this);
+ var question = Messages.strDeletingTrackingEntry;
+ $anchor.confirm(question, $anchor.attr('href'), function (url) {
+ Functions.ajaxShowMessage();
+ AJAX.source = $anchor;
+ var argSep = CommonParams.get('arg_separator');
+ var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
+ params += argSep + 'ajax_page_request=1';
+ $.post(url, params, AJAX.responseHandler);
+ });
+ });
+});
diff --git a/srcs/phpmyadmin/js/table/zoom_plot_jqplot.js b/srcs/phpmyadmin/js/table/zoom_plot_jqplot.js
new file mode 100644
index 0000000..659e6a7
--- /dev/null
+++ b/srcs/phpmyadmin/js/table/zoom_plot_jqplot.js
@@ -0,0 +1,607 @@
+// TODO: change the axis
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ ** @fileoverview JavaScript functions used on tbl_select.php
+ **
+ ** @requires jQuery
+ ** @requires js/functions.js
+ **/
+
+
+/**
+ ** Display Help/Info
+ **/
+function displayHelp () {
+ $('<div></div>')
+ .append(Messages.strDisplayHelp)
+ .appendTo('#page_content')
+ .dialog({
+ width: 450,
+ height: 'auto',
+ title: Messages.strHelpTitle
+ });
+ return false;
+}
+
+/**
+ ** Extend the array object for max function
+ ** @param array
+ **/
+Array.max = function (array) {
+ return Math.max.apply(Math, array);
+};
+
+/**
+ ** Extend the array object for min function
+ ** @param array
+ **/
+Array.min = function (array) {
+ return Math.min.apply(Math, array);
+};
+
+/**
+ ** Checks if a string contains only numeric value
+ ** @param n: String (to be checked)
+ **/
+function isNumeric (n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+}
+
+/**
+ ** Checks if an object is empty
+ ** @param n: Object (to be checked)
+ **/
+function isEmpty (obj) {
+ var name;
+ for (name in obj) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ ** Converts a date/time into timestamp
+ ** @param val String Date
+ ** @param type Sring Field type(datetime/timestamp/time/date)
+ **/
+function getTimeStamp (val, type) {
+ if (type.toString().search(/datetime/i) !== -1 ||
+ type.toString().search(/timestamp/i) !== -1
+ ) {
+ return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', val);
+ } else if (type.toString().search(/time/i) !== -1) {
+ return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', '1970-01-01 ' + val);
+ } else if (type.toString().search(/date/i) !== -1) {
+ return $.datepicker.parseDate('yy-mm-dd', val);
+ }
+}
+
+/**
+ ** Classifies the field type into numeric,timeseries or text
+ ** @param field: field type (as in database structure)
+ **/
+function getType (field) {
+ if (field.toString().search(/int/i) !== -1 ||
+ field.toString().search(/decimal/i) !== -1 ||
+ field.toString().search(/year/i) !== -1
+ ) {
+ return 'numeric';
+ } else if (field.toString().search(/time/i) !== -1 ||
+ field.toString().search(/date/i) !== -1
+ ) {
+ return 'time';
+ } else {
+ return 'text';
+ }
+}
+
+/**
+ * Unbind all event handlers before tearing down a page
+ */
+AJAX.registerTeardown('table/zoom_plot_jqplot.js', function () {
+ $('#tableid_0').off('change');
+ $('#tableid_1').off('change');
+ $('#tableid_2').off('change');
+ $('#tableid_3').off('change');
+ $('#inputFormSubmitId').off('click');
+ $('#togglesearchformlink').off('click');
+ $(document).off('keydown', '#dataDisplay :input');
+ $('button.button-reset').off('click');
+ $('div#resizer').off('resizestop');
+ $('div#querychart').off('jqplotDataClick');
+});
+
+AJAX.registerOnload('table/zoom_plot_jqplot.js', function () {
+ var currentChart = null;
+ var searchedDataKey = null;
+ var xLabel = $('#tableid_0').val();
+ var yLabel = $('#tableid_1').val();
+ // will be updated via Ajax
+ var xType = $('#types_0').val();
+ var yType = $('#types_1').val();
+ var dataLabel = $('#dataLabel').val();
+
+ // Get query result
+ var searchedData;
+ try {
+ searchedData = JSON.parse($('#querydata').html());
+ } catch (err) {
+ searchedData = null;
+ }
+
+ /**
+ ** Input form submit on field change
+ **/
+
+ // first column choice corresponds to the X axis
+ $('#tableid_0').on('change', function () {
+ // AJAX request for field type, collation, operators, and value field
+ $.post('tbl_zoom_select.php', {
+ 'ajax_request' : true,
+ 'change_tbl_info' : true,
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'table' : CommonParams.get('table'),
+ 'field' : $('#tableid_0').val(),
+ 'it' : 0
+ }, function (data) {
+ $('#tableFieldsId').find('tr:eq(1) td:eq(0)').html(data.field_type);
+ $('#tableFieldsId').find('tr:eq(1) td:eq(1)').html(data.field_collation);
+ $('#tableFieldsId').find('tr:eq(1) td:eq(2)').html(data.field_operators);
+ $('#tableFieldsId').find('tr:eq(1) td:eq(3)').html(data.field_value);
+ xLabel = $('#tableid_0').val();
+ $('#types_0').val(data.field_type);
+ xType = data.field_type;
+ $('#collations_0').val(data.field_collations);
+ Functions.addDateTimePicker();
+ });
+ });
+
+ // second column choice corresponds to the Y axis
+ $('#tableid_1').on('change', function () {
+ // AJAX request for field type, collation, operators, and value field
+ $.post('tbl_zoom_select.php', {
+ 'ajax_request' : true,
+ 'change_tbl_info' : true,
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'table' : CommonParams.get('table'),
+ 'field' : $('#tableid_1').val(),
+ 'it' : 1
+ }, function (data) {
+ $('#tableFieldsId').find('tr:eq(2) td:eq(0)').html(data.field_type);
+ $('#tableFieldsId').find('tr:eq(2) td:eq(1)').html(data.field_collation);
+ $('#tableFieldsId').find('tr:eq(2) td:eq(2)').html(data.field_operators);
+ $('#tableFieldsId').find('tr:eq(2) td:eq(3)').html(data.field_value);
+ yLabel = $('#tableid_1').val();
+ $('#types_1').val(data.field_type);
+ yType = data.field_type;
+ $('#collations_1').val(data.field_collations);
+ Functions.addDateTimePicker();
+ });
+ });
+
+ $('#tableid_2').on('change', function () {
+ // AJAX request for field type, collation, operators, and value field
+ $.post('tbl_zoom_select.php', {
+ 'ajax_request' : true,
+ 'change_tbl_info' : true,
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'table' : CommonParams.get('table'),
+ 'field' : $('#tableid_2').val(),
+ 'it' : 2
+ }, function (data) {
+ $('#tableFieldsId').find('tr:eq(4) td:eq(0)').html(data.field_type);
+ $('#tableFieldsId').find('tr:eq(4) td:eq(1)').html(data.field_collation);
+ $('#tableFieldsId').find('tr:eq(4) td:eq(2)').html(data.field_operators);
+ $('#tableFieldsId').find('tr:eq(4) td:eq(3)').html(data.field_value);
+ $('#types_2').val(data.field_type);
+ $('#collations_2').val(data.field_collations);
+ Functions.addDateTimePicker();
+ });
+ });
+
+ $('#tableid_3').on('change', function () {
+ // AJAX request for field type, collation, operators, and value field
+ $.post('tbl_zoom_select.php', {
+ 'ajax_request' : true,
+ 'change_tbl_info' : true,
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'table' : CommonParams.get('table'),
+ 'field' : $('#tableid_3').val(),
+ 'it' : 3
+ }, function (data) {
+ $('#tableFieldsId').find('tr:eq(5) td:eq(0)').html(data.field_type);
+ $('#tableFieldsId').find('tr:eq(5) td:eq(1)').html(data.field_collation);
+ $('#tableFieldsId').find('tr:eq(5) td:eq(2)').html(data.field_operators);
+ $('#tableFieldsId').find('tr:eq(5) td:eq(3)').html(data.field_value);
+ $('#types_3').val(data.field_type);
+ $('#collations_3').val(data.field_collations);
+ Functions.addDateTimePicker();
+ });
+ });
+
+ /**
+ * Input form validation
+ **/
+ $('#inputFormSubmitId').on('click', function () {
+ if ($('#tableid_0').get(0).selectedIndex === 0 || $('#tableid_1').get(0).selectedIndex === 0) {
+ Functions.ajaxShowMessage(Messages.strInputNull);
+ } else if (xLabel === yLabel) {
+ Functions.ajaxShowMessage(Messages.strSameInputs);
+ }
+ });
+
+ /**
+ ** Prepare a div containing a link, otherwise it's incorrectly displayed
+ ** after a couple of clicks
+ **/
+ $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>')
+ .insertAfter('#zoom_search_form')
+ // don't show it until we have results on-screen
+ .hide();
+
+ $('#togglesearchformlink')
+ .html(Messages.strShowSearchCriteria)
+ .on('click', function () {
+ var $link = $(this);
+ $('#zoom_search_form').slideToggle();
+ if ($link.text() === Messages.strHideSearchCriteria) {
+ $link.text(Messages.strShowSearchCriteria);
+ } else {
+ $link.text(Messages.strHideSearchCriteria);
+ }
+ // avoid default click action
+ return false;
+ });
+
+ /**
+ ** Set dialog properties for the data display form
+ **/
+ var buttonOptions = {};
+ /*
+ * Handle saving of a row in the editor
+ */
+ buttonOptions[Messages.strSave] = function () {
+ // Find changed values by comparing form values with selectedRow Object
+ var newValues = {};// Stores the values changed from original
+ var sqlTypes = {};
+ var it = 0;
+ var xChange = false;
+ var yChange = false;
+ var key;
+ var tempGetVal = function () {
+ return $(this).val();
+ };
+ for (key in selectedRow) {
+ var oldVal = selectedRow[key];
+ var newVal = ($('#edit_fields_null_id_' + it).prop('checked')) ? null : $('#edit_fieldID_' + it).val();
+ if (newVal instanceof Array) { // when the column is of type SET
+ newVal = $('#edit_fieldID_' + it).map(tempGetVal).get().join(',');
+ }
+ if (oldVal !== newVal) {
+ selectedRow[key] = newVal;
+ newValues[key] = newVal;
+ if (key === xLabel) {
+ xChange = true;
+ searchedData[searchedDataKey][xLabel] = newVal;
+ } else if (key === yLabel) {
+ yChange = true;
+ searchedData[searchedDataKey][yLabel] = newVal;
+ }
+ }
+ var $input = $('#edit_fieldID_' + it);
+ if ($input.hasClass('bit')) {
+ sqlTypes[key] = 'bit';
+ } else {
+ sqlTypes[key] = null;
+ }
+ it++;
+ } // End data update
+
+ // Update the chart series and replot
+ if (xChange || yChange) {
+ // Logic similar to plot generation, replot only if xAxis changes or yAxis changes.
+ // Code includes a lot of checks so as to replot only when necessary
+ if (xChange) {
+ xCord[searchedDataKey] = selectedRow[xLabel];
+ // [searchedDataKey][0] contains the x value
+ if (xType === 'numeric') {
+ series[0][searchedDataKey][0] = selectedRow[xLabel];
+ } else if (xType === 'time') {
+ series[0][searchedDataKey][0] =
+ getTimeStamp(selectedRow[xLabel], $('#types_0').val());
+ } else {
+ series[0][searchedDataKey][0] = '';
+ // TODO: text values
+ }
+ currentChart.series[0].data = series[0];
+ // TODO: axis changing
+ currentChart.replot();
+ }
+ if (yChange) {
+ yCord[searchedDataKey] = selectedRow[yLabel];
+ // [searchedDataKey][1] contains the y value
+ if (yType === 'numeric') {
+ series[0][searchedDataKey][1] = selectedRow[yLabel];
+ } else if (yType === 'time') {
+ series[0][searchedDataKey][1] =
+ getTimeStamp(selectedRow[yLabel], $('#types_1').val());
+ } else {
+ series[0][searchedDataKey][1] = '';
+ // TODO: text values
+ }
+ currentChart.series[0].data = series[0];
+ // TODO: axis changing
+ currentChart.replot();
+ }
+ } // End plot update
+
+ // Generate SQL query for update
+ if (!isEmpty(newValues)) {
+ var sqlQuery = 'UPDATE `' + CommonParams.get('table') + '` SET ';
+ for (key in newValues) {
+ sqlQuery += '`' + key + '`=';
+ var value = newValues[key];
+
+ // null
+ if (value === null) {
+ sqlQuery += 'NULL, ';
+
+ // empty
+ } else if ($.trim(value) === '') {
+ sqlQuery += '\'\', ';
+
+ // other
+ } else {
+ // type explicitly identified
+ if (sqlTypes[key] !== null) {
+ if (sqlTypes[key] === 'bit') {
+ sqlQuery += 'b\'' + value + '\', ';
+ }
+ // type not explicitly identified
+ } else {
+ if (!isNumeric(value)) {
+ sqlQuery += '\'' + value + '\', ';
+ } else {
+ sqlQuery += value + ', ';
+ }
+ }
+ }
+ }
+ // remove two extraneous characters ', '
+ sqlQuery = sqlQuery.substring(0, sqlQuery.length - 2);
+ sqlQuery += ' WHERE ' + Sql.urlDecode(searchedData[searchedDataKey].where_clause);
+
+ // Post SQL query to sql.php
+ $.post('sql.php', {
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'ajax_request' : true,
+ 'sql_query' : sqlQuery,
+ 'inline_edit' : false
+ }, function (data) {
+ if (typeof data !== 'undefined' && data.success === true) {
+ $('#sqlqueryresultsouter').html(data.sql_query);
+ Functions.highlightSql($('#sqlqueryresultsouter'));
+ } else {
+ Functions.ajaxShowMessage(data.error, false);
+ }
+ }); // End $.post
+ }// End database update
+ $('#dataDisplay').dialog('close');
+ };
+ buttonOptions[Messages.strCancel] = function () {
+ $(this).dialog('close');
+ };
+ $('#dataDisplay').dialog({
+ autoOpen: false,
+ title: Messages.strDataPointContent,
+ modal: true,
+ buttons: buttonOptions,
+ width: $('#dataDisplay').width() + 80,
+ open: function () {
+ $(this).find('input[type=checkbox]').css('margin', '0.5em');
+ }
+ });
+ /**
+ * Attach Ajax event handlers for input fields
+ * in the dialog. Used to submit the Ajax
+ * request when the ENTER key is pressed.
+ */
+ $(document).on('keydown', '#dataDisplay :input', function (e) {
+ if (e.which === 13) { // 13 is the ENTER key
+ e.preventDefault();
+ if (typeof buttonOptions[Messages.strSave] === 'function') {
+ buttonOptions[Messages.strSave].call();
+ }
+ }
+ });
+
+
+ /*
+ * Generate plot using jqplot
+ */
+
+ if (searchedData !== null) {
+ $('#zoom_search_form')
+ .slideToggle()
+ .hide();
+ $('#togglesearchformlink')
+ .text(Messages.strShowSearchCriteria);
+ $('#togglesearchformdiv').show();
+ var selectedRow;
+ var series = [];
+ var xCord = [];
+ var yCord = [];
+ var xVal;
+ var yVal;
+ var format;
+
+ var options = {
+ series: [
+ // for a scatter plot
+ { showLine: false }
+ ],
+ grid: {
+ drawBorder: false,
+ shadow: false,
+ background: 'rgba(0,0,0,0)'
+ },
+ axes: {
+ xaxis: {
+ label: $('#tableid_0').val(),
+ labelRenderer: $.jqplot.CanvasAxisLabelRenderer
+ },
+ yaxis: {
+ label: $('#tableid_1').val(),
+ labelRenderer: $.jqplot.CanvasAxisLabelRenderer
+ }
+ },
+ highlighter: {
+ show: true,
+ tooltipAxes: 'y',
+ yvalues: 2,
+ // hide the first y value
+ formatString: '<span class="hide">%s</span>%s'
+ },
+ cursor: {
+ show: true,
+ zoom: true,
+ showTooltip: false
+ }
+ };
+
+ // If data label is not set, do not show tooltips
+ if (dataLabel === '') {
+ options.highlighter.show = false;
+ }
+
+ // Classify types as either numeric,time,text
+ xType = getType(xType);
+ yType = getType(yType);
+
+ // could have multiple series but we'll have just one
+ series[0] = [];
+
+ if (xType === 'time') {
+ var originalXType = $('#types_0').val();
+ if (originalXType === 'date') {
+ format = '%Y-%m-%d';
+ }
+ // TODO: does not seem to work
+ // else if (originalXType === 'time') {
+ // format = '%H:%M';
+ // } else {
+ // format = '%Y-%m-%d %H:%M';
+ // }
+ $.extend(options.axes.xaxis, {
+ renderer: $.jqplot.DateAxisRenderer,
+ tickOptions: {
+ formatString: format
+ }
+ });
+ }
+ if (yType === 'time') {
+ var originalYType = $('#types_1').val();
+ if (originalYType === 'date') {
+ format = '%Y-%m-%d';
+ }
+ $.extend(options.axes.yaxis, {
+ renderer: $.jqplot.DateAxisRenderer,
+ tickOptions: {
+ formatString: format
+ }
+ });
+ }
+
+ $.each(searchedData, function (key, value) {
+ if (xType === 'numeric') {
+ xVal = parseFloat(value[xLabel]);
+ }
+ if (xType === 'time') {
+ xVal = getTimeStamp(value[xLabel], originalXType);
+ }
+ if (yType === 'numeric') {
+ yVal = parseFloat(value[yLabel]);
+ }
+ if (yType === 'time') {
+ yVal = getTimeStamp(value[yLabel], originalYType);
+ }
+ series[0].push([
+ xVal,
+ yVal,
+ // extra Y values
+ value[dataLabel], // for highlighter
+ // (may set an undefined value)
+ value.where_clause, // for click on point
+ key // key from searchedData
+ ]);
+ });
+
+ // under IE 8, the initial display is mangled; after a manual
+ // resizing, it's ok
+ // under IE 9, everything is fine
+ currentChart = $.jqplot('querychart', series, options);
+ currentChart.resetZoom();
+
+ $('button.button-reset').on('click', function (event) {
+ event.preventDefault();
+ currentChart.resetZoom();
+ });
+
+ $('div#resizer').resizable();
+ $('div#resizer').on('resizestop', function () {
+ // make room so that the handle will still appear
+ $('div#querychart').height($('div#resizer').height() * 0.96);
+ $('div#querychart').width($('div#resizer').width() * 0.96);
+ currentChart.replot({ resetAxes: true });
+ });
+
+ $('div#querychart').on('jqplotDataClick',
+ function (event, seriesIndex, pointIndex, data) {
+ searchedDataKey = data[4]; // key from searchedData (global)
+ var fieldId = 0;
+ var postParams = {
+ 'ajax_request' : true,
+ 'get_data_row' : true,
+ 'server' : CommonParams.get('server'),
+ 'db' : CommonParams.get('db'),
+ 'table' : CommonParams.get('table'),
+ 'where_clause' : data[3]
+ };
+
+ $.post('tbl_zoom_select.php', postParams, function (data) {
+ // Row is contained in data.row_info,
+ // now fill the displayResultForm with row values
+ var key;
+ for (key in data.row_info) {
+ var $field = $('#edit_fieldID_' + fieldId);
+ var $fieldNull = $('#edit_fields_null_id_' + fieldId);
+ if (data.row_info[key] === null) {
+ $fieldNull.prop('checked', true);
+ $field.val('');
+ } else {
+ $fieldNull.prop('checked', false);
+ if ($field.attr('multiple')) { // when the column is of type SET
+ $field.val(data.row_info[key].split(','));
+ } else {
+ $field.val(data.row_info[key]);
+ }
+ }
+ fieldId++;
+ }
+ selectedRow = data.row_info;
+ });
+
+ $('#dataDisplay').dialog('open');
+ }
+ );
+ }
+
+ $('#help_dialog').on('click', function () {
+ displayHelp();
+ });
+});