1 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 * @fileoverview functions used wherever an sql query form is used
6 * @requires js/functions.js
11 * decode a string URL_encoded
14 * @return string the URL-decoded string
16 function PMA_urldecode(str) {
17 return decodeURIComponent(str.replace(/\+/g, '%20'));
21 * Get the field name for the current field. Required to construct the query
24 * @param $this_field jQuery object that points to the current field's tr
25 * @param disp_mode string
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();
37 var this_field_index = $this_field.index();
38 if(window.parent.text_dir == 'ltr') {
39 // 4 columns to account for the checkbox, edit, delete and appended inline edit anchors but index is zero-based so substract 3
40 var field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index-3 )+') a').text();
41 // happens when just one row (headings contain no a)
42 if ("" == field_name) {
43 field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index-3 )+')').text();
47 var field_name = $('#table_results').find('thead').find('th:nth('+ this_field_index+') a').text();
51 field_name = $.trim(field_name);
57 * The function that iterates over each row in the table_results and appends a
58 * new inline edit anchor to each table row.
60 * @param disp_mode string
62 function appendInlineAnchor(disp_mode) {
63 if(disp_mode == 'vertical') {
64 // there can be one or two tr containing this class, depending
65 // on the ModifyDeleteAtLeft and ModifyDeleteAtRight cfg parameters
66 $('#table_results tr')
67 .find('.edit_row_anchor')
68 .removeClass('.edit_row_anchor')
69 .parent().each(function() {
70 var $this_tr = $(this);
71 var $cloned_tr = $this_tr.clone();
73 var $img_object = $cloned_tr.find('img:first').attr('title', PMA_messages['strInlineEdit']);
76 .addClass('inline_edit_anchor')
77 .find('a').attr('href', '#')
79 .text(PMA_messages['strInlineEdit'])
80 .prepend($img_object);
82 $cloned_tr.insertAfter($this_tr);
85 $("#table_results").find('tr').find(':checkbox').closest('tr').find('th')
86 .attr('rowspan', '4');
89 $('.edit_row_anchor').each(function() {
92 $this_td.removeClass('edit_row_anchor');
94 var $cloned_anchor = $this_td.clone();
96 var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']);
98 $cloned_anchor.addClass('inline_edit_anchor')
99 .find('a').attr('href', '#')
101 .text(PMA_messages['strInlineEdit'])
102 .prepend($img_object);
104 $this_td.after($cloned_anchor);
107 $('#rowsDeleteForm').find('thead').find('th').each(function() {
108 if($(this).attr('colspan') == 3) {
109 $(this).attr('colspan', '4')
120 * @description <p>Ajax scripts for sql and browse pages</p>
122 * Actions ajaxified here:
124 * <li>Retrieve results of an SQL query</li>
125 * <li>Paginate the results table</li>
126 * <li>Sort the results table</li>
127 * <li>Change table according to display options</li>
128 * <li>Inline editing of data</li>
131 * @name document.ready
134 $(document).ready(function() {
137 * Set a parameter for all Ajax queries made on this page. Don't let the
138 * web server serve cached pages
145 * current value of the direction in which the table is displayed
150 var disp_mode = $("#top_direction_dropdown").val();
153 * Update value of {@link jQuery.disp_mode} everytime the direction dropdown changes value
155 * @name direction_dropdown_change
157 $("#top_direction_dropdown, #bottom_direction_dropdown").live('change', function(event) {
158 disp_mode = $(this).val();
162 * Attach the {@link appendInlineAnchor} function to a custom event, which
163 * will be triggered manually everytime the table of results is reloaded
165 * @name sqlqueryresults_live
167 $("#sqlqueryresults").live('appendAnchor',function() {
168 appendInlineAnchor(disp_mode);
172 * Trigger the appendAnchor event to prepare the first table for inline edit
175 * @name sqlqueryresults_trigger
177 $("#sqlqueryresults").trigger('appendAnchor');
180 * Append the "Show/Hide query box" message to the query input form
183 * @name appendToggleSpan
185 // do not add this link more than once
186 if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
187 $('<a id="togglequerybox"></a>')
188 .html(PMA_messages['strHideQueryBox'])
189 .appendTo("#sqlqueryform");
191 // Attach the toggling of the query box visibility to a click
192 $("#togglequerybox").bind('click', function() {
194 $link.siblings().slideToggle("medium");
195 if ($link.text() == PMA_messages['strHideQueryBox']) {
196 $link.text(PMA_messages['strShowQueryBox']);
198 $link.text(PMA_messages['strHideQueryBox']);
200 // avoid default click action
206 * Ajax Event handler for 'SQL Query Submit'
208 * @see PMA_ajaxShowMessage()
210 * @name sqlqueryform_submit
212 $("#sqlqueryform").live('submit', function(event) {
213 event.preventDefault();
214 // remove any div containing a previous error message
215 $('.error').remove();
218 PMA_ajaxShowMessage();
220 if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
221 $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
224 $.post($(this).attr('action'), $(this).serialize() , function(data) {
225 if(data.success == true) {
226 PMA_ajaxShowMessage(data.message);
227 $('#sqlqueryresults').show();
228 // this happens if a USE command was typed
229 if (typeof data.reload != 'undefined') {
230 $form.find('input[name=db]').val(data.db);
231 // need to regenerate the whole upper part
232 $form.find('input[name=ajax_request]').remove();
233 $form.append('<input type="hidden" name="reload" value="true" />');
234 $.post('db_sql.php', $form.serialize(), function(data) {
235 $('body').html(data);
236 }); // end inner post
239 else if (data.success == false ) {
240 // show an error message that stays on screen
241 $('#sqlqueryform').before(data.error);
242 $('#sqlqueryresults').hide();
245 $('#sqlqueryresults').show();
246 $("#sqlqueryresults").html(data);
247 $("#sqlqueryresults").trigger('appendAnchor');
248 if($("#togglequerybox").siblings(":visible").length > 0) {
249 $("#togglequerybox").trigger('click');
253 }) // end SQL Query submit
256 * Ajax Event handlers for Paginating the results table
260 * Paginate when we click any of the navigation buttons
262 * @name paginate_nav_button_click
263 * @uses PMA_ajaxShowMessage()
265 $("input[name=navig]").live('click', function(event) {
267 event.preventDefault();
269 PMA_ajaxShowMessage();
272 * @var the_form Object referring to the form element that paginates the results table
274 var the_form = $(this).parent("form");
276 $(the_form).append('<input type="hidden" name="ajax_request" value="true" />');
278 $.post($(the_form).attr('action'), $(the_form).serialize(), function(data) {
279 $("#sqlqueryresults").html(data);
280 $("#sqlqueryresults").trigger('appendAnchor');
282 })// end Paginate results table
285 * Paginate results with Page Selector dropdown
287 * @name paginate_dropdown_change
289 $("#pageselector").live('change', function(event) {
290 event.preventDefault();
292 PMA_ajaxShowMessage();
294 $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
295 $("#sqlqueryresults").html(data);
296 $("#sqlqueryresults").trigger('appendAnchor');
298 })// end Paginate results with Page Selector
301 * Ajax Event handler for sorting the results table
303 * @name table_results_sort_click
305 $("#table_results").find("a[title=Sort]").live('click', function(event) {
306 event.preventDefault();
308 PMA_ajaxShowMessage();
310 $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
311 $("#sqlqueryresults").html(data);
312 $("#sqlqueryresults").trigger('appendAnchor');
314 })//end Sort results table
317 * Ajax Event handler for the display options
319 * @name displayOptionsForm_submit
321 $("#displayOptionsForm").live('submit', function(event) {
322 event.preventDefault();
324 $.post($(this).attr('action'), $(this).serialize() + '&ajax_request=true' , function(data) {
325 $("#sqlqueryresults").html(data);
326 $("#sqlqueryresults").trigger('appendAnchor');
329 //end displayOptionsForm handler
332 * Ajax Event handlers for Inline Editing
336 * On click, replace the fields of current row with an input/textarea
338 * @name inline_edit_start
339 * @see PMA_ajaxShowMessage()
340 * @see getFieldName()
342 $(".inline_edit_anchor").live('click', function(event) {
344 event.preventDefault();
346 $(this).removeClass('inline_edit_anchor').addClass('inline_edit_active');
348 // Initialize some variables
349 if(disp_mode == 'vertical') {
351 * @var this_row_index Index of the current <td> in the parent <tr>
352 * Current <td> is the inline edit anchor.
354 var this_row_index = $(this).index();
356 * @var $input_siblings Object referring to all inline editable events from same row
358 var $input_siblings = $(this).parents('tbody').find('tr').find('.data_inline_edit:nth('+this_row_index+')');
360 * @var where_clause String containing the WHERE clause to select this row
362 var where_clause = $(this).parents('tbody').find('tr').find('.where_clause:nth('+this_row_index+')').val();
365 var $input_siblings = $(this).parent('tr').find('.data_inline_edit');
366 var where_clause = $(this).parent('tr').find('.where_clause').val();
369 $input_siblings.each(function() {
372 * @var data_value Current value of this field
374 var data_value = $(this).html();
376 // We need to retrieve the value from the server for truncated/relation fields
377 // Find the field name
380 * @var this_field Object referring to this field (<td>)
382 var $this_field = $(this);
384 * @var field_name String containing the name of this field.
385 * @see getFieldName()
387 var field_name = getFieldName($this_field, disp_mode);
389 // In each input sibling, wrap the current value in a textarea
390 // and store the current value in a hidden span
391 if($this_field.is(':not(.truncated, .transformed, .relation, .enum, .null)')) {
392 // handle non-truncated, non-transformed, non-relation values
393 // We don't need to get any more data, just wrap the value
394 $this_field.html('<textarea>'+data_value+'</textarea>')
395 .append('<span class="original_data">'+data_value+'</span>');
396 $(".original_data").hide();
398 else if($this_field.is('.truncated, .transformed')) {
400 //handle truncated/transformed values values
403 * @var sql_query String containing the SQL query used to retrieve value of truncated/transformed data
405 var sql_query = 'SELECT ' + field_name + ' FROM ' + window.parent.table + ' WHERE ' + where_clause;
407 // Make the Ajax call and get the data, wrap it and insert it
409 'token' : window.parent.token,
410 'db' : window.parent.db,
411 'ajax_request' : true,
412 'sql_query' : sql_query,
415 if(data.success == true) {
416 $this_field.html('<textarea>'+data.value+'</textarea>')
417 .append('<span class="original_data">'+data_value+'</span>');
418 $(".original_data").hide();
421 PMA_ajaxShowMessage(data.error);
425 else if($this_field.is('.relation')) {
430 * @var curr_value String containing the current value of this relational field
432 var curr_value = $this_field.find('a').text();
435 * @var post_params Object containing parameters for the POST request
438 'ajax_request' : true,
439 'get_relational_values' : true,
440 'db' : window.parent.db,
441 'table' : window.parent.table,
442 'column' : field_name,
443 'token' : window.parent.token,
444 'curr_value' : curr_value
447 $.post('sql.php', post_params, function(data) {
448 $this_field.html(data.dropdown)
449 .append('<span class="original_data">'+data_value+'</span>');
450 $(".original_data").hide();
453 else if($this_field.is('.enum')) {
457 * @var curr_value String containing the current value of this relational field
459 var curr_value = $this_field.text();
462 * @var post_params Object containing parameters for the POST request
465 'ajax_request' : true,
466 'get_enum_values' : true,
467 'db' : window.parent.db,
468 'table' : window.parent.table,
469 'column' : field_name,
470 'token' : window.parent.token,
471 'curr_value' : curr_value
474 $.post('sql.php', post_params, function(data) {
475 $this_field.html(data.dropdown)
476 .append('<span class="original_data">'+data_value+'</span>');
477 $(".original_data").hide();
480 else if($this_field.is('.null')) {
482 $this_field.html('<textarea></textarea>')
483 .append('<span class="original_data">NULL</span>');
484 $(".original_data").hide();
487 }) // End On click, replace the current field with an input/textarea
490 * After editing, clicking again should post data
493 * @name inline_edit_save
494 * @see PMA_ajaxShowMessage()
495 * @see getFieldName()
497 $(".inline_edit_active").live('click', function(event) {
499 event.preventDefault();
502 * @var $this_td Object referring to the td containing the
503 * "Inline Edit" link that was clicked to save the row that is
507 var $this_td = $(this);
509 // Initialize variables
510 if(disp_mode == 'vertical') {
512 * @var this_td_index Index of the current <td> in the parent <tr>
513 * Current <td> is the inline edit anchor.
515 var this_td_index = $this_td.index();
517 * @var $input_siblings Object referring to all inline editable events from same row
519 var $input_siblings = $this_td.parents('tbody').find('tr').find('.data_inline_edit:nth('+this_td_index+')');
521 * @var where_clause String containing the WHERE clause to select this row
523 var where_clause = $this_td.parents('tbody').find('tr').find('.where_clause:nth('+this_td_index+')').val();
526 var $input_siblings = $this_td.parent('tr').find('.data_inline_edit');
527 var where_clause = $this_td.parent('tr').find('.where_clause').val();
531 * @var nonunique Boolean, whether this row is unique or not
533 if($this_td.is('.nonunique')) {
540 // Collect values of all fields to submit, we don't know which changed
542 * @var params_to_submit Array containing the name/value pairs of all fields
544 var params_to_submit = {};
546 * @var relation_fields Array containing the name/value pairs of relational fields
548 var relation_fields = {};
550 * @var transform_fields Array containing the name/value pairs for transformed fields
552 var transform_fields = {};
554 * @var transformation_fields Boolean, if there are any transformed fields in this row
556 var transformation_fields = false;
558 $input_siblings.each(function() {
561 * @var this_field Object referring to this field (<td>)
563 var $this_field = $(this);
565 * @var field_name String containing the name of this field.
566 * @see getFieldName()
568 var field_name = getFieldName($this_field, disp_mode);
571 * @var this_field_params Array temporary storage for the name/value of current field
573 var this_field_params = {};
575 if($this_field.is('.transformed')) {
576 transformation_fields = true;
579 if($this_field.is(":not(.relation, .enum)")) {
580 this_field_params[field_name] = $this_field.find('textarea').val();
581 if($this_field.is('.transformed')) {
582 $.extend(transform_fields, this_field_params);
586 this_field_params[field_name] = $this_field.find('select').val();
588 if($this_field.is('.relation')) {
589 $.extend(relation_fields, this_field_params);
593 $.extend(params_to_submit, this_field_params);
597 * @var sql_query String containing the SQL query to update this row
599 var sql_query = 'UPDATE ' + window.parent.table + ' SET ';
601 $.each(params_to_submit, function(key, value) {
602 if(value.length == 0) {
605 sql_query += ' ' + key + "='" + value + "' , ";
607 //Remove the last ',' appended in the above loop
608 sql_query = sql_query.replace(/,\s$/, '');
609 sql_query += ' WHERE ' + PMA_urldecode(where_clause);
612 * @var rel_fields_list String, url encoded representation of {@link relations_fields}
614 var rel_fields_list = $.param(relation_fields);
617 * @var transform_fields_list String, url encoded representation of {@link transform_fields}
619 var transform_fields_list = $.param(transform_fields);
621 // Make the Ajax post after setting all parameters
623 * @var post_params Object containing parameters for the POST request
625 var post_params = {'ajax_request' : true,
626 'sql_query' : sql_query,
627 'disp_direction' : disp_mode,
628 'token' : window.parent.token,
629 'db' : window.parent.db,
630 'table' : window.parent.table,
631 'clause_is_unique' : nonunique,
632 'where_clause' : where_clause,
633 'rel_fields_list' : rel_fields_list,
634 'do_transformations' : transformation_fields,
635 'transform_fields_list' : transform_fields_list,
637 'submit_type' : 'save'
640 $.post('tbl_replace.php', post_params, function(data) {
641 if(data.success == true) {
642 PMA_ajaxShowMessage(data.message);
643 $this_td.removeClass('inline_edit_active').addClass('inline_edit_anchor');
645 $input_siblings.each(function() {
646 // Inline edit post has been successful.
647 if($(this).is(':not(.relation, .enum)')) {
649 * @var new_html String containing value of the data field after edit
651 var new_html = $(this).find('textarea').val();
653 if($(this).is('.transformed')) {
654 var field_name = getFieldName($(this), disp_mode);
655 var this_field = $(this);
657 $.each(data.transformations, function(key, value) {
658 if(key == field_name) {
659 if($(this_field).is('.text_plain, .application_octetstream')) {
664 var new_value = $(this_field).find('textarea').val();
665 new_html = $(value).append(new_value);
673 var new_html = $(this).find('select').val();
674 if($(this).is('.relation')) {
675 var field_name = getFieldName($(this), disp_mode);
676 var this_field = $(this);
678 $.each(data.relations, function(key, value) {
679 if(key == field_name) {
680 var new_value = $(this_field).find('select').val();
681 new_html = $(value).append(new_value);
687 $(this).html(new_html);
691 PMA_ajaxShowMessage(data.error);
694 }) // End After editing, clicking again should post data
695 }, 'top.frame_content') // end $(document).ready()
698 * Starting from some th, change the class of all td under it
700 function PMA_changeClassForColumn($this_th, klass) {
701 // index 0 is the th containing the big T
702 var th_index = $this_th.index();
703 // .eq() is zero-based
705 var $tr_with_data = $this_th.closest('table').find('tbody tr ').has('td.data_inline_edit');
706 $tr_with_data.each(function() {
707 $(this).find('td.data_inline_edit:eq('+th_index+')').toggleClass(klass);
711 $(document).ready(function() {
713 * column highlighting in horizontal mode when hovering over the column header
715 $('.column_heading').live('hover', function() {
716 PMA_changeClassForColumn($(this), 'hover');
720 * column marking in horizontal mode when clicking the column header
722 $('.column_heading').live('click', function() {
723 PMA_changeClassForColumn($(this), 'marked');