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