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_obj jQuery object that points to the current field's tr
25 * @param disp_mode string
27 function getFieldName(this_field_obj, disp_mode) {
29 if(disp_mode == 'vertical') {
30 var field_name = $(this_field_obj).siblings('th').find('a').text();
33 var this_field_index = $(this_field_obj).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_obj).parents('table').find('thead').find('th:nth('+ (this_field_index-3 )+') a').text();
39 var field_name = $(this_field_obj).parents('table').find('thead').find('th:nth('+ this_field_index+') a').text();
43 field_name = $.trim(field_name);
49 * The function that iterates over each row in the table_results and appends a
50 * new inline edit anchor to each table row.
52 * @param disp_mode string
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').find('.edit_row_anchor').parent().each(function() {
59 var $this_tr = $(this);
60 var $cloned_tr = $this_tr.clone();
62 var $img_object = $cloned_tr.find('img:first').attr('title', PMA_messages['strInlineEdit']);
65 .find('a').attr('href', '#')
67 .text(PMA_messages['strInlineEdit'])
68 .prepend($img_object);
70 $cloned_tr.insertAfter($this_tr);
73 $("#table_results").find('tr').find(':checkbox').closest('tr').find('th')
74 .attr('rowspan', '4');
77 $('.edit_row_anchor').each(function() {
80 $this_td.removeClass('edit_row_anchor');
82 var $cloned_anchor = $this_td.clone();
84 var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']);
86 $cloned_anchor.addClass('edit_row_anchor')
87 .find('a').attr('href', '#')
89 .text(PMA_messages['strInlineEdit'])
90 .prepend($img_object);
92 $this_td.after($cloned_anchor);
95 $('#rowsDeleteForm').find('thead').find('th').each(function() {
96 if($(this).attr('colspan') == 3) {
97 $(this).attr('colspan', '4')
108 * @description <p>Ajax scripts for sql and browse pages</p>
110 * Actions ajaxified here:
112 * <li>Retrieve results of an SQL query</li>
113 * <li>Paginate the results table</li>
114 * <li>Sort the results table</li>
115 * <li>Change table according to display options</li>
116 * <li>Inline editing of data</li>
119 * @name document.ready
122 $(document).ready(function() {
125 * Set a parameter for all Ajax queries made on this page. Don't let the
126 * web server serve cached pages
133 * current value of the direction in which the table is displayed
138 var disp_mode = $("#top_direction_dropdown").val();
141 * Update value of {@link jQuery.disp_mode} everytime the direction dropdown changes value
143 * @name direction_dropdown_change
145 $("#top_direction_dropdown, #bottom_direction_dropdown").live('change', function(event) {
146 disp_mode = $(this).val();
150 * Attach the {@link appendInlineAnchor} function to a custom event, which
151 * will be triggered manually everytime the table of results is reloaded
153 * @name sqlqueryresults_live
155 $("#sqlqueryresults").live('appendAnchor',function() {
156 appendInlineAnchor(disp_mode);
160 * Trigger the appendAnchor event to prepare the first table for inline edit
163 * @name sqlqueryresults_trigger
165 $("#sqlqueryresults").trigger('appendAnchor');
168 * Append the "Show/Hide query box" message to the query input form
171 * @name appendToggleSpan
173 // do not add this link more than once
174 if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
175 $('<a id="togglequerybox"></a>')
176 .html(PMA_messages['strHideQueryBox'])
177 .appendTo("#sqlqueryform");
179 // Attach the toggling of the query box visibility to a click
180 $("#togglequerybox").bind('click', function() {
182 $link.siblings().slideToggle("medium");
183 if ($link.text() == PMA_messages['strHideQueryBox']) {
184 $link.text(PMA_messages['strShowQueryBox']);
186 $link.text(PMA_messages['strHideQueryBox']);
188 // avoid default click action
194 * Ajax Event handler for 'SQL Query Submit'
196 * @see PMA_ajaxShowMessage()
198 * @name sqlqueryform_submit
200 $("#sqlqueryform").live('submit', function(event) {
201 event.preventDefault();
202 // remove any div containing a previous error message
203 $('.error').remove();
206 PMA_ajaxShowMessage();
208 if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
209 $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
212 $.post($(this).attr('action'), $(this).serialize() , function(data) {
213 if(data.success == true) {
214 PMA_ajaxShowMessage(data.message);
215 $('#sqlqueryresults').show();
216 // this happens if a USE command was typed
217 if (typeof data.reload != 'undefined') {
218 $form.find('input[name=db]').val(data.db);
219 // need to regenerate the whole upper part
220 $form.find('input[name=ajax_request]').remove();
221 $form.append('<input type="hidden" name="reload" value="true" />');
222 $.post('db_sql.php', $form.serialize(), function(data) {
223 $('body').html(data);
224 }); // end inner post
227 else if (data.success == false ) {
228 // show an error message that stays on screen
229 $('#sqlqueryform').before(data.error);
230 $('#sqlqueryresults').hide();
233 $('#sqlqueryresults').show();
234 $("#sqlqueryresults").html(data);
235 $("#sqlqueryresults").trigger('appendAnchor');
236 if($("#togglequerybox").siblings(":visible").length > 0) {
237 $("#togglequerybox").trigger('click');
241 }) // end SQL Query submit
244 * Ajax Event handlers for Paginating the results table
248 * Paginate when we click any of the navigation buttons
250 * @name paginate_nav_button_click
251 * @uses PMA_ajaxShowMessage()
253 $("input[name=navig]").live('click', function(event) {
255 event.preventDefault();
257 PMA_ajaxShowMessage();
260 * @var the_form Object referring to the form element that paginates the results table
262 var the_form = $(this).parent("form");
264 $(the_form).append('<input type="hidden" name="ajax_request" value="true" />');
266 $.post($(the_form).attr('action'), $(the_form).serialize(), function(data) {
267 $("#sqlqueryresults").html(data);
268 $("#sqlqueryresults").trigger('appendAnchor');
270 })// end Paginate results table
273 * Paginate results with Page Selector dropdown
275 * @name paginate_dropdown_change
277 $("#pageselector").live('change', function(event) {
278 event.preventDefault();
280 PMA_ajaxShowMessage();
282 $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
283 $("#sqlqueryresults").html(data);
284 $("#sqlqueryresults").trigger('appendAnchor');
286 })// end Paginate results with Page Selector
289 * Ajax Event handler for sorting the results table
291 * @name table_results_sort_click
293 $("#table_results").find("a[title=Sort]").live('click', function(event) {
294 event.preventDefault();
296 PMA_ajaxShowMessage();
298 $.get($(this).attr('href'), $(this).serialize() + '&ajax_request=true', function(data) {
299 $("#sqlqueryresults").html(data);
300 $("#sqlqueryresults").trigger('appendAnchor');
302 })//end Sort results table
305 * Ajax Event handler for the display options
307 * @name displayOptionsForm_submit
309 $("#displayOptionsForm").live('submit', function(event) {
310 event.preventDefault();
312 $.post($(this).attr('action'), $(this).serialize() + '&ajax_request=true' , function(data) {
313 $("#sqlqueryresults").html(data);
314 $("#sqlqueryresults").trigger('appendAnchor');
317 //end displayOptionsForm handler
320 * Ajax Event handlers for Inline Editing
324 * On click, replace the fields of current row with an input/textarea
326 * @name inline_edit_start
327 * @see PMA_ajaxShowMessage()
328 * @see getFieldName()
330 $(".edit_row_anchor").live('click', function(event) {
332 event.preventDefault();
334 $(this).removeClass('edit_row_anchor').addClass('edit_row_anchor_active');
336 // Initialize some variables
337 if(disp_mode == 'vertical') {
339 * @var this_row_index Index of the current <td> in the parent <tr>
340 * Current <td> is the inline edit anchor.
342 var this_row_index = $(this).index();
344 * @var input_siblings Object referring to all inline editable events from same row
346 var input_siblings = $(this).parents('tbody').find('tr').find('.data_inline_edit:nth('+this_row_index+')');
348 * @var where_clause String containing the WHERE clause to select this row
350 var where_clause = $(this).parents('tbody').find('tr').find('.where_clause:nth('+this_row_index+')').val();
353 var input_siblings = $(this).parent('tr').find('.data_inline_edit');
354 var where_clause = $(this).parent('tr').find('.where_clause').val();
357 $(input_siblings).each(function() {
360 * @var data_value Current value of this field
362 var data_value = $(this).html();
364 // We need to retrieve the value from the server for truncated/relation fields
365 // Find the field name
368 * @var this_field Object referring to this field (<td>)
370 var this_field = $(this);
372 * @var field_name String containing the name of this field.
373 * @see getFieldName()
375 var field_name = getFieldName($(this), disp_mode);
377 // In each input sibling, wrap the current value in a textarea
378 // and store the current value in a hidden span
379 if($(this).is(':not(.truncated, .transformed, .relation, .enum, .null)')) {
380 // handle non-truncated, non-transformed, non-relation values
381 // We don't need to get any more data, just wrap the value
382 $(this).html('<textarea>'+data_value+'</textarea>')
383 .append('<span class="original_data">'+data_value+'</span>');
384 $(".original_data").hide();
386 else if($(this).is('.truncated, .transformed')) {
388 //handle truncated/transformed values values
391 * @var sql_query String containing the SQL query used to retrieve value of truncated/transformed data
393 var sql_query = 'SELECT ' + field_name + ' FROM ' + window.parent.table + ' WHERE ' + where_clause;
395 // Make the Ajax call and get the data, wrap it and insert it
397 'token' : window.parent.token,
398 'db' : window.parent.db,
399 'ajax_request' : true,
400 'sql_query' : sql_query,
403 if(data.success == true) {
404 $(this_field).html('<textarea>'+data.value+'</textarea>')
405 .append('<span class="original_data">'+data_value+'</span>');
406 $(".original_data").hide();
409 PMA_ajaxShowMessage(data.error);
413 else if($(this).is('.relation')) {
418 * @var curr_value String containing the current value of this relational field
420 var curr_value = $(this).find('a').text();
423 * @var post_params Object containing parameters for the POST request
426 'ajax_request' : true,
427 'get_relational_values' : true,
428 'db' : window.parent.db,
429 'table' : window.parent.table,
430 'column' : field_name,
431 'token' : window.parent.token,
432 'curr_value' : curr_value
435 $.post('sql.php', post_params, function(data) {
436 $(this_field).html(data.dropdown)
437 .append('<span class="original_data">'+data_value+'</span>');
438 $(".original_data").hide();
441 else if($(this).is('.enum')) {
445 * @var curr_value String containing the current value of this relational field
447 var curr_value = $(this).text();
450 * @var post_params Object containing parameters for the POST request
453 'ajax_request' : true,
454 'get_enum_values' : true,
455 'db' : window.parent.db,
456 'table' : window.parent.table,
457 'column' : field_name,
458 'token' : window.parent.token,
459 'curr_value' : curr_value
462 $.post('sql.php', post_params, function(data) {
463 $(this_field).html(data.dropdown)
464 .append('<span class="original_data">'+data_value+'</span>');
465 $(".original_data").hide();
468 else if($(this).is('.null')) {
470 $(this_field).html('<textarea></textarea>')
471 .append('<span class="original_data">NULL</span>');
472 $(".original_data").hide();
475 }) // End On click, replace the current field with an input/textarea
478 * After editing, clicking again should post data
481 * @name inline_edit_save
482 * @see PMA_ajaxShowMessage()
483 * @see getFieldName()
485 $(".edit_row_anchor_active").live('click', function(event) {
487 event.preventDefault();
490 * @var $this_td Object referring to the td containing the
491 * "Inline Edit" link that was clicked to save the row that is
495 var $this_td = $(this);
497 // Initialize variables
498 if(disp_mode == 'vertical') {
500 * @var this_td_index Index of the current <td> in the parent <tr>
501 * Current <td> is the inline edit anchor.
503 var this_td_index = $this_td.index();
505 * @var input_siblings Object referring to all inline editable events from same row
507 var input_siblings = $this_td.parents('tbody').find('tr').find('.data_inline_edit:nth('+this_td_index+')');
509 * @var where_clause String containing the WHERE clause to select this row
511 var where_clause = $this_td.parents('tbody').find('tr').find('.where_clause:nth('+this_td_index+')').val();
514 var input_siblings = $this_td.parent('tr').find('.data_inline_edit');
515 var where_clause = $this_td.parent('tr').find('.where_clause').val();
519 * @var nonunique Boolean, whether this row is unique or not
521 if($this_td.is('.nonunique')) {
528 // Collect values of all fields to submit, we don't know which changed
530 * @var params_to_submit Array containing the name/value pairs of all fields
532 var params_to_submit = {};
534 * @var relation_fields Array containing the name/value pairs of relational fields
536 var relation_fields = {};
538 * @var transform_fields Array containing the name/value pairs for transformed fields
540 var transform_fields = {};
542 * @var transformation_fields Boolean, if there are any transformed fields in this row
544 var transformation_fields = false;
546 $(input_siblings).each(function() {
549 * @var this_field Object referring to this field (<td>)
551 var $this_field = $(this);
553 * @var field_name String containing the name of this field.
554 * @see getFieldName()
556 var field_name = getFieldName($this_field, disp_mode);
559 * @var this_field_params Array temporary storage for the name/value of current field
561 var this_field_params = {};
563 if($this_field.is('.transformed')) {
564 transformation_fields = true;
567 if($this_field.is(":not(.relation, .enum)")) {
568 this_field_params[field_name] = $this_field.find('textarea').val();
569 if($this_field.is('.transformed')) {
570 $.extend(transform_fields, this_field_params);
574 this_field_params[field_name] = $this_field.find('select').val();
576 if($this_field.is('.relation')) {
577 $.extend(relation_fields, this_field_params);
581 $.extend(params_to_submit, this_field_params);
585 * @var sql_query String containing the SQL query to update this row
587 var sql_query = 'UPDATE ' + window.parent.table + ' SET ';
589 $.each(params_to_submit, function(key, value) {
590 if(value.length == 0) {
593 sql_query += ' ' + key + "='" + value + "' , ";
595 //Remove the last ',' appended in the above loop
596 sql_query = sql_query.replace(/,\s$/, '');
597 sql_query += ' WHERE ' + PMA_urldecode(where_clause);
600 * @var rel_fields_list String, url encoded representation of {@link relations_fields}
602 var rel_fields_list = $.param(relation_fields);
605 * @var transform_fields_list String, url encoded representation of {@link transform_fields}
607 var transform_fields_list = $.param(transform_fields);
609 // Make the Ajax post after setting all parameters
611 * @var post_params Object containing parameters for the POST request
613 var post_params = {'ajax_request' : true,
614 'sql_query' : sql_query,
615 'disp_direction' : disp_mode,
616 'token' : window.parent.token,
617 'db' : window.parent.db,
618 'table' : window.parent.table,
619 'clause_is_unique' : nonunique,
620 'where_clause' : where_clause,
621 'rel_fields_list' : rel_fields_list,
622 'do_transformations' : transformation_fields,
623 'transform_fields_list' : transform_fields_list,
627 $.post('tbl_replace.php', post_params, function(data) {
628 if(data.success == true) {
629 PMA_ajaxShowMessage(data.message);
630 $this_td.removeClass('edit_row_anchor_active').addClass('edit_row_anchor');
632 $(input_siblings).each(function() {
633 // Inline edit post has been successful.
634 if($(this).is(':not(.relation, .enum)')) {
636 * @var new_html String containing value of the data field after edit
638 var new_html = $(this).find('textarea').val();
640 if($(this).is('.transformed')) {
641 var field_name = getFieldName($(this), disp_mode);
642 var this_field = $(this);
644 $.each(data.transformations, function(key, value) {
645 if(key == field_name) {
646 if($(this_field).is('.text_plain, .application_octetstream')) {
651 var new_value = $(this_field).find('textarea').val();
652 new_html = $(value).append(new_value);
660 var new_html = $(this).find('select').val();
661 if($(this).is('.relation')) {
662 var field_name = getFieldName($(this), disp_mode);
663 var this_field = $(this);
665 $.each(data.relations, function(key, value) {
666 if(key == field_name) {
667 var new_value = $(this_field).find('select').val();
668 new_html = $(value).append(new_value);
674 $(this).html(new_html);
678 PMA_ajaxShowMessage(data.error);
681 }) // End After editing, clicking again should post data
682 }, 'top.frame_content') // end $(document).ready()