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