Translation update done using Pootle.
[phpmyadmin/crack.git] / js / sql.js
blobbd068cf4b851961fb4d31a6371bd49d9ec67f099
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 /**
11  * decode a string URL_encoded
12  *
13  * @param string str
14  * @return string the URL-decoded string
15  */
16 var data_vt;
17 function PMA_urldecode(str) {
18     return decodeURIComponent(str.replace(/\+/g, '%20'));
21 function PMA_urlencode(str) {
22     return encodeURIComponent(str.replace(/\%20/g, '+'));
25 /**
26  * Get the field name for the current field.  Required to construct the query
27  * for inline editing
28  *
29  * @param   $this_field  jQuery object that points to the current field's tr
30  * @param   disp_mode    string
31  */
32 function getFieldName($this_field, disp_mode) {
34     if(disp_mode == 'vertical') {
35         var field_name = $this_field.siblings('th').find('a').text();
36         // happens when just one row (headings contain no a)
37         if ("" == field_name) {
38             field_name = $this_field.siblings('th').text();
39         }
40     }
41     else {
42         var this_field_index = $this_field.index();
43         // ltr or rtl direction does not impact how the DOM was generated
44         //
45         // 5 columns to account for the checkbox, edit, appended inline edit, copy and delete anchors but index is zero-based so substract 4
46         var field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index-4 )+') a').text();
47         // happens when just one row (headings contain no a)
48         if ("" == field_name) {
49             field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index-4 )+')').text();
50         }
51     }
53     field_name = $.trim(field_name);
55     return field_name;
58 /**
59  * The function that iterates over each row in the table_results and appends a
60  * new inline edit anchor to each table row.
61  *
62  */
63 function appendInlineAnchor() {
64     var disp_mode = $("#top_direction_dropdown").val();
66     if (disp_mode == 'vertical') {
67         // there can be one or two tr containing this class, depending
68         // on the ModifyDeleteAtLeft and ModifyDeleteAtRight cfg parameters
69         $('#table_results tr')
70             .find('.edit_row_anchor')
71             .removeClass('edit_row_anchor')
72             .parent().each(function() {
73             var $this_tr = $(this);
74             var $cloned_tr = $this_tr.clone();
76             var $img_object = $cloned_tr.find('img:first').attr('title', PMA_messages['strInlineEdit']);
77             if ($img_object.length != 0) {
78                 var img_src = $img_object.attr('src').replace(/b_edit/,'b_inline_edit');
79                 $img_object.attr('src', img_src);
80             }
82             $cloned_tr.find('td')
83              .addClass('inline_edit_anchor')
84              .find('a').attr('href', '#')
85              .find('span')
86              .text(' ' + PMA_messages['strInlineEdit'])
87              .prepend($img_object);
89             $cloned_tr.insertAfter($this_tr);
90         });
92         $('#rowsDeleteForm').find('tbody').find('th').each(function() {
93             var $this_th = $(this);
94             if ($this_th.attr('rowspan') == 4) {
95                 $this_th.attr('rowspan', '5');
96             }
97         });
98     }
99     else {
100         // horizontal mode
101         $('.edit_row_anchor').each(function() {
103             var $this_td = $(this);
104             $this_td.removeClass('edit_row_anchor');
106             var $cloned_anchor = $this_td.clone();
108             var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']);
109             if ($img_object.length != 0) {
110                 var img_src = $img_object.attr('src').replace(/b_edit/,'b_inline_edit');
111                 $img_object.attr('src', img_src);
112                 $cloned_anchor
113                  .find('a').attr('href', '#')
114                  .find('span')
115                  .text(' ' + PMA_messages['strInlineEdit']);
116                 $cloned_anchor
117                  .find('span')
118                  .first()
119                  .prepend($img_object);
120             } else {
121                 // the link was too big so <input type="image"> is there
122                 $img_object = $cloned_anchor.find('input:image').attr('title', PMA_messages['strInlineEdit']);
123                 var img_src = $img_object.attr('src').replace(/b_edit/,'b_inline_edit');
124                 $img_object.attr('src', img_src);
125                 $cloned_anchor
126                  .find('.clickprevimage')
127                  .text(' ' + PMA_messages['strInlineEdit']);
128             }
130             $cloned_anchor
131              .addClass('inline_edit_anchor');
133             $this_td.after($cloned_anchor);
134         });
136         $('#rowsDeleteForm').find('thead, tbody').find('th').each(function() {
137             var $this_th = $(this);
138             if ($this_th.attr('colspan') == 4) {
139                 $this_th.attr('colspan', '5');
140             }
141         });
142     }
145 /**#@+
146  * @namespace   jQuery
147  */
150  * @description <p>Ajax scripts for sql and browse pages</p>
152  * Actions ajaxified here:
153  * <ul>
154  * <li>Retrieve results of an SQL query</li>
155  * <li>Paginate the results table</li>
156  * <li>Sort the results table</li>
157  * <li>Change table according to display options</li>
158  * <li>Inline editing of data</li>
159  * </ul>
161  * @name        document.ready
162  * @memberOf    jQuery
163  */
164 $(document).ready(function() {
166     /**
167      * Set a parameter for all Ajax queries made on this page.  Don't let the
168      * web server serve cached pages
169      */
170     $.ajaxSetup({
171         cache: 'false'
172     });
174     /**
175      * current value of the direction in which the table is displayed
176      * @type    String
177      * @fieldOf jQuery
178      * @name    disp_mode
179      */
180     var disp_mode = $("#top_direction_dropdown").val();
182     /**
183      * Update value of {@link jQuery.disp_mode} everytime the direction dropdown changes value
184      * @memberOf    jQuery
185      * @name        direction_dropdown_change
186      */
187     $("#top_direction_dropdown, #bottom_direction_dropdown").live('change', function(event) {
188         disp_mode = $(this).val();
189     })
191     /**
192      * Attach the {@link appendInlineAnchor} function to a custom event, which
193      * will be triggered manually everytime the table of results is reloaded
194      * @memberOf    jQuery
195      */
196     $("#sqlqueryresults").live('appendAnchor',function() {
197         appendInlineAnchor();
198     })
200     /**
201      * Trigger the appendAnchor event to prepare the first table for inline edit
202      * (see $GLOBALS['cfg']['AjaxEnable'])
203      * @memberOf    jQuery
204      * @name        sqlqueryresults_trigger
205      */
206     $("#sqlqueryresults.ajax").trigger('appendAnchor');
208     /**
209      * Append the "Show/Hide query box" message to the query input form
210      *
211      * @memberOf jQuery
212      * @name    appendToggleSpan
213      */
214     // do not add this link more than once
215     if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
216         $('<a id="togglequerybox"></a>')
217         .html(PMA_messages['strHideQueryBox'])
218         .appendTo("#sqlqueryform")
219         // initially hidden because at this point, nothing else
220         // appears under the link
221         .hide();
223         // Attach the toggling of the query box visibility to a click
224         $("#togglequerybox").bind('click', function() {
225             var $link = $(this)
226             $link.siblings().slideToggle("fast");
227             if ($link.text() == PMA_messages['strHideQueryBox']) {
228                 $link.text(PMA_messages['strShowQueryBox']);
229                 // cheap trick to add a spacer between the menu tabs
230                 // and "Show query box"; feel free to improve!
231                 $('#togglequerybox_spacer').remove();
232                 $link.before('<br id="togglequerybox_spacer" />');
233             } else {
234                 $link.text(PMA_messages['strHideQueryBox']);
235             }
236             // avoid default click action
237             return false;
238         })
239     }
241     /**
242      * Ajax Event handler for 'SQL Query Submit'
243      *
244      * @see         PMA_ajaxShowMessage()
245      * @see         $cfg['AjaxEnable']
246      * @memberOf    jQuery
247      * @name        sqlqueryform_submit
248      */
249     $("#sqlqueryform.ajax").live('submit', function(event) {
250         event.preventDefault();
251         // remove any div containing a previous error message
252         $('.error').remove();
254         $form = $(this);
255         var $msgbox = PMA_ajaxShowMessage();
257         PMA_prepareForAjaxRequest($form);
259         $.post($(this).attr('action'), $(this).serialize() , function(data) {
260             if(data.success == true) {
261                 // fade out previous messages, if any
262                 $('.success').fadeOut();
263                 $('.sqlquery_message').fadeOut();
264                 // show a message that stays on screen
265                 if (typeof data.sql_query != 'undefined') {
266                     $('<div class="sqlquery_message"></div>')
267                      .html(data.sql_query)
268                      .insertBefore('#sqlqueryform');
269                     // unnecessary div that came from data.sql_query
270                     $('.notice').remove();
271                 } else {
272                     $('#sqlqueryform').before(data.message);
273                 }
274                 $('#sqlqueryresults').show();
275                 // this happens if a USE command was typed
276                 if (typeof data.reload != 'undefined') {
277                     // Unbind the submit event before reloading. See bug #3295529
278                     $("#sqlqueryform.ajax").die('submit');
279                     $form.find('input[name=db]').val(data.db);
280                     // need to regenerate the whole upper part
281                     $form.find('input[name=ajax_request]').remove();
282                     $form.append('<input type="hidden" name="reload" value="true" />');
283                     $.post('db_sql.php', $form.serialize(), function(data) {
284                         $('body').html(data);
285                     }); // end inner post
286                 }
287             }
288             else if (data.success == false ) {
289                 // show an error message that stays on screen
290                 $('#sqlqueryform').before(data.error);
291                 $('#sqlqueryresults').hide();
292             }
293             else {
294                 // real results are returned
295                 // fade out previous messages, if any
296                 $('.success').fadeOut();
297                 $('.sqlquery_message').fadeOut();
298                 $received_data = $(data);
299                 $zero_row_results = $received_data.find('textarea[name="sql_query"]');
300                 // if zero rows are returned from the query execution
301                 if ($zero_row_results.length > 0) {
302                     $('#sqlquery').val($zero_row_results.val());
303                 } else {
304                     $('#sqlqueryresults').show();
305                     $("#sqlqueryresults").html(data);
306                     $("#sqlqueryresults").trigger('appendAnchor');
307                     $('#togglequerybox').show();
308                     if($("#togglequerybox").siblings(":visible").length > 0) {
309                         $("#togglequerybox").trigger('click');
310                     }
311                     PMA_init_slider();
312                 }
313             }
314             PMA_ajaxRemoveMessage($msgbox);
316         }) // end $.post()
317     }) // end SQL Query submit
319     /**
320      * Ajax Event handlers for Paginating the results table
321      */
323     /**
324      * Paginate when we click any of the navigation buttons
325      * (only if the element has the ajax class, see $cfg['AjaxEnable'])
326      * @memberOf    jQuery
327      * @name        paginate_nav_button_click
328      * @uses        PMA_ajaxShowMessage()
329      * @see         $cfg['AjaxEnable']
330      */
331     $("input[name=navig].ajax").live('click', function(event) {
332         /** @lends jQuery */
333         event.preventDefault();
335         var $msgbox = PMA_ajaxShowMessage();
337         /**
338          * @var $the_form    Object referring to the form element that paginates the results table
339          */
340         var $the_form = $(this).parent("form");
342         $the_form.append('<input type="hidden" name="ajax_request" value="true" />');
344         $.post($the_form.attr('action'), $the_form.serialize(), function(data) {
345             $("#sqlqueryresults").html(data);
346             $("#sqlqueryresults").trigger('appendAnchor');
347             PMA_init_slider();
348             
349             PMA_ajaxRemoveMessage($msgbox);
350         }) // end $.post()
351     })// end Paginate results table
353     /**
354      * Paginate results with Page Selector dropdown
355      * @memberOf    jQuery
356      * @name        paginate_dropdown_change
357      * @see         $cfg['AjaxEnable']
358      */
359     $("#pageselector").live('change', function(event) {
360         var $the_form = $(this).parent("form");
362         if ($(this).hasClass('ajax')) {
363             event.preventDefault();
365             var $msgbox = PMA_ajaxShowMessage();
367             $.post($the_form.attr('action'), $the_form.serialize() + '&ajax_request=true', function(data) {
368                 $("#sqlqueryresults").html(data);
369                 $("#sqlqueryresults").trigger('appendAnchor');
370                 PMA_init_slider();
371                 PMA_ajaxRemoveMessage($msgbox); 
372             }) // end $.post()
373         } else {
374             $the_form.submit();
375         }
377     })// end Paginate results with Page Selector
379     /**
380      * Ajax Event handler for sorting the results table
381      * @memberOf    jQuery
382      * @name        table_results_sort_click
383      * @see         $cfg['AjaxEnable']
384      */
385     $("#table_results.ajax").find("a[title=Sort]").live('click', function(event) {
386         event.preventDefault();
388         var $msgbox = PMA_ajaxShowMessage();
390         $anchor = $(this);
392         $.get($anchor.attr('href'), $anchor.serialize() + '&ajax_request=true', function(data) {
393             $("#sqlqueryresults")
394              .html(data)
395              .trigger('appendAnchor');
396             PMA_ajaxRemoveMessage($msgbox);
397         }) // end $.get()
398     })//end Sort results table
400     /**
401      * Ajax Event handler for the display options
402      * @memberOf    jQuery
403      * @name        displayOptionsForm_submit
404      * @see         $cfg['AjaxEnable']
405      */
406     $("#displayOptionsForm.ajax").live('submit', function(event) {
407         event.preventDefault();
409         $form = $(this);
411         $.post($form.attr('action'), $form.serialize() + '&ajax_request=true' , function(data) {
412             $("#sqlqueryresults")
413              .html(data)
414              .trigger('appendAnchor');
415             PMA_init_slider();
416         }) // end $.post()
417     })
418     //end displayOptionsForm handler
420     /**
421      * Ajax Event handlers for Inline Editing
422      */
424     /**
425      * On click, replace the fields of current row with an input/textarea
426      * @memberOf    jQuery
427      * @name        inline_edit_start
428      * @see         PMA_ajaxShowMessage()
429      * @see         getFieldName()
430      */
431     $(".inline_edit_anchor span a").live('click', function(event) {
432         /** @lends jQuery */
433         event.preventDefault();
435         var $edit_td = $(this).parents('td');
436         $edit_td.removeClass('inline_edit_anchor').addClass('inline_edit_active').parent('tr').addClass('noclick');
438         // Adding submit and hide buttons to inline edit <td>.
439         // For "hide" button the original data to be restored is 
440         //  kept in the jQuery data element 'original_data' inside the <td>.
441         // Looping through all columns or rows, to find the required data and then storing it in an array.
443         var $this_children = $edit_td.children('span.nowrap').children('a').children('span.nowrap');
444         if (disp_mode != 'vertical') {
445             $this_children.empty();
446             $this_children.text(PMA_messages['strSave']);
447         } else {
448             // vertical
449             data_vt = $this_children.html();
450             $this_children.text(PMA_messages['strSave']);
451         }
453         var hide_link = '<br /><br /><a id="hide">' + PMA_messages['strHide'] + '</a>';
454         if (disp_mode != 'vertical') {
455             $edit_td.append(hide_link);
456             $('#table_results tbody tr td a#hide').click(function() {
457                 $this_children = $(this).siblings('span.nowrap').children('a').children('span.nowrap');
458                 $this_children.empty();
459                 $this_children.text(PMA_messages['strInlineEdit']);
461                 var $this_hide = $(this).parent();
462                 $this_hide.removeClass("inline_edit_active hover").addClass("inline_edit_anchor");
463                 $this_hide.parent().removeClass("hover noclick");
464                 $this_hide.siblings().removeClass("hover");
466                 var last_column = $this_hide.siblings().length;
467                 var txt = '';
468                 for(var i = 4; i < last_column; i++) {
469                     if($this_hide.siblings("td:eq(" + i + ")").hasClass("inline_edit") == false) {
470                         continue;
471                     }
472                     txt = $this_hide.siblings("td:eq(" + i + ")").data('original_data');
473                     if($this_hide.siblings("td:eq(" + i + ")").children().length != 0) {
474                         $this_hide.siblings("td:eq(" + i + ")").empty();
475                         $this_hide.siblings("td:eq(" + i + ")").append(txt);
476                     }
477                 }
478                 $(this).prev().prev().remove();
479                 $(this).prev().remove();
480                 $(this).remove();
481             });
482         } else {
483             var txt = '';
484             var rows = $edit_td.parent().siblings().length;
486             $edit_td.append(hide_link);
487             $('#table_results tbody tr td a#hide').click(function() {
488                 var pos = $(this).parent().index();
489                 var $chg_submit = $(this).parent().children('span.nowrap').children('a').children('span.nowrap');
490                 $chg_submit.empty();
491                 $chg_submit.append(data_vt);
493                 var $this_row = $(this).parents('tr');
494                 // changing inline_edit_active to inline_edit_anchor
495                 $this_row.siblings("tr:eq(3) td:eq(" + pos + ")").removeClass("inline_edit_active").addClass("inline_edit_anchor");
497                 // removing marked and hover classes.
498                 $this_row.parent('tbody').find('tr').find("td:eq(" + pos + ")").removeClass("marked hover");
500                 for( var i = 6; i <= rows + 2; i++){
501                     if( $this_row.siblings("tr:eq(" + i + ") td:eq(" + pos + ")").hasClass("inline_edit") == false) {
502                         continue;
503                     }
504                     txt = $this_row.siblings("tr:eq(" + i + ") td:eq(" + pos + ")").data('original_data');
505                     $this_row.siblings("tr:eq(" + i + ") td:eq(" + pos + ")").empty();
506                     $this_row.siblings("tr:eq(" + i + ") td:eq(" + pos + ")").append(txt);
507                 }
508                 $(this).prev().remove();
509                 $(this).prev().remove();
510                 $(this).remove();
511             });
512         }
514         // Initialize some variables
515         if(disp_mode == 'vertical') {
516             /**
517              * @var this_row_index  Index of the current <td> in the parent <tr>
518              *                      Current <td> is the inline edit anchor.
519              */
520             var this_row_index = $edit_td.index();
521             /**
522              * @var $input_siblings  Object referring to all inline editable events from same row
523              */
524             var $input_siblings = $edit_td.parents('tbody').find('tr').find('.inline_edit:nth('+this_row_index+')');
525             /**
526              * @var where_clause    String containing the WHERE clause to select this row
527              */
528             var where_clause = $edit_td.parents('tbody').find('tr').find('.where_clause:nth('+this_row_index+')').val();
529         }
530         // horizontal mode
531         else {
532             var this_row_index = $edit_td.parent().index();
533             var $input_siblings = $edit_td.parent('tr').find('.inline_edit');
534             var where_clause = $edit_td.parent('tr').find('.where_clause').val();
535         }
537         $input_siblings.each(function() {
538             /** @lends jQuery */
539             /**
540              * @var data_value  Current value of this field
541              */
542             var data_value = $(this).html();
544             // We need to retrieve the value from the server for truncated/relation fields
545             // Find the field name
547             /**
548              * @var this_field  Object referring to this field (<td>)
549              */
550             var $this_field = $(this);
551             /**
552              * @var field_name  String containing the name of this field.
553              * @see getFieldName()
554              */
555             var field_name = getFieldName($this_field, disp_mode);
556             /**
557              * @var relation_curr_value String current value of the field (for fields that are foreign keyed).
558              */
559             var relation_curr_value = $this_field.find('a').text();
560             /**
561              * @var relation_key_or_display_column String relational key if in 'Relational display column' mode,
562              * relational display column if in 'Relational key' mode (for fields that are foreign keyed).
563              */
564             var relation_key_or_display_column = $this_field.find('a').attr('title');
565             /**
566              * @var curr_value String current value of the field (for fields that are of type enum or set).
567              */
568             var curr_value = $this_field.text();
570             if($this_field.is(':not(.not_null)')){
571                 // add a checkbox to mark null for all the field that are nullable.
572                 $this_field.html('<div class="null_div">Null :<input type="checkbox" class="checkbox_null_'+ field_name + '_' + this_row_index +'"></div>');
573                 // check the 'checkbox_null_<field_name>_<row_index>' if the corresponding value is null
574                 if($this_field.is('.null')) {
575                     $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', true);
576                 }
578                 // if the select/editor is changed un-check the 'checkbox_null_<field_name>_<row_index>'.
579                 if ($this_field.is('.enum, .set')) {
580                     $this_field.find('select').live('change', function(e) {
581                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
582                     })
583                 } else if ($this_field.is('.relation')) {
584                     $this_field.find('select').live('change', function(e) {
585                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
586                     })
587                     $this_field.find('.browse_foreign').live('click', function(e) {
588                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
589                     })
590                 } else {
591                     $this_field.find('textarea').live('keypress', function(e) {
592                         $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false);
593                     })
594                 }
596                 // if 'checkbox_null_<field_name>_<row_index>' is clicked empty the corresponding select/editor.
597                 $('.checkbox_null_' + field_name + '_' + this_row_index).bind('click', function(e) {
598                     if ($this_field.is('.enum')) {
599                         $this_field.find('select').attr('value', '');
600                     } else if ($this_field.is('.set')) {
601                         $this_field.find('select').find('option').each(function() {
602                             var $option = $(this);
603                             $option.attr('selected', false);
604                         })
605                     } else if ($this_field.is('.relation')) {
606                         // if the dropdown is there to select the foreign value
607                         if ($this_field.find('select').length > 0) {
608                             $this_field.find('select').attr('value', '');
609                         // if foriegn value is selected by browsing foreing values
610                         } else {
611                             $this_field.find('span.curr_value').empty();
612                         }
613                     } else {
614                         $this_field.find('textarea').val('');
615                     }
616                 })
618             } else {
619                 $this_field.html('<div class="null_div"></div>');
620             }
622             // In each input sibling, wrap the current value in a textarea
623             // and store the current value in a hidden span
624             if($this_field.is(':not(.truncated, .transformed, .relation, .enum, .set, .null)')) {
625                 // handle non-truncated, non-transformed, non-relation values
626                 // We don't need to get any more data, just wrap the value
627                 $this_field.append('<textarea>'+data_value+'</textarea>');
628                 $this_field.data('original_data', data_value);
629             }
630             else if($this_field.is('.truncated, .transformed')) {
631                 /** @lends jQuery */
632                 //handle truncated/transformed values values
634                 /**
635                  * @var sql_query   String containing the SQL query used to retrieve value of truncated/transformed data
636                  */
637                 var sql_query = 'SELECT `' + field_name + '` FROM `' + window.parent.table + '` WHERE ' + PMA_urldecode(where_clause);
639                 // Make the Ajax call and get the data, wrap it and insert it
640                 $.post('sql.php', {
641                     'token' : window.parent.token,
642                     'db' : window.parent.db,
643                     'ajax_request' : true,
644                     'sql_query' : sql_query,
645                     'inline_edit' : true
646                 }, function(data) {
647                     if(data.success == true) {
648                         $this_field.append('<textarea>'+data.value+'</textarea>');
649                         $this_field.data('original_data', data_value);
650                     }
651                     else {
652                         PMA_ajaxShowMessage(data.error);
653                     }
654                 }) // end $.post()
655             }
656             else if($this_field.is('.relation')) {
657                 /** @lends jQuery */
658                 //handle relations
660                 /**
661                  * @var post_params Object containing parameters for the POST request
662                  */
663                 var post_params = {
664                         'ajax_request' : true,
665                         'get_relational_values' : true,
666                         'db' : window.parent.db,
667                         'table' : window.parent.table,
668                         'column' : field_name,
669                         'token' : window.parent.token,
670                         'curr_value' : relation_curr_value,
671                         'relation_key_or_display_column' : relation_key_or_display_column
672                 }
674                 $.post('sql.php', post_params, function(data) {
675                     $this_field.append(data.dropdown);
676                     $this_field.data('original_data', data_value);
677                 }) // end $.post()
678             }
679             else if($this_field.is('.enum')) {
680                 /** @lends jQuery */
681                 //handle enum fields
683                 /**
684                  * @var post_params Object containing parameters for the POST request
685                  */
686                 var post_params = {
687                         'ajax_request' : true,
688                         'get_enum_values' : true,
689                         'db' : window.parent.db,
690                         'table' : window.parent.table,
691                         'column' : field_name,
692                         'token' : window.parent.token,
693                         'curr_value' : curr_value
694                 }
695                 $.post('sql.php', post_params, function(data) {
696                     $this_field.append(data.dropdown);
697                     $this_field.data('original_data', data_value);
698                 }) // end $.post()
699             }
700             else if($this_field.is('.set')) {
701                 /** @lends jQuery */
702                 //handle set fields
704                 /**
705                  * @var post_params Object containing parameters for the POST request
706                  */
707                 var post_params = {
708                         'ajax_request' : true,
709                         'get_set_values' : true,
710                         'db' : window.parent.db,
711                         'table' : window.parent.table,
712                         'column' : field_name,
713                         'token' : window.parent.token,
714                         'curr_value' : curr_value
715                 }
717                 $.post('sql.php', post_params, function(data) {
718                     $this_field.append(data.select);
719                     $this_field.data('original_data', data_value);
720                 }) // end $.post()
721             }
722             else if($this_field.is('.null')) {
723                 //handle null fields
724                 $this_field.append('<textarea></textarea>');
725                 $this_field.data('original_data', 'NULL');
726             }
727         })
728     }) // End On click, replace the current field with an input/textarea
730     /**
731      * After editing, clicking again should post data
732      *
733      * @memberOf    jQuery
734      * @name        inline_edit_save
735      * @see         PMA_ajaxShowMessage()
736      * @see         getFieldName()
737      */
738     $(".inline_edit_active span a").live('click', function(event) {
739         /** @lends jQuery */
741         event.preventDefault();
743         /**
744          * @var $this_td    Object referring to the td containing the
745          * "Inline Edit" link that was clicked to save the row that is
746          * being edited
747          *
748          */
749         var $this_td = $(this).parent().parent();
750         var $test_element = ''; // to test the presence of a element
752         // Initialize variables
753         if(disp_mode == 'vertical') {
754             /**
755              * @var this_td_index  Index of the current <td> in the parent <tr>
756              *                      Current <td> is the inline edit anchor.
757              */
758             var this_td_index = $this_td.index();
759             /**
760              * @var $input_siblings  Object referring to all inline editable events from same row
761              */
762             var $input_siblings = $this_td.parents('tbody').find('tr').find('.inline_edit:nth('+this_td_index+')');
763             /**
764              * @var where_clause    String containing the WHERE clause to select this row
765              */
766             var where_clause = $this_td.parents('tbody').find('tr').find('.where_clause:nth('+this_td_index+')').val();
767         } else {
768             var $input_siblings = $this_td.parent('tr').find('.inline_edit');
769             var where_clause = $this_td.parent('tr').find('.where_clause').val();
770         }
772         /**
773          * @var nonunique   Boolean, whether this row is unique or not
774          */
775         if($this_td.is('.nonunique')) {
776             var nonunique = 0;
777         }
778         else {
779             var nonunique = 1;
780         }
782         // Collect values of all fields to submit, we don't know which changed
783         /**
784          * @var relation_fields Array containing the name/value pairs of relational fields
785          */
786         var relation_fields = {};
787         /**
788          * @var relational_display string 'K' if relational key, 'D' if relational display column 
789          */
790         var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D';
791         /**
792          * @var transform_fields    Array containing the name/value pairs for transformed fields
793          */
794         var transform_fields = {};
795         /**
796          * @var transformation_fields   Boolean, if there are any transformed fields in this row
797          */
798         var transformation_fields = false;
800         /**
801          * @var sql_query String containing the SQL query to update this row
802          */
803         var sql_query = 'UPDATE `' + window.parent.table + '` SET ';
805         var need_to_post = false;
806         
807         var new_clause = '';
808         var prev_index = -1;
810         $input_siblings.each(function() {
811             /** @lends jQuery */
812             /**
813              * @var this_field  Object referring to this field (<td>)
814              */
815             var $this_field = $(this);
817             /**
818              * @var field_name  String containing the name of this field.
819              * @see getFieldName()
820              */
821             var field_name = getFieldName($this_field, disp_mode);
823             /**
824              * @var this_field_params   Array temporary storage for the name/value of current field
825              */
826             var this_field_params = {};
828             if($this_field.is('.transformed')) {
829                 transformation_fields =  true;
830             }
831             /**
832              * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
833              */
834             var is_null = $this_field.find('input:checkbox').is(':checked');
835             var value;
837             if (is_null) {
838                 sql_query += ' `' + field_name + "`=NULL , ";
839                 need_to_post = true;
840             } else {
841                 if($this_field.is(":not(.relation, .enum, .set)")) {
842                     this_field_params[field_name] = $this_field.find('textarea').val();
843                     if($this_field.is('.transformed')) {
844                         $.extend(transform_fields, this_field_params);
845                     }
846                 } else if ($this_field.is('.set')) {
847                     $test_element = $this_field.find('select');
848                     this_field_params[field_name] = $test_element.map(function(){
849                         return $(this).val();
850                     }).get().join(",");
851                 } else {
852                     // results from a drop-down
853                     $test_element = $this_field.find('select');
854                     if ($test_element.length != 0) {
855                         this_field_params[field_name] = $test_element.val();
856                     }
858                     // results from Browse foreign value
859                     $test_element = $this_field.find('span.curr_value');
860                     if ($test_element.length != 0) {
861                         this_field_params[field_name] = $test_element.text();
862                     }
864                     if($this_field.is('.relation')) {
865                         $.extend(relation_fields, this_field_params);
866                     }
867                 }
868                     if(where_clause.indexOf(field_name) > prev_index){
869                         new_clause += '`' + window.parent.table + '`.' + '`' + field_name + "` = '" + this_field_params[field_name].replace(/'/g,"''") + "'" + ' AND ';
870                     }
871                 if (this_field_params[field_name] != $this_field.data('original_data')) {
872                     sql_query += ' `' + field_name + "`='" + this_field_params[field_name].replace(/'/g, "''") + "' , ";
873                     need_to_post = true;
874                 }
875             }
876         })
878         /*  
879          * update the where_clause, remove the last appended ' AND '
880          * */
882         //Remove the last ',' appended in the above loop
883         sql_query = sql_query.replace(/,\s$/, '');
884         new_clause = new_clause.substring(0, new_clause.length-5);
885         new_clause = PMA_urlencode(new_clause);
886         sql_query += ' WHERE ' + PMA_urldecode(where_clause);
887         /**
888          * @var rel_fields_list  String, url encoded representation of {@link relations_fields}
889          */
890         var rel_fields_list = $.param(relation_fields);
892         /**
893          * @var transform_fields_list  String, url encoded representation of {@link transform_fields}
894          */
895         var transform_fields_list = $.param(transform_fields);
897         // if inline_edit is successful, we need to go back to default view
898         var $del_hide = $(this).parent();
899         var $chg_submit = $(this);
901         if (need_to_post) {
902             // Make the Ajax post after setting all parameters
903             /**
904              * @var post_params Object containing parameters for the POST request
905              */
906             var post_params = {'ajax_request' : true,
907                             'sql_query' : sql_query,
908                             'disp_direction' : disp_mode,
909                             'token' : window.parent.token,
910                             'db' : window.parent.db,
911                             'table' : window.parent.table,
912                             'clause_is_unique' : nonunique,
913                             'where_clause' : where_clause,
914                             'rel_fields_list' : rel_fields_list,
915                             'do_transformations' : transformation_fields,
916                             'transform_fields_list' : transform_fields_list,
917                             'relational_display' : relational_display,
918                             'goto' : 'sql.php',
919                             'submit_type' : 'save'
920                           };
922             $.post('tbl_replace.php', post_params, function(data) {
923                 if(data.success == true) {
924                     PMA_ajaxShowMessage(data.message);
925                     if(disp_mode == 'vertical') {
926                         $this_td.parents('tbody').find('tr').find('.where_clause:nth(' + this_td_index + ')').attr('value', new_clause);
927                     }
928                     else {
929                         $this_td.parent('tr').find('.where_clause').attr('value', new_clause);
930                     }
931                     // remove possible previous feedback message
932                     $('#result_query').remove();
933                     if (typeof data.sql_query != 'undefined') {
934                         // display feedback
935                         $('#sqlqueryresults').prepend(data.sql_query);
936                     }
937                     PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data, disp_mode);
938                 } else {
939                     PMA_ajaxShowMessage(data.error);
940                 };
941             }) // end $.post()
942         } else {
943             // no posting was done but still need to display the row
944             // in its previous format
945             PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, '', disp_mode);
946         }
947     }) // End After editing, clicking again should post data
948 }, 'top.frame_content') // end $(document).ready()
952  * Visually put back the row in the state it was before entering Inline edit 
954  * (when called in the situation where no posting was done, the data
955  * parameter is empty) 
956  */
957 function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data, disp_mode) {
959     // deleting the hide button
960     // remove <br><br><a> tags
961     for ( var i = 0; i <= 2; i++) {
962         $del_hide.next().remove();
963     }
964     if(disp_mode != 'vertical'){
965         $chg_submit.empty();
966         $chg_submit.html('<span class="nowrap"></span>');
967         $chg_submit.children('span.nowrap').text(PMA_messages['strInlineEdit']);
968     } else {
969         $chg_submit.children('span.nowrap').empty();
970         $chg_submit.children('span.nowrap').append(data_vt);
971     }
973     // changing inline_edit_active to inline_edit_anchor
974     $this_td.removeClass('inline_edit_active').addClass('inline_edit_anchor');
976     // removing hover, marked and noclick classes
977     $this_td.parent('tr').removeClass('noclick');
978     if(disp_mode != 'vertical') {
979         $this_td.parent('tr').removeClass('hover').find('td').removeClass('hover');
980     } else {
981         $this_td.parents('tbody').find('tr').find('td:eq(' + $this_td.index() + ')').removeClass('marked');
982     }
984     $input_siblings.each(function() {
985         // Inline edit post has been successful.
986         $this_sibling = $(this);
988         var is_null = $this_sibling.find('input:checkbox').is(':checked');
989         if (is_null) {
990             $this_sibling.html('NULL');
991             $this_sibling.addClass('null');
992         } else {
993             $this_sibling.removeClass('null');
994             if($this_sibling.is(':not(.relation, .enum, .set)')) {
995                 /**
996                  * @var new_html    String containing value of the data field after edit
997                  */
998                 var new_html = $this_sibling.find('textarea').val();
1000                 if($this_sibling.is('.transformed')) {
1001                     var field_name = getFieldName($this_sibling, disp_mode);
1002                     if (typeof data.transformations != 'undefined') {
1003                         $.each(data.transformations, function(key, value) {
1004                             if(key == field_name) {
1005                                 if($this_sibling.is('.text_plain, .application_octetstream')) {
1006                                     new_html = value;
1007                                     return false;
1008                                 } else {
1009                                     var new_value = $this_sibling.find('textarea').val();
1010                                     new_html = $(value).append(new_value);
1011                                     return false;
1012                                 }
1013                             }
1014                         })
1015                     }
1016                 }
1017             } else {
1018                 var new_html = '';
1019                 var new_value = '';
1020                 $test_element = $this_sibling.find('select');
1021                 if ($test_element.length != 0) {
1022                     new_value = $test_element.val();
1023                 }
1024                 $test_element = $this_sibling.find('span.curr_value');
1025                 if ($test_element.length != 0) {
1026                     new_value = $test_element.text();
1027                 }
1029                 if($this_sibling.is('.relation')) {
1030                     var field_name = getFieldName($this_sibling, disp_mode);
1031                     if (typeof data.relations != 'undefined') {
1032                         $.each(data.relations, function(key, value) {
1033                             if(key == field_name) {
1034                                 new_html = $(value);
1035                                 return false;
1036                             }
1037                         })
1038                     }
1039                 } else if ($this_sibling.is('.enum')) {
1040                     new_html = new_value;
1041                 } else if ($this_sibling.is('.set')) {
1042                     if (new_value != null) {
1043                         $.each(new_value, function(key, value) {
1044                             new_html = new_html + value + ',';
1045                         })
1046                         new_html = new_html.substring(0, new_html.length-1);
1047                     }
1048                 }
1049             }
1050             $this_sibling.html(new_html);
1051         }
1052     })
1056  * Starting from some th, change the class of all td under it
1057  */
1058 function PMA_changeClassForColumn($this_th, newclass) {
1059     // index 0 is the th containing the big T
1060     var th_index = $this_th.index();
1061     var has_big_t = !$this_th.closest('tr').children(':first').hasClass('column_heading');
1062     // .eq() is zero-based
1063     if (has_big_t) {
1064         th_index--;
1065     }
1066     var $tds = $this_th.closest('table').find('tbody tr').find('td.data:eq('+th_index+')');
1067     if ($this_th.data('has_class_'+newclass)) {
1068         $tds.removeClass(newclass);
1069         $this_th.data('has_class_'+newclass, false);
1070     } else {
1071         $tds.addClass(newclass);
1072         $this_th.data('has_class_'+newclass, true);
1073     }
1076 $(document).ready(function() {
1078     $('.browse_foreign').live('click', function(e) {
1079         e.preventDefault();
1080         window.open(this.href, 'foreigners', 'width=640,height=240,scrollbars=yes,resizable=yes');
1081         $anchor = $(this);
1082         $anchor.addClass('browse_foreign_clicked');
1083         return false;
1084     });
1086     /**
1087      * vertical column highlighting in horizontal mode when hovering over the column header
1088      */
1089     $('.column_heading').live('hover', function() {
1090         PMA_changeClassForColumn($(this), 'hover');
1091         });
1093     /**
1094      * vertical column marking in horizontal mode when clicking the column header
1095      */
1096     $('.column_heading').live('click', function() {
1097         PMA_changeClassForColumn($(this), 'marked');
1098         });
1101 /**#@- */