Merge remote-tracking branch 'origin/QA_4_7' into QA_4_7
[phpmyadmin.git] / js / rte.js
blob572f385dddf0703d02d84a4b6c8ffc53b1490a2d
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * JavaScript functionality for Routines, Triggers and Events.
4  *
5  * @package PhpMyadmin
6  */
7 /**
8  * @var RTE Contains all the JavaScript functionality
9  *          for Routines, Triggers and Events
10  */
11 var RTE = {
12     /**
13      * Construct for the object that provides the
14      * functionality for Routines, Triggers and Events
15      */
16     object: function (type) {
17         $.extend(this, RTE.COMMON);
18         this.editorType = type;
20         switch (type) {
21         case 'routine':
22             $.extend(this, RTE.ROUTINE);
23             break;
24         case 'trigger':
25             // nothing extra yet for triggers
26             break;
27         case 'event':
28             $.extend(this, RTE.EVENT);
29             break;
30         default:
31             break;
32         }
33     },
34     /**
35      * @var string param_template Template for a row in the routine editor
36      */
37     param_template: ''
40 /**
41  * @var RTE.COMMON a JavaScript namespace containing the functionality
42  *                 for Routines, Triggers and Events
43  *
44  *                 This namespace is extended by the functionality required
45  *                 to handle a specific item (a routine, trigger or event)
46  *                 in the relevant javascript files in this folder
47  */
48 RTE.COMMON = {
49     /**
50      * @var $ajaxDialog Query object containing the reference to the
51      *                  dialog that contains the editor
52      */
53     $ajaxDialog: null,
54     /**
55      * @var syntaxHiglighter Reference to the codemirror editor
56      */
57     syntaxHiglighter: null,
58     /**
59      * @var buttonOptions Object containing options for
60      *                    the jQueryUI dialog buttons
61      */
62     buttonOptions: {},
63     /**
64      * @var editorType Type of the editor
65      */
66     editorType: null,
67     /**
68      * Validate editor form fields.
69      */
70     validate: function () {
71         /**
72          * @var $elm a jQuery object containing the reference
73          *           to an element that is being validated
74          */
75         var $elm = null;
76         // Common validation. At the very least the name
77         // and the definition must be provided for an item
78         $elm = $('table.rte_table').last().find('input[name=item_name]');
79         if ($elm.val() === '') {
80             $elm.focus();
81             alert(PMA_messages.strFormEmpty);
82             return false;
83         }
84         $elm = $('table.rte_table').find('textarea[name=item_definition]');
85         if ($elm.val() === '') {
86             if (this.syntaxHiglighter !== null) {
87                 this.syntaxHiglighter.focus();
88             }
89             else {
90                 $('textarea[name=item_definition]').last().focus();
91             }
92             alert(PMA_messages.strFormEmpty);
93             return false;
94         }
95         // The validation has so far passed, so now
96         // we can validate item-specific fields.
97         return this.validateCustom();
98     }, // end validate()
99     /**
100      * Validate custom editor form fields.
101      * This function can be overridden by
102      * other files in this folder
103      */
104     validateCustom: function () {
105         return true;
106     }, // end validateCustom()
107     /**
108      * Execute some code after the ajax
109      * dialog for the editor is shown.
110      * This function can be overridden by
111      * other files in this folder
112      */
113     postDialogShow: function () {
114         // Nothing by default
115     }, // end postDialogShow()
117     exportDialog: function ($this) {
118         var $msg = PMA_ajaxShowMessage();
119         if ($this.hasClass('mult_submit')) {
120             var combined = {
121                 success: true,
122                 title: PMA_messages.strExport,
123                 message: '',
124                 error: ''
125             };
126             // export anchors of all selected rows
127             var export_anchors = $('input.checkall:checked').parents('tr').find('.export_anchor');
128             var count = export_anchors.length;
129             var returnCount = 0;
131             // No routine is exportable (due to privilege issues)
132             if (count === 0) {
133                 PMA_ajaxShowMessage(PMA_messages.NoExportable);
134             }
136             export_anchors.each(function () {
137                 $.get($(this).attr('href'), {'ajax_request': true}, function (data) {
138                     returnCount++;
139                     if (data.success === true) {
140                         combined.message += "\n" + data.message + "\n";
141                         if (returnCount == count) {
142                             showExport(combined);
143                         }
144                     } else {
145                         // complain even if one export is failing
146                         combined.success = false;
147                         combined.error += "\n" + data.error + "\n";
148                         if (returnCount == count) {
149                             showExport(combined);
150                         }
151                     }
152                 });
153             });
154         } else {
155             $.get($this.attr('href'), {'ajax_request': true}, showExport);
156         }
157         PMA_ajaxRemoveMessage($msg);
159         function showExport(data) {
160             if (data.success === true) {
161                 PMA_ajaxRemoveMessage($msg);
162                 /**
163                  * @var button_options Object containing options
164                  *                     for jQueryUI dialog buttons
165                  */
166                 var button_options = {};
167                 button_options[PMA_messages.strClose] = function () {
168                     $(this).dialog("close").remove();
169                 };
170                 /**
171                  * Display the dialog to the user
172                  */
173                 data.message = '<textarea cols="40" rows="15" style="width: 100%;">' + data.message + '</textarea>';
174                 var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
175                     width: 500,
176                     buttons: button_options,
177                     title: data.title
178                 });
179                 // Attach syntax highlighted editor to export dialog
180                 /**
181                  * @var $elm jQuery object containing the reference
182                  *           to the Export textarea.
183                  */
184                 var $elm = $ajaxDialog.find('textarea');
185                 PMA_getSQLEditor($elm);
186             } else {
187                 PMA_ajaxShowMessage(data.error, false);
188             }
189         } // end showExport()
190     },  // end exportDialog()
191     editorDialog: function (is_new, $this) {
192         var that = this;
193         /**
194          * @var $edit_row jQuery object containing the reference to
195          *                the row of the the item being edited
196          *                from the list of items
197          */
198         var $edit_row = null;
199         if ($this.hasClass('edit_anchor')) {
200             // Remeber the row of the item being edited for later,
201             // so that if the edit is successful, we can replace the
202             // row with info about the modified item.
203             $edit_row = $this.parents('tr');
204         }
205         /**
206          * @var $msg jQuery object containing the reference to
207          *           the AJAX message shown to the user
208          */
209         var $msg = PMA_ajaxShowMessage();
210         $.get($this.attr('href'), {'ajax_request': true}, function (data) {
211             if (data.success === true) {
212                 // We have successfully fetched the editor form
213                 PMA_ajaxRemoveMessage($msg);
214                 // Now define the function that is called when
215                 // the user presses the "Go" button
216                 that.buttonOptions[PMA_messages.strGo] = function () {
217                     // Move the data from the codemirror editor back to the
218                     // textarea, where it can be used in the form submission.
219                     if (typeof CodeMirror != 'undefined') {
220                         that.syntaxHiglighter.save();
221                     }
222                     // Validate editor and submit request, if passed.
223                     if (that.validate()) {
224                         /**
225                          * @var data Form data to be sent in the AJAX request
226                          */
227                         var data = $('form.rte_form').last().serialize();
228                         $msg = PMA_ajaxShowMessage(
229                             PMA_messages.strProcessingRequest
230                         );
231                         var url = $('form.rte_form').last().attr('action');
232                         $.post(url, data, function (data) {
233                             if (data.success === true) {
234                                 // Item created successfully
235                                 PMA_ajaxRemoveMessage($msg);
236                                 PMA_slidingMessage(data.message);
237                                 that.$ajaxDialog.dialog('close');
238                                 // If we are in 'edit' mode, we must
239                                 // remove the reference to the old row.
240                                 if (mode === 'edit' && $edit_row !== null ) {
241                                     $edit_row.remove();
242                                 }
243                                 // Sometimes, like when moving a trigger from
244                                 // a table to another one, the new row should
245                                 // not be inserted into the list. In this case
246                                 // "data.insert" will be set to false.
247                                 if (data.insert) {
248                                     // Insert the new row at the correct
249                                     // location in the list of items
250                                     /**
251                                      * @var text Contains the name of an item from
252                                      *           the list that is used in comparisons
253                                      *           to find the correct location where
254                                      *           to insert a new row.
255                                      */
256                                     var text = '';
257                                     /**
258                                      * @var inserted Whether a new item has been
259                                      *               inserted in the list or not
260                                      */
261                                     var inserted = false;
262                                     $('table.data').find('tr').each(function () {
263                                         text = $(this)
264                                                 .children('td')
265                                                 .eq(0)
266                                                 .find('strong')
267                                                 .text()
268                                                 .toUpperCase();
269                                         text = $.trim(text);
270                                         if (text !== '' && text > data.name) {
271                                             $(this).before(data.new_row);
272                                             inserted = true;
273                                             return false;
274                                         }
275                                     });
276                                     if (! inserted) {
277                                         // If we didn't manage to insert the row yet,
278                                         // it must belong at the end of the list,
279                                         // so we insert it there.
280                                         $('table.data').append(data.new_row);
281                                     }
282                                     // Fade-in the new row
283                                     $('tr.ajaxInsert')
284                                         .show('slow')
285                                         .removeClass('ajaxInsert');
286                                 } else if ($('table.data').find('tr').has('td').length === 0) {
287                                     // If we are not supposed to insert the new row,
288                                     // we will now check if the table is empty and
289                                     // needs to be hidden. This will be the case if
290                                     // we were editing the only item in the list,
291                                     // which we removed and will not be inserting
292                                     // something else in its place.
293                                     $('table.data').hide("slow", function () {
294                                         $('#nothing2display').show("slow");
295                                     });
296                                 }
297                                 // Now we have inserted the row at the correct
298                                 // position, but surely at least some row classes
299                                 // are wrong now. So we will itirate throught
300                                 // all rows and assign correct classes to them
301                                 /**
302                                  * @var ct Count of processed rows
303                                  */
304                                 var ct = 0;
305                                 /**
306                                  * @var rowclass Class to be attached to the row
307                                  *               that is being processed
308                                  */
309                                 var rowclass = '';
310                                 $('table.data').find('tr').has('td').each(function () {
311                                     rowclass = (ct % 2 === 0) ? 'odd' : 'even';
312                                     $(this).removeClass().addClass(rowclass);
313                                     ct++;
314                                 });
315                                 // If this is the first item being added, remove
316                                 // the "No items" message and show the list.
317                                 if ($('table.data').find('tr').has('td').length > 0 &&
318                                     $('#nothing2display').is(':visible')
319                                     ) {
320                                     $('#nothing2display').hide("slow", function () {
321                                         $('table.data').show("slow");
322                                     });
323                                 }
324                                 PMA_reloadNavigation();
325                             } else {
326                                 PMA_ajaxShowMessage(data.error, false);
327                             }
328                         }); // end $.post()
329                     } // end "if (that.validate())"
330                 }; // end of function that handles the submission of the Editor
331                 that.buttonOptions[PMA_messages.strClose] = function () {
332                     $(this).dialog("close");
333                 };
334                 /**
335                  * Display the dialog to the user
336                  */
337                 that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
338                     width: 700,
339                     minWidth: 500,
340                     maxHeight: $(window).height(),
341                     buttons: that.buttonOptions,
342                     title: data.title,
343                     modal: true,
344                     open: function () {
345                         if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
346                             $('#rteDialog').dialog("option", "height", $(window).height());
347                         }
348                         $(this).find('input[name=item_name]').focus();
349                         $(this).find('input.datefield').each(function () {
350                             PMA_addDatepicker($(this).css('width', '95%'), 'date');
351                         });
352                         $(this).find('input.datetimefield').each(function () {
353                             PMA_addDatepicker($(this).css('width', '95%'), 'datetime');
354                         });
355                         $.datepicker.initialized = false;
356                     },
357                     close: function () {
358                         $(this).remove();
359                     }
360                 });
361                 /**
362                  * @var mode Used to remeber whether the editor is in
363                  *           "Edit" or "Add" mode
364                  */
365                 var mode = 'add';
366                 if ($('input[name=editor_process_edit]').length > 0) {
367                     mode = 'edit';
368                 }
369                 // Attach syntax highlighted editor to the definition
370                 /**
371                  * @var elm jQuery object containing the reference to
372                  *                 the Definition textarea.
373                  */
374                 var $elm = $('textarea[name=item_definition]').last();
375                 var linterOptions = {};
376                 linterOptions[that.editorType + '_editor'] = true;
377                 that.syntaxHiglighter = PMA_getSQLEditor($elm, {}, null, linterOptions);
379                 // Execute item-specific code
380                 that.postDialogShow(data);
381             } else {
382                 PMA_ajaxShowMessage(data.error, false);
383             }
384         }); // end $.get()
385     },
387     dropDialog: function ($this) {
388         /**
389          * @var $curr_row Object containing reference to the current row
390          */
391         var $curr_row = $this.parents('tr');
392         /**
393          * @var question String containing the question to be asked for confirmation
394          */
395         var question = $('<div/>').text(
396             $curr_row.children('td').children('.drop_sql').html()
397         );
398         // We ask for confirmation first here, before submitting the ajax request
399         $this.PMA_confirm(question, $this.attr('href'), function (url) {
400             /**
401              * @var msg jQuery object containing the reference to
402              *          the AJAX message shown to the user
403              */
404             var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
405             var params = {
406                 'is_js_confirmed': 1,
407                 'ajax_request': true,
408                 'token': PMA_commonParams.get('token')
409             };
410             $.post(url, params, function (data) {
411                 if (data.success === true) {
412                     /**
413                      * @var $table Object containing reference
414                      *             to the main list of elements
415                      */
416                     var $table = $curr_row.parent();
417                     // Check how many rows will be left after we remove
418                     // the one that the user has requested us to remove
419                     if ($table.find('tr').length === 3) {
420                         // If there are two rows left, it means that they are
421                         // the header of the table and the rows that we are
422                         // about to remove, so after the removal there will be
423                         // nothing to show in the table, so we hide it.
424                         $table.hide("slow", function () {
425                             $(this).find('tr.even, tr.odd').remove();
426                             $('.withSelected').remove();
427                             $('#nothing2display').show("slow");
428                         });
429                     } else {
430                         $curr_row.hide("slow", function () {
431                             $(this).remove();
432                             // Now we have removed the row from the list, but maybe
433                             // some row classes are wrong now. So we will itirate
434                             // throught all rows and assign correct classes to them.
435                             /**
436                              * @var ct Count of processed rows
437                              */
438                             var ct = 0;
439                             /**
440                              * @var rowclass Class to be attached to the row
441                              *               that is being processed
442                              */
443                             var rowclass = '';
444                             $table.find('tr').has('td').each(function () {
445                                 rowclass = (ct % 2 === 1) ? 'odd' : 'even';
446                                 $(this).removeClass().addClass(rowclass);
447                                 ct++;
448                             });
449                         });
450                     }
451                     // Get rid of the "Loading" message
452                     PMA_ajaxRemoveMessage($msg);
453                     // Show the query that we just executed
454                     PMA_slidingMessage(data.sql_query);
455                     PMA_reloadNavigation();
456                 } else {
457                     PMA_ajaxShowMessage(data.error, false);
458                 }
459             }); // end $.post()
460         }); // end $.PMA_confirm()
461     },
463     dropMultipleDialog: function ($this) {
464         // We ask for confirmation here
465         $this.PMA_confirm(PMA_messages.strDropRTEitems, '', function (url) {
466             /**
467              * @var msg jQuery object containing the reference to
468              *          the AJAX message shown to the user
469              */
470             var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
472             // drop anchors of all selected rows
473             var drop_anchors = $('input.checkall:checked').parents('tr').find('.drop_anchor');
474             var success = true;
475             var count = drop_anchors.length;
476             var returnCount = 0;
478             drop_anchors.each(function () {
479                 var $anchor = $(this);
480                 /**
481                  * @var $curr_row Object containing reference to the current row
482                  */
483                 var $curr_row = $anchor.parents('tr');
484                 var params = {
485                     'is_js_confirmed': 1,
486                     'ajax_request': true,
487                     'token': PMA_commonParams.get('token')
488                 };
489                 $.post($anchor.attr('href'), params, function (data) {
490                     returnCount++;
491                     if (data.success === true) {
492                         /**
493                          * @var $table Object containing reference
494                          *             to the main list of elements
495                          */
496                         var $table = $curr_row.parent();
497                         // Check how many rows will be left after we remove
498                         // the one that the user has requested us to remove
499                         if ($table.find('tr').length === 3) {
500                             // If there are two rows left, it means that they are
501                             // the header of the table and the rows that we are
502                             // about to remove, so after the removal there will be
503                             // nothing to show in the table, so we hide it.
504                             $table.hide("slow", function () {
505                                 $(this).find('tr.even, tr.odd').remove();
506                                 $('.withSelected').remove();
507                                 $('#nothing2display').show("slow");
508                             });
509                         } else {
510                             $curr_row.hide("fast", function () {
511                                 $(this).remove();
512                                 // Now we have removed the row from the list, but maybe
513                                 // some row classes are wrong now. So we will itirate
514                                 // throught all rows and assign correct classes to them.
515                                 /**
516                                  * @var ct Count of processed rows
517                                  */
518                                 var ct = 0;
519                                 /**
520                                  * @var rowclass Class to be attached to the row
521                                  *               that is being processed
522                                  */
523                                 var rowclass = '';
524                                 $table.find('tr').has('td').each(function () {
525                                     rowclass = (ct % 2 === 1) ? 'odd' : 'even';
526                                     $(this).removeClass().addClass(rowclass);
527                                     ct++;
528                                 });
529                             });
530                         }
531                         if (returnCount == count) {
532                             if (success) {
533                                 // Get rid of the "Loading" message
534                                 PMA_ajaxRemoveMessage($msg);
535                                 $('#rteListForm_checkall').prop({checked: false, indeterminate: false});
536                             }
537                             PMA_reloadNavigation();
538                         }
539                     } else {
540                         PMA_ajaxShowMessage(data.error, false);
541                         success = false;
542                         if (returnCount == count) {
543                             PMA_reloadNavigation();
544                         }
545                     }
546                 }); // end $.post()
547             }); // end drop_anchors.each()
548         }); // end $.PMA_confirm()
549     }
550 }; // end RTE namespace
553  * @var RTE.EVENT JavaScript functionality for events
554  */
555 RTE.EVENT = {
556     validateCustom: function () {
557         /**
558          * @var elm a jQuery object containing the reference
559          *          to an element that is being validated
560          */
561         var $elm = null;
562         if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'RECURRING') {
563             // The interval field must not be empty for recurring events
564             $elm = this.$ajaxDialog.find('input[name=item_interval_value]');
565             if ($elm.val() === '') {
566                 $elm.focus();
567                 alert(PMA_messages.strFormEmpty);
568                 return false;
569             }
570         } else {
571             // The execute_at field must not be empty for "once off" events
572             $elm = this.$ajaxDialog.find('input[name=item_execute_at]');
573             if ($elm.val() === '') {
574                 $elm.focus();
575                 alert(PMA_messages.strFormEmpty);
576                 return false;
577             }
578         }
579         return true;
580     }
584  * @var RTE.ROUTINE JavaScript functionality for routines
585  */
586 RTE.ROUTINE = {
587     /**
588      * Overriding the postDialogShow() function defined in common.js
589      *
590      * @param data JSON-encoded data from the ajax request
591      */
592     postDialogShow: function (data) {
593         // Cache the template for a parameter table row
594         RTE.param_template = data.param_template;
595         var that = this;
596         // Make adjustments in the dialog to make it AJAX compatible
597         $('td.routine_param_remove').show();
598         $('input[name=routine_removeparameter]').remove();
599         $('input[name=routine_addparameter]').css('width', '100%');
600         // Enable/disable the 'options' dropdowns for parameters as necessary
601         $('table.routine_params_table').last().find('th[colspan=2]').attr('colspan', '1');
602         $('table.routine_params_table').last().find('tr').has('td').each(function () {
603             that.setOptionsForParameter(
604                 $(this).find('select[name^=item_param_type]'),
605                 $(this).find('input[name^=item_param_length]'),
606                 $(this).find('select[name^=item_param_opts_text]'),
607                 $(this).find('select[name^=item_param_opts_num]')
608             );
609         });
610         // Enable/disable the 'options' dropdowns for
611         // function return value as necessary
612         this.setOptionsForParameter(
613             $('table.rte_table').last().find('select[name=item_returntype]'),
614             $('table.rte_table').last().find('input[name=item_returnlength]'),
615             $('table.rte_table').last().find('select[name=item_returnopts_text]'),
616             $('table.rte_table').last().find('select[name=item_returnopts_num]')
617         );
618         // Allow changing parameter order
619         $('.routine_params_table tbody').sortable({
620             containment: '.routine_params_table tbody',
621             handle: '.dragHandle',
622             stop: function(event, ui) {
623                 that.reindexParameters();
624             },
625         });
626     },
627     /**
628      * Reindexes the parameters after dropping a parameter or reordering parameters
629      */
630     reindexParameters: function () {
631         /**
632          * @var index Counter used for reindexing the input
633          *            fields in the routine parameters table
634          */
635         var index = 0;
636         $('table.routine_params_table tbody').find('tr').each(function () {
637             $(this).find(':input').each(function () {
638                 /**
639                  * @var inputname The value of the name attribute of
640                  *                the input field being reindexed
641                  */
642                 var inputname = $(this).attr('name');
643                 if (inputname.substr(0, 14) === 'item_param_dir') {
644                     $(this).attr('name', inputname.substr(0, 14) + '[' + index + ']');
645                 } else if (inputname.substr(0, 15) === 'item_param_name') {
646                     $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']');
647                 } else if (inputname.substr(0, 15) === 'item_param_type') {
648                     $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']');
649                 } else if (inputname.substr(0, 17) === 'item_param_length') {
650                     $(this).attr('name', inputname.substr(0, 17) + '[' + index + ']');
651                     $(this).attr('id', 'item_param_length_' + index);
652                 } else if (inputname.substr(0, 20) === 'item_param_opts_text') {
653                     $(this).attr('name', inputname.substr(0, 20) + '[' + index + ']');
654                 } else if (inputname.substr(0, 19) === 'item_param_opts_num') {
655                     $(this).attr('name', inputname.substr(0, 19) + '[' + index + ']');
656                 }
657             });
658             index++;
659         });
660     },
661     /**
662      * Overriding the validateCustom() function defined in common.js
663      */
664     validateCustom: function () {
665         /**
666          * @var isSuccess Stores the outcome of the validation
667          */
668         var isSuccess = true;
669         /**
670          * @var inputname The value of the "name" attribute for
671          *                the field that is being processed
672          */
673         var inputname = '';
674         this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () {
675             // Every parameter of a routine must have
676             // a non-empty direction, name and type
677             if (isSuccess) {
678                 $(this).find(':input').each(function () {
679                     inputname = $(this).attr('name');
680                     if (inputname.substr(0, 14) === 'item_param_dir' ||
681                         inputname.substr(0, 15) === 'item_param_name' ||
682                         inputname.substr(0, 15) === 'item_param_type') {
683                         if ($(this).val() === '') {
684                             $(this).focus();
685                             isSuccess = false;
686                             return false;
687                         }
688                     }
689                 });
690             } else {
691                 return false;
692             }
693         });
694         if (! isSuccess) {
695             alert(PMA_messages.strFormEmpty);
696             return false;
697         }
698         this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () {
699             // SET, ENUM, VARCHAR and VARBINARY fields must have length/values
700             var $inputtyp = $(this).find('select[name^=item_param_type]');
701             var $inputlen = $(this).find('input[name^=item_param_length]');
702             if ($inputtyp.length && $inputlen.length) {
703                 if (($inputtyp.val() === 'ENUM' || $inputtyp.val() === 'SET' || $inputtyp.val().substr(0, 3) === 'VAR') &&
704                     $inputlen.val() === ''
705                    ) {
706                     $inputlen.focus();
707                     isSuccess = false;
708                     return false;
709                 }
710             }
711         });
712         if (! isSuccess) {
713             alert(PMA_messages.strFormEmpty);
714             return false;
715         }
716         if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'FUNCTION') {
717             // The length/values of return variable for functions must
718             // be set, if the type is SET, ENUM, VARCHAR or VARBINARY.
719             var $returntyp = this.$ajaxDialog.find('select[name=item_returntype]');
720             var $returnlen = this.$ajaxDialog.find('input[name=item_returnlength]');
721             if (($returntyp.val() === 'ENUM' || $returntyp.val() === 'SET' || $returntyp.val().substr(0, 3) === 'VAR') &&
722                 $returnlen.val() === ''
723                 ) {
724                 $returnlen.focus();
725                 alert(PMA_messages.strFormEmpty);
726                 return false;
727             }
728         }
729         if ($('select[name=item_type]').find(':selected').val() === 'FUNCTION') {
730             // A function must contain a RETURN statement in its definition
731             if (this.$ajaxDialog.find('table.rte_table').find('textarea[name=item_definition]').val().toUpperCase().indexOf('RETURN') < 0) {
732                 this.syntaxHiglighter.focus();
733                 alert(PMA_messages.MissingReturn);
734                 return false;
735             }
736         }
737         return true;
738     },
739     /**
740      * Enable/disable the "options" dropdown and "length" input for
741      * parameters and the return variable in the routine editor
742      * as necessary.
743      *
744      * @param type a jQuery object containing the reference
745      *             to the "Type" dropdown box
746      * @param len  a jQuery object containing the reference
747      *             to the "Length" input box
748      * @param text a jQuery object containing the reference
749      *             to the dropdown box with options for
750      *             parameters of text type
751      * @param num  a jQuery object containing the reference
752      *             to the dropdown box with options for
753      *             parameters of numeric type
754      */
755     setOptionsForParameter: function ($type, $len, $text, $num) {
756         /**
757          * @var no_opts a jQuery object containing the reference
758          *              to an element to be displayed when no
759          *              options are available
760          */
761         var $no_opts = $text.parent().parent().find('.no_opts');
762         /**
763          * @var no_len a jQuery object containing the reference
764          *             to an element to be displayed when no
765          *             "length/values" field is available
766          */
767         var $no_len  = $len.parent().parent().find('.no_len');
769         // Process for parameter options
770         switch ($type.val()) {
771         case 'TINYINT':
772         case 'SMALLINT':
773         case 'MEDIUMINT':
774         case 'INT':
775         case 'BIGINT':
776         case 'DECIMAL':
777         case 'FLOAT':
778         case 'DOUBLE':
779         case 'REAL':
780             $text.parent().hide();
781             $num.parent().show();
782             $no_opts.hide();
783             break;
784         case 'TINYTEXT':
785         case 'TEXT':
786         case 'MEDIUMTEXT':
787         case 'LONGTEXT':
788         case 'CHAR':
789         case 'VARCHAR':
790         case 'SET':
791         case 'ENUM':
792             $text.parent().show();
793             $num.parent().hide();
794             $no_opts.hide();
795             break;
796         default:
797             $text.parent().hide();
798             $num.parent().hide();
799             $no_opts.show();
800             break;
801         }
802         // Process for parameter length
803         switch ($type.val()) {
804         case 'DATE':
805         case 'TINYBLOB':
806         case 'TINYTEXT':
807         case 'BLOB':
808         case 'TEXT':
809         case 'MEDIUMBLOB':
810         case 'MEDIUMTEXT':
811         case 'LONGBLOB':
812         case 'LONGTEXT':
813             $text.closest('tr').find('a:first').hide();
814             $len.parent().hide();
815             $no_len.show();
816             break;
817         default:
818             if ($type.val() == 'ENUM' || $type.val() == 'SET') {
819                 $text.closest('tr').find('a:first').show();
820             } else {
821                 $text.closest('tr').find('a:first').hide();
822             }
823             $len.parent().show();
824             $no_len.hide();
825             break;
826         }
827     },
828     executeDialog: function ($this) {
829         var that = this;
830         /**
831          * @var msg jQuery object containing the reference to
832          *          the AJAX message shown to the user
833          */
834         var $msg = PMA_ajaxShowMessage();
835         var params = {
836             'ajax_request': true,
837             'token': PMA_commonParams.get('token')
838         };
839         $.post($this.attr('href'), params, function (data) {
840             if (data.success === true) {
841                 PMA_ajaxRemoveMessage($msg);
842                 // If 'data.dialog' is true we show a dialog with a form
843                 // to get the input parameters for routine, otherwise
844                 // we just show the results of the query
845                 if (data.dialog) {
846                     // Define the function that is called when
847                     // the user presses the "Go" button
848                     that.buttonOptions[PMA_messages.strGo] = function () {
849                         /**
850                          * @var data Form data to be sent in the AJAX request
851                          */
852                         var data = $('form.rte_form').last().serialize();
853                         $msg = PMA_ajaxShowMessage(
854                             PMA_messages.strProcessingRequest
855                         );
856                         $.post('db_routines.php', data, function (data) {
857                             if (data.success === true) {
858                                 // Routine executed successfully
859                                 PMA_ajaxRemoveMessage($msg);
860                                 PMA_slidingMessage(data.message);
861                                 $ajaxDialog.dialog('close');
862                             } else {
863                                 PMA_ajaxShowMessage(data.error, false);
864                             }
865                         });
866                     };
867                     that.buttonOptions[PMA_messages.strClose] = function () {
868                         $(this).dialog("close");
869                     };
870                     /**
871                      * Display the dialog to the user
872                      */
873                     var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
874                         width: 650,
875                         buttons: that.buttonOptions,
876                         title: data.title,
877                         modal: true,
878                         close: function () {
879                             $(this).remove();
880                         }
881                     });
882                     $ajaxDialog.find('input[name^=params]').first().focus();
883                     /**
884                      * Attach the datepickers to the relevant form fields
885                      */
886                     $ajaxDialog.find('input.datefield, input.datetimefield').each(function () {
887                         PMA_addDatepicker($(this).css('width', '95%'));
888                     });
889                     /*
890                     * Define the function if the user presses enter
891                     */
892                     $('form.rte_form').on('keyup', function (event) {
893                         event.preventDefault();
894                         if (event.keyCode === 13) {
895                             /**
896                             * @var data Form data to be sent in the AJAX request
897                             */
898                             var data = $(this).serialize();
899                             $msg = PMA_ajaxShowMessage(
900                                 PMA_messages.strProcessingRequest
901                             );
902                             var url = $(this).attr('action');
903                             $.post(url, data, function (data) {
904                                 if (data.success === true) {
905                                     // Routine executed successfully
906                                     PMA_ajaxRemoveMessage($msg);
907                                     PMA_slidingMessage(data.message);
908                                     $('form.rte_form').off('keyup');
909                                     $ajaxDialog.remove();
910                                 } else {
911                                     PMA_ajaxShowMessage(data.error, false);
912                                 }
913                             });
914                         }
915                     });
916                 } else {
917                     // Routine executed successfully
918                     PMA_slidingMessage(data.message);
919                 }
920             } else {
921                 PMA_ajaxShowMessage(data.error, false);
922             }
923         }); // end $.post()
924     }
928  * Attach Ajax event handlers for the Routines, Triggers and Events editor
929  */
930 $(function () {
931     /**
932      * Attach Ajax event handlers for the Add/Edit functionality.
933      */
934     $(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) {
935         event.preventDefault();
936         var type = $(this).attr('href').substr(0, $(this).attr('href').indexOf('?'));
937         if (type.indexOf('routine') != -1) {
938             type = 'routine';
939         } else if (type.indexOf('trigger') != -1) {
940             type = 'trigger';
941         } else if (type.indexOf('event') != -1) {
942             type = 'event';
943         } else {
944             type = '';
945         }
946         var dialog = new RTE.object(type);
947         dialog.editorDialog($(this).hasClass('add_anchor'), $(this));
948     }); // end $(document).on()
950     /**
951      * Attach Ajax event handlers for the Execute routine functionality
952      */
953     $(document).on('click', 'a.ajax.exec_anchor', function (event) {
954         event.preventDefault();
955         var dialog = new RTE.object('routine');
956         dialog.executeDialog($(this));
957     }); // end $(document).on()
959     /**
960      * Attach Ajax event handlers for Export of Routines, Triggers and Events
961      */
962     $(document).on('click', 'a.ajax.export_anchor', function (event) {
963         event.preventDefault();
964         var dialog = new RTE.object();
965         dialog.exportDialog($(this));
966     }); // end $(document).on()
968     $(document).on('click', '#rteListForm.ajax .mult_submit[value="export"]', function (event) {
969         event.preventDefault();
970         var dialog = new RTE.object();
971         dialog.exportDialog($(this));
972     }); // end $(document).on()
974     /**
975      * Attach Ajax event handlers for Drop functionality
976      * of Routines, Triggers and Events.
977      */
978     $(document).on('click', 'a.ajax.drop_anchor', function (event) {
979         event.preventDefault();
980         var dialog = new RTE.object();
981         dialog.dropDialog($(this));
982     }); // end $(document).on()
984     $(document).on('click', '#rteListForm.ajax .mult_submit[value="drop"]', function (event) {
985         event.preventDefault();
986         var dialog = new RTE.object();
987         dialog.dropMultipleDialog($(this));
988     }); // end $(document).on()
990     /**
991      * Attach Ajax event handlers for the "Change event/routine type"
992      * functionality in the events editor, so that the correct
993      * rows are shown in the editor when changing the event type
994      */
995     $(document).on('change', 'select[name=item_type]', function () {
996         $(this)
997         .closest('table')
998         .find('tr.recurring_event_row, tr.onetime_event_row, tr.routine_return_row, .routine_direction_cell')
999         .toggle();
1000     }); // end $(document).on()
1002     /**
1003      * Attach Ajax event handlers for the "Change parameter type"
1004      * functionality in the routines editor, so that the correct
1005      * option/length fields, if any, are shown when changing
1006      * a parameter type
1007      */
1008     $(document).on('change', 'select[name^=item_param_type]', function () {
1009         /**
1010          * @var row jQuery object containing the reference to
1011          *          a row in the routine parameters table
1012          */
1013         var $row = $(this).parents('tr').first();
1014         var rte = new RTE.object('routine');
1015         rte.setOptionsForParameter(
1016             $row.find('select[name^=item_param_type]'),
1017             $row.find('input[name^=item_param_length]'),
1018             $row.find('select[name^=item_param_opts_text]'),
1019             $row.find('select[name^=item_param_opts_num]')
1020         );
1021     }); // end $(document).on()
1023     /**
1024      * Attach Ajax event handlers for the "Change the type of return
1025      * variable of function" functionality, so that the correct fields,
1026      * if any, are shown when changing the function return type type
1027      */
1028     $(document).on('change', 'select[name=item_returntype]', function () {
1029         var rte = new RTE.object('routine');
1030         var $table = $(this).closest('table.rte_table');
1031         rte.setOptionsForParameter(
1032             $table.find('select[name=item_returntype]'),
1033             $table.find('input[name=item_returnlength]'),
1034             $table.find('select[name=item_returnopts_text]'),
1035             $table.find('select[name=item_returnopts_num]')
1036         );
1037     }); // end $(document).on()
1039     /**
1040      * Attach Ajax event handlers for the "Add parameter to routine" functionality
1041      */
1042     $(document).on('click', 'input[name=routine_addparameter]', function (event) {
1043         event.preventDefault();
1044         /**
1045          * @var routine_params_table jQuery object containing the reference
1046          *                           to the routine parameters table
1047          */
1048         var $routine_params_table = $(this).closest('div.ui-dialog').find('.routine_params_table');
1049         /**
1050          * @var new_param_row A string containing the HTML code for the
1051          *                    new row for the routine parameters table
1052          */
1053         var new_param_row = RTE.param_template.replace(/%s/g, $routine_params_table.find('tr').length - 1);
1054         // Append the new row to the parameters table
1055         $routine_params_table.append(new_param_row);
1056         // Make sure that the row is correctly shown according to the type of routine
1057         if ($(this).closest('div.ui-dialog').find('table.rte_table select[name=item_type]').val() === 'FUNCTION') {
1058             $('tr.routine_return_row').show();
1059             $('td.routine_direction_cell').hide();
1060         }
1061         /**
1062          * @var newrow jQuery object containing the reference to the newly
1063          *             inserted row in the routine parameters table
1064          */
1065         var $newrow = $(this).closest('div.ui-dialog').find('table.routine_params_table').find('tr').has('td').last();
1066         // Enable/disable the 'options' dropdowns for parameters as necessary
1067         var rte = new RTE.object('routine');
1068         rte.setOptionsForParameter(
1069             $newrow.find('select[name^=item_param_type]'),
1070             $newrow.find('input[name^=item_param_length]'),
1071             $newrow.find('select[name^=item_param_opts_text]'),
1072             $newrow.find('select[name^=item_param_opts_num]')
1073         );
1074     }); // end $(document).on()
1076     /**
1077      * Attach Ajax event handlers for the
1078      * "Remove parameter from routine" functionality
1079      */
1080     $(document).on('click', 'a.routine_param_remove_anchor', function (event) {
1081         event.preventDefault();
1082         $(this).parent().parent().remove();
1083         // After removing a parameter, the indices of the name attributes in
1084         // the input fields lose the correct order and need to be reordered.
1085         RTE.ROUTINE.reindexParameters();
1086     }); // end $(document).on()
1087 }); // end of $()