2 * @fileoverview function used for index manipulation pages
3 * @name Table Structure
7 * @required js/functions.js
10 /* global fulltextIndexes:writable, indexes:writable, primaryIndexes:writable, spatialIndexes:writable, uniqueIndexes:writable */ // js/functions.js
15 * Returns the array of indexes based on the index choice
17 * @param indexChoice index choice
19 Indexes.getIndexArray = function (indexChoice) {
20 var sourceArray = null;
22 switch (indexChoice.toLowerCase()) {
24 sourceArray = primaryIndexes;
27 sourceArray = uniqueIndexes;
30 sourceArray = indexes;
33 sourceArray = fulltextIndexes;
36 sourceArray = spatialIndexes;
45 * Hides/shows the inputs and submits appropriately depending
46 * on whether the index type chosen is 'SPATIAL' or not.
48 Indexes.checkIndexType = function () {
50 * @var Object Dropdown to select the index choice.
52 var $selectIndexChoice = $('#select_index_choice');
54 * @var Object Dropdown to select the index type.
56 var $selectIndexType = $('#select_index_type');
58 * @var Object Table header for the size column.
60 var $sizeHeader = $('#index_columns').find(document.querySelectorAll('thead tr th:nth-child(2)'));
62 * @var Object Inputs to specify the columns for the index.
64 var $columnInputs = $('select[name="index[columns][names][]"]');
66 * @var Object Inputs to specify sizes for columns of the index.
68 var $sizeInputs = $('input[name="index[columns][sub_parts][]"]');
70 * @var Object Footer containg the controllers to add more columns
72 var $addMore = $('#index_frm').find('.add_more');
74 if ($selectIndexChoice.val() === 'SPATIAL') {
75 // Disable and hide the size column
77 $sizeInputs.each(function () {
79 .prop('disabled', true)
83 // Disable and hide the columns of the index other than the first one
85 $columnInputs.each(function () {
86 var $columnInput = $(this);
89 .prop('disabled', true)
96 // Hide controllers to add more columns
99 // Enable and show the size column
101 $sizeInputs.each(function () {
103 .prop('disabled', false)
104 .parent('td').show();
107 // Enable and show the columns of the index
108 $columnInputs.each(function () {
110 .prop('disabled', false)
111 .parent('td').show();
114 // Show controllers to add more columns
118 if ($selectIndexChoice.val() === 'SPATIAL' ||
119 $selectIndexChoice.val() === 'FULLTEXT') {
120 $selectIndexType.val('').prop('disabled', true);
122 $selectIndexType.prop('disabled', false);
127 * Sets current index information into form parameters.
129 * @param array source_array Array containing index columns
130 * @param string index_choice Choice of index
134 Indexes.setIndexFormParameters = function (sourceArray, indexChoice) {
135 if (indexChoice === 'index') {
136 $('input[name="indexes"]').val(JSON.stringify(sourceArray));
138 $('input[name="' + indexChoice + '_indexes"]').val(JSON.stringify(sourceArray));
143 * Removes a column from an Index.
145 * @param string col_index Index of column in form
149 Indexes.removeColumnFromIndex = function (colIndex) {
150 // Get previous index details.
151 var previousIndex = $('select[name="field_key[' + colIndex + ']"]')
153 if (previousIndex.length) {
154 previousIndex = previousIndex.split(',');
155 var sourceArray = Indexes.getIndexArray(previousIndex[0]);
156 if (sourceArray === null) {
160 // Remove column from index array.
161 var sourceLength = sourceArray[previousIndex[1]].columns.length;
162 for (var i = 0; i < sourceLength; i++) {
163 if (sourceArray[previousIndex[1]].columns[i].col_index === colIndex) {
164 sourceArray[previousIndex[1]].columns.splice(i, 1);
168 // Remove index completely if no columns left.
169 if (sourceArray[previousIndex[1]].columns.length === 0) {
170 sourceArray.splice(previousIndex[1], 1);
173 // Update current index details.
174 $('select[name="field_key[' + colIndex + ']"]').attr('data-index', '');
175 // Update form index parameters.
176 Indexes.setIndexFormParameters(sourceArray, previousIndex[0].toLowerCase());
181 * Adds a column to an Index.
183 * @param array source_array Array holding corresponding indexes
184 * @param string array_index Index of an INDEX in array
185 * @param string index_choice Choice of Index
186 * @param string col_index Index of column on form
190 Indexes.addColumnToIndex = function (sourceArray, arrayIndex, indexChoice, colIndex) {
192 // Remove column from other indexes (if any).
193 Indexes.removeColumnFromIndex(colIndex);
195 var indexName = $('input[name="index[Key_name]"]').val();
196 var indexComment = $('input[name="index[Index_comment]"]').val();
197 var keyBlockSize = $('input[name="index[Key_block_size]"]').val();
198 var parser = $('input[name="index[Parser]"]').val();
199 var indexType = $('select[name="index[Index_type]"]').val();
201 $('#index_columns').find('tbody').find('tr').each(function () {
202 // Get columns in particular order.
203 var colIndex = $(this).find('select[name="index[columns][names][]"]').val();
204 var size = $(this).find('input[name="index[columns][sub_parts][]"]').val();
206 'col_index': colIndex,
211 // Update or create an index.
212 sourceArray[arrayIndex] = {
213 'Key_name': indexName,
214 'Index_comment': indexComment,
215 'Index_choice': indexChoice.toUpperCase(),
216 'Key_block_size': keyBlockSize,
218 'Index_type': indexType,
222 // Display index name (or column list)
223 var displayName = indexName;
224 if (displayName === '') {
225 var columnNames = [];
226 $.each(columns, function () {
227 columnNames.push($('input[name="field_name[' + this.col_index + ']"]').val());
229 displayName = '[' + columnNames.join(', ') + ']';
231 $.each(columns, function () {
232 var id = 'index_name_' + this.col_index + '_8';
233 var $name = $('#' + id);
234 if ($name.length === 0) {
235 $name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
236 $name.insertAfter($('select[name="field_key[' + this.col_index + ']"]'));
238 var $text = $('<small>').text(displayName);
243 // Update index details on form.
244 $('select[name="field_key[' + colIndex + ']"]')
245 .attr('data-index', indexChoice + ',' + arrayIndex);
247 Indexes.setIndexFormParameters(sourceArray, indexChoice.toLowerCase());
251 * Get choices list for a column to create a composite index with.
253 * @param string index_choice Choice of index
254 * @param array source_array Array hodling columns for particular index
256 * @return jQuery Object
258 Indexes.getCompositeIndexList = function (sourceArray, colIndex) {
259 // Remove any previous list.
260 if ($('#composite_index_list').length) {
261 $('#composite_index_list').remove();
265 var $compositeIndexList = $(
266 '<ul id="composite_index_list">' +
267 '<div>' + Messages.strCompositeWith + '</div>' +
271 // Add each column to list available for composite index.
272 var sourceLength = sourceArray.length;
273 var alreadyPresent = false;
274 for (var i = 0; i < sourceLength; i++) {
275 var subArrayLen = sourceArray[i].columns.length;
276 var columnNames = [];
277 for (var j = 0; j < subArrayLen; j++) {
279 $('input[name="field_name[' + sourceArray[i].columns[j].col_index + ']"]').val()
282 if (colIndex === sourceArray[i].columns[j].col_index) {
283 alreadyPresent = true;
287 $compositeIndexList.append(
289 '<input type="radio" name="composite_with" ' +
290 (alreadyPresent ? 'checked="checked"' : '') +
291 ' id="composite_index_' + i + '" value="' + i + '">' +
292 '<label for="composite_index_' + i + '">' + columnNames.join(', ') +
298 return $compositeIndexList;
302 * Shows 'Add Index' dialog.
304 * @param array source_array Array holding particluar index
305 * @param string array_index Index of an INDEX in array
306 * @param array target_columns Columns for an INDEX
307 * @param string col_index Index of column on form
308 * @param object index Index detail object
309 * @param bool showDialog Whether to show index creation dialog or not
313 Indexes.showAddIndexDialog = function (sourceArray, arrayIndex, targetColumns, colIndex, index, showDialog) {
314 var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true;
315 // Prepare post-data.
316 var $table = $('input[name="table"]');
317 var table = $table.length > 0 ? $table.val() : '';
319 'server': CommonParams.get('server'),
320 'db': $('input[name="db"]').val(),
323 'create_edit_table': 1,
328 for (var i = 0; i < targetColumns.length; i++) {
329 var columnName = $('input[name="field_name[' + targetColumns[i] + ']"]').val();
330 var columnType = $('select[name="field_type[' + targetColumns[i] + ']"]').val().toLowerCase();
331 columns[columnName] = [columnType, targetColumns[i]];
333 postData.columns = JSON.stringify(columns);
335 var buttonOptions = {};
336 buttonOptions[Messages.strGo] = function () {
337 var isMissingValue = false;
338 $('select[name="index[columns][names][]"]').each(function () {
339 if ($(this).val() === '') {
340 isMissingValue = true;
344 if (! isMissingValue) {
345 Indexes.addColumnToIndex(
352 Functions.ajaxShowMessage(
353 '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' +
354 ' class="icon ic_s_error"> ' + Messages.strMissingColumn +
363 buttonOptions[Messages.strCancel] = function () {
365 // Handle state on 'Cancel'.
366 var $selectList = $('select[name="field_key[' + colIndex + ']"]');
367 if (! $selectList.attr('data-index').length) {
368 $selectList.find('option[value*="none"]').attr('selected', 'selected');
370 var previousIndex = $selectList.attr('data-index').split(',');
371 $selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]')
372 .attr('selected', 'selected');
375 $(this).dialog('close');
377 var $msgbox = Functions.ajaxShowMessage();
378 $.post('index.php?route=/table/indexes', postData, function (data) {
379 if (data.success === false) {
380 // in the case of an error, show the error message returned.
381 Functions.ajaxShowMessage(data.error, false);
383 Functions.ajaxRemoveMessage($msgbox);
384 var $div = $('<div></div>');
385 if (showDialogLocal) {
386 // Show dialog if the request was successful
387 if ($('#addIndex').length > 0) {
388 $('#addIndex').remove();
391 .append(data.message)
393 title: Messages.strAddIndex,
396 create: function () {
397 $(this).on('keypress', function (e) {
398 if (e.which === 13 || e.keyCode === 13 || window.event.keyCode === 13) {
400 buttonOptions[Messages.strGo]();
406 Functions.checkIndexName('index_frm');
407 Functions.showHints($div);
408 Functions.initSlider();
409 $('#index_columns').find('td').each(function () {
410 $(this).css('width', $(this).width() + 'px');
412 $('#index_columns').find('tbody').sortable({
414 containment: $('#index_columns').find('tbody'),
419 buttons: buttonOptions,
426 .append(data.message);
427 $div.css({ 'display' : 'none' });
428 $div.appendTo($('body'));
429 $div.attr({ 'id' : 'addIndex' });
430 var isMissingValue = false;
431 $('select[name="index[columns][names][]"]').each(function () {
432 if ($(this).val() === '') {
433 isMissingValue = true;
437 if (! isMissingValue) {
438 Indexes.addColumnToIndex(
445 Functions.ajaxShowMessage(
446 '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' +
447 ' class="icon ic_s_error"> ' + Messages.strMissingColumn +
459 * Creates a advanced index type selection dialog.
461 * @param array source_array Array holding a particular type of indexes
462 * @param string index_choice Choice of index
463 * @param string col_index Index of new column on form
467 Indexes.indexTypeSelectionDialog = function (sourceArray, indexChoice, colIndex) {
468 var $singleColumnRadio = $('<input type="radio" id="single_column" name="index_choice"' +
469 ' checked="checked">' +
470 '<label for="single_column">' + Messages.strCreateSingleColumnIndex + '</label>');
471 var $compositeIndexRadio = $('<input type="radio" id="composite_index"' +
472 ' name="index_choice">' +
473 '<label for="composite_index">' + Messages.strCreateCompositeIndex + '</label>');
474 var $dialogContent = $('<fieldset id="advance_index_creator"></fieldset>');
475 $dialogContent.append('<legend>' + indexChoice.toUpperCase() + '</legend>');
478 // For UNIQUE/INDEX type, show choice for single-column and composite index.
479 $dialogContent.append($singleColumnRadio);
480 $dialogContent.append($compositeIndexRadio);
482 var buttonOptions = {};
484 buttonOptions[Messages.strGo] = function () {
485 if ($('#single_column').is(':checked')) {
487 'Key_name': (indexChoice === 'primary' ? 'PRIMARY' : ''),
488 'Index_choice': indexChoice.toUpperCase()
490 Indexes.showAddIndexDialog(sourceArray, (sourceArray.length), [colIndex], colIndex, index);
493 if ($('#composite_index').is(':checked')) {
494 if ($('input[name="composite_with"]').length !== 0 && $('input[name="composite_with"]:checked').length === 0
496 Functions.ajaxShowMessage(
497 '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title=""' +
498 ' alt="" class="icon ic_s_error"> ' +
499 Messages.strFormEmpty +
506 var arrayIndex = $('input[name="composite_with"]:checked').val();
507 var sourceLength = sourceArray[arrayIndex].columns.length;
508 var targetColumns = [];
509 for (var i = 0; i < sourceLength; i++) {
510 targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
512 targetColumns.push(colIndex);
514 Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex,
515 sourceArray[arrayIndex]);
520 buttonOptions[Messages.strCancel] = function () {
521 // Handle state on 'Cancel'.
522 var $selectList = $('select[name="field_key[' + colIndex + ']"]');
523 if (! $selectList.attr('data-index').length) {
524 $selectList.find('option[value*="none"]').attr('selected', 'selected');
526 var previousIndex = $selectList.attr('data-index').split(',');
527 $selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]')
528 .attr('selected', 'selected');
532 $('<div></div>').append($dialogContent).dialog({
536 title: Messages.strAddIndex,
538 buttons: buttonOptions,
540 $('#composite_index').on('change', function () {
541 if ($(this).is(':checked')) {
542 $dialogContent.append(Indexes.getCompositeIndexList(sourceArray, colIndex));
545 $('#single_column').on('change', function () {
546 if ($(this).is(':checked')) {
547 if ($('#composite_index_list').length) {
548 $('#composite_index_list').remove();
554 $('#composite_index').off('change');
555 $('#single_column').off('change');
562 * Unbind all event handlers before tearing down a page
564 AJAX.registerTeardown('indexes.js', function () {
565 $(document).off('click', '#save_index_frm');
566 $(document).off('click', '#preview_index_frm');
567 $(document).off('change', '#select_index_choice');
568 $(document).off('click', 'a.drop_primary_key_index_anchor.ajax');
569 $(document).off('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax');
570 $(document).off('click', '#index_frm input[type=submit]');
571 $('body').off('change', 'select[name*="field_key"]');
572 $(document).off('click', '.show_index_dialog');
576 * @description <p>Ajax scripts for table index page</p>
578 * Actions ajaxified here:
580 * <li>Showing/hiding inputs depending on the index type chosen</li>
581 * <li>create/edit/drop indexes</li>
584 AJAX.registerOnload('indexes.js', function () {
585 // Re-initialize variables.
589 fulltextIndexes = [];
592 // for table creation form
593 var $engineSelector = $('.create_table_form select[name=tbl_storage_engine]');
594 if ($engineSelector.length) {
595 Functions.hideShowConnection($engineSelector);
598 var $form = $('#index_frm');
599 if ($form.length > 0) {
600 Functions.showIndexEditDialog($form);
603 $(document).on('click', '#save_index_frm', function (event) {
604 event.preventDefault();
605 var $form = $('#index_frm');
606 var argsep = CommonParams.get('arg_separator');
607 var submitData = $form.serialize() + argsep + 'do_save_data=1' + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
608 Functions.ajaxShowMessage(Messages.strProcessingRequest);
610 $.post($form.attr('action'), submitData, AJAX.responseHandler);
613 $(document).on('click', '#preview_index_frm', function (event) {
614 event.preventDefault();
615 Functions.previewSql($('#index_frm'));
618 $(document).on('change', '#select_index_choice', function (event) {
619 event.preventDefault();
620 Indexes.checkIndexType();
621 Functions.checkIndexName('index_frm');
625 * Ajax Event handler for 'Drop Index'
627 $(document).on('click', 'a.drop_primary_key_index_anchor.ajax', function (event) {
628 event.preventDefault();
629 var $anchor = $(this);
631 * @var $currRow Object containing reference to the current field's row
633 var $currRow = $anchor.parents('tr');
634 /** @var {number} rows Number of columns in the key */
635 var rows = $anchor.parents('td').attr('rowspan') || 1;
636 /** @var {number} $rowsToHide Rows that should be hidden */
637 var $rowsToHide = $currRow;
638 for (var i = 1, $lastRow = $currRow.next(); i < rows; i++, $lastRow = $lastRow.next()) {
639 $rowsToHide = $rowsToHide.add($lastRow);
642 var question = Functions.escapeHtml(
643 $currRow.children('td')
644 .children('.drop_primary_key_index_msg')
648 Functions.confirmPreviewSql(question, $anchor.attr('href'), function (url) {
649 var $msg = Functions.ajaxShowMessage(Messages.strDroppingPrimaryKeyIndex, false);
650 var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
651 $.post(url, params, function (data) {
652 if (typeof data !== 'undefined' && data.success === true) {
653 Functions.ajaxRemoveMessage($msg);
654 var $tableRef = $rowsToHide.closest('table');
655 if ($rowsToHide.length === $tableRef.find('tbody > tr').length) {
656 // We are about to remove all rows from the table
657 $tableRef.hide('medium', function () {
658 $('div.no_indexes_defined').show('medium');
659 $rowsToHide.remove();
661 $tableRef.siblings('.alert-primary').hide('medium');
663 // We are removing some of the rows only
664 $rowsToHide.hide('medium', function () {
668 if ($('.result_query').length) {
669 $('.result_query').remove();
671 if (data.sql_query) {
672 $('<div class="result_query"></div>')
673 .html(data.sql_query)
674 .prependTo('#structure_content');
675 Functions.highlightSql($('#page_content'));
678 CommonActions.refreshMain('index.php?route=/table/structure');
680 Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
684 }); // end Drop Primary Key/Index
687 *Ajax event handler for index edit
689 $(document).on('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax', function (event) {
690 event.preventDefault();
693 if ($(this).find('a').length === 0) {
695 var valid = Functions.checkFormElementInRange(
696 $(this).closest('form')[0],
698 'Column count has to be larger than zero.'
703 url = $(this).closest('form').serialize();
704 title = Messages.strAddIndex;
707 url = $(this).find('a').getPostData();
708 title = Messages.strEditIndex;
710 url += CommonParams.get('arg_separator') + 'ajax_request=true';
711 Functions.indexEditorDialog(url, title, function () {
712 CommonActions.refreshMain('index.php?route=/table/structure');
717 * Ajax event handler for advanced index creation during table creation
718 * and column addition.
720 $('body').on('change', 'select[name*="field_key"]', function (e, showDialog) {
721 var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true;
722 // Index of column on Table edit and create page.
723 var colIndex = /\d+/.exec($(this).attr('name'));
724 colIndex = colIndex[0];
725 // Choice of selected index.
726 var indexChoice = /[a-z]+/.exec($(this).val());
727 indexChoice = indexChoice[0];
728 // Array containing corresponding indexes.
729 var sourceArray = null;
731 if (indexChoice === 'none') {
732 Indexes.removeColumnFromIndex(colIndex);
733 var id = 'index_name_' + '0' + '_8';
734 var $name = $('#' + id);
735 if ($name.length === 0) {
736 $name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
737 $name.insertAfter($('select[name="field_key[' + '0' + ']"]'));
743 // Select a source array.
744 sourceArray = Indexes.getIndexArray(indexChoice);
745 if (sourceArray === null) {
749 if (sourceArray.length === 0) {
751 'Key_name': (indexChoice === 'primary' ? 'PRIMARY' : ''),
752 'Index_choice': indexChoice.toUpperCase()
754 Indexes.showAddIndexDialog(sourceArray, 0, [colIndex], colIndex, index, showDialogLocal);
756 if (indexChoice === 'primary') {
758 var sourceLength = sourceArray[arrayIndex].columns.length;
759 var targetColumns = [];
760 for (var i = 0; i < sourceLength; i++) {
761 targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
763 targetColumns.push(colIndex);
764 Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex,
765 sourceArray[arrayIndex], showDialogLocal);
767 // If there are multiple columns selected for an index, show advanced dialog.
768 Indexes.indexTypeSelectionDialog(sourceArray, indexChoice, colIndex);
773 $(document).on('click', '.show_index_dialog', function (e) {
776 // Get index details.
777 var previousIndex = $(this).prev('select')
781 var indexChoice = previousIndex[0];
782 var arrayIndex = previousIndex[1];
784 var sourceArray = Indexes.getIndexArray(indexChoice);
785 if (sourceArray !== null) {
786 var sourceLength = sourceArray[arrayIndex].columns.length;
788 var targetColumns = [];
789 for (var i = 0; i < sourceLength; i++) {
790 targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
793 Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, -1, sourceArray[arrayIndex]);
797 $('#index_frm').on('submit', function () {
798 if (typeof(this.elements['index[Key_name]'].disabled) !== 'undefined') {
799 this.elements['index[Key_name]'].disabled = false;