Translation update done using Pootle.
[phpmyadmin-themes.git] / js / sql.js
blob57cb76123678353f056feb693a92cb69eb51a214
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 function PMA_urldecode(str) {
17     return decodeURIComponent(str.replace(/\+/g, '%20'));
20 /**
21  * Get the field name for the current field.  Required to construct the query
22  * for inline editing
23  *
24  * @param   $this_field  jQuery object that points to the current field's tr
25  * @param   disp_mode    string
26  */
27 function getFieldName($this_field, disp_mode) {
29     if(disp_mode == 'vertical') {
30         var field_name = $this_field.siblings('th').find('a').text();
31     }
32     else {
33         var this_field_index = $this_field.index();
34         if(window.parent.text_dir == 'ltr') {
35             // 4 columns to account for the checkbox, edit, delete and appended inline edit anchors but index is zero-based so substract 3
36             var field_name = $this_field.parents('table').find('thead').find('th:nth('+ (this_field_index-3 )+') a').text();
37         }
38         else {
39             var field_name = $this_field.parents('table').find('thead').find('th:nth('+ this_field_index+') a').text();
40         }
41     }
43     field_name = $.trim(field_name);
45     return field_name;
48 /**
49  * The function that iterates over each row in the table_results and appends a
50  * new inline edit anchor to each table row.
51  *
52  * @param   disp_mode   string
53  */
54 function appendInlineAnchor(disp_mode) {
55     if(disp_mode == 'vertical') {
56         // there can be one or two tr containing this class, depending
57         // on the ModifyDeleteAtLeft and ModifyDeleteAtRight cfg parameters 
58         $('#table_results tr')
59             .find('.edit_row_anchor')
60             .removeClass('.edit_row_anchor')
61             .parent().each(function() {
62             var $this_tr = $(this);
63             var $cloned_tr = $this_tr.clone();
65             var $img_object = $cloned_tr.find('img:first').attr('title', PMA_messages['strInlineEdit']);
67             $cloned_tr.find('td')
68              .addClass('inline_edit_anchor')
69              .find('a').attr('href', '#')
70              .find('span')
71              .text(PMA_messages['strInlineEdit'])
72              .prepend($img_object);
74             $cloned_tr.insertAfter($this_tr);
75         });
77         $("#table_results").find('tr').find(':checkbox').closest('tr').find('th')
78          .attr('rowspan', '4');
79     }
80     else {
81         $('.edit_row_anchor').each(function() {
83             $this_td = $(this)
84             $this_td.removeClass('edit_row_anchor');
86             var $cloned_anchor = $this_td.clone();
88             var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']);
90             $cloned_anchor.addClass('inline_edit_anchor')
91             .find('a').attr('href', '#')
92             .find('span')
93             .text(PMA_messages['strInlineEdit'])
94             .prepend($img_object);
96             $this_td.after($cloned_anchor);
97         });
99         $('#rowsDeleteForm').find('thead').find('th').each(function() {
100             if($(this).attr('colspan') == 3) {
101                 $(this).attr('colspan', '4')
102             }
103         })
104     }
107 /**#@+
108  * @namespace   jQuery
109  */
112  * @description <p>Ajax scripts for sql and browse pages</p>
114  * Actions ajaxified here:
115  * <ul>
116  * <li>Retrieve results of an SQL query</li>
117  * <li>Paginate the results table</li>
118  * <li>Sort the results table</li>
119  * <li>Change table according to display options</li>
120  * <li>Inline editing of data</li>
121  * </ul>
123  * @name        document.ready
124  * @memberOf    jQuery
125  */
126 $(document).ready(function() {
128     /**
129      * Set a parameter for all Ajax queries made on this page.  Don't let the
130      * web server serve cached pages
131      */
132     $.ajaxSetup({
133         cache: 'false'
134     });
136     /**
137      * current value of the direction in which the table is displayed
138      * @type    String
139      * @fieldOf jQuery
140      * @name    disp_mode
141      */
142     var disp_mode = $("#top_direction_dropdown").val();
144     /**
145      * Update value of {@link jQuery.disp_mode} everytime the direction dropdown changes value
146      * @memberOf    jQuery
147      * @name        direction_dropdown_change
148      */
149     $("#top_direction_dropdown, #bottom_direction_dropdown").live('change', function(event) {
150         disp_mode = $(this).val();
151     })
153     /**
154      * Attach the {@link appendInlineAnchor} function to a custom event, which
155      * will be triggered manually everytime the table of results is reloaded
156      * @memberOf    jQuery
157      * @name        sqlqueryresults_live
158      */
159     $("#sqlqueryresults").live('appendAnchor',function() {
160         appendInlineAnchor(disp_mode);
161     })
163     /**
164      * Trigger the appendAnchor event to prepare the first table for inline edit
165      *
166      * @memberOf    jQuery
167      * @name        sqlqueryresults_trigger
168      */
169     $("#sqlqueryresults").trigger('appendAnchor');
171     /**
172      * Append the "Show/Hide query box" message to the query input form
173      *
174      * @memberOf jQuery
175      * @name    appendToggleSpan
176      */
177     // do not add this link more than once
178     if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
179         $('<a id="togglequerybox"></a>')
180         .html(PMA_messages['strHideQueryBox'])
181         .appendTo("#sqlqueryform");
183         // Attach the toggling of the query box visibility to a click
184         $("#togglequerybox").bind('click', function() {
185             var $link = $(this)
186             $link.siblings().slideToggle("medium");
187             if ($link.text() == PMA_messages['strHideQueryBox']) {
188                 $link.text(PMA_messages['strShowQueryBox']);
189             } else {
190                 $link.text(PMA_messages['strHideQueryBox']);
191             }
192             // avoid default click action
193             return false;
194         })
195     }
196     
197     /**
198      * Ajax Event handler for 'SQL Query Submit'
199      *
200      * @see         PMA_ajaxShowMessage()
201      * @memberOf    jQuery
202      * @name        sqlqueryform_submit
203      */
204     $("#sqlqueryform").live('submit', function(event) {
205         event.preventDefault();
206         // remove any div containing a previous error message
207         $('.error').remove();
209         $form = $(this);
210         PMA_ajaxShowMessage();
212             if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
213                 $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
214             }
216         $.post($(this).attr('action'), $(this).serialize() , function(data) {
217             if(data.success == true) {
218                 PMA_ajaxShowMessage(data.message);
219                 $('#sqlqueryresults').show();
220                 // this happens if a USE command was typed
221                 if (typeof data.reload != 'undefined') {
222                     $form.find('input[name=db]').val(data.db);
223                     // need to regenerate the whole upper part
224                     $form.find('input[name=ajax_request]').remove();
225                     $form.append('<input type="hidden" name="reload" value="true" />');
226                     $.post('db_sql.php', $form.serialize(), function(data) {
227                         $('body').html(data);
228                     }); // end inner post
229                 }
230             }
231             else if (data.success == false ) {
232                 // show an error message that stays on screen 
233                 $('#sqlqueryform').before(data.error);
234                 $('#sqlqueryresults').hide();
235             }
236             else {
237                 $('#sqlqueryresults').show();
238                 $("#sqlqueryresults").html(data);
239                 $("#sqlqueryresults").trigger('appendAnchor');
240                 if($("#togglequerybox").siblings(":visible").length > 0) {
241                     $("#togglequerybox").trigger('click');
242                 }
243             }
244         }) // end $.post()
245     }) // end SQL Query submit
247     /**
248      * Ajax Event handlers for Paginating the results table
249      */
251     /**
252      * Paginate when we click any of the navigation buttons
253      * @memberOf    jQuery
254      * @name        paginate_nav_button_click
255      * @uses        PMA_ajaxShowMessage()
256      */
257     $("input[name=navig]").live('click', function(event) {
258         /** @lends jQuery */
259         event.preventDefault();
261         PMA_ajaxShowMessage();
262         
263         /**
264          * @var the_form    Object referring to the form element that paginates the results table
265          */
266         var the_form = $(this).parent("form");
268         $(the_form).append('<input type="hidden" name="ajax_request" value="true" />');
270         $.post($(the_form).attr('action'), $(the_form).serialize(), function(data) {
271             $("#sqlqueryresults").html(data);
272             $("#sqlqueryresults").trigger('appendAnchor');
273         }) // end $.post()
274     })// end Paginate results table
276     /**
277      * Paginate results with Page Selector dropdown
278      * @memberOf    jQuery
279      * @name        paginate_dropdown_change
280      */
281     $("#pageselector").live('change', function(event) {
282         event.preventDefault();
284         PMA_ajaxShowMessage();
286         $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
287             $("#sqlqueryresults").html(data);
288             $("#sqlqueryresults").trigger('appendAnchor');
289         }) // end $.get()
290     })// end Paginate results with Page Selector
292     /**
293      * Ajax Event handler for sorting the results table
294      * @memberOf    jQuery
295      * @name        table_results_sort_click
296      */
297     $("#table_results").find("a[title=Sort]").live('click', function(event) {
298         event.preventDefault();
300         PMA_ajaxShowMessage();
302         $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
303             $("#sqlqueryresults").html(data);
304             $("#sqlqueryresults").trigger('appendAnchor');
305         }) // end $.get()
306     })//end Sort results table
308     /**
309      * Ajax Event handler for the display options
310      * @memberOf    jQuery
311      * @name        displayOptionsForm_submit
312      */
313     $("#displayOptionsForm").live('submit', function(event) {
314         event.preventDefault();
316         $.post($(this).attr('action'), $(this).serialize() + '&ajax_request=true' , function(data) {
317             $("#sqlqueryresults").html(data);
318             $("#sqlqueryresults").trigger('appendAnchor');
319         }) // end $.post()
320     })
321     //end displayOptionsForm handler
323     /**
324      * Ajax Event handlers for Inline Editing
325      */
327     /**
328      * On click, replace the fields of current row with an input/textarea
329      * @memberOf    jQuery
330      * @name        inline_edit_start
331      * @see         PMA_ajaxShowMessage()
332      * @see         getFieldName()
333      */
334     $(".inline_edit_anchor").live('click', function(event) {
335         /** @lends jQuery */
336         event.preventDefault();
338         $(this).removeClass('inline_edit_anchor').addClass('inline_edit_active');
340         // Initialize some variables
341         if(disp_mode == 'vertical') {
342             /**
343              * @var this_row_index  Index of the current <td> in the parent <tr>
344              *                      Current <td> is the inline edit anchor.
345              */
346             var this_row_index = $(this).index();
347             /**
348              * @var $input_siblings  Object referring to all inline editable events from same row
349              */
350             var $input_siblings = $(this).parents('tbody').find('tr').find('.data_inline_edit:nth('+this_row_index+')');
351             /**
352              * @var where_clause    String containing the WHERE clause to select this row
353              */
354             var where_clause = $(this).parents('tbody').find('tr').find('.where_clause:nth('+this_row_index+')').val();
355         }
356         else {
357             var $input_siblings = $(this).parent('tr').find('.data_inline_edit');
358             var where_clause = $(this).parent('tr').find('.where_clause').val();
359         }
361         $input_siblings.each(function() {
362             /** @lends jQuery */
363             /**
364              * @var data_value  Current value of this field
365              */
366             var data_value = $(this).html();
368             // We need to retrieve the value from the server for truncated/relation fields
369             // Find the field name
370             
371             /**
372              * @var this_field  Object referring to this field (<td>)
373              */
374             var $this_field = $(this);
375             /**
376              * @var field_name  String containing the name of this field.
377              * @see getFieldName()
378              */
379             var field_name = getFieldName($this_field, disp_mode);
381             // In each input sibling, wrap the current value in a textarea
382             // and store the current value in a hidden span
383             if($this_field.is(':not(.truncated, .transformed, .relation, .enum, .null)')) {
384                 // handle non-truncated, non-transformed, non-relation values
385                 // We don't need to get any more data, just wrap the value
386                 $this_field.html('<textarea>'+data_value+'</textarea>')
387                 .append('<span class="original_data">'+data_value+'</span>');
388                 $(".original_data").hide();
389             }
390             else if($this_field.is('.truncated, .transformed')) {
391                 /** @lends jQuery */
392                 //handle truncated/transformed values values
394                 /**
395                  * @var sql_query   String containing the SQL query used to retrieve value of truncated/transformed data
396                  */
397                 var sql_query = 'SELECT ' + field_name + ' FROM ' + window.parent.table + ' WHERE ' + where_clause;
399                 // Make the Ajax call and get the data, wrap it and insert it
400                 $.post('sql.php', {
401                     'token' : window.parent.token,
402                     'db' : window.parent.db,
403                     'ajax_request' : true,
404                     'sql_query' : sql_query,
405                     'inline_edit' : true
406                 }, function(data) {
407                     if(data.success == true) {
408                         $this_field.html('<textarea>'+data.value+'</textarea>')
409                         .append('<span class="original_data">'+data_value+'</span>');
410                         $(".original_data").hide();
411                     }
412                     else {
413                         PMA_ajaxShowMessage(data.error);
414                     }
415                 }) // end $.post()
416             }
417             else if($this_field.is('.relation')) {
418                 /** @lends jQuery */
419                 //handle relations
421                 /**
422                  * @var curr_value  String containing the current value of this relational field
423                  */
424                 var curr_value = $this_field.find('a').text();
426                 /**
427                  * @var post_params Object containing parameters for the POST request
428                  */
429                 var post_params = {
430                         'ajax_request' : true,
431                         'get_relational_values' : true,
432                         'db' : window.parent.db,
433                         'table' : window.parent.table,
434                         'column' : field_name,
435                         'token' : window.parent.token,
436                         'curr_value' : curr_value
437                 }
439                 $.post('sql.php', post_params, function(data) {
440                     $this_field.html(data.dropdown)
441                     .append('<span class="original_data">'+data_value+'</span>');
442                     $(".original_data").hide();
443                 }) // end $.post()
444             }
445             else if($this_field.is('.enum')) {
446                 /** @lends jQuery */
447                 //handle enum fields
448                 /**
449                  * @var curr_value  String containing the current value of this relational field
450                  */
451                 var curr_value = $this_field.text();
453                 /**
454                  * @var post_params Object containing parameters for the POST request
455                  */
456                 var post_params = {
457                         'ajax_request' : true,
458                         'get_enum_values' : true,
459                         'db' : window.parent.db,
460                         'table' : window.parent.table,
461                         'column' : field_name,
462                         'token' : window.parent.token,
463                         'curr_value' : curr_value
464                 }
466                 $.post('sql.php', post_params, function(data) {
467                     $this_field.html(data.dropdown)
468                     .append('<span class="original_data">'+data_value+'</span>');
469                     $(".original_data").hide();
470                 }) // end $.post()
471             }
472             else if($this_field.is('.null')) {
473                 //handle null fields
474                 $this_field.html('<textarea></textarea>')
475                 .append('<span class="original_data">NULL</span>');
476                 $(".original_data").hide();
477             }
478         })
479     }) // End On click, replace the current field with an input/textarea
481     /**
482      * After editing, clicking again should post data
483      *
484      * @memberOf    jQuery
485      * @name        inline_edit_save
486      * @see         PMA_ajaxShowMessage()
487      * @see         getFieldName()
488      */
489     $(".inline_edit_active").live('click', function(event) {
490         /** @lends jQuery */
491         event.preventDefault();
493         /**
494          * @var $this_td    Object referring to the td containing the 
495          * "Inline Edit" link that was clicked to save the row that is 
496          * being edited
497          *
498          */
499         var $this_td = $(this);
501         // Initialize variables
502         if(disp_mode == 'vertical') {
503             /**
504              * @var this_td_index  Index of the current <td> in the parent <tr>
505              *                      Current <td> is the inline edit anchor.
506              */
507             var this_td_index = $this_td.index();
508             /**
509              * @var $input_siblings  Object referring to all inline editable events from same row
510              */
511             var $input_siblings = $this_td.parents('tbody').find('tr').find('.data_inline_edit:nth('+this_td_index+')');
512             /**
513              * @var where_clause    String containing the WHERE clause to select this row
514              */
515             var where_clause = $this_td.parents('tbody').find('tr').find('.where_clause:nth('+this_td_index+')').val();
516         }
517         else {
518             var $input_siblings = $this_td.parent('tr').find('.data_inline_edit');
519             var where_clause = $this_td.parent('tr').find('.where_clause').val();
520         }
522         /**
523          * @var nonunique   Boolean, whether this row is unique or not
524          */
525         if($this_td.is('.nonunique')) {
526             var nonunique = 0;
527         }
528         else {
529             var nonunique = 1;
530         }
532         // Collect values of all fields to submit, we don't know which changed
533         /**
534          * @var params_to_submit    Array containing the name/value pairs of all fields
535          */
536         var params_to_submit = {};
537         /**
538          * @var relation_fields Array containing the name/value pairs of relational fields
539          */
540         var relation_fields = {};
541         /**
542          * @var transform_fields    Array containing the name/value pairs for transformed fields
543          */
544         var transform_fields = {};
545         /**
546          * @var transformation_fields   Boolean, if there are any transformed fields in this row
547          */
548         var transformation_fields = false;
550         $input_siblings.each(function() {
551             /** @lends jQuery */
552             /**
553              * @var this_field  Object referring to this field (<td>)
554              */
555             var $this_field = $(this);
556             /**
557              * @var field_name  String containing the name of this field.
558              * @see getFieldName()
559              */
560             var field_name = getFieldName($this_field, disp_mode);
562             /**
563              * @var this_field_params   Array temporary storage for the name/value of current field
564              */
565             var this_field_params = {};
567             if($this_field.is('.transformed')) {
568                 transformation_fields =  true;
569             }
571             if($this_field.is(":not(.relation, .enum)")) {
572                 this_field_params[field_name] = $this_field.find('textarea').val();
573                 if($this_field.is('.transformed')) {
574                     $.extend(transform_fields, this_field_params);
575                 }
576             }
577             else {
578                 this_field_params[field_name] = $this_field.find('select').val();
580                 if($this_field.is('.relation')) {
581                     $.extend(relation_fields, this_field_params);
582                 }
583             }
585             $.extend(params_to_submit, this_field_params);
586         })
588         /**
589          * @var sql_query   String containing the SQL query to update this row
590          */
591         var sql_query = 'UPDATE ' + window.parent.table + ' SET ';
593         $.each(params_to_submit, function(key, value) {
594             if(value.length == 0) {
595                 value = 'NULL'
596             }
597            sql_query += ' ' + key + "='" + value + "' , ";
598         })
599         //Remove the last ',' appended in the above loop
600         sql_query = sql_query.replace(/,\s$/, '');
601         sql_query += ' WHERE ' + PMA_urldecode(where_clause);
603         /**
604          * @var rel_fields_list  String, url encoded representation of {@link relations_fields}
605          */
606         var rel_fields_list = $.param(relation_fields);
608         /**
609          * @var transform_fields_list  String, url encoded representation of {@link transform_fields}
610          */
611         var transform_fields_list = $.param(transform_fields);
613         // Make the Ajax post after setting all parameters
614         /**
615          * @var post_params Object containing parameters for the POST request
616          */
617         var post_params = {'ajax_request' : true,
618                             'sql_query' : sql_query,
619                             'disp_direction' : disp_mode,
620                             'token' : window.parent.token,
621                             'db' : window.parent.db,
622                             'table' : window.parent.table,
623                             'clause_is_unique' : nonunique,
624                             'where_clause' : where_clause,
625                             'rel_fields_list' : rel_fields_list,
626                             'do_transformations' : transformation_fields,
627                             'transform_fields_list' : transform_fields_list,
628                             'goto' : 'sql.php',
629                             'submit_type' : 'save'
630                           };
632         $.post('tbl_replace.php', post_params, function(data) {
633             if(data.success == true) {
634                 PMA_ajaxShowMessage(data.message);
635                 $this_td.removeClass('inline_edit_active').addClass('inline_edit_anchor');
637                 $input_siblings.each(function() {
638                     // Inline edit post has been successful.
639                     if($(this).is(':not(.relation, .enum)')) {
640                         /**
641                          * @var new_html    String containing value of the data field after edit
642                          */
643                         var new_html = $(this).find('textarea').val();
645                         if($(this).is('.transformed')) {
646                             var field_name = getFieldName($(this), disp_mode);
647                             var this_field = $(this);
649                             $.each(data.transformations, function(key, value) {
650                                 if(key == field_name) {
651                                     if($(this_field).is('.text_plain, .application_octetstream')) {
652                                         new_html = value;
653                                         return false;
654                                     }
655                                     else {
656                                         var new_value = $(this_field).find('textarea').val();
657                                         new_html = $(value).append(new_value);
658                                         return false;
659                                     }
660                                 }
661                             })
662                         }
663                     }
664                     else {
665                         var new_html = $(this).find('select').val();
666                         if($(this).is('.relation')) {
667                             var field_name = getFieldName($(this), disp_mode);
668                             var this_field = $(this);
670                             $.each(data.relations, function(key, value) {
671                                 if(key == field_name) {
672                                     var new_value = $(this_field).find('select').val();
673                                     new_html = $(value).append(new_value);
674                                     return false;
675                                 }
676                             })
677                         }
678                     }
679                     $(this).html(new_html);
680                 })
681             }
682             else {
683                 PMA_ajaxShowMessage(data.error);
684             };
685         }) // end $.post()
686     }) // End After editing, clicking again should post data
687 }, 'top.frame_content') // end $(document).ready()
690  * Starting from some th, change the class of all td under it
691  */
692 function PMA_changeClassForColumn($this_th, klass) {
693     // index 0 is the th containing the big T
694     var th_index = $this_th.index();
695     // .eq() is zero-based
696     th_index--;
697     var $tr_with_data = $this_th.closest('table').find('tbody tr ').has('td.data_inline_edit');
698     $tr_with_data.each(function() {
699         $(this).find('td.data_inline_edit:eq('+th_index+')').toggleClass(klass);
700     });
703 $(document).ready(function() {
704     /**
705      * column highlighting in horizontal mode when hovering over the column header
706      */
707     $('.column_heading').live('hover', function() {
708         PMA_changeClassForColumn($(this), 'hover'); 
709         });
711     /**
712      * column marking in horizontal mode when clicking the column header
713      */
714     $('.column_heading').live('click', function() {
715         PMA_changeClassForColumn($(this), 'marked'); 
716         });
719 /**#@- */