Merge remote-tracking branch 'origin/master'
[phpmyadmin.git] / js / indexes.js
blobde08192dc2631ac225a0d27bf6ca3f2b199b6080
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * @fileoverview    function used for index manipulation pages
4  * @name            Table Structure
5  *
6  * @requires    jQuery
7  * @requires    jQueryUI
8  * @required    js/functions.js
9  */
11 /**
12  * Hides/shows the inputs and submits appropriately depending
13  * on whether the index type chosen is 'SPATIAL' or not.
14  */
15 function checkIndexType()
17     /**
18      * @var Object Dropdown to select the index choice.
19      */
20     var $select_index_choice = $('#select_index_choice');
21     /**
22      * @var Object Dropdown to select the index type.
23      */
24     var $select_index_type = $('#select_index_type');
25     /**
26      * @var Object Table header for the size column.
27      */
28     var $size_header = $('#index_columns thead tr th:nth-child(2)');
29     /**
30      * @var Object Inputs to specify the columns for the index.
31      */
32     var $column_inputs = $('select[name="index[columns][names][]"]');
33     /**
34      * @var Object Inputs to specify sizes for columns of the index.
35      */
36     var $size_inputs = $('input[name="index[columns][sub_parts][]"]');
37     /**
38      * @var Object Footer containg the controllers to add more columns
39      */
40     var $add_more = $('#index_frm .add_more');
42     if ($select_index_choice.val() == 'SPATIAL') {
43         // Disable and hide the size column
44         $size_header.hide();
45         $size_inputs.each(function () {
46             $(this)
47                 .prop('disabled', true)
48                 .parent('td').hide();
49         });
51         // Disable and hide the columns of the index other than the first one
52         var initial = true;
53         $column_inputs.each(function () {
54             $column_input = $(this);
55             if (! initial) {
56                 $column_input
57                     .prop('disabled', true)
58                     .parent('td').hide();
59             } else {
60                 initial = false;
61             }
62         });
64         // Hide controllers to add more columns
65         $add_more.hide();
66     } else {
67         // Enable and show the size column
68         $size_header.show();
69         $size_inputs.each(function () {
70             $(this)
71                 .prop('disabled', false)
72                 .parent('td').show();
73         });
75         // Enable and show the columns of the index
76         $column_inputs.each(function () {
77             $(this)
78                 .prop('disabled', false)
79                 .parent('td').show();
80         });
82         // Show controllers to add more columns
83         $add_more.show();
84     }
86     if ($select_index_choice.val() == 'SPATIAL' ||
87             $select_index_choice.val() == 'FULLTEXT') {
88         $select_index_type.val('').prop('disabled', true);
89     } else {
90         $select_index_type.prop('disabled', false)
91     }
94 /**
95  * Sets current index information into form parameters.
96  *
97  * @param array  source_array Array containing index columns
98  * @param string index_choice Choice of index
99  *
100  * @return void
101  */
102 function PMA_setIndexFormParameters(source_array, index_choice)
104     if (index_choice == 'index') {
105         $('input[name="indexes"]').val(JSON.stringify(source_array));
106     } else {
107         $('input[name="' + index_choice + '_indexes"]').val(JSON.stringify(source_array));
108     }
112  * Removes a column from an Index.
114  * @param string col_index Index of column in form
116  * @return void
117  */
118 function PMA_removeColumnFromIndex(col_index)
120     // Get previous index details.
121     var previous_index = $('select[name="field_key[' + col_index + ']"]')
122         .attr('data-index');
123     if (previous_index.length) {
124         previous_index = previous_index.split(',');
125         var source_array = PMA_getIndexArray(previous_index[0]);
126         if (source_array == null) {
127             return;
128         }
130         // Remove column from index array.
131         var source_length = source_array[previous_index[1]].columns.length;
132         for (var i=0; i<source_length; i++) {
133             if (source_array[previous_index[1]].columns[i].col_index == col_index) {
134                 source_array[previous_index[1]].columns.splice(i, 1);
135             }
136         }
138         // Remove index completely if no columns left.
139         if (source_array[previous_index[1]].columns.length === 0) {
140             source_array.splice(previous_index[1], 1);
141         }
143         // Update current index details.
144         $('select[name="field_key[' + col_index + ']"]').attr('data-index', '');
145         // Update form index parameters.
146         PMA_setIndexFormParameters(source_array, previous_index[0].toLowerCase());
147     }
151  * Adds a column to an Index.
153  * @param array  source_array Array holding corresponding indexes
154  * @param string array_index  Index of an INDEX in array
155  * @param string index_choice Choice of Index
156  * @param string col_index    Index of column on form
158  * @return void
159  */
160 function PMA_addColumnToIndex(source_array, array_index, index_choice, col_index)
162     if (col_index >= 0) {
163         // Remove column from other indexes (if any).
164         PMA_removeColumnFromIndex(col_index);
165     }
166     var index_name = $('input[name="index[Key_name]"]').val();
167     var index_comment = $('input[name="index[Index_comment]"]').val();
168     var key_block_size = $('input[name="index[Key_block_size]"]').val();
169     var parser = $('input[name="index[Parser]"]').val();
170     var index_type = $('select[name="index[Index_type]"]').val();
171     var columns = [];
172     $('#index_columns tbody').find('tr').each(function () {
173         // Get columns in particular order.
174         var col_index = $(this).find('select[name="index[columns][names][]"]').val();
175         var size = $(this).find('input[name="index[columns][sub_parts][]"]').val();
176         columns.push({
177             'col_index': col_index,
178             'size': size
179         });
180     });
182     // Update or create an index.
183     source_array[array_index] = {
184         'Key_name': index_name,
185         'Index_comment': index_comment,
186         'Index_choice': index_choice.toUpperCase(),
187         'Key_block_size': key_block_size,
188         'Parser': parser,
189         'Index_type': index_type,
190         'columns': columns
191     };
193     // Display index name (or column list)
194     var displayName = index_name;
195     if (displayName == '') {
196         var columnNames = [];
197         $.each(columns, function () {
198             columnNames.push($('input[name="field_name[' +  this.col_index + ']"]').val());
199         });
200         displayName = '[' + columnNames.join(', ') + ']';
201     }
202     $.each(columns, function () {
203         var id = 'index_name_' + this.col_index + '_8';
204         var $name = $('#' + id);
205         if ($name.length == 0) {
206             $name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
207             $name.insertAfter($('select[name="field_key[' + this.col_index + ']"]'));
208         }
209         var $text = $('<small>').text(displayName);
210         $name.html($text);
211     });
213     if (col_index >= 0) {
214         // Update index details on form.
215         $('select[name="field_key[' + col_index + ']"]')
216             .attr('data-index', index_choice + ',' + array_index);
217     }
218     PMA_setIndexFormParameters(source_array, index_choice.toLowerCase());
222  * Get choices list for a column to create a composite index with.
224  * @param string index_choice Choice of index
225  * @param array  source_array Array hodling columns for particular index
227  * @return jQuery Object
228  */
229 function PMA_getCompositeIndexList(source_array, col_index)
231     // Remove any previous list.
232     if ($('#composite_index_list').length) {
233         $('#composite_index_list').remove();
234     }
236     // Html list.
237     var $composite_index_list = $(
238         '<ul id="composite_index_list">' +
239         '<div>' + PMA_messages.strCompositeWith + '</div>' +
240         '</ul>'
241     );
243     // Add each column to list available for composite index.
244     var source_length = source_array.length;
245     var already_present = false;
246     for (var i=0; i<source_length; i++) {
247         var sub_array_len = source_array[i].columns.length;
248         var column_names = [];
249         for (var j=0; j<sub_array_len; j++) {
250             column_names.push(
251                 $('input[name="field_name[' + source_array[i].columns[j].col_index + ']"]').val()
252             );
254             if (col_index == source_array[i].columns[j].col_index) {
255                 already_present = true;
256             }
257         }
259         $composite_index_list.append(
260             '<li>' +
261             '<input type="radio" name="composite_with" ' +
262             (already_present ? 'checked="checked"' : '') +
263             ' id="composite_index_' + i + '" value="' + i + '">' +
264             '<label for="composite_index_' + i + '">' + column_names.join(', ') +
265             '</lablel>' +
266             '</li>'
267         );
268     }
270     return $composite_index_list;
274  * Shows 'Add Index' dialog.
276  * @param array  source_array   Array holding particluar index
277  * @param string array_index    Index of an INDEX in array
278  * @param array  target_columns Columns for an INDEX
279  * @param string col_index      Index of column on form
280  * @param object index          Index detail object
282  * @return void
283  */
284 function PMA_showAddIndexDialog(source_array, array_index, target_columns, col_index, index)
286     // Prepare post-data.
287     var $table = $('input[name="table"]');
288     var table = $table.length > 0 ? $table.val() : '';
289     var post_data = {
290         token: $('input[name="token"]').val(),
291         server:  $('input[name="server"]').val(),
292         db: $('input[name="db"]').val(),
293         table: table,
294         ajax_request: 1,
295         create_edit_table: 1,
296         index: index
297     };
299     var columns = {};
300     for (var i=0; i<target_columns.length; i++) {
301         var column_name = $('input[name="field_name[' + target_columns[i] + ']"]').val();
302         var column_type = $('select[name="field_type[' + target_columns[i] + ']"]').val().toLowerCase();
303         columns[column_name] = [column_type, target_columns[i]];
304     }
305     post_data.columns = JSON.stringify(columns);
307     var button_options = {};
308     button_options[PMA_messages.strGo] = function () {
309         var is_missing_value = false;
310         $('select[name="index[columns][names][]"]').each(function () {
311             if ($(this).val() === '') {
312                 is_missing_value = true;
313             }
314         });
316         if (! is_missing_value) {
317             PMA_addColumnToIndex(
318                 source_array,
319                 array_index,
320                 index.Index_choice,
321                 col_index
322             );
323         } else {
324             PMA_ajaxShowMessage(
325                 '<div class="error"><img src="themes/dot.gif" title="" alt=""' +
326                 ' class="icon ic_s_error" /> ' + PMA_messages.strMissingColumn +
327                 ' </div>', false
328             );
330             return false;
331         }
333         $(this).dialog('close');
334     };
335     button_options[PMA_messages.strCancel] = function () {
336         if (col_index >= 0) {
337             // Handle state on 'Cancel'.
338             var $select_list = $('select[name="field_key[' + col_index + ']"]');
339             if (! $select_list.attr('data-index').length) {
340                 $select_list.find('option[value*="none"]').attr('selected', 'selected');
341             } else {
342                 var previous_index = $select_list.attr('data-index').split(',');
343                 $select_list.find('option[value*="' + previous_index[0].toLowerCase() + '"]')
344                     .attr('selected', 'selected');
345             }
346         }
347         $(this).dialog('close');
348     };
349     var $msgbox = PMA_ajaxShowMessage();
350     $.post("tbl_indexes.php", post_data, function (data) {
351         if (data.success === false) {
352             //in the case of an error, show the error message returned.
353             PMA_ajaxShowMessage(data.error, false);
354         } else {
355             PMA_ajaxRemoveMessage($msgbox);
356             // Show dialog if the request was successful
357             var $div = $('<div/>');
358             $div
359             .append(data.message)
360             .dialog({
361                 title: PMA_messages.strAddIndex,
362                 width: 450,
363                 minHeight: 250,
364                 open: function () {
365                     checkIndexName("index_frm");
366                     PMA_showHints($div);
367                     PMA_init_slider();
368                     $('#index_columns td').each(function () {
369                         $(this).css("width", $(this).width() + 'px');
370                     });
371                     $('#index_columns tbody').sortable({
372                         axis: 'y',
373                         containment: $("#index_columns tbody"),
374                         tolerance: 'pointer'
375                     });
376                     // We dont need the slider at this moment.
377                     $(this).find('fieldset.tblFooters').remove();
378                 },
379                 modal: true,
380                 buttons: button_options,
381                 close: function () {
382                     $(this).remove();
383                 }
384             });
385         }
386     });
390  * Creates a advanced index type selection dialog.
392  * @param array  source_array Array holding a particular type of indexes
393  * @param string index_choice Choice of index
394  * @param string col_index    Index of new column on form
396  * @return void
397  */
398 function PMA_indexTypeSelectionDialog(source_array, index_choice, col_index)
400     var $single_column_radio = $('<input type="radio" id="single_column" name="index_choice"' +
401         ' checked="checked">' +
402         '<label for="single_column">' + PMA_messages.strCreateSingleColumnIndex + '</label>');
403     var $composite_index_radio = $('<input type="radio" id="composite_index"' +
404         ' name="index_choice">' +
405         '<label for="composite_index">' + PMA_messages.strCreateCompositeIndex + '</label>');
406     var $dialog_content = $('<fieldset id="advance_index_creator"></fieldset>');
407     $dialog_content.append('<legend>' + index_choice.toUpperCase() + '</legend>');
410     // For UNIQUE/INDEX type, show choice for single-column and composite index.
411     $dialog_content.append($single_column_radio);
412     $dialog_content.append($composite_index_radio);
414     var button_options = {};
415     // 'OK' operation.
416     button_options[PMA_messages.strGo] = function () {
417         if ($('#single_column').is(':checked')) {
418             var index = {
419                 'Key_name': (index_choice == 'primary' ? 'PRIMARY' : ''),
420                 'Index_choice': index_choice.toUpperCase()
421             };
422             PMA_showAddIndexDialog(source_array, (source_array.length), [col_index], col_index, index);
423         }
425         if ($('#composite_index').is(':checked')) {
426             if ($('input[name="composite_with"]').length !== 0 && $('input[name="composite_with"]:checked').length === 0
427             ) {
428                 PMA_ajaxShowMessage(
429                     '<div class="error"><img src="themes/dot.gif" title=""' +
430                     ' alt="" class="icon ic_s_error" /> ' +
431                     PMA_messages.strFormEmpty +
432                     ' </div>',
433                     false
434                 );
435                 return false;
436             }
438             var array_index = $('input[name="composite_with"]:checked').val();
439             var source_length = source_array[array_index].columns.length;
440             var target_columns = [];
441             for (var i=0; i<source_length; i++) {
442                 target_columns.push(source_array[array_index].columns[i].col_index);
443             }
444             target_columns.push(col_index);
446             PMA_showAddIndexDialog(source_array, array_index, target_columns, col_index,
447              source_array[array_index]);
448         }
450         $(this).remove();
451     };
452     button_options[PMA_messages.strCancel] = function () {
453         // Handle state on 'Cancel'.
454         var $select_list = $('select[name="field_key[' + col_index + ']"]');
455         if (! $select_list.attr('data-index').length) {
456             $select_list.find('option[value*="none"]').attr('selected', 'selected');
457         } else {
458             var previous_index = $select_list.attr('data-index').split(',');
459             $select_list.find('option[value*="' + previous_index[0].toLowerCase() + '"]')
460                 .attr('selected', 'selected');
461         }
462         $(this).remove();
463     };
464     var $dialog = $('<div/>').append($dialog_content).dialog({
465         minWidth: 525,
466         minHeight: 200,
467         modal: true,
468         title: PMA_messages.strAddIndex,
469         resizable: false,
470         buttons: button_options,
471         open: function () {
472             $('#composite_index').on('change', function () {
473                 if ($(this).is(':checked')) {
474                     $dialog_content.append(PMA_getCompositeIndexList(source_array, col_index));
475                 }
476             });
477             $('#single_column').on('change', function () {
478                 if ($(this).is(':checked')) {
479                     if ($('#composite_index_list').length) {
480                         $('#composite_index_list').remove();
481                     }
482                 }
483             });
484         },
485         close: function () {
486             $('#composite_index').off('change');
487             $('#single_column').off('change');
488             $(this).remove();
489         }
490     });
494  * Returns the array of indexes based on the index choice
496  * @param index_choice index choice
497  */
498 function PMA_getIndexArray(index_choice)
500     var source_array = null;
502     switch (index_choice.toLowerCase()) {
503     case 'primary':
504         source_array = primary_indexes;
505         break;
506     case 'unique':
507         source_array = unique_indexes;
508         break;
509     case 'index':
510         source_array = indexes;
511         break;
512     case 'fulltext':
513         source_array = fulltext_indexes;
514         break;
515     case 'spatial':
516         source_array = spatial_indexes;
517         break;
518     default:
519         return null;
520     }
521     return source_array;
526  * Unbind all event handlers before tearing down a page
527  */
528 AJAX.registerTeardown('indexes.js', function () {
529     $(document).off('click', '#save_index_frm');
530     $(document).off('click', '#preview_index_frm');
531     $(document).off('change', '#select_index_choice');
532     $(document).off('click', 'a.drop_primary_key_index_anchor.ajax');
533     $(document).off('click', "#table_index tbody tr td.edit_index.ajax, #indexes .add_index.ajax");
534     $(document).off('click', '#index_frm input[type=submit]');
535     $('body').off('change', 'select[name*="field_key"]');
536     $(document).off('click', '.show_index_dialog');
540  * @description <p>Ajax scripts for table index page</p>
542  * Actions ajaxified here:
543  * <ul>
544  * <li>Showing/hiding inputs depending on the index type chosen</li>
545  * <li>create/edit/drop indexes</li>
546  * </ul>
547  */
548 AJAX.registerOnload('indexes.js', function () {
549     // Re-initialize variables.
550     primary_indexes = [];
551     unique_indexes = [];
552     indexes = [];
553     fulltext_indexes = [];
554     spatial_indexes = [];
556     // for table creation form
557     var $engine_selector = $('.create_table_form select[name=tbl_storage_engine]');
558     if ($engine_selector.length) {
559         PMA_hideShowConnection($engine_selector);
560     }
562     var $form = $("#index_frm");
563     if ($form.length > 0) {
564         showIndexEditDialog($form);
565     }
567     $(document).on('click', '#save_index_frm', function (event) {
568         event.preventDefault();
569         var $form = $("#index_frm");
570         var submitData = $form.serialize() + '&do_save_data=1&ajax_request=true&ajax_page_request=true';
571         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
572         AJAX.source = $form;
573         $.post($form.attr('action'), submitData, AJAX.responseHandler);
574     });
576     $(document).on('click', '#preview_index_frm', function (event) {
577         event.preventDefault();
578         PMA_previewSQL($('#index_frm'));
579     });
581     $(document).on('change', '#select_index_choice', function (event) {
582         event.preventDefault();
583         checkIndexType();
584         checkIndexName("index_frm");
585     });
587     /**
588      * Ajax Event handler for 'Drop Index'
589      */
590     $(document).on('click', 'a.drop_primary_key_index_anchor.ajax', function (event) {
591         event.preventDefault();
592         var $anchor = $(this);
593         /**
594          * @var $curr_row    Object containing reference to the current field's row
595          */
596         var $curr_row = $anchor.parents('tr');
597         /** @var    Number of columns in the key */
598         var rows = $anchor.parents('td').attr('rowspan') || 1;
599         /** @var    Rows that should be hidden */
600         var $rows_to_hide = $curr_row;
601         for (var i = 1, $last_row = $curr_row.next(); i < rows; i++, $last_row = $last_row.next()) {
602             $rows_to_hide = $rows_to_hide.add($last_row);
603         }
605         var question = escapeHtml(
606             $curr_row.children('td')
607                 .children('.drop_primary_key_index_msg')
608                 .val()
609         );
611         $anchor.PMA_confirm(question, $anchor.attr('href'), function (url) {
612             var $msg = PMA_ajaxShowMessage(PMA_messages.strDroppingPrimaryKeyIndex, false);
613             $.get(url, {'is_js_confirmed': 1, 'ajax_request': true}, function (data) {
614                 if (typeof data !== 'undefined' && data.success === true) {
615                     PMA_ajaxRemoveMessage($msg);
616                     var $table_ref = $rows_to_hide.closest('table');
617                     if ($rows_to_hide.length == $table_ref.find('tbody > tr').length) {
618                         // We are about to remove all rows from the table
619                         $table_ref.hide('medium', function () {
620                             $('div.no_indexes_defined').show('medium');
621                             $rows_to_hide.remove();
622                         });
623                         $table_ref.siblings('div.notice').hide('medium');
624                     } else {
625                         // We are removing some of the rows only
626                         toggleRowColors($rows_to_hide.last().next());
627                         $rows_to_hide.hide("medium", function () {
628                             $(this).remove();
629                         });
630                     }
631                     if ($('.result_query').length) {
632                         $('.result_query').remove();
633                     }
634                     if (data.sql_query) {
635                         $('<div class="result_query"></div>')
636                             .html(data.sql_query)
637                             .prependTo('#structure_content');
638                         PMA_highlightSQL($('#page_content'));
639                     }
640                     PMA_commonActions.refreshMain(false, function () {
641                         $("a.ajax[href^=#indexes]").click();
642                     });
643                     PMA_reloadNavigation();
644                 } else {
645                     PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest + " : " + data.error, false);
646                 }
647             }); // end $.get()
648         }); // end $.PMA_confirm()
649     }); //end Drop Primary Key/Index
651     /**
652      *Ajax event handler for index edit
653     **/
654     $(document).on('click', "#table_index tbody tr td.edit_index.ajax, #indexes .add_index.ajax", function (event) {
655         event.preventDefault();
656         var url, title;
657         if ($(this).find("a").length === 0) {
658             // Add index
659             var valid = checkFormElementInRange(
660                 $(this).closest('form')[0],
661                 'added_fields',
662                 'Column count has to be larger than zero.'
663             );
664             if (! valid) {
665                 return;
666             }
667             url = $(this).closest('form').serialize();
668             title = PMA_messages.strAddIndex;
669         } else {
670             // Edit index
671             url = $(this).find("a").attr("href");
672             if (url.substring(0, 16) == "tbl_indexes.php?") {
673                 url = url.substring(16, url.length);
674             }
675             title = PMA_messages.strEditIndex;
676         }
677         url += "&ajax_request=true";
678         indexEditorDialog(url, title, function () {
679             // refresh the page using ajax
680             PMA_commonActions.refreshMain(false, function () {
681                 $("a.ajax[href^=#indexes]").click();
682             });
683         });
684     });
686     /**
687      * Ajax event handler for advanced index creation during table creation
688      * and column addition.
689      */
690     $('body').on('change', 'select[name*="field_key"]', function () {
691         // Index of column on Table edit and create page.
692         var col_index = /\d+/.exec($(this).attr('name'));
693         col_index = col_index[0];
694         // Choice of selected index.
695         var index_choice = /[a-z]+/.exec($(this).val());
696         index_choice = index_choice[0];
697         // Array containing corresponding indexes.
698         var source_array = null;
700         if (index_choice == 'none') {
701             PMA_removeColumnFromIndex(col_index);
702             return false;
703         }
705         // Select a source array.
706         var source_array = PMA_getIndexArray(index_choice);
707         if (source_array == null) {
708             return;
709         }
711         if (source_array.length === 0) {
712             var index = {
713                 'Key_name': (index_choice == 'primary' ? 'PRIMARY' : ''),
714                 'Index_choice': index_choice.toUpperCase()
715             };
716             PMA_showAddIndexDialog(source_array, 0, [col_index], col_index, index);
717         } else {
718             if (index_choice == 'primary') {
719                 var array_index = 0;
720                 var source_length = source_array[array_index].columns.length;
721                 var target_columns = [];
722                 for (var i=0; i<source_length; i++) {
723                     target_columns.push(source_array[array_index].columns[i].col_index);
724                 }
725                 target_columns.push(col_index);
727                 PMA_showAddIndexDialog(source_array, array_index, target_columns, col_index,
728                  source_array[array_index]);
729             } else {
730                 // If there are multiple columns selected for an index, show advanced dialog.
731                 PMA_indexTypeSelectionDialog(source_array, index_choice, col_index);
732             }
733         }
734     });
736     $(document).on('click', '.show_index_dialog', function (e) {
737         e.preventDefault();
739         // Get index details.
740         var previous_index = $(this).prev('select')
741             .attr('data-index')
742             .split(',');
744         var index_choice = previous_index[0];
745         var array_index  = previous_index[1];
747         var source_array = PMA_getIndexArray(index_choice);
748         var source_length = source_array[array_index].columns.length;
750         var target_columns = [];
751         for (var i = 0; i < source_length; i++) {
752             target_columns.push(source_array[array_index].columns[i].col_index);
753         }
755         PMA_showAddIndexDialog(source_array, array_index, target_columns, -1, source_array[array_index]);
756     })