Merge remote-tracking branch 'origin/master' into drizzle
[phpmyadmin.git] / js / sql.js
blobfb24f5da667b6b19b88d256f73f83685a4093dae
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * @fileoverview    functions used wherever an sql query form is used
4  *
5  * @requires    jQuery
6  * @requires    js/functions.js
7  *
8  */
10 var $data_a;
12 /**
13  * decode a string URL_encoded
14  *
15  * @param string str
16  * @return string the URL-decoded string
17  */
18 function PMA_urldecode(str) {
19     return decodeURIComponent(str.replace(/\+/g, '%20'));
22 function PMA_urlencode(str) {
23     return encodeURIComponent(str.replace(/\%20/g, '+'));
26 /**
27  * Get the field name for the current field.  Required to construct the query
28  * for inline editing
29  *
30  * @param   $this_field  jQuery object that points to the current field's tr
31  */
32 function getFieldName($this_field) {
34     var this_field_index = $this_field.index();
35     // ltr or rtl direction does not impact how the DOM was generated
36     // check if the action column in the left exist
37     var leftActionExist = !$('#table_results').find('th:first').hasClass('draggable');
38     // 5 columns to account for the checkbox, edit, appended inline edit, copy and delete anchors but index is zero-based so substract 4
39     var field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index - (leftActionExist ? 4 : 0)) + ') a').text();
40     // happens when just one row (headings contain no a)
41     if ("" == field_name) {
42         field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index - (leftActionExist ? 4 : 0)) + ')').text();
43     }
45     field_name = $.trim(field_name);
47     return field_name;
50 /**
51  * The function that iterates over each row in the table_results and appends a
52  * new inline edit anchor to each table row.
53  *
54  */
55 function appendInlineAnchor() {
56     // TODO: remove two lines below if vertical display mode has been completely removed
57     var disp_mode = $("#top_direction_dropdown").val();
58     if (disp_mode != 'vertical') {
59         $('.edit_row_anchor').each(function() {
61             var $this_td = $(this);
62             $this_td.removeClass('edit_row_anchor');
64             var $cloned_anchor = $this_td.clone();
66             var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']);
67             if ($img_object.length != 0) {
68                 var img_class = $img_object.attr('class').replace(/b_edit/,'b_inline_edit');
69                 $img_object.attr('class', img_class);
70                 $cloned_anchor.find('a').attr('href', '#');
71                 var $edit_span = $cloned_anchor.find('span:contains("' + PMA_messages['strEdit'] + '")');
72                 var $span = $cloned_anchor.find('a').find('span');
73                 if ($edit_span.length > 0) {
74                     $span.text(' ' + PMA_messages['strInlineEdit']);
75                     $span.prepend($img_object);
76                 } else {
77                     $span.text('');
78                     $span.append($img_object);
79                 }
80             } else {
81                 // Only text is displayed. See $cfg['PropertiesIconic']
82                 $cloned_anchor.find('a').attr('href', '#');
83                 $cloned_anchor.find('a span').text(PMA_messages['strInlineEdit']);
85                 // the link was too big so <input type="image"> is there
86                 $img_object = $cloned_anchor.find('input:image').attr('title', PMA_messages['strInlineEdit']);
87                 if ($img_object.length > 0) {
88                     var img_class = $img_object.attr('class').replace(/b_edit/,'b_inline_edit');
89                     $img_object.attr('class', img_class);
90                 }
91                 $cloned_anchor
92                  .find('.clickprevimage')
93                  .text(' ' + PMA_messages['strInlineEdit']);
94             }
96             $cloned_anchor
97              .addClass('inline_edit_anchor');
99             $this_td.after($cloned_anchor);
100         });
102         $('#resultsForm').find('thead, tbody').find('th').each(function() {
103             var $this_th = $(this);
104             if ($this_th.attr('colspan') == 4) {
105                 $this_th.attr('colspan', '5');
106             }
107         });
108     }
111 /**#@+
112  * @namespace   jQuery
113  */
116  * @description <p>Ajax scripts for sql and browse pages</p>
118  * Actions ajaxified here:
119  * <ul>
120  * <li>Retrieve results of an SQL query</li>
121  * <li>Paginate the results table</li>
122  * <li>Sort the results table</li>
123  * <li>Change table according to display options</li>
124  * <li>Inline editing of data</li>
125  * </ul>
127  * @name        document.ready
128  * @memberOf    jQuery
129  */
130 $(document).ready(function() {
132     /**
133      * Set a parameter for all Ajax queries made on this page.  Don't let the
134      * web server serve cached pages
135      */
136     $.ajaxSetup({
137         cache: 'false'
138     });
140     /* Hides the bookmarkoptions checkboxes when the bookmark label is empty */
141     $('input#bkm_label').keyup(function() {
142         $('input#id_bkm_all_users, input#id_bkm_replace')
143             .parent()
144             .toggle($(this).attr('value').length > 0);
145     }).trigger('keyup');
147     /**
148      * Attach the {@link appendInlineAnchor} function to a custom event, which
149      * will be triggered manually everytime the table of results is reloaded
150      * @memberOf    jQuery
151      */
152     $("#sqlqueryresults").live('appendAnchor',function() {
153         appendInlineAnchor();
154     })
156     /**
157      * Attach the {@link makegrid} function to a custom event, which will be
158      * triggered manually everytime the table of results is reloaded
159      * @memberOf    jQuery
160      */
161     $("#sqlqueryresults").live('makegrid', function() {
162         $('#table_results').makegrid();
163     })
165     /**
166      * Attach the {@link refreshgrid} function to a custom event, which will be
167      * triggered manually everytime the table of results is manipulated (e.g., by inline edit)
168      * @memberOf    jQuery
169      */
170     $("#sqlqueryresults").live('refreshgrid', function() {
171         $('#table_results').refreshgrid();
172     })
174     /**
175      * Trigger the appendAnchor event to prepare the first table for inline edit
176      * (see $GLOBALS['cfg']['AjaxEnable'])
177      * @memberOf    jQuery
178      * @name        sqlqueryresults_trigger
179      */
180     $("#sqlqueryresults.ajax").trigger('appendAnchor');
182     /**
183      * Append the "Show/Hide query box" message to the query input form
184      *
185      * @memberOf jQuery
186      * @name    appendToggleSpan
187      */
188     // do not add this link more than once
189     if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
190         $('<a id="togglequerybox"></a>')
191         .html(PMA_messages['strHideQueryBox'])
192         .appendTo("#sqlqueryform")
193         // initially hidden because at this point, nothing else
194         // appears under the link
195         .hide();
197         // Attach the toggling of the query box visibility to a click
198         $("#togglequerybox").bind('click', function() {
199             var $link = $(this)
200             $link.siblings().slideToggle("fast");
201             if ($link.text() == PMA_messages['strHideQueryBox']) {
202                 $link.text(PMA_messages['strShowQueryBox']);
203                 // cheap trick to add a spacer between the menu tabs
204                 // and "Show query box"; feel free to improve!
205                 $('#togglequerybox_spacer').remove();
206                 $link.before('<br id="togglequerybox_spacer" />');
207             } else {
208                 $link.text(PMA_messages['strHideQueryBox']);
209             }
210             // avoid default click action
211             return false;
212         })
213     }
215     /**
216      * Ajax Event handler for 'SQL Query Submit'
217      *
218      * @see         PMA_ajaxShowMessage()
219      * @see         $cfg['AjaxEnable']
220      * @memberOf    jQuery
221      * @name        sqlqueryform_submit
222      */
223     $("#sqlqueryform.ajax").live('submit', function(event) {
224         event.preventDefault();
226         var $form = $(this);
227         if (! checkSqlQuery($form[0])) {
228             return false;
229         }
231         // remove any div containing a previous error message
232         $('.error').remove();
234         var $msgbox = PMA_ajaxShowMessage();
235         var $sqlqueryresults = $('#sqlqueryresults');
237         PMA_prepareForAjaxRequest($form);
239         $.post($form.attr('action'), $form.serialize() , function(data) {
240             if (data.success == true) {
241                 // fade out previous messages, if any
242                 $('.success').fadeOut();
243                 $('.sqlquery_message').fadeOut();
244                 // show a message that stays on screen
245                 if (typeof data.sql_query != 'undefined') {
246                     $('<div class="sqlquery_message"></div>')
247                      .html(data.sql_query)
248                      .insertBefore('#sqlqueryform');
249                     // unnecessary div that came from data.sql_query
250                     $('.notice').remove();
251                 } else {
252                     $('#sqlqueryform').before(data.message);
253                 }
254                 $sqlqueryresults.show();
255                 // this happens if a USE command was typed
256                 if (typeof data.reload != 'undefined') {
257                     // Unbind the submit event before reloading. See bug #3295529
258                     $("#sqlqueryform.ajax").die('submit');
259                     $form.find('input[name=db]').val(data.db);
260                     // need to regenerate the whole upper part
261                     $form.find('input[name=ajax_request]').remove();
262                     $form.append('<input type="hidden" name="reload" value="true" />');
263                     $.post('db_sql.php', $form.serialize(), function(data) {
264                         $('body').html(data);
265                     }); // end inner post
266                 }
267             }
268             else if (data.success == false ) {
269                 // show an error message that stays on screen
270                 $('#sqlqueryform').before(data.error);
271                 $sqlqueryresults.hide();
272             }
273             else {
274                 // real results are returned
275                 // fade out previous messages, if any
276                 $('.success').fadeOut();
277                 $('.sqlquery_message').fadeOut();
278                 var $received_data = $(data);
279                 var $zero_row_results = $received_data.find('textarea[name="sql_query"]');
280                 // if zero rows are returned from the query execution
281                 if ($zero_row_results.length > 0) {
282                     $('#sqlquery').val($zero_row_results.val());
283                 } else {
284                     $sqlqueryresults
285                      .show()
286                      .html(data)
287                      .trigger('appendAnchor')
288                      .trigger('makegrid');
289                     $('#togglequerybox').show();
290                     if ($("#togglequerybox").siblings(":visible").length > 0) {
291                         $("#togglequerybox").trigger('click');
292                     }
293                     PMA_init_slider();
294                 }
295             }
296             PMA_ajaxRemoveMessage($msgbox);
298         }) // end $.post()
299     }) // end SQL Query submit
301     /**
302      * Ajax Event handlers for Paginating the results table
303      */
305     /**
306      * Paginate when we click any of the navigation buttons
307      * (only if the element has the ajax class, see $cfg['AjaxEnable'])
308      * @memberOf    jQuery
309      * @name        paginate_nav_button_click
310      * @uses        PMA_ajaxShowMessage()
311      * @see         $cfg['AjaxEnable']
312      */
313     $("input[name=navig].ajax").live('click', function(event) {
314         /** @lends jQuery */
315         event.preventDefault();
317         var $msgbox = PMA_ajaxShowMessage();
319         /**
320          * @var $form    Object referring to the form element that paginates the results table
321          */
322         var $form = $(this).parent("form");
324         PMA_prepareForAjaxRequest($form);
326         $.post($form.attr('action'), $form.serialize(), function(data) {
327             $("#sqlqueryresults")
328              .html(data)
329              .trigger('appendAnchor')
330              .trigger('makegrid');
331             PMA_init_slider();
333             PMA_ajaxRemoveMessage($msgbox);
334         }) // end $.post()
335     })// end Paginate results table
337     /**
338      * Paginate results with Page Selector dropdown
339      * @memberOf    jQuery
340      * @name        paginate_dropdown_change
341      * @see         $cfg['AjaxEnable']
342      */
343     $("#pageselector").live('change', function(event) {
344         var $form = $(this).parent("form");
346         if ($(this).hasClass('ajax')) {
347             event.preventDefault();
349             var $msgbox = PMA_ajaxShowMessage();
351             $.post($form.attr('action'), $form.serialize() + '&ajax_request=true', function(data) {
352                 $("#sqlqueryresults")
353                  .html(data)
354                  .trigger('appendAnchor')
355                  .trigger('makegrid');
356                 PMA_init_slider();
357                 PMA_ajaxRemoveMessage($msgbox);
358             }) // end $.post()
359         } else {
360             $form.submit();
361         }
363     })// end Paginate results with Page Selector
365     /**
366      * Ajax Event handler for sorting the results table
367      * @memberOf    jQuery
368      * @name        table_results_sort_click
369      * @see         $cfg['AjaxEnable']
370      */
371     $("#table_results.ajax").find("a[title=Sort]").live('click', function(event) {
372         event.preventDefault();
374         var $msgbox = PMA_ajaxShowMessage();
376         $anchor = $(this);
378         $.get($anchor.attr('href'), $anchor.serialize() + '&ajax_request=true', function(data) {
379             $("#sqlqueryresults")
380              .html(data)
381              .trigger('appendAnchor')
382              .trigger('makegrid');
383             PMA_ajaxRemoveMessage($msgbox);
384         }) // end $.get()
385     })//end Sort results table
387     /**
388      * Ajax Event handler for the display options
389      * @memberOf    jQuery
390      * @name        displayOptionsForm_submit
391      * @see         $cfg['AjaxEnable']
392      */
393     $("#displayOptionsForm.ajax").live('submit', function(event) {
394         event.preventDefault();
396         $form = $(this);
398         $.post($form.attr('action'), $form.serialize() + '&ajax_request=true' , function(data) {
399             $("#sqlqueryresults")
400              .html(data)
401              .trigger('appendAnchor')
402              .trigger('makegrid');
403             PMA_init_slider();
404         }) // end $.post()
405     })
406     //end displayOptionsForm handler
408     /**
409      * Ajax Event handlers for Inline Editing
410      */
412     /**
413      * On click, replace the fields of current row with an input/textarea
414      * @memberOf    jQuery
415      * @name        inline_edit_start
416      * @see         PMA_ajaxShowMessage()
417      * @see         getFieldName()
418      */
419     $(".inline_edit_anchor span a").live('click', function(event) {
420         /** @lends jQuery */
421         event.preventDefault();
423         var $edit_td = $(this).parents('td');
424         $edit_td.removeClass('inline_edit_anchor').addClass('inline_edit_active').parent('tr').addClass('noclick');
426         // Adding submit and hide buttons to inline edit <td>.
427         // For "hide" button the original data to be restored is
428         //  kept in the jQuery data element 'original_data' inside the <td>.
429         // Looping through all columns or rows, to find the required data and then storing it in an array.
431         var $this_children = $edit_td.children('span.nowrap').children('a').children('span.nowrap');
432         // Keep the original data preserved.
433         $data_a = $edit_td.children('span.nowrap').children('a').clone();
435         // Change the inline edit to save.
436         var $img_object = $this_children.find('img');
438         // If texts are displayed. See $cfg['PropertiesIconic']
439         if ($this_children.parent('a').find('span:contains("' + PMA_messages['strInlineEdit'] + '")').length > 0) {
440             $this_children.text(' ' + PMA_messages['strSave']);
441         } else {
442             $this_children.empty();
443         }
445         // If icons are displayed. See $cfg['PropertiesIconic']
446         if ($img_object.length > 0) {
447             $img_object.attr('title', PMA_messages['strSave']);
448             var img_class = $img_object.attr('class').replace(/b_inline_edit/,'b_save');
449             $img_object.attr('class', img_class);
450             $this_children.prepend($img_object);
451         }
453         // Clone the save link and change it to create the hide link.
454         var $hide_a = $edit_td.children('span.nowrap').children('a').clone().attr('id', 'hide');
455         var $hide_span = $hide_a.find('span');
456         var $img_object = $hide_a.find('span img');
458         // If texts are displayed. See $cfg['PropertiesIconic']
459         if ($hide_a.find('span:contains("' + PMA_messages['strSave'] + '")').length > 0) {
460             $hide_span.text(' ' + PMA_messages['strHide']);
461         } else {
462             $hide_span.empty();
463         }
465         // If icons are displayed. See $cfg['PropertiesIconic']
466         if ($img_object.length > 0) {
467             $img_object.attr('title', PMA_messages['strHide']);
468             var img_class = $img_object.attr('class').replace(/b_save/,'b_close');
469             $img_object.attr('class', img_class);
470             $hide_span.prepend($img_object);
471         }
473         // Add hide icon and/or text.
474         $edit_td.children('span.nowrap').append($('<br /><br />')).append($hide_a);
476         $('#table_results tbody tr td span a#hide').click(function() {
477             var $this_hide = $(this).parents('td');
479             var $this_span = $this_hide.find('span');
480             $this_span.find('a, br').remove();
481             $this_span.append($data_a.clone());
483             $this_hide.removeClass("inline_edit_active hover").addClass("inline_edit_anchor");
484             $this_hide.parent().removeClass("hover noclick");
485             $this_hide.siblings().removeClass("hover");
487             var $input_siblings = $this_hide.parent('tr').find('.inline_edit');
488             var txt = '';
489             $input_siblings.each(function() {
490                 var $this_hide_siblings = $(this);
491                 txt = $this_hide_siblings.data('original_data');
492                 if($this_hide_siblings.children('span').children().length != 0) {
493                     $this_hide_siblings.children('span').empty();
494                     $this_hide_siblings.children('span').append(txt);
495                 }
496             });
497             $(this).prev().prev().remove();
498             $(this).prev().remove();
499             $(this).remove();
501             // refresh the grid
502             $("#sqlqueryresults").trigger('refreshgrid');
503         });
505         // Initialize some variables
506         var this_row_index = $edit_td.parent().index();
507         var $input_siblings = $edit_td.parent('tr').find('.inline_edit');
508         var where_clause = $edit_td.parent('tr').find('.where_clause').val();
510         $input_siblings.each(function() {
511             /** @lends jQuery */
512             /**
513              * @var data_value  Current value of this field
514              */
515             var data_value = $(this).children('span').html();
517             // We need to retrieve the value from the server for truncated/relation fields
518             // Find the field name
520             /**
521              * @var this_field  Object referring to this field (<td>)
522              */
523             var $this_field = $(this);
524             /**
525              * @var this_field_span Object referring to this field's child (<span>)
526              */
527             var $this_field_span = $(this).children('span');
528             /**
529              * @var field_name  String containing the name of this field.
530              * @see getFieldName()
531              */
532             var field_name = getFieldName($this_field);
533             /**
534              * @var relation_curr_value String current value of the field (for fields that are foreign keyed).
535              */
536             var relation_curr_value = $this_field.find('a').text();
537             /**
538              * @var relation_key_or_display_column String relational key if in 'Relational display column' mode,
539              * relational display column if in 'Relational key' mode (for fields that are foreign keyed).
540              */
541             var relation_key_or_display_column = $this_field.find('a').attr('title');
542             /**
543              * @var curr_value String current value of the field (for fields that are of type enum or set).
544              */
545             var curr_value = $this_field_span.text();
547             if($this_field.is(':not(.not_null)')){
548                 // add a checkbox to mark null for all the field that are nullable.
549                 $this_field_span.html('<div class="null_div">Null :<input type="checkbox" class="checkbox_null_'+ field_name + '_' + this_row_index +'"></div>');
550                 // check the 'checkbox_null_<field_name>_<row_index>' if the corresponding value is null
551                 if($this_field.is('.null')) {
552                     $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', true);
553                 }
555                 // if the select/editor is changed un-check the 'checkbox_null_<field_name>_<row_index>'.
556                 if ($this_field.is('.enum, .set')) {
557                     $this_field.find('select').live('change', function(e) {
558                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
559                     })
560                 } else if ($this_field.is('.relation')) {
561                     $this_field.find('select').live('change', function(e) {
562                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
563                     })
564                     $this_field.find('.browse_foreign').live('click', function(e) {
565                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
566                     })
567                 } else {
568                     $this_field.find('textarea').live('keypress', function(e) {
569                         // FF errorneously triggers for modifier keys such as tab (bug #3357837)
570                         if (e.which != 0) {
571                             $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
572                         }
573                     })
574                 }
576                 // if 'checkbox_null_<field_name>_<row_index>' is clicked empty the corresponding select/editor.
577                 $('.checkbox_null_' + field_name + '_' + this_row_index).bind('click', function(e) {
578                     if ($this_field.is('.enum')) {
579                         $this_field.find('select').attr('value', '');
580                     } else if ($this_field.is('.set')) {
581                         $this_field.find('select').find('option').each(function() {
582                             var $option = $(this);
583                             $option.attr('selected', false);
584                         })
585                     } else if ($this_field.is('.relation')) {
586                         // if the dropdown is there to select the foreign value
587                         if ($this_field.find('select').length > 0) {
588                             $this_field.find('select').attr('value', '');
589                         // if foriegn value is selected by browsing foreing values
590                         } else {
591                             $this_field.find('span.curr_value').empty();
592                         }
593                     } else {
594                         $this_field.find('textarea').val('');
595                     }
596                 })
598             } else {
599                 $this_field_span.html('<div class="null_div"></div>');
600             }
602             // In each input sibling, wrap the current value in a textarea
603             // and store the current value in a hidden span
604             if($this_field.is(':not(.truncated, .transformed, .relation, .enum, .set, .null)')) {
605                 // handle non-truncated, non-transformed, non-relation values
607                 value = data_value.replace("<br>", "\n");
608                 // We don't need to get any more data, just wrap the value
609                 $this_field_span.append('<textarea>' + value + '</textarea>');
610                 $this_field.data('original_data', data_value);
611             }
612             else if($this_field.is('.truncated, .transformed')) {
613                 /** @lends jQuery */
614                 //handle truncated/transformed values values
616                 /**
617                  * @var sql_query   String containing the SQL query used to retrieve value of truncated/transformed data
618                  */
619                 var sql_query = 'SELECT `' + field_name + '` FROM `' + window.parent.table + '` WHERE ' + PMA_urldecode(where_clause);
621                 // Make the Ajax call and get the data, wrap it and insert it
622                 $.post('sql.php', {
623                     'token' : window.parent.token,
624                     'db' : window.parent.db,
625                     'ajax_request' : true,
626                     'sql_query' : sql_query,
627                     'inline_edit' : true
628                 }, function(data) {
629                     if(data.success == true) {
630                         $this_field_span.append('<textarea>'+data.value+'</textarea>');
631                         $this_field.data('original_data', data_value);
632                         $("#sqlqueryresults").trigger('refreshgrid');
633                     }
634                     else {
635                         PMA_ajaxShowMessage(data.error);
636                     }
637                 }) // end $.post()
638             }
639             else if($this_field.is('.relation')) {
640                 /** @lends jQuery */
641                 //handle relations
643                 /**
644                  * @var post_params Object containing parameters for the POST request
645                  */
646                 var post_params = {
647                         'ajax_request' : true,
648                         'get_relational_values' : true,
649                         'db' : window.parent.db,
650                         'table' : window.parent.table,
651                         'column' : field_name,
652                         'token' : window.parent.token,
653                         'curr_value' : relation_curr_value,
654                         'relation_key_or_display_column' : relation_key_or_display_column
655                 }
657                 $.post('sql.php', post_params, function(data) {
658                     $this_field_span.append(data.dropdown);
659                     $this_field.data('original_data', data_value);
660                     $("#sqlqueryresults").trigger('refreshgrid');
661                 }) // end $.post()
662             }
663             else if($this_field.is('.enum')) {
664                 /** @lends jQuery */
665                 //handle enum fields
667                 /**
668                  * @var post_params Object containing parameters for the POST request
669                  */
670                 var post_params = {
671                         'ajax_request' : true,
672                         'get_enum_values' : true,
673                         'db' : window.parent.db,
674                         'table' : window.parent.table,
675                         'column' : field_name,
676                         'token' : window.parent.token,
677                         'curr_value' : curr_value
678                 }
679                 $.post('sql.php', post_params, function(data) {
680                     $this_field_span.append(data.dropdown);
681                     $this_field.data('original_data', data_value);
682                     $("#sqlqueryresults").trigger('refreshgrid');
683                 }) // end $.post()
684             }
685             else if($this_field.is('.set')) {
686                 /** @lends jQuery */
687                 //handle set fields
689                 /**
690                  * @var post_params Object containing parameters for the POST request
691                  */
692                 var post_params = {
693                         'ajax_request' : true,
694                         'get_set_values' : true,
695                         'db' : window.parent.db,
696                         'table' : window.parent.table,
697                         'column' : field_name,
698                         'token' : window.parent.token,
699                         'curr_value' : curr_value
700                 }
702                 $.post('sql.php', post_params, function(data) {
703                     $this_field_span.append(data.select);
704                     $this_field.data('original_data', data_value);
705                     $("#sqlqueryresults").trigger('refreshgrid');
706                 }) // end $.post()
707             }
708             else if($this_field.is('.null')) {
709                 //handle null fields
710                 $this_field_span.append('<textarea></textarea>');
711                 $this_field.data('original_data', 'NULL');
712             }
713         });
715         // refresh the grid
716         $("#sqlqueryresults").trigger('refreshgrid');
718     }) // End On click, replace the current field with an input/textarea
720     /**
721      * After editing, clicking again should post data
722      *
723      * @memberOf    jQuery
724      * @name        inline_edit_save
725      * @see         PMA_ajaxShowMessage()
726      * @see         getFieldName()
727      */
728     $(".inline_edit_active span a").live('click', function(event) {
729         /** @lends jQuery */
731         event.preventDefault();
733         /**
734          * @var $this_td    Object referring to the td containing the
735          * "Inline Edit" link that was clicked to save the row that is
736          * being edited
737          *
738          */
739         var $this_td = $(this).parents('td');
740         var $test_element = ''; // to test the presence of a element
742         // Initialize variables
743         var $input_siblings = $this_td.parent('tr').find('.inline_edit');
744         var where_clause = $this_td.parent('tr').find('.where_clause').val();
746         /**
747          * @var nonunique   Boolean, whether this row is unique or not
748          */
749         if($this_td.is('.nonunique')) {
750             var nonunique = 0;
751         }
752         else {
753             var nonunique = 1;
754         }
756         // Collect values of all fields to submit, we don't know which changed
757         /**
758          * @var relation_fields Array containing the name/value pairs of relational fields
759          */
760         var relation_fields = {};
761         /**
762          * @var relational_display string 'K' if relational key, 'D' if relational display column
763          */
764         var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D';
765         /**
766          * @var transform_fields    Array containing the name/value pairs for transformed fields
767          */
768         var transform_fields = {};
769         /**
770          * @var transformation_fields   Boolean, if there are any transformed fields in this row
771          */
772         var transformation_fields = false;
774         /**
775          * @var sql_query String containing the SQL query to update this row
776          */
777         var sql_query = 'UPDATE `' + window.parent.table + '` SET ';
779         var need_to_post = false;
781         var new_clause = '';
782         var prev_index = -1;
784         $input_siblings.each(function() {
785             /** @lends jQuery */
786             /**
787              * @var this_field  Object referring to this field (<td>)
788              */
789             var $this_field = $(this);
791             /**
792              * @var field_name  String containing the name of this field.
793              * @see getFieldName()
794              */
795             var field_name = getFieldName($this_field);
797             /**
798              * @var this_field_params   Array temporary storage for the name/value of current field
799              */
800             var this_field_params = {};
802             if($this_field.is('.transformed')) {
803                 transformation_fields =  true;
804             }
805             /**
806              * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
807              */
808             var is_null = $this_field.find('input:checkbox').is(':checked');
809             var value;
810             var addQuotes = true;
812             if (is_null) {
813                 sql_query += ' `' + field_name + "`=NULL , ";
814                 need_to_post = true;
815             } else {
816                 if($this_field.is(":not(.relation, .enum, .set, .bit)")) {
817                     this_field_params[field_name] = $this_field.find('textarea').val();
818                     if($this_field.is('.transformed')) {
819                         $.extend(transform_fields, this_field_params);
820                     }
821                 } else if ($this_field.is('.bit')) {
822                     this_field_params[field_name] = '0b' + $this_field.find('textarea').val();
823                     addQuotes = false;
824                 } else if ($this_field.is('.set')) {
825                     $test_element = $this_field.find('select');
826                     this_field_params[field_name] = $test_element.map(function(){
827                         return $(this).val();
828                     }).get().join(",");
829                 } else {
830                     // results from a drop-down
831                     $test_element = $this_field.find('select');
832                     if ($test_element.length != 0) {
833                         this_field_params[field_name] = $test_element.val();
834                     }
836                     // results from Browse foreign value
837                     $test_element = $this_field.find('span.curr_value');
838                     if ($test_element.length != 0) {
839                         this_field_params[field_name] = $test_element.text();
840                     }
842                     if($this_field.is('.relation')) {
843                         $.extend(relation_fields, this_field_params);
844                     }
845                 }
846                     if(where_clause.indexOf(field_name) > prev_index){
847                         new_clause += '`' + window.parent.table + '`.' + '`' + field_name + "` = '" + this_field_params[field_name].replace(/'/g,"''") + "'" + ' AND ';
848                     }
849                 if (this_field_params[field_name] != $this_field.data('original_data')) {
850                     if (addQuotes == true) {
851                         sql_query += ' `' + field_name + "`='" + this_field_params[field_name].replace(/'/g, "''") + "', ";
852                     } else {
853                         sql_query += ' `' + field_name + "`=" + this_field_params[field_name].replace(/'/g, "''") + ", ";
854                     }
855                     need_to_post = true;
856                 }
857             }
858         })
860         /*
861          * update the where_clause, remove the last appended ' AND '
862          * */
864         //Remove the last ',' appended in the above loop
865         sql_query = sql_query.replace(/,\s$/, '');
866         //Fix non-escaped backslashes
867         sql_query = sql_query.replace(/\\/g, '\\\\');
868         new_clause = new_clause.substring(0, new_clause.length-5);
869         new_clause = PMA_urlencode(new_clause);
870         sql_query += ' WHERE ' + PMA_urldecode(where_clause);
871         // Avoid updating more than one row in case there is no primary key
872         // (happened only for duplicate rows)
873         sql_query += ' LIMIT 1';
874         /**
875          * @var rel_fields_list  String, url encoded representation of {@link relations_fields}
876          */
877         var rel_fields_list = $.param(relation_fields);
879         /**
880          * @var transform_fields_list  String, url encoded representation of {@link transform_fields}
881          */
882         var transform_fields_list = $.param(transform_fields);
884         // if inline_edit is successful, we need to go back to default view
885         var $del_hide = $(this).parent();
886         var $chg_submit = $(this);
888         if (need_to_post) {
889             // Make the Ajax post after setting all parameters
890             /**
891              * @var post_params Object containing parameters for the POST request
892              */
893             var post_params = {'ajax_request' : true,
894                             'sql_query' : sql_query,
895                             'token' : window.parent.token,
896                             'db' : window.parent.db,
897                             'table' : window.parent.table,
898                             'clause_is_unique' : nonunique,
899                             'where_clause' : where_clause,
900                             'rel_fields_list' : rel_fields_list,
901                             'do_transformations' : transformation_fields,
902                             'transform_fields_list' : transform_fields_list,
903                             'relational_display' : relational_display,
904                             'goto' : 'sql.php',
905                             'submit_type' : 'save'
906                           };
908             $.post('tbl_replace.php', post_params, function(data) {
909                 if(data.success == true) {
910                     PMA_ajaxShowMessage(data.message);
911                     $this_td.parent('tr').find('.where_clause').attr('value', new_clause);
912                     // remove possible previous feedback message
913                     $('#result_query').remove();
914                     if (typeof data.sql_query != 'undefined') {
915                         // display feedback
916                         $('#sqlqueryresults').prepend(data.sql_query);
917                     }
918                     PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data);
919                 } else {
920                     PMA_ajaxShowMessage(data.error);
921                 };
922             }) // end $.post()
923         } else {
924             // no posting was done but still need to display the row
925             // in its previous format
926             PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, '');
927         }
928     }) // End After editing, clicking again should post data
931  * Ajax Event for table row change
932  * */
933     $("#resultsForm.ajax .mult_submit[value=edit]").live('click', function(event){
934         event.preventDefault();
936         /*Check whether atleast one row is selected for change*/
937         if ($("#table_results tbody tr, #table_results tbody tr td").hasClass("marked")) {
938             var div = $('<div id="change_row_dialog"></div>');
940             /**
941              *  @var    button_options  Object that stores the options passed to jQueryUI
942              *                          dialog
943              */
944             var button_options = {};
945             // in the following function we need to use $(this)
946             button_options[PMA_messages['strCancel']] = function() {$(this).parent().dialog('close').remove();}
948             var button_options_error = {};
949             button_options_error[PMA_messages['strOK']] = function() {$(this).parent().dialog('close').remove();}
950             var $form = $("#resultsForm");
951             var $msgbox = PMA_ajaxShowMessage();
953             $.get( $form.attr('action'), $form.serialize()+"&ajax_request=true&submit_mult=row_edit", function(data) {
954                 //in the case of an error, show the error message returned.
955                 if (data.success != undefined && data.success == false) {
956                     div
957                     .append(data.error)
958                     .dialog({
959                         title: PMA_messages['strChangeTbl'],
960                         height: 230,
961                         width: 900,
962                         open: PMA_verifyTypeOfAllColumns,
963                         buttons : button_options_error
964                     })// end dialog options
965                 } else {
966                     div
967                     .append(data)
968                     .dialog({
969                         title: PMA_messages['strChangeTbl'],
970                         height: 600,
971                         width: 900,
972                         open: PMA_verifyTypeOfAllColumns,
973                         buttons : button_options
974                     })
975                     //Remove the top menu container from the dialog
976                     .find("#topmenucontainer").hide()
977                     ; // end dialog options
978                     $(".insertRowTable").addClass("ajax");
979                     $("#buttonYes").addClass("ajax");
980                 }
981                 PMA_ajaxRemoveMessage($msgbox);
982             }) // end $.get()
983         } else {
984             PMA_ajaxShowMessage(PMA_messages['strNoRowSelected']);
985         }
986     });
989  * Click action for "Go" button in ajax dialog insertForm -> insertRowTable
990  */
991     $("#insertForm .insertRowTable.ajax input[value=Go]").live('click', function(event) {
992         event.preventDefault();
993         /**
994          *  @var    the_form    object referring to the insert form
995          */
996         var $form = $("#insertForm");
997         PMA_prepareForAjaxRequest($form);
998         //User wants to submit the form
999         $.post($form.attr('action'), $form.serialize(), function(data) {
1000             if (data.success == true) {
1001                 PMA_ajaxShowMessage(data.message);
1002                 if ($("#pageselector").length != 0) {
1003                     $("#pageselector").trigger('change');
1004                 } else {
1005                     $("input[name=navig].ajax").trigger('click');
1006                 }
1008             } else {
1009                 PMA_ajaxShowMessage(data.error);
1010                 $("#table_results tbody tr.marked .multi_checkbox " +
1011                         ", #table_results tbody tr td.marked .multi_checkbox").prop("checked", false);
1012                 $("#table_results tbody tr.marked .multi_checkbox " +
1013                         ", #table_results tbody tr td.marked .multi_checkbox").removeClass("last_clicked");
1014                 $("#table_results tbody tr" +
1015                         ", #table_results tbody tr td").removeClass("marked");
1016             }
1017             if ($("#change_row_dialog").length > 0) {
1018                 $("#change_row_dialog").dialog("close").remove();
1019             }
1020             /**Update the row count at the tableForm*/
1021             $("#result_query").remove();
1022             $("#sqlqueryresults").prepend(data.sql_query);
1023             $("#result_query .notice").remove();
1024             $("#result_query").prepend((data.message));
1025         }) // end $.post()
1026     }) // end insert table button "Go"
1028 /**$("#buttonYes.ajax").live('click'
1029  * Click action for #buttonYes button in ajax dialog insertForm
1030  */
1031     $("#buttonYes.ajax").live('click', function(event){
1032         event.preventDefault();
1033         /**
1034          *  @var    the_form    object referring to the insert form
1035          */
1036         var $form = $("#insertForm");
1037         /**Get the submit type and the after insert type in the form*/
1038         var selected_submit_type = $("#insertForm").find("#actions_panel .control_at_footer option:selected").attr('value');
1039         var selected_after_insert = $("#insertForm").find("#actions_panel select[name=after_insert] option:selected").attr('value');
1040         $("#result_query").remove();
1041         PMA_prepareForAjaxRequest($form);
1042         //User wants to submit the form
1043         $.post($form.attr('action'), $form.serialize() , function(data) {
1044             if (data.success == true) {
1045                 PMA_ajaxShowMessage(data.message);
1046                 if (selected_submit_type == "showinsert") {
1047                     $("#sqlqueryresults").prepend(data.sql_query);
1048                     $("#result_query .notice").remove();
1049                     $("#result_query").prepend(data.message);
1050                     $("#table_results tbody tr.marked .multi_checkbox " +
1051                         ", #table_results tbody tr td.marked .multi_checkbox").prop("checked", false);
1052                     $("#table_results tbody tr.marked .multi_checkbox " +
1053                         ", #table_results tbody tr td.marked .multi_checkbox").removeClass("last_clicked");
1054                     $("#table_results tbody tr" +
1055                         ", #table_results tbody tr td").removeClass("marked");
1056                 } else {
1057                     if ($("#pageselector").length != 0) {
1058                         $("#pageselector").trigger('change');
1059                     } else {
1060                         $("input[name=navig].ajax").trigger('click');
1061                     }
1062                     $("#result_query").remove();
1063                     $("#sqlqueryresults").prepend(data.sql_query);
1064                     $("#result_query .notice").remove();
1065                     $("#result_query").prepend((data.message));
1066                 }
1067             } else {
1068                 PMA_ajaxShowMessage(data.error);
1069                 $("#table_results tbody tr.marked .multi_checkbox " +
1070                     ", #table_results tbody tr td.marked .multi_checkbox").prop("checked", false);
1071                 $("#table_results tbody tr.marked .multi_checkbox " +
1072                     ", #table_results tbody tr td.marked .multi_checkbox").removeClass("last_clicked");
1073                 $("#table_results tbody tr" +
1074                     ", #table_results tbody tr td").removeClass("marked");
1075             }
1076             if ($("#change_row_dialog").length > 0) {
1077                 $("#change_row_dialog").dialog("close").remove();
1078             }
1079         }) // end $.post()
1080     });
1082 }, 'top.frame_content') // end $(document).ready()
1086  * Visually put back the row in the state it was before entering Inline edit
1088  * (when called in the situation where no posting was done, the data
1089  * parameter is empty)
1090  */
1091 function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data) {
1093     // deleting the hide button. remove <br><br><a> tags
1094     $del_hide.find('a, br').remove();
1095     // append inline edit button.
1096     $del_hide.append($data_a.clone());
1098     // changing inline_edit_active to inline_edit_anchor
1099     $this_td.removeClass('inline_edit_active').addClass('inline_edit_anchor');
1101     // removing hover, marked and noclick classes
1102     $this_td.parent('tr').removeClass('noclick');
1103     $this_td.parent('tr').removeClass('hover').find('td').removeClass('hover');
1105     $input_siblings.each(function() {
1106         // Inline edit post has been successful.
1107         $this_sibling = $(this);
1108         $this_sibling_span = $(this).children('span');
1110         var is_null = $this_sibling.find('input:checkbox').is(':checked');
1111         if (is_null) {
1112             $this_sibling_span.html('NULL');
1113             $this_sibling.addClass('null');
1114         } else {
1115             $this_sibling.removeClass('null');
1116             if($this_sibling.is(':not(.relation, .enum, .set)')) {
1117                 /**
1118                  * @var new_html    String containing value of the data field after edit
1119                  */
1120                 var new_html = $this_sibling.find('textarea').val();
1122                 if($this_sibling.is('.transformed')) {
1123                     var field_name = getFieldName($this_sibling);
1124                     if (typeof data.transformations != 'undefined') {
1125                         $.each(data.transformations, function(key, value) {
1126                             if(key == field_name) {
1127                                 if($this_sibling.is('.text_plain, .application_octetstream')) {
1128                                     new_html = value;
1129                                     return false;
1130                                 } else {
1131                                     var new_value = $this_sibling.find('textarea').val();
1132                                     new_html = $(value).append(new_value);
1133                                     return false;
1134                                 }
1135                             }
1136                         })
1137                     }
1138                 }
1139             } else {
1140                 var new_html = '';
1141                 var new_value = '';
1142                 $test_element = $this_sibling.find('select');
1143                 if ($test_element.length != 0) {
1144                     new_value = $test_element.val();
1145                 }
1146                 $test_element = $this_sibling.find('span.curr_value');
1147                 if ($test_element.length != 0) {
1148                     new_value = $test_element.text();
1149                 }
1151                 if($this_sibling.is('.relation')) {
1152                     var field_name = getFieldName($this_sibling);
1153                     if (typeof data.relations != 'undefined') {
1154                         $.each(data.relations, function(key, value) {
1155                             if(key == field_name) {
1156                                 new_html = $(value);
1157                                 return false;
1158                             }
1159                         })
1160                     }
1161                 } else if ($this_sibling.is('.enum')) {
1162                     new_html = new_value;
1163                 } else if ($this_sibling.is('.set')) {
1164                     if (new_value != null) {
1165                         $.each(new_value, function(key, value) {
1166                             new_html = new_html + value + ',';
1167                         })
1168                         new_html = new_html.substring(0, new_html.length-1);
1169                     }
1170                 }
1171             }
1172             $this_sibling_span.html(new_html);
1173         }
1174     })
1176     // refresh the grid
1177     $("#sqlqueryresults").trigger('refreshgrid');
1181  * Starting from some th, change the class of all td under it.
1182  * If isAddClass is specified, it will be used to determine whether to add or remove the class.
1183  */
1184 function PMA_changeClassForColumn($this_th, newclass, isAddClass) {
1185     // index 0 is the th containing the big T
1186     var th_index = $this_th.index();
1187     var has_big_t = !$this_th.closest('tr').children(':first').hasClass('column_heading');
1188     // .eq() is zero-based
1189     if (has_big_t) {
1190         th_index--;
1191     }
1192     var $tds = $this_th.closest('table').find('tbody tr').find('td.data:eq('+th_index+')');
1193     if (isAddClass == undefined) {
1194         $tds.toggleClass(newclass);
1195     } else {
1196         $tds.toggleClass(newclass, isAddClass);
1197     }
1200 $(document).ready(function() {
1202     $('.browse_foreign').live('click', function(e) {
1203         e.preventDefault();
1204         window.open(this.href, 'foreigners', 'width=640,height=240,scrollbars=yes,resizable=yes');
1205         $anchor = $(this);
1206         $anchor.addClass('browse_foreign_clicked');
1207         return false;
1208     });
1210     /**
1211      * vertical column highlighting in horizontal mode when hovering over the column header
1212      */
1213     $('.column_heading.pointer').live('hover', function(e) {
1214         PMA_changeClassForColumn($(this), 'hover', e.type == 'mouseenter');
1215         });
1217     /**
1218      * vertical column marking in horizontal mode when clicking the column header
1219      */
1220     $('.column_heading.marker').live('click', function() {
1221         PMA_changeClassForColumn($(this), 'marked');
1222         });
1224     /**
1225      * create resizable table
1226      */
1227     $("#sqlqueryresults").trigger('makegrid');
1231  * Profiling Chart
1232  */
1233 function createProfilingChart() {
1234     if ($('#profilingchart').length == 0) {
1235         return;
1236     }
1238     var cdata = new Array();
1239     $.each(jQuery.parseJSON($('#profilingchart').html()),function(key,value) {
1240         cdata.push([key,parseFloat(value)]);
1241     });
1243     // Prevent the user from seeing the JSON code
1244     $('div#profilingchart').html('').show();
1246     PMA_createChart({
1247         chart: {
1248             renderTo: 'profilingchart',
1249             backgroundColor: $('#sqlqueryresults fieldset').css('background-color')
1250         },
1251         title: { text:'', margin:0 },
1252         series: [{
1253             type:'pie',
1254             name: 'Query execution time',
1255             data: cdata
1256         }],
1257         plotOptions: {
1258             pie: {
1259                 allowPointSelect: true,
1260                 cursor: 'pointer',
1261                 dataLabels: {
1262                     enabled: true,
1263                     distance: 35,
1264                     formatter: function() {
1265                         return '<b>'+ this.point.name +'</b><br/>'+ Highcharts.numberFormat(this.percentage, 2) +' %';
1266                    }
1267                 }
1268             }
1269         },
1270         tooltip: {
1271             formatter: function() { return '<b>'+ this.point.name +'</b><br/>'+this.y+'s<br/>('+Highcharts.numberFormat(this.percentage, 2) +' %)'; }
1272         }
1273     });
1277 /**#@- */