Translated using Weblate (Korean)
[phpmyadmin.git] / js / functions.js
blobb6f024913bb49147086863885b482cc378116cb4
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * general function, usually for data manipulation pages
4  *
5  */
7 /**
8  * @var $table_clone reference to the action links on the tbl_structure page
9  */
10 var $table_clone = false;
12 /**
13  * @var sql_box_locked lock for the sqlbox textarea in the querybox/querywindow
14  */
15 var sql_box_locked = false;
17 /**
18  * @var array holds elements which content should only selected once
19  */
20 var only_once_elements = [];
22 /**
23  * @var   int   ajax_message_count   Number of AJAX messages shown since page load
24  */
25 var ajax_message_count = 0;
27 /**
28  * @var codemirror_editor object containing CodeMirror editor of the query editor in SQL tab
29  */
30 var codemirror_editor = false;
32 /**
33  * @var codemirror_editor object containing CodeMirror editor of the inline query editor
34  */
35 var codemirror_inline_editor = false;
37 /**
38  * @var chart_activeTimeouts object active timeouts that refresh the charts. When disabling a realtime chart, this can be used to stop the continuous ajax requests
39  */
40 var chart_activeTimeouts = {};
42 /**
43  * Make sure that ajax requests will not be cached
44  * by appending a random variable to their parameters
45  */
46 $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
47     var nocache = new Date().getTime() + "" + Math.floor(Math.random() * 1000000);
48     if (typeof options.data == "string") {
49         options.data += "&_nocache=" + nocache;
50     } else if (typeof options.data == "object") {
51         options.data = $.extend(originalOptions.data, {'_nocache' : nocache});
52     }
53 });
55 /**
56  * Add a hidden field to the form to indicate that this will be an
57  * Ajax request (only if this hidden field does not exist)
58  *
59  * @param object   the form
60  */
61 function PMA_prepareForAjaxRequest($form)
63     if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
64         $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
65     }
68 /**
69  * Generate a new password and copy it to the password input areas
70  *
71  * @param object   the form that holds the password fields
72  *
73  * @return boolean  always true
74  */
75 function suggestPassword(passwd_form)
77     // restrict the password to just letters and numbers to avoid problems:
78     // "editors and viewers regard the password as multiple words and
79     // things like double click no longer work"
80     var pwchars = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
81     var passwordlength = 16;    // do we want that to be dynamic?  no, keep it simple :)
82     var passwd = passwd_form.generated_pw;
83     passwd.value = '';
85     for (var i = 0; i < passwordlength; i++) {
86         passwd.value += pwchars.charAt(Math.floor(Math.random() * pwchars.length));
87     }
88     passwd_form.text_pma_pw.value = passwd.value;
89     passwd_form.text_pma_pw2.value = passwd.value;
90     return true;
93 /**
94  * Version string to integer conversion.
95  */
96 function parseVersionString(str)
98     if (typeof(str) != 'string') { return false; }
99     var add = 0;
100     // Parse possible alpha/beta/rc/
101     var state = str.split('-');
102     if (state.length >= 2) {
103         if (state[1].substr(0, 2) == 'rc') {
104             add = - 20 - parseInt(state[1].substr(2), 10);
105         } else if (state[1].substr(0, 4) == 'beta') {
106             add =  - 40 - parseInt(state[1].substr(4), 10);
107         } else if (state[1].substr(0, 5) == 'alpha') {
108             add =  - 60 - parseInt(state[1].substr(5), 10);
109         } else if (state[1].substr(0, 3) == 'dev') {
110             /* We don't handle dev, it's git snapshot */
111             add = 0;
112         }
113     }
114     // Parse version
115     var x = str.split('.');
116     // Use 0 for non existing parts
117     var maj = parseInt(x[0], 10) || 0;
118     var min = parseInt(x[1], 10) || 0;
119     var pat = parseInt(x[2], 10) || 0;
120     var hotfix = parseInt(x[3], 10) || 0;
121     return  maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
125  * Indicates current available version on main page.
126  */
127 function PMA_current_version(data)
129     if (data && data.version && data.date) {
130         var current = parseVersionString(pmaversion);
131         var latest = parseVersionString(data.version);
132         var version_information_message = PMA_messages.strLatestAvailable + ' ' + escapeHtml(data.version);
133         if (latest > current) {
134             var message = $.sprintf(
135                 PMA_messages.strNewerVersion,
136                 escapeHtml(data.version),
137                 escapeHtml(data.date)
138             );
139             var htmlClass = 'notice';
140             if (Math.floor(latest / 10000) === Math.floor(current / 10000)) {
141                 /* Security update */
142                 htmlClass = 'error';
143             }
144             $('#maincontainer').after('<div class="' + htmlClass + '">' + message + '</div>');
145         }
146         if (latest === current) {
147             version_information_message = ' (' + PMA_messages.strUpToDate + ')';
148         }
149         $('#li_pma_version').append(version_information_message);
150     }
154  * Loads Git revision data from ajax for index.php
155  */
156 function PMA_display_git_revision()
158     $('#is_git_revision').remove();
159     $.get(
160         "index.php",
161         {
162             "server": PMA_commonParams.get('server'),
163             "token": PMA_commonParams.get('token'),
164             "git_revision": true,
165             "ajax_request": true
166         },
167         function (data) {
168             if (data.success === true) {
169                 $(data.message).insertAfter('#li_pma_version');
170             }
171         }
172     );
176  * for libraries/display_change_password.lib.php
177  *     libraries/user_password.php
179  */
181 function displayPasswordGenerateButton()
183     $('#tr_element_before_generate_password').parent().append('<tr class="vmiddle"><td>' + PMA_messages.strGeneratePassword + '</td><td><input type="button" class="button" id="button_generate_password" value="' + PMA_messages.strGenerate + '" onclick="suggestPassword(this.form)" /><input type="text" name="generated_pw" id="generated_pw" /></td></tr>');
184     $('#div_element_before_generate_password').parent().append('<div class="item"><label for="button_generate_password">' + PMA_messages.strGeneratePassword + ':</label><span class="options"><input type="button" class="button" id="button_generate_password" value="' + PMA_messages.strGenerate + '" onclick="suggestPassword(this.form)" /></span><input type="text" name="generated_pw" id="generated_pw" /></div>');
188  * Adds a date/time picker to an element
190  * @param object  $this_element   a jQuery object pointing to the element
191  */
192 function PMA_addDatepicker($this_element, options)
194     var showTimeOption = false;
195     if ($this_element.is('.datetimefield')) {
196         showTimeOption = true;
197     }
199     var defaultOptions = {
200         showOn: 'button',
201         buttonImage: themeCalendarImage, // defined in js/messages.php
202         buttonImageOnly: true,
203         stepMinutes: 1,
204         stepHours: 1,
205         showSecond: true,
206         showMillisec: true,
207         showMicrosec: true,
208         showTimepicker: showTimeOption,
209         showButtonPanel: false,
210         dateFormat: 'yy-mm-dd', // yy means year with four digits
211         timeFormat: 'HH:mm:ss.lc',
212         altFieldTimeOnly: false,
213         showAnim: '',
214         beforeShow: function (input, inst) {
215             // Remember that we came from the datepicker; this is used
216             // in tbl_change.js by verificationsAfterFieldChange()
217             $this_element.data('comes_from', 'datepicker');
219             // Fix wrong timepicker z-index, doesn't work without timeout
220             setTimeout(function () {
221                 $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index'));
222             }, 0);
223         },
224         onClose: function (dateText, dp_inst) {
225             // The value is no more from the date picker
226             $this_element.data('comes_from', '');
227         }
228     };
229     if (showTimeOption || (typeof(options) != 'undefined'  && options.showTimepicker)) {
230         $this_element.datetimepicker($.extend(defaultOptions, options));
231     } else {
232         $this_element.datepicker($.extend(defaultOptions, options));
233     }
237  * selects the content of a given object, f.e. a textarea
239  * @param object  element     element of which the content will be selected
240  * @param var     lock        variable which holds the lock for this element
241  *                              or true, if no lock exists
242  * @param boolean only_once   if true this is only done once
243  *                              f.e. only on first focus
244  */
245 function selectContent(element, lock, only_once)
247     if (only_once && only_once_elements[element.name]) {
248         return;
249     }
251     only_once_elements[element.name] = true;
253     if (lock) {
254         return;
255     }
257     element.select();
261  * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query.
262  * This function is called while clicking links
264  * @param object   the link
265  * @param object   the sql query to submit
267  * @return boolean  whether to run the query or not
268  */
269 function confirmLink(theLink, theSqlQuery)
271     // Confirmation is not required in the configuration file
272     // or browser is Opera (crappy js implementation)
273     if (PMA_messages.strDoYouReally === '' || typeof(window.opera) != 'undefined') {
274         return true;
275     }
277     var is_confirmed = confirm($.sprintf(PMA_messages.strDoYouReally, theSqlQuery));
278     if (is_confirmed) {
279         if ($(theLink).hasClass('formLinkSubmit')) {
280             var name = 'is_js_confirmed';
281             if ($(theLink).attr('href').indexOf('usesubform') != -1) {
282                 name = 'subform[' + $(theLink).attr('href').substr('#').match(/usesubform\[(\d+)\]/i)[1] + '][is_js_confirmed]';
283             }
285             $(theLink).parents('form').append('<input type="hidden" name="' + name + '" value="1" />');
286         } else if (typeof(theLink.href) != 'undefined') {
287             theLink.href += '&is_js_confirmed=1';
288         } else if (typeof(theLink.form) != 'undefined') {
289             theLink.form.action += '?is_js_confirmed=1';
290         }
291     }
293     return is_confirmed;
294 } // end of the 'confirmLink()' function
297  * Displays an error message if a "DROP DATABASE" statement is submitted
298  * while it isn't allowed, else confirms a "DROP/DELETE/ALTER" query before
299  * sumitting it if required.
300  * This function is called by the 'checkSqlQuery()' js function.
302  * @param object   the form
303  * @param object   the sql query textarea
305  * @return boolean  whether to run the query or not
307  * @see     checkSqlQuery()
308  */
309 function confirmQuery(theForm1, sqlQuery1)
311     // Confirmation is not required in the configuration file
312     if (PMA_messages.strDoYouReally === '') {
313         return true;
314     }
316     // "DROP DATABASE" statement isn't allowed
317     if (PMA_messages.strNoDropDatabases !== '') {
318         var drop_re = new RegExp('(^|;)\\s*DROP\\s+(IF EXISTS\\s+)?DATABASE\\s', 'i');
319         if (drop_re.test(sqlQuery1.value)) {
320             alert(PMA_messages.strNoDropDatabases);
321             theForm1.reset();
322             sqlQuery1.focus();
323             return false;
324         } // end if
325     } // end if
327     // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
328     //
329     // TODO: find a way (if possible) to use the parser-analyser
330     // for this kind of verification
331     // For now, I just added a ^ to check for the statement at
332     // beginning of expression
334     var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|DATABASE|PROCEDURE)\\s', 'i');
335     var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
336     var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
337     var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
339     if (do_confirm_re_0.test(sqlQuery1.value) ||
340         do_confirm_re_1.test(sqlQuery1.value) ||
341         do_confirm_re_2.test(sqlQuery1.value) ||
342         do_confirm_re_3.test(sqlQuery1.value)) {
343         var message;
344         if (sqlQuery1.value.length > 100) {
345             message = sqlQuery1.value.substr(0, 100) + '\n    ...';
346         } else {
347             message = sqlQuery1.value;
348         }
349         var is_confirmed = confirm($.sprintf(PMA_messages.strDoYouReally, message));
350         // statement is confirmed -> update the
351         // "is_js_confirmed" form field so the confirm test won't be
352         // run on the server side and allows to submit the form
353         if (is_confirmed) {
354             theForm1.elements['is_js_confirmed'].value = 1;
355             return true;
356         }
357         // statement is rejected -> do not submit the form
358         else {
359             window.focus();
360             sqlQuery1.focus();
361             return false;
362         } // end if (handle confirm box result)
363     } // end if (display confirm box)
365     return true;
366 } // end of the 'confirmQuery()' function
369  * Displays an error message if the user submitted the sql query form with no
370  * sql query, else checks for "DROP/DELETE/ALTER" statements
372  * @param object   the form
374  * @return boolean  always false
376  * @see     confirmQuery()
377  */
378 function checkSqlQuery(theForm)
381     // First check if codemirror is active.
382     if (codemirror_editor) {
383         theForm.elements['sql_query'].value = codemirror_editor.getValue();
384     }
386     var sqlQuery = theForm.elements['sql_query'];
388     var isEmpty  = 1;
390     var space_re = new RegExp('\\s+');
391     if (typeof(theForm.elements['sql_file']) != 'undefined' &&
392             theForm.elements['sql_file'].value.replace(space_re, '') !== '') {
393         return true;
394     }
395     if (typeof(theForm.elements['sql_localfile']) != 'undefined' &&
396             theForm.elements['sql_localfile'].value.replace(space_re, '') !== '') {
397         return true;
398     }
399     if (isEmpty && typeof(theForm.elements['id_bookmark']) != 'undefined' &&
400             (theForm.elements['id_bookmark'].value !== null || theForm.elements['id_bookmark'].value !== '') &&
401             theForm.elements['id_bookmark'].selectedIndex !== 0) {
402         return true;
403     }
404     // Checks for "DROP/DELETE/ALTER" statements
405     if (sqlQuery.value.replace(space_re, '') !== '') {
406         if (confirmQuery(theForm, sqlQuery)) {
407             return true;
408         } else {
409             return false;
410         }
411     }
412     theForm.reset();
413     isEmpty = 1;
415     if (isEmpty) {
416         sqlQuery.select();
417         alert(PMA_messages.strFormEmpty);
418         sqlQuery.focus();
419         return false;
420     }
422     return true;
423 } // end of the 'checkSqlQuery()' function
426  * Check if a form's element is empty.
427  * An element containing only spaces is also considered empty
429  * @param object   the form
430  * @param string   the name of the form field to put the focus on
432  * @return boolean  whether the form field is empty or not
433  */
434 function emptyCheckTheField(theForm, theFieldName)
436     var theField = theForm.elements[theFieldName];
437     var space_re = new RegExp('\\s+');
438     return (theField.value.replace(space_re, '') === '') ? 1 : 0;
439 } // end of the 'emptyCheckTheField()' function
443  * Check whether a form field is empty or not
445  * @param object   the form
446  * @param string   the name of the form field to put the focus on
448  * @return boolean  whether the form field is empty or not
449  */
450 function emptyFormElements(theForm, theFieldName)
452     var theField = theForm.elements[theFieldName];
453     var isEmpty = emptyCheckTheField(theForm, theFieldName);
456     return isEmpty;
457 } // end of the 'emptyFormElements()' function
461  * Ensures a value submitted in a form is numeric and is in a range
463  * @param object   the form
464  * @param string   the name of the form field to check
465  * @param integer  the minimum authorized value
466  * @param integer  the maximum authorized value
468  * @return boolean  whether a valid number has been submitted or not
469  */
470 function checkFormElementInRange(theForm, theFieldName, message, min, max)
472     var theField         = theForm.elements[theFieldName];
473     var val              = parseInt(theField.value, 10);
475     if (typeof(min) == 'undefined') {
476         min = 0;
477     }
478     if (typeof(max) == 'undefined') {
479         max = Number.MAX_VALUE;
480     }
482     // It's not a number
483     if (isNaN(val)) {
484         theField.select();
485         alert(PMA_messages.strEnterValidNumber);
486         theField.focus();
487         return false;
488     }
489     // It's a number but it is not between min and max
490     else if (val < min || val > max) {
491         theField.select();
492         alert($.sprintf(message, val));
493         theField.focus();
494         return false;
495     }
496     // It's a valid number
497     else {
498         theField.value = val;
499     }
500     return true;
502 } // end of the 'checkFormElementInRange()' function
505 function checkTableEditForm(theForm, fieldsCnt)
507     // TODO: avoid sending a message if user just wants to add a line
508     // on the form but has not completed at least one field name
510     var atLeastOneField = 0;
511     var i, elm, elm2, elm3, val, id;
513     for (i = 0; i < fieldsCnt; i++) {
514         id = "#field_" + i + "_2";
515         elm = $(id);
516         val = elm.val();
517         if (val == 'VARCHAR' || val == 'CHAR' || val == 'BIT' || val == 'VARBINARY' || val == 'BINARY') {
518             elm2 = $("#field_" + i + "_3");
519             val = parseInt(elm2.val(), 10);
520             elm3 = $("#field_" + i + "_1");
521             if (isNaN(val) && elm3.val() !== "") {
522                 elm2.select();
523                 alert(PMA_messages.strEnterValidLength);
524                 elm2.focus();
525                 return false;
526             }
527         }
529         if (atLeastOneField === 0) {
530             id = "field_" + i + "_1";
531             if (!emptyCheckTheField(theForm, id)) {
532                 atLeastOneField = 1;
533             }
534         }
535     }
536     if (atLeastOneField === 0) {
537         var theField = theForm.elements["field_0_1"];
538         alert(PMA_messages.strFormEmpty);
539         theField.focus();
540         return false;
541     }
543     // at least this section is under jQuery
544     if ($("input.textfield[name='table']").val() === "") {
545         alert(PMA_messages.strFormEmpty);
546         $("input.textfield[name='table']").focus();
547         return false;
548     }
551     return true;
552 } // enf of the 'checkTableEditForm()' function
555  * Unbind all event handlers before tearing down a page
556  */
557 AJAX.registerTeardown('functions.js', function () {
558     $('input:checkbox.checkall').die('click');
560 AJAX.registerOnload('functions.js', function () {
561     /**
562      * Row marking in horizontal mode (use "live" so that it works also for
563      * next pages reached via AJAX); a tr may have the class noclick to remove
564      * this behavior.
565      */
567     $('input:checkbox.checkall').live('click', function (e) {
568         var $tr = $(this).closest('tr');
570         // make the table unselectable (to prevent default highlighting when shift+click)
571         //$tr.parents('table').noSelect();
573         if (!e.shiftKey || last_clicked_row == -1) {
574             // usual click
576             // XXX: FF fires two click events for <label> (label and checkbox), so we need to handle this differently
577             var $checkbox = $tr.find(':checkbox');
578             if ($checkbox.length) {
579                 // checkbox in a row, add or remove class depending on checkbox state
580                 var checked = $checkbox.prop('checked');
581                 if (!$(e.target).is(':checkbox, label')) {
582                     checked = !checked;
583                     $checkbox.prop('checked', checked).trigger('change');
584                 }
585                 if (checked) {
586                     $tr.addClass('marked');
587                 } else {
588                     $tr.removeClass('marked');
589                 }
590                 last_click_checked = checked;
591             } else {
592                 // normal data table, just toggle class
593                 $tr.toggleClass('marked');
594                 last_click_checked = false;
595             }
597             // remember the last clicked row
598             last_clicked_row = last_click_checked ? $('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr) : -1;
599             last_shift_clicked_row = -1;
600         } else {
601             // handle the shift click
602             PMA_clearSelection();
603             var start, end;
605             // clear last shift click result
606             if (last_shift_clicked_row >= 0) {
607                 if (last_shift_clicked_row >= last_clicked_row) {
608                     start = last_clicked_row;
609                     end = last_shift_clicked_row;
610                 } else {
611                     start = last_shift_clicked_row;
612                     end = last_clicked_row;
613                 }
614                 $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
615                     .slice(start, end + 1)
616                     .removeClass('marked')
617                     .find(':checkbox')
618                     .prop('checked', false)
619                     .trigger('change');
620             }
622             // handle new shift click
623             var curr_row = $('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr);
624             if (curr_row >= last_clicked_row) {
625                 start = last_clicked_row;
626                 end = curr_row;
627             } else {
628                 start = curr_row;
629                 end = last_clicked_row;
630             }
631             $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
632                 .slice(start, end + 1)
633                 .addClass('marked')
634                 .find(':checkbox')
635                 .prop('checked', true)
636                 .trigger('change');
638             // remember the last shift clicked row
639             last_shift_clicked_row = curr_row;
640         }
641     });
643     addDateTimePicker();
645     /**
646      * Add attribute to text boxes for iOS devices (based on bugID: 3508912)
647      */
648     if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) {
649         $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off');
650     }
654  * True if last click is to check a row.
655  */
656 var last_click_checked = false;
659  * Zero-based index of last clicked row.
660  * Used to handle the shift + click event in the code above.
661  */
662 var last_clicked_row = -1;
665  * Zero-based index of last shift clicked row.
666  */
667 var last_shift_clicked_row = -1;
670  * Row highlighting in horizontal mode (use "live"
671  * so that it works also for pages reached via AJAX)
672  */
673 /*AJAX.registerOnload('functions.js', function () {
674     $('tr.odd, tr.even').live('hover',function (event) {
675         var $tr = $(this);
676         $tr.toggleClass('hover',event.type=='mouseover');
677         $tr.children().toggleClass('hover',event.type=='mouseover');
678     });
679 })*/
682  * This array is used to remember mark status of rows in browse mode
683  */
684 var marked_row = [];
687  * marks all rows and selects its first checkbox inside the given element
688  * the given element is usaly a table or a div containing the table or tables
690  * @param container    DOM element
691  */
692 function markAllRows(container_id)
695     $("#" + container_id).find("input:checkbox:enabled").prop('checked', true)
696     .trigger("change")
697     .parents("tr").addClass("marked");
698     return true;
702  * marks all rows and selects its first checkbox inside the given element
703  * the given element is usaly a table or a div containing the table or tables
705  * @param container    DOM element
706  */
707 function unMarkAllRows(container_id)
710     $("#" + container_id).find("input:checkbox:enabled").prop('checked', false)
711     .trigger("change")
712     .parents("tr").removeClass("marked");
713     return true;
717  * Checks/unchecks all checkbox in given conainer (f.e. a form, fieldset or div)
719  * @param string   container_id  the container id
720  * @param boolean  state         new value for checkbox (true or false)
721  * @return boolean  always true
722  */
723 function setCheckboxes(container_id, state)
726     $("#" + container_id).find("input:checkbox").prop('checked', state);
727     return true;
728 } // end of the 'setCheckboxes()' function
731   * Checks/unchecks all options of a <select> element
732   *
733   * @param string   the form name
734   * @param string   the element name
735   * @param boolean  whether to check or to uncheck options
736   *
737   * @return boolean  always true
738   */
739 function setSelectOptions(the_form, the_select, do_check)
741     $("form[name='" + the_form + "'] select[name='" + the_select + "']").find("option").prop('selected', do_check);
742     return true;
743 } // end of the 'setSelectOptions()' function
746  * Sets current value for query box.
747  */
748 function setQuery(query)
750     if (codemirror_editor) {
751         codemirror_editor.setValue(query);
752         codemirror_editor.focus();
753     } else {
754         document.sqlform.sql_query.value = query;
755         document.sqlform.sql_query.focus();
756     }
761   * Create quick sql statements.
762   *
763   */
764 function insertQuery(queryType)
766     if (queryType == "clear") {
767         setQuery('');
768         return;
769     }
771     var query = "";
772     var myListBox = document.sqlform.dummy;
773     var table = document.sqlform.table.value;
775     if (myListBox.options.length > 0) {
776         sql_box_locked = true;
777         var chaineAj = "";
778         var valDis = "";
779         var editDis = "";
780         var NbSelect = 0;
781         for (var i = 0; i < myListBox.options.length; i++) {
782             NbSelect++;
783             if (NbSelect > 1) {
784                 chaineAj += ", ";
785                 valDis += ",";
786                 editDis += ",";
787             }
788             chaineAj += myListBox.options[i].value;
789             valDis += "[value-" + NbSelect + "]";
790             editDis += myListBox.options[i].value + "=[value-" + NbSelect + "]";
791         }
792         if (queryType == "selectall") {
793             query = "SELECT * FROM `" + table + "` WHERE 1";
794         } else if (queryType == "select") {
795             query = "SELECT " + chaineAj + " FROM `" + table + "` WHERE 1";
796         } else if (queryType == "insert") {
797             query = "INSERT INTO `" + table + "`(" + chaineAj + ") VALUES (" + valDis + ")";
798         } else if (queryType == "update") {
799             query = "UPDATE `" + table + "` SET " + editDis + " WHERE 1";
800         } else if (queryType == "delete") {
801             query = "DELETE FROM `" + table + "` WHERE 1";
802         }
803         setQuery(query);
804         sql_box_locked = false;
805     }
810   * Inserts multiple fields.
811   *
812   */
813 function insertValueQuery()
815     var myQuery = document.sqlform.sql_query;
816     var myListBox = document.sqlform.dummy;
818     if (myListBox.options.length > 0) {
819         sql_box_locked = true;
820         var chaineAj = "";
821         var NbSelect = 0;
822         for (var i = 0; i < myListBox.options.length; i++) {
823             if (myListBox.options[i].selected) {
824                 NbSelect++;
825                 if (NbSelect > 1) {
826                     chaineAj += ", ";
827                 }
828                 chaineAj += myListBox.options[i].value;
829             }
830         }
832         /* CodeMirror support */
833         if (codemirror_editor) {
834             codemirror_editor.replaceSelection(chaineAj);
835         //IE support
836         } else if (document.selection) {
837             myQuery.focus();
838             var sel = document.selection.createRange();
839             sel.text = chaineAj;
840             document.sqlform.insert.focus();
841         }
842         //MOZILLA/NETSCAPE support
843         else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart == "0") {
844             var startPos = document.sqlform.sql_query.selectionStart;
845             var endPos = document.sqlform.sql_query.selectionEnd;
846             var chaineSql = document.sqlform.sql_query.value;
848             myQuery.value = chaineSql.substring(0, startPos) + chaineAj + chaineSql.substring(endPos, chaineSql.length);
849         } else {
850             myQuery.value += chaineAj;
851         }
852         sql_box_locked = false;
853     }
857  * Add a date/time picker to each element that needs it
858  * (only when jquery-ui-timepicker-addon.js is loaded)
859  */
860 function addDateTimePicker() {
861     if ($.timepicker !== undefined) {
862         $('input.datefield, input.datetimefield').each(function () {
863             PMA_addDatepicker($(this));
864         });
865     }
869   * Refresh/resize the WYSIWYG scratchboard
870   */
871 function refreshLayout()
873     var $elm = $('#pdflayout');
874     var orientation = $('#orientation_opt').val();
875     var paper = 'A4';
876     if ($('#paper_opt').length == 1) {
877         paper = $('#paper_opt').val();
878     }
879     var posa = 'y';
880     var posb = 'x';
881     if (orientation == 'P') {
882         posa = 'x';
883         posb = 'y';
884     }
885     $elm.css('width', pdfPaperSize(paper, posa) + 'px');
886     $elm.css('height', pdfPaperSize(paper, posb) + 'px');
890  * Initializes positions of elements.
891  */
892 function TableDragInit() {
893     $('.pdflayout_table').each(function () {
894         var $this = $(this);
895         var number = $this.data('number');
896         var x = $('#c_table_' + number + '_x').val();
897         var y = $('#c_table_' + number + '_y').val();
898         $this.css('left', x + 'px');
899         $this.css('top', y + 'px');
900         /* Make elements draggable */
901         $this.draggable({
902             containment: "parent",
903             drag: function (evt, ui) {
904                 var number = $this.data('number');
905                 $('#c_table_' + number + '_x').val(parseInt(ui.position.left, 10));
906                 $('#c_table_' + number + '_y').val(parseInt(ui.position.top, 10));
907             }
908         });
909     });
913  * Resets drag and drop positions.
914  */
915 function resetDrag() {
916     $('.pdflayout_table').each(function () {
917         var $this = $(this);
918         var x = $this.data('x');
919         var y = $this.data('y');
920         $this.css('left', x + 'px');
921         $this.css('top', y + 'px');
922     });
926  * User schema handlers.
927  */
928 $(function () {
929     /* Move in scratchboard on manual change */
930     $('.position-change').live('change', function () {
931         var $this = $(this);
932         var $elm = $('#table_' + $this.data('number'));
933         $elm.css($this.data('axis'), $this.val() + 'px');
934     });
935     /* Refresh on paper size/orientation change */
936     $('.paper-change').live('change', function () {
937         var $elm = $('#pdflayout');
938         if ($elm.css('visibility') == 'visible') {
939             refreshLayout();
940             TableDragInit();
941         }
942     });
943     /* Show/hide the WYSIWYG scratchboard */
944     $('#toggle-dragdrop').live('click', function () {
945         var $elm = $('#pdflayout');
946         if ($elm.css('visibility') == 'hidden') {
947             refreshLayout();
948             TableDragInit();
949             $elm.css('visibility', 'visible');
950             $elm.css('display', 'block');
951             $('#showwysiwyg').val('1');
952         } else {
953             $elm.css('visibility', 'hidden');
954             $elm.css('display', 'none');
955             $('#showwysiwyg').val('0');
956         }
957     });
958     /* Reset scratchboard */
959     $('#reset-dragdrop').live('click', function () {
960         resetDrag();
961     });
965  * Returns paper sizes for a given format
966  */
967 function pdfPaperSize(format, axis)
969     switch (format.toUpperCase()) {
970     case '4A0':
971         if (axis == 'x') {
972             return 4767.87;
973         } else {
974             return 6740.79;
975         }
976         break;
977     case '2A0':
978         if (axis == 'x') {
979             return 3370.39;
980         } else {
981             return 4767.87;
982         }
983         break;
984     case 'A0':
985         if (axis == 'x') {
986             return 2383.94;
987         } else {
988             return 3370.39;
989         }
990         break;
991     case 'A1':
992         if (axis == 'x') {
993             return 1683.78;
994         } else {
995             return 2383.94;
996         }
997         break;
998     case 'A2':
999         if (axis == 'x') {
1000             return 1190.55;
1001         } else {
1002             return 1683.78;
1003         }
1004         break;
1005     case 'A3':
1006         if (axis == 'x') {
1007             return 841.89;
1008         } else {
1009             return 1190.55;
1010         }
1011         break;
1012     case 'A4':
1013         if (axis == 'x') {
1014             return 595.28;
1015         } else {
1016             return 841.89;
1017         }
1018         break;
1019     case 'A5':
1020         if (axis == 'x') {
1021             return 419.53;
1022         } else {
1023             return 595.28;
1024         }
1025         break;
1026     case 'A6':
1027         if (axis == 'x') {
1028             return 297.64;
1029         } else {
1030             return 419.53;
1031         }
1032         break;
1033     case 'A7':
1034         if (axis == 'x') {
1035             return 209.76;
1036         } else {
1037             return 297.64;
1038         }
1039         break;
1040     case 'A8':
1041         if (axis == 'x') {
1042             return 147.40;
1043         } else {
1044             return 209.76;
1045         }
1046         break;
1047     case 'A9':
1048         if (axis == 'x') {
1049             return 104.88;
1050         } else {
1051             return 147.40;
1052         }
1053         break;
1054     case 'A10':
1055         if (axis == 'x') {
1056             return 73.70;
1057         } else {
1058             return 104.88;
1059         }
1060         break;
1061     case 'B0':
1062         if (axis == 'x') {
1063             return 2834.65;
1064         } else {
1065             return 4008.19;
1066         }
1067         break;
1068     case 'B1':
1069         if (axis == 'x') {
1070             return 2004.09;
1071         } else {
1072             return 2834.65;
1073         }
1074         break;
1075     case 'B2':
1076         if (axis == 'x') {
1077             return 1417.32;
1078         } else {
1079             return 2004.09;
1080         }
1081         break;
1082     case 'B3':
1083         if (axis == 'x') {
1084             return 1000.63;
1085         } else {
1086             return 1417.32;
1087         }
1088         break;
1089     case 'B4':
1090         if (axis == 'x') {
1091             return 708.66;
1092         } else {
1093             return 1000.63;
1094         }
1095         break;
1096     case 'B5':
1097         if (axis == 'x') {
1098             return 498.90;
1099         } else {
1100             return 708.66;
1101         }
1102         break;
1103     case 'B6':
1104         if (axis == 'x') {
1105             return 354.33;
1106         } else {
1107             return 498.90;
1108         }
1109         break;
1110     case 'B7':
1111         if (axis == 'x') {
1112             return 249.45;
1113         } else {
1114             return 354.33;
1115         }
1116         break;
1117     case 'B8':
1118         if (axis == 'x') {
1119             return 175.75;
1120         } else {
1121             return 249.45;
1122         }
1123         break;
1124     case 'B9':
1125         if (axis == 'x') {
1126             return 124.72;
1127         } else {
1128             return 175.75;
1129         }
1130         break;
1131     case 'B10':
1132         if (axis == 'x') {
1133             return 87.87;
1134         } else {
1135             return 124.72;
1136         }
1137         break;
1138     case 'C0':
1139         if (axis == 'x') {
1140             return 2599.37;
1141         } else {
1142             return 3676.54;
1143         }
1144         break;
1145     case 'C1':
1146         if (axis == 'x') {
1147             return 1836.85;
1148         } else {
1149             return 2599.37;
1150         }
1151         break;
1152     case 'C2':
1153         if (axis == 'x') {
1154             return 1298.27;
1155         } else {
1156             return 1836.85;
1157         }
1158         break;
1159     case 'C3':
1160         if (axis == 'x') {
1161             return 918.43;
1162         } else {
1163             return 1298.27;
1164         }
1165         break;
1166     case 'C4':
1167         if (axis == 'x') {
1168             return 649.13;
1169         } else {
1170             return 918.43;
1171         }
1172         break;
1173     case 'C5':
1174         if (axis == 'x') {
1175             return 459.21;
1176         } else {
1177             return 649.13;
1178         }
1179         break;
1180     case 'C6':
1181         if (axis == 'x') {
1182             return 323.15;
1183         } else {
1184             return 459.21;
1185         }
1186         break;
1187     case 'C7':
1188         if (axis == 'x') {
1189             return 229.61;
1190         } else {
1191             return 323.15;
1192         }
1193         break;
1194     case 'C8':
1195         if (axis == 'x') {
1196             return 161.57;
1197         } else {
1198             return 229.61;
1199         }
1200         break;
1201     case 'C9':
1202         if (axis == 'x') {
1203             return 113.39;
1204         } else {
1205             return 161.57;
1206         }
1207         break;
1208     case 'C10':
1209         if (axis == 'x') {
1210             return 79.37;
1211         } else {
1212             return 113.39;
1213         }
1214         break;
1215     case 'RA0':
1216         if (axis == 'x') {
1217             return 2437.80;
1218         } else {
1219             return 3458.27;
1220         }
1221         break;
1222     case 'RA1':
1223         if (axis == 'x') {
1224             return 1729.13;
1225         } else {
1226             return 2437.80;
1227         }
1228         break;
1229     case 'RA2':
1230         if (axis == 'x') {
1231             return 1218.90;
1232         } else {
1233             return 1729.13;
1234         }
1235         break;
1236     case 'RA3':
1237         if (axis == 'x') {
1238             return 864.57;
1239         } else {
1240             return 1218.90;
1241         }
1242         break;
1243     case 'RA4':
1244         if (axis == 'x') {
1245             return 609.45;
1246         } else {
1247             return 864.57;
1248         }
1249         break;
1250     case 'SRA0':
1251         if (axis == 'x') {
1252             return 2551.18;
1253         } else {
1254             return 3628.35;
1255         }
1256         break;
1257     case 'SRA1':
1258         if (axis == 'x') {
1259             return 1814.17;
1260         } else {
1261             return 2551.18;
1262         }
1263         break;
1264     case 'SRA2':
1265         if (axis == 'x') {
1266             return 1275.59;
1267         } else {
1268             return 1814.17;
1269         }
1270         break;
1271     case 'SRA3':
1272         if (axis == 'x') {
1273             return 907.09;
1274         } else {
1275             return 1275.59;
1276         }
1277         break;
1278     case 'SRA4':
1279         if (axis == 'x') {
1280             return 637.80;
1281         } else {
1282             return 907.09;
1283         }
1284         break;
1285     case 'LETTER':
1286         if (axis == 'x') {
1287             return 612.00;
1288         } else {
1289             return 792.00;
1290         }
1291         break;
1292     case 'LEGAL':
1293         if (axis == 'x') {
1294             return 612.00;
1295         } else {
1296             return 1008.00;
1297         }
1298         break;
1299     case 'EXECUTIVE':
1300         if (axis == 'x') {
1301             return 521.86;
1302         } else {
1303             return 756.00;
1304         }
1305         break;
1306     case 'FOLIO':
1307         if (axis == 'x') {
1308             return 612.00;
1309         } else {
1310             return 936.00;
1311         }
1312         break;
1313     } // end switch
1315     return 0;
1319  * Unbind all event handlers before tearing down a page
1320  */
1321 AJAX.registerTeardown('functions.js', function () {
1322     $("a.inline_edit_sql").die('click');
1323     $("input#sql_query_edit_save").die('click');
1324     $("input#sql_query_edit_discard").die('click');
1325     $('input.sqlbutton').unbind('click');
1326     $("#export_type").unbind('change');
1327     $('#sqlquery').unbind('keydown');
1328     $('#sql_query_edit').unbind('keydown');
1330     if (codemirror_inline_editor) {
1331         // Copy the sql query to the text area to preserve it.
1332         $('#sql_query_edit').text(codemirror_inline_editor.getValue());
1333         $(codemirror_inline_editor.getWrapperElement()).unbind('keydown');
1334         codemirror_inline_editor.toTextArea();
1335         codemirror_inline_editor = false;
1336     }
1337     if (codemirror_editor) {
1338         $(codemirror_editor.getWrapperElement()).unbind('keydown');
1339     }
1343  * Jquery Coding for inline editing SQL_QUERY
1344  */
1345 AJAX.registerOnload('functions.js', function () {
1346     // If we are coming back to the page by clicking forward button
1347     // of the browser, bind the code mirror to inline query editor.
1348     bindCodeMirrorToInlineEditor();
1349     $("a.inline_edit_sql").live('click', function () {
1350         if ($('#sql_query_edit').length) {
1351             // An inline query editor is already open,
1352             // we don't want another copy of it
1353             return false;
1354         }
1356         var $form = $(this).prev('form');
1357         var sql_query  = $form.find("input[name='sql_query']").val();
1358         var $inner_sql = $(this).parent().prev().find('.inner_sql');
1359         var $sql_highlight = $(this).parent().prev().find('.sql-highlight');
1360         var old_text   = $inner_sql.html();
1362         var new_content = "<textarea name=\"sql_query_edit\" id=\"sql_query_edit\">" + sql_query + "</textarea>\n";
1363         new_content    += "<input type=\"submit\" id=\"sql_query_edit_save\" class=\"button btnSave\" value=\"" + PMA_messages.strGo + "\"/>\n";
1364         new_content    += "<input type=\"button\" id=\"sql_query_edit_discard\" class=\"button btnDiscard\" value=\"" + PMA_messages.strCancel + "\"/>\n";
1365         var $editor_area = $('div#inline_editor');
1366         if ($editor_area.length === 0) {
1367             $editor_area = $('<div id="inline_editor_outer"></div>');
1368             $editor_area.insertBefore($inner_sql);
1369         }
1370         $editor_area.html(new_content);
1371         $inner_sql.hide();
1372         $sql_highlight.hide();
1374         bindCodeMirrorToInlineEditor();
1375         return false;
1376     });
1378     $("input#sql_query_edit_save").live('click', function () {
1379         var sql_query;
1380         if (codemirror_inline_editor) {
1381             sql_query = codemirror_inline_editor.getValue();
1382         } else {
1383             sql_query = $(this).prev().val();
1384         }
1386         var $form = $("a.inline_edit_sql").prev('form');
1387         var $fake_form = $('<form>', {action: 'import.php', method: 'post'})
1388                 .append($form.find("input[name=server], input[name=db], input[name=table], input[name=token]").clone())
1389                 .append($('<input/>', {type: 'hidden', name: 'show_query', value: 1}))
1390                 .append($('<input/>', {type: 'hidden', name: 'sql_query', value: sql_query}));
1391         $fake_form.appendTo($('body')).submit();
1392     });
1394     $("input#sql_query_edit_discard").live('click', function () {
1395         $('div#inline_editor_outer')
1396             .siblings('.sql-highlight').show();
1397         $('div#inline_editor_outer').remove();
1398     });
1400     $('input.sqlbutton').click(function (evt) {
1401         insertQuery(evt.target.id);
1402         return false;
1403     });
1405     $("#export_type").change(function () {
1406         if ($("#export_type").val() == 'svg') {
1407             $("#show_grid_opt").prop("disabled", true);
1408             $("#orientation_opt").prop("disabled", true);
1409             $("#with_doc").prop("disabled", true);
1410             $("#show_table_dim_opt").removeProp("disabled");
1411             $("#all_tables_same_width").removeProp("disabled");
1412             $("#paper_opt").removeProp("disabled");
1413             $("#show_color_opt").removeProp("disabled");
1414             //$(this).css("background-color","yellow");
1415         } else if ($("#export_type").val() == 'dia') {
1416             $("#show_grid_opt").prop("disabled", true);
1417             $("#with_doc").prop("disabled", true);
1418             $("#show_table_dim_opt").prop("disabled", true);
1419             $("#all_tables_same_width").prop("disabled", true);
1420             $("#paper_opt").removeProp("disabled");
1421             $("#show_color_opt").removeProp("disabled");
1422             $("#orientation_opt").removeProp("disabled");
1423         } else if ($("#export_type").val() == 'eps') {
1424             $("#show_grid_opt").prop("disabled", true);
1425             $("#orientation_opt").removeProp("disabled");
1426             $("#with_doc").prop("disabled", true);
1427             $("#show_table_dim_opt").prop("disabled", true);
1428             $("#all_tables_same_width").prop("disabled", true);
1429             $("#paper_opt").prop("disabled", true);
1430             $("#show_color_opt").prop("disabled", true);
1431         } else if ($("#export_type").val() == 'pdf') {
1432             $("#show_grid_opt").removeProp("disabled");
1433             $("#orientation_opt").removeProp("disabled");
1434             $("#with_doc").removeProp("disabled");
1435             $("#show_table_dim_opt").removeProp("disabled");
1436             $("#all_tables_same_width").removeProp("disabled");
1437             $("#paper_opt").removeProp("disabled");
1438             $("#show_color_opt").removeProp("disabled");
1439         } else {
1440             // nothing
1441         }
1442     });
1444     if ($('#input_username')) {
1445         if ($('#input_username').val() === '') {
1446             $('#input_username').focus();
1447         } else {
1448             $('#input_password').focus();
1449         }
1450     }
1454  * Binds the CodeMirror to the text area used to inline edit a query.
1455  */
1456 function bindCodeMirrorToInlineEditor() {
1457     var $inline_editor = $('#sql_query_edit');
1458     if ($inline_editor.length > 0) {
1459         if (typeof CodeMirror !== 'undefined') {
1460             var height = $('#sql_query_edit').css('height');
1461             codemirror_inline_editor = CodeMirror.fromTextArea($inline_editor[0], {
1462                 lineNumbers: true,
1463                 matchBrackets: true,
1464                 indentUnit: 4,
1465                 mode: "text/x-mysql",
1466                 lineWrapping: true
1467             });
1468             codemirror_inline_editor.getScrollerElement().style.height = height;
1469             codemirror_inline_editor.refresh();
1470             codemirror_inline_editor.focus();
1471             $(codemirror_inline_editor.getWrapperElement()).bind(
1472                 'keydown',
1473                 catchKeypressesFromSqlTextboxes
1474             );
1475         } else {
1476             $inline_editor.focus().bind(
1477                 'keydown',
1478                 catchKeypressesFromSqlTextboxes
1479             );
1480         }
1481     }
1484 function catchKeypressesFromSqlTextboxes(event) {
1485     // ctrl-enter is 10 in chrome and ie, but 13 in ff
1486     if (event.ctrlKey && (event.keyCode == 13 || event.keyCode == 10)) {
1487         if ($('#sql_query_edit').length > 0) {
1488             $("#sql_query_edit_save").trigger('click');
1489         } else if ($('#sqlquery').length > 0) {
1490             $("#button_submit_query").trigger('click');
1491         }
1492     }
1496  * Adds doc link to single highlighted SQL element
1497  */
1498 function PMA_doc_add($elm, params)
1500     var url = $.sprintf(
1501         mysql_doc_template,
1502         params[0]
1503     );
1504     if (params.length > 1) {
1505         url += '#' + params[1];
1506     }
1507     var content = $elm.text();
1508     $elm.text('');
1509     $elm.append('<a target="mysql_doc" class="cm-sql-doc" href="' + url + '">' + content + '</a>');
1513  * Generates doc links for keywords inside highlighted SQL
1514  */
1515 function PMA_doc_keyword(idx, elm)
1517     var $elm = $(elm);
1518     /* Skip already processed ones */
1519     if ($elm.find('a').length > 0) {
1520         return;
1521     }
1522     var keyword = $elm.text().toUpperCase();
1523     var $next = $elm.next('.cm-keyword');
1524     if ($next) {
1525         var next_keyword = $next.text().toUpperCase();
1526         var full = keyword + ' ' + next_keyword;
1528         var $next2 = $next.next('.cm-keyword');
1529         if ($next2) {
1530             var next2_keyword = $next2.text().toUpperCase();
1531             var full2 = full + ' ' + next2_keyword;
1532             if (full2 in mysql_doc_keyword) {
1533                 PMA_doc_add($elm, mysql_doc_keyword[full2]);
1534                 PMA_doc_add($next, mysql_doc_keyword[full2]);
1535                 PMA_doc_add($next2, mysql_doc_keyword[full2]);
1536                 return;
1537             }
1538         }
1539         if (full in mysql_doc_keyword) {
1540             PMA_doc_add($elm, mysql_doc_keyword[full]);
1541             PMA_doc_add($next, mysql_doc_keyword[full]);
1542             return;
1543         }
1544     }
1545     if (keyword in mysql_doc_keyword) {
1546         PMA_doc_add($elm, mysql_doc_keyword[keyword]);
1547     }
1551  * Generates doc links for builtins inside highlighted SQL
1552  */
1553 function PMA_doc_builtin(idx, elm)
1555     var $elm = $(elm);
1556     var builtin = $elm.text().toUpperCase();
1557     if (builtin in mysql_doc_builtin) {
1558         PMA_doc_add($elm, mysql_doc_builtin[builtin]);
1559     }
1563  * Higlights SQL using CodeMirror.
1564  */
1565 function PMA_highlightSQL(base)
1567     var $elm = base.find('code.sql');
1568     $elm.each(function () {
1569         var $sql = $(this);
1570         var $pre = $sql.find('pre');
1571         /* We only care about visible elements to avoid double processing */
1572         if ($pre.is(":visible")) {
1573             var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
1574             $sql.append($highlight);
1575             if (typeof CodeMirror != 'undefined') {
1576                 CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]);
1577                 $pre.hide();
1578                 $highlight.find('.cm-keyword').each(PMA_doc_keyword);
1579                 $highlight.find('.cm-builtin').each(PMA_doc_builtin);
1580             }
1581         }
1582     });
1586  * Show a message on the top of the page for an Ajax request
1588  * Sample usage:
1590  * 1) var $msg = PMA_ajaxShowMessage();
1591  * This will show a message that reads "Loading...". Such a message will not
1592  * disappear automatically and cannot be dismissed by the user. To remove this
1593  * message either the PMA_ajaxRemoveMessage($msg) function must be called or
1594  * another message must be show with PMA_ajaxShowMessage() function.
1596  * 2) var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
1597  * This is a special case. The behaviour is same as above,
1598  * just with a different message
1600  * 3) var $msg = PMA_ajaxShowMessage('The operation was successful');
1601  * This will show a message that will disappear automatically and it can also
1602  * be dismissed by the user.
1604  * 4) var $msg = PMA_ajaxShowMessage('Some error', false);
1605  * This will show a message that will not disappear automatically, but it
1606  * can be dismissed by the user after he has finished reading it.
1608  * @param string  message     string containing the message to be shown.
1609  *                              optional, defaults to 'Loading...'
1610  * @param mixed   timeout     number of milliseconds for the message to be visible
1611  *                              optional, defaults to 5000. If set to 'false', the
1612  *                              notification will never disappear
1613  * @return jQuery object       jQuery Element that holds the message div
1614  *                              this object can be passed to PMA_ajaxRemoveMessage()
1615  *                              to remove the notification
1616  */
1617 function PMA_ajaxShowMessage(message, timeout)
1619     /**
1620      * @var self_closing Whether the notification will automatically disappear
1621      */
1622     var self_closing = true;
1623     /**
1624      * @var dismissable Whether the user will be able to remove
1625      *                  the notification by clicking on it
1626      */
1627     var dismissable = true;
1628     // Handle the case when a empty data.message is passed.
1629     // We don't want the empty message
1630     if (message === '') {
1631         return true;
1632     } else if (! message) {
1633         // If the message is undefined, show the default
1634         message = PMA_messages.strLoading;
1635         dismissable = false;
1636         self_closing = false;
1637     } else if (message == PMA_messages.strProcessingRequest) {
1638         // This is another case where the message should not disappear
1639         dismissable = false;
1640         self_closing = false;
1641     }
1642     // Figure out whether (or after how long) to remove the notification
1643     if (timeout === undefined) {
1644         timeout = 5000;
1645     } else if (timeout === false) {
1646         self_closing = false;
1647     }
1648     // Create a parent element for the AJAX messages, if necessary
1649     if ($('#loading_parent').length === 0) {
1650         $('<div id="loading_parent"></div>')
1651         .prependTo("body");
1652     }
1653     // Update message count to create distinct message elements every time
1654     ajax_message_count++;
1655     // Remove all old messages, if any
1656     $("span.ajax_notification[id^=ajax_message_num]").remove();
1657     /**
1658      * @var    $retval    a jQuery object containing the reference
1659      *                    to the created AJAX message
1660      */
1661     var $retval = $(
1662             '<span class="ajax_notification" id="ajax_message_num_' +
1663             ajax_message_count +
1664             '"></span>'
1665     )
1666     .hide()
1667     .appendTo("#loading_parent")
1668     .html(message)
1669     .show();
1670     // If the notification is self-closing we should create a callback to remove it
1671     if (self_closing) {
1672         $retval
1673         .delay(timeout)
1674         .fadeOut('medium', function () {
1675             if ($(this).is('.dismissable')) {
1676                 $(this).tooltip('destroy');
1677             }
1678             // Remove the notification
1679             $(this).remove();
1680         });
1681     }
1682     // If the notification is dismissable we need to add the relevant class to it
1683     // and add a tooltip so that the users know that it can be removed
1684     if (dismissable) {
1685         $retval.addClass('dismissable').css('cursor', 'pointer');
1686         /**
1687          * Add a tooltip to the notification to let the user know that (s)he
1688          * can dismiss the ajax notification by clicking on it.
1689          */
1690         PMA_tooltip(
1691             $retval,
1692             'span',
1693             PMA_messages.strDismiss
1694         );
1695     }
1696     PMA_highlightSQL($retval);
1698     return $retval;
1702  * Removes the message shown for an Ajax operation when it's completed
1704  * @param jQuery object   jQuery Element that holds the notification
1706  * @return nothing
1707  */
1708 function PMA_ajaxRemoveMessage($this_msgbox)
1710     if ($this_msgbox !== undefined && $this_msgbox instanceof jQuery) {
1711         $this_msgbox
1712         .stop(true, true)
1713         .fadeOut('medium');
1714         if ($this_msgbox.is('.dismissable')) {
1715             if ($.isFunction($this_msgbox.tooltip)) {
1716                 $this_msgbox.tooltip('destroy');
1717             }
1718         } else {
1719             $this_msgbox.remove();
1720         }
1721     }
1724 // This event only need to be fired once after the initial page load
1725 $(function () {
1726     /**
1727      * Allows the user to dismiss a notification
1728      * created with PMA_ajaxShowMessage()
1729      */
1730     $('span.ajax_notification.dismissable').live('click', function () {
1731         PMA_ajaxRemoveMessage($(this));
1732     });
1733     /**
1734      * The below two functions hide the "Dismiss notification" tooltip when a user
1735      * is hovering a link or button that is inside an ajax message
1736      */
1737     $('span.ajax_notification a, span.ajax_notification button, span.ajax_notification input')
1738     .live('mouseover', function () {
1739         $(this).parents('span.ajax_notification').tooltip('disable');
1740     })
1741     .live('mouseout', function () {
1742         $(this).parents('span.ajax_notification').tooltip('enable');
1743     });
1747  * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected
1748  */
1749 function PMA_showNoticeForEnum(selectElement)
1751     var enum_notice_id = selectElement.attr("id").split("_")[1];
1752     enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2], 10) + 1);
1753     var selectedType = selectElement.val();
1754     if (selectedType == "ENUM" || selectedType == "SET") {
1755         $("p#enum_notice_" + enum_notice_id).show();
1756     } else {
1757         $("p#enum_notice_" + enum_notice_id).hide();
1758     }
1762  * Creates a Profiling Chart with jqplot. Used in sql.js
1763  * and in server_status_monitor.js
1764  */
1765 function PMA_createProfilingChartJqplot(target, data)
1767     return $.jqplot(target, [data],
1768         {
1769             seriesDefaults: {
1770                 renderer: $.jqplot.PieRenderer,
1771                 rendererOptions: {
1772                     showDataLabels:  true
1773                 }
1774             },
1775             highlighter: {
1776                 show: true,
1777                 tooltipLocation: 'se',
1778                 sizeAdjust: 0,
1779                 tooltipAxes: 'pieref',
1780                 useAxesFormatters: false,
1781                 formatString: '%s, %.9Ps'
1782             },
1783             legend: {
1784                 show: true,
1785                 location: 'e'
1786             },
1787             // from http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines#Color_Palette
1788             seriesColors: [
1789                 '#fce94f',
1790                 '#fcaf3e',
1791                 '#e9b96e',
1792                 '#8ae234',
1793                 '#729fcf',
1794                 '#ad7fa8',
1795                 '#ef2929',
1796                 '#eeeeec',
1797                 '#888a85',
1798                 '#c4a000',
1799                 '#ce5c00',
1800                 '#8f5902',
1801                 '#4e9a06',
1802                 '#204a87',
1803                 '#5c3566',
1804                 '#a40000',
1805                 '#babdb6',
1806                 '#2e3436'
1807             ]
1808         }
1809     );
1813  * Formats a profiling duration nicely (in us and ms time).
1814  * Used in server_status_monitor.js
1816  * @param  integer    Number to be formatted, should be in the range of microsecond to second
1817  * @param  integer    Accuracy, how many numbers right to the comma should be
1818  * @return string     The formatted number
1819  */
1820 function PMA_prettyProfilingNum(num, acc)
1822     if (!acc) {
1823         acc = 2;
1824     }
1825     acc = Math.pow(10, acc);
1826     if (num * 1000 < 0.1) {
1827         num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ';
1828     } else if (num < 0.1) {
1829         num = Math.round(acc * (num * 1000)) / acc + 'm';
1830     } else {
1831         num = Math.round(acc * num) / acc;
1832     }
1834     return num + 's';
1839  * Formats a SQL Query nicely with newlines and indentation. Depends on Codemirror and MySQL Mode!
1841  * @param string      Query to be formatted
1842  * @return string      The formatted query
1843  */
1844 function PMA_SQLPrettyPrint(string)
1846     if (typeof CodeMirror == 'undefined') {
1847         return string;
1848     }
1850     var mode = CodeMirror.getMode({}, "text/x-mysql");
1851     var stream = new CodeMirror.StringStream(string);
1852     var state = mode.startState();
1853     var token, tokens = [];
1854     var output = '';
1855     var tabs = function (cnt) {
1856         var ret = '';
1857         for (var i = 0; i < 4 * cnt; i++) {
1858             ret += " ";
1859         }
1860         return ret;
1861     };
1863     // "root-level" statements
1864     var statements = {
1865         'select': ['select', 'from', 'on', 'where', 'having', 'limit', 'order by', 'group by'],
1866         'update': ['update', 'set', 'where'],
1867         'insert into': ['insert into', 'values']
1868     };
1869     // don't put spaces before these tokens
1870     var spaceExceptionsBefore = {';': true, ',': true, '.': true, '(': true};
1871     // don't put spaces after these tokens
1872     var spaceExceptionsAfter = {'.': true};
1874     // Populate tokens array
1875     var str = '';
1876     while (! stream.eol()) {
1877         stream.start = stream.pos;
1878         token = mode.token(stream, state);
1879         if (token !== null) {
1880             tokens.push([token, stream.current().toLowerCase()]);
1881         }
1882     }
1884     var currentStatement = tokens[0][1];
1886     if (! statements[currentStatement]) {
1887         return string;
1888     }
1889     // Holds all currently opened code blocks (statement, function or generic)
1890     var blockStack = [];
1891     // Holds the type of block from last iteration (the current is in blockStack[0])
1892     var previousBlock;
1893     // If a new code block is found, newBlock contains its type for one iteration and vice versa for endBlock
1894     var newBlock, endBlock;
1895     // How much to indent in the current line
1896     var indentLevel = 0;
1897     // Holds the "root-level" statements
1898     var statementPart, lastStatementPart = statements[currentStatement][0];
1900     blockStack.unshift('statement');
1902     // Iterate through every token and format accordingly
1903     for (var i = 0; i < tokens.length; i++) {
1904         previousBlock = blockStack[0];
1906         // New block => push to stack
1907         if (tokens[i][1] == '(') {
1908             if (i < tokens.length - 1 && tokens[i + 1][0] == 'statement-verb') {
1909                 blockStack.unshift(newBlock = 'statement');
1910             } else if (i > 0 && tokens[i - 1][0] == 'builtin') {
1911                 blockStack.unshift(newBlock = 'function');
1912             } else {
1913                 blockStack.unshift(newBlock = 'generic');
1914             }
1915         } else {
1916             newBlock = null;
1917         }
1919         // Block end => pop from stack
1920         if (tokens[i][1] == ')') {
1921             endBlock = blockStack[0];
1922             blockStack.shift();
1923         } else {
1924             endBlock = null;
1925         }
1927         // A subquery is starting
1928         if (i > 0 && newBlock == 'statement') {
1929             indentLevel++;
1930             output += "\n" + tabs(indentLevel) + tokens[i][1] + ' ' + tokens[i + 1][1].toUpperCase() + "\n" + tabs(indentLevel + 1);
1931             currentStatement = tokens[i + 1][1];
1932             i++;
1933             continue;
1934         }
1936         // A subquery is ending
1937         if (endBlock == 'statement' && indentLevel > 0) {
1938             output += "\n" + tabs(indentLevel);
1939             indentLevel--;
1940         }
1942         // One less indentation for statement parts (from, where, order by, etc.) and a newline
1943         statementPart = statements[currentStatement].indexOf(tokens[i][1]);
1944         if (statementPart != -1) {
1945             if (i > 0) {
1946                 output += "\n";
1947             }
1948             output += tabs(indentLevel) + tokens[i][1].toUpperCase();
1949             output += "\n" + tabs(indentLevel + 1);
1950             lastStatementPart = tokens[i][1];
1951         }
1952         // Normal indentatin and spaces for everything else
1953         else {
1954             if (! spaceExceptionsBefore[tokens[i][1]] &&
1955                ! (i > 0 && spaceExceptionsAfter[tokens[i - 1][1]]) &&
1956                output.charAt(output.length - 1) != ' ') {
1957                 output += " ";
1958             }
1959             if (tokens[i][0] == 'keyword') {
1960                 output += tokens[i][1].toUpperCase();
1961             } else {
1962                 output += tokens[i][1];
1963             }
1964         }
1966         // split columns in select and 'update set' clauses, but only inside statements blocks
1967         if ((lastStatementPart == 'select' || lastStatementPart == 'where'  || lastStatementPart == 'set') &&
1968             tokens[i][1] == ',' && blockStack[0] == 'statement') {
1970             output += "\n" + tabs(indentLevel + 1);
1971         }
1973         // split conditions in where clauses, but only inside statements blocks
1974         if (lastStatementPart == 'where' &&
1975             (tokens[i][1] == 'and' || tokens[i][1] == 'or' || tokens[i][1] == 'xor')) {
1977             if (blockStack[0] == 'statement') {
1978                 output += "\n" + tabs(indentLevel + 1);
1979             }
1980             // Todo: Also split and or blocks in newlines & identation++
1981             //if (blockStack[0] == 'generic')
1982              //   output += ...
1983         }
1984     }
1985     return output;
1989  * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not
1990  *  return a jQuery object yet and hence cannot be chained
1992  * @param string      question
1993  * @param string      url         URL to be passed to the callbackFn to make
1994  *                                  an Ajax call to
1995  * @param function    callbackFn  callback to execute after user clicks on OK
1996  */
1998 jQuery.fn.PMA_confirm = function (question, url, callbackFn) {
1999     var confirmState = PMA_commonParams.get('confirm');
2000     // when the Confirm directive is set to false in config.inc.php
2001     // and not changed in user prefs, confirmState is ""
2002     // when it's unticked in user prefs, confirmState is 1
2003     if (confirmState === "" || confirmState === "1") {
2004         // user does not want to confirm
2005         if ($.isFunction(callbackFn)) {
2006             callbackFn.call(this, url);
2007             return true;
2008         }
2009     }
2010     if (PMA_messages.strDoYouReally === '') {
2011         return true;
2012     }
2014     /**
2015      * @var    button_options  Object that stores the options passed to jQueryUI
2016      *                          dialog
2017      */
2018     var button_options = {};
2019     button_options[PMA_messages.strOK] = function () {
2020         $(this).dialog("close");
2022         if ($.isFunction(callbackFn)) {
2023             callbackFn.call(this, url);
2024         }
2025     };
2026     button_options[PMA_messages.strCancel] = function () {
2027         $(this).dialog("close");
2028     };
2030     $('<div/>', {'id': 'confirm_dialog'})
2031     .prepend(question)
2032     .dialog({
2033         buttons: button_options,
2034         close: function () {
2035             $(this).remove();
2036         },
2037         modal: true
2038     });
2042  * jQuery function to sort a table's body after a new row has been appended to it.
2043  * Also fixes the even/odd classes of the table rows at the end.
2045  * @param string      text_selector   string to select the sortKey's text
2047  * @return jQuery Object for chaining purposes
2048  */
2049 jQuery.fn.PMA_sort_table = function (text_selector) {
2050     return this.each(function () {
2052         /**
2053          * @var table_body  Object referring to the table's <tbody> element
2054          */
2055         var table_body = $(this);
2056         /**
2057          * @var rows    Object referring to the collection of rows in {@link table_body}
2058          */
2059         var rows = $(this).find('tr').get();
2061         //get the text of the field that we will sort by
2062         $.each(rows, function (index, row) {
2063             row.sortKey = $.trim($(row).find(text_selector).text().toLowerCase());
2064         });
2066         //get the sorted order
2067         rows.sort(function (a, b) {
2068             if (a.sortKey < b.sortKey) {
2069                 return -1;
2070             }
2071             if (a.sortKey > b.sortKey) {
2072                 return 1;
2073             }
2074             return 0;
2075         });
2077         //pull out each row from the table and then append it according to it's order
2078         $.each(rows, function (index, row) {
2079             $(table_body).append(row);
2080             row.sortKey = null;
2081         });
2083         //Re-check the classes of each row
2084         $(this).find('tr:odd')
2085         .removeClass('even').addClass('odd')
2086         .end()
2087         .find('tr:even')
2088         .removeClass('odd').addClass('even');
2089     });
2093  * Unbind all event handlers before tearing down a page
2094  */
2095 AJAX.registerTeardown('functions.js', function () {
2096     $("#create_table_form_minimal.ajax").die('submit');
2097     $("form.create_table_form.ajax").die('submit');
2098     $("form.create_table_form.ajax input[name=submit_num_fields]").die('click');
2099     $("form.create_table_form.ajax input").die('keyup');
2103  * jQuery coding for 'Create Table'.  Used on db_operations.php,
2104  * db_structure.php and db_tracking.php (i.e., wherever
2105  * libraries/display_create_table.lib.php is used)
2107  * Attach Ajax Event handlers for Create Table
2108  */
2109 AJAX.registerOnload('functions.js', function () {
2110     /**
2111      * Attach event handler for submission of create table form (save)
2112      */
2113     $("form.create_table_form.ajax").live('submit', function (event) {
2114         event.preventDefault();
2116         /**
2117          * @var    the_form    object referring to the create table form
2118          */
2119         var $form = $(this);
2121         /*
2122          * First validate the form; if there is a problem, avoid submitting it
2123          *
2124          * checkTableEditForm() needs a pure element and not a jQuery object,
2125          * this is why we pass $form[0] as a parameter (the jQuery object
2126          * is actually an array of DOM elements)
2127          */
2129         if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) {
2130             PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2131             PMA_prepareForAjaxRequest($form);
2132             //User wants to submit the form
2133             $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
2134                 if (data.success === true) {
2135                     $('#properties_message')
2136                      .removeClass('error')
2137                      .html('');
2138                     PMA_ajaxShowMessage(data.message);
2139                     // Only if the create table dialog (distinct panel) exists
2140                     if ($("#create_table_dialog").length > 0) {
2141                         $("#create_table_dialog").dialog("close").remove();
2142                     }
2143                     $('#tableslistcontainer').before(data.formatted_sql);
2145                     /**
2146                      * @var tables_table    Object referring to the <tbody> element that holds the list of tables
2147                      */
2148                     var tables_table = $("#tablesForm").find("tbody").not("#tbl_summary_row");
2149                     // this is the first table created in this db
2150                     if (tables_table.length === 0) {
2151                         PMA_commonActions.refreshMain(
2152                             PMA_commonParams.get('opendb_url')
2153                         );
2154                     } else {
2155                         /**
2156                          * @var curr_last_row   Object referring to the last <tr> element in {@link tables_table}
2157                          */
2158                         var curr_last_row = $(tables_table).find('tr:last');
2159                         /**
2160                          * @var curr_last_row_index_string   String containing the index of {@link curr_last_row}
2161                          */
2162                         var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0];
2163                         /**
2164                          * @var curr_last_row_index Index of {@link curr_last_row}
2165                          */
2166                         var curr_last_row_index = parseFloat(curr_last_row_index_string);
2167                         /**
2168                          * @var new_last_row_index   Index of the new row to be appended to {@link tables_table}
2169                          */
2170                         var new_last_row_index = curr_last_row_index + 1;
2171                         /**
2172                          * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table}
2173                          */
2174                         var new_last_row_id = 'checkbox_tbl_' + new_last_row_index;
2176                         data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id);
2177                         //append to table
2178                         $(data.new_table_string)
2179                          .appendTo(tables_table);
2181                         //Sort the table
2182                         $(tables_table).PMA_sort_table('th');
2184                         // Adjust summary row
2185                         PMA_adjustTotals();
2186                     }
2188                     //Refresh navigation as a new table has been added
2189                     PMA_reloadNavigation();
2190                 } else {
2191                     PMA_ajaxShowMessage(
2192                         '<div class="error">' + data.error + '</div>',
2193                         false
2194                     );
2195                 }
2196             }); // end $.post()
2197         } // end if (checkTableEditForm() )
2198     }); // end create table form (save)
2200     /**
2201      * Attach event handler for create table form (add fields)
2202      */
2203     $("form.create_table_form.ajax input[name=submit_num_fields]").live('click', function (event) {
2204         event.preventDefault();
2205         /**
2206          * @var    the_form    object referring to the create table form
2207          */
2208         var $form = $(this).closest('form');
2210         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2211         PMA_prepareForAjaxRequest($form);
2213         //User wants to add more fields to the table
2214         $.post($form.attr('action'), $form.serialize() + "&submit_num_fields=1", function (data) {
2215             if (data.success) {
2216                 $("#page_content").html(data.message);
2217                 PMA_highlightSQL($('#page_content'));
2218                 PMA_verifyColumnsProperties();
2219                 PMA_ajaxRemoveMessage($msgbox);
2220             } else {
2221                 PMA_ajaxShowMessage(data.error);
2222             }
2223         }); //end $.post()
2224     }); // end create table form (add fields)
2226     $("form.create_table_form.ajax input").live('keydown', function (event) {
2227         if (event.keyCode == 13) {
2228             event.preventDefault();
2229             event.stopImmediatePropagation();
2230             $(this)
2231                 .closest('form')
2232                 .append('<input type="hidden" name="do_save_data" value="1" />')
2233                 .submit();
2234         }
2235     });
2240  * Unbind all event handlers before tearing down a page
2241  */
2242 AJAX.registerTeardown('functions.js', function () {
2243     $("#copyTable.ajax").die('submit');
2244     $("#moveTableForm").die('submit');
2245     $("#tableOptionsForm").die('submit');
2246     $("#tbl_maintenance li a.maintain_action.ajax").die('click');
2249  * jQuery coding for 'Table operations'.  Used on tbl_operations.php
2250  * Attach Ajax Event handlers for Table operations
2251  */
2252 AJAX.registerOnload('functions.js', function () {
2253     /**
2254      *Ajax action for submitting the "Copy table"
2255     **/
2256     $("#copyTable.ajax").live('submit', function (event) {
2257         event.preventDefault();
2258         var $form = $(this);
2259         PMA_prepareForAjaxRequest($form);
2260         $.post($form.attr('action'), $form.serialize() + "&submit_copy=Go", function (data) {
2261             if (data.success === true) {
2262                 if ($form.find("input[name='switch_to_new']").prop('checked')) {
2263                     PMA_commonParams.set(
2264                         'db',
2265                         data.db
2266                     );
2267                     PMA_commonParams.set(
2268                         'table',
2269                         $form.find("input[name='new_name']").val()
2270                     );
2271                     PMA_commonActions.refreshMain(false, function () {
2272                         PMA_ajaxShowMessage(data.message);
2273                     });
2274                 } else {
2275                     PMA_ajaxShowMessage(data.message);
2276                 }
2277                 // Refresh navigation when the table is copied
2278                 PMA_reloadNavigation();
2279             } else {
2280                 PMA_ajaxShowMessage(data.error, false);
2281             }
2282         }); // end $.post()
2283     });//end of copyTable ajax submit
2285     /**
2286      *Ajax action for submitting the "Move table"
2287      */
2288     $("#moveTableForm").live('submit', function (event) {
2289         event.preventDefault();
2290         var $form = $(this);
2291         var db = $form.find('select[name=target_db]').val();
2292         var tbl = $form.find('input[name=new_name]').val();
2293         PMA_prepareForAjaxRequest($form);
2294         $.post($form.attr('action'), $form.serialize() + "&submit_move=1", function (data) {
2295             if (data.success === true) {
2296                 PMA_commonParams.set('db', db);
2297                 PMA_commonParams.set('table', tbl);
2298                 PMA_commonActions.refreshMain(false, function () {
2299                     PMA_ajaxShowMessage(data.message);
2300                 });
2301                 // Refresh navigation when the table is copied
2302                 PMA_reloadNavigation();
2303             } else {
2304                 PMA_ajaxShowMessage(data.error, false);
2305             }
2306         }); // end $.post()
2307     });
2309     /**
2310      * Ajax action for submitting the "Table options"
2311      */
2312     $("#tableOptionsForm").live('submit', function (event) {
2313         event.preventDefault();
2314         event.stopPropagation();
2315         var $form = $(this);
2316         var $tblNameField = $form.find('input[name=new_name]');
2317         if ($tblNameField.val() !== $tblNameField[0].defaultValue) {
2318             // reload page and navigation if the table has been renamed
2319             PMA_prepareForAjaxRequest($form);
2320             var tbl = $tblNameField.val();
2321             $.post($form.attr('action'), $form.serialize(), function (data) {
2322                 if (data.success === true) {
2323                     PMA_commonParams.set('table', tbl);
2324                     PMA_commonActions.refreshMain(false, function () {
2325                         $('#page_content').html(data.message);
2326                         PMA_highlightSQL($('#page_content'));
2327                     });
2328                 } else {
2329                     PMA_ajaxShowMessage(data.error, false);
2330                 }
2331             }); // end $.post()
2332         } else {
2333             $form.removeClass('ajax').submit().addClass('ajax');
2334         }
2335     });
2337     /**
2338      *Ajax events for actions in the "Table maintenance"
2339     **/
2340     $("#tbl_maintenance li a.maintain_action.ajax").live('click', function (event) {
2341         event.preventDefault();
2342         if ($("#sqlqueryresults").length !== 0) {
2343             $("#sqlqueryresults").remove();
2344         }
2345         if ($("#result_query").length !== 0) {
2346             $("#result_query").remove();
2347         }
2348         //variables which stores the common attributes
2349         $.post($(this).attr('href'), { ajax_request: 1 }, function (data) {
2350             function scrollToTop() {
2351                 $('html, body').animate({ scrollTop: 0 });
2352             }
2353             if (data.success === true && data.sql_query !== undefined) {
2354                 PMA_ajaxShowMessage(data.message);
2355                 $("<div id='sqlqueryresults' class='ajax'></div>").prependTo("#page_content");
2356                 $("#sqlqueryresults").html(data.sql_query);
2357                 PMA_highlightSQL($('#page_content'));
2358                 scrollToTop();
2359             } else if (data.success === true) {
2360                 var $temp_div = $("<div id='temp_div'></div>");
2361                 $temp_div.html(data.message);
2362                 var $success = $temp_div.find("#result_query .success");
2363                 PMA_ajaxShowMessage($success);
2364                 $("<div id='sqlqueryresults' class='ajax'></div>").prependTo("#page_content");
2365                 $("#sqlqueryresults").html(data.message);
2366                 PMA_highlightSQL($('#page_content'));
2367                 PMA_init_slider();
2368                 $("#sqlqueryresults").children("fieldset,br").remove();
2369                 scrollToTop();
2370             } else {
2371                 var $temp_div = $("<div id='temp_div'></div>");
2372                 $temp_div.html(data.error);
2373                 var $error = $temp_div.find("code").addClass("error");
2374                 PMA_ajaxShowMessage($error, false);
2375             }
2376         }); // end $.post()
2377     });//end of table maintanance ajax click
2378 }); //end $(document).ready for 'Table operations'
2381  * Unbind all event handlers before tearing down a page
2382  */
2383 AJAX.registerTeardown('functions.js', function () {
2384     $("#drop_db_anchor.ajax").die('click');
2387  * Attach Ajax event handlers for Drop Database. Moved here from db_structure.js
2388  * as it was also required on db_create.php
2389  */
2390 AJAX.registerOnload('functions.js', function () {
2391     $("#drop_db_anchor.ajax").live('click', function (event) {
2392         event.preventDefault();
2393         /**
2394          * @var question    String containing the question to be asked for confirmation
2395          */
2396         var question = PMA_messages.strDropDatabaseStrongWarning + ' ';
2397         question += $.sprintf(
2398             PMA_messages.strDoYouReally,
2399             'DROP DATABASE ' + escapeHtml(PMA_commonParams.get('db'))
2400         );
2401         $(this).PMA_confirm(question, $(this).attr('href'), function (url) {
2402             PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2403             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function (data) {
2404                 if (data.success) {
2405                     //Database deleted successfully, refresh both the frames
2406                     PMA_reloadNavigation();
2407                     PMA_commonParams.set('db', '');
2408                     PMA_commonActions.refreshMain(
2409                         'server_databases.php',
2410                         function () {
2411                             PMA_ajaxShowMessage(data.message);
2412                         }
2413                     );
2414                 } else {
2415                     PMA_ajaxShowMessage(data.error, false);
2416                 }
2417             });
2418         });
2419     });
2420 }); // end of $() for Drop Database
2423  * Validates the password field in a form
2425  * @see    PMA_messages.strPasswordEmpty
2426  * @see    PMA_messages.strPasswordNotSame
2427  * @param  object $the_form The form to be validated
2428  * @return bool
2429  */
2430 function PMA_checkPassword($the_form)
2432     // Did the user select 'no password'?
2433     if ($the_form.find('#nopass_1').is(':checked')) {
2434         return true;
2435     } else {
2436         var $pred = $the_form.find('#select_pred_password');
2437         if ($pred.length && ($pred.val() == 'none' || $pred.val() == 'keep')) {
2438             return true;
2439         }
2440     }
2442     var $password = $the_form.find('input[name=pma_pw]');
2443     var $password_repeat = $the_form.find('input[name=pma_pw2]');
2444     var alert_msg = false;
2446     if ($password.val() === '') {
2447         alert_msg = PMA_messages.strPasswordEmpty;
2448     } else if ($password.val() != $password_repeat.val()) {
2449         alert_msg = PMA_messages.strPasswordNotSame;
2450     }
2452     if (alert_msg) {
2453         alert(alert_msg);
2454         $password.val('');
2455         $password_repeat.val('');
2456         $password.focus();
2457         return false;
2458     }
2459     return true;
2463  * Unbind all event handlers before tearing down a page
2464  */
2465 AJAX.registerTeardown('functions.js', function () {
2466     $('#change_password_anchor.ajax').die('click');
2469  * Attach Ajax event handlers for 'Change Password' on index.php
2470  */
2471 AJAX.registerOnload('functions.js', function () {
2473     /**
2474      * Attach Ajax event handler on the change password anchor
2475      */
2476     $('#change_password_anchor.ajax').live('click', function (event) {
2477         event.preventDefault();
2479         var $msgbox = PMA_ajaxShowMessage();
2481         /**
2482          * @var button_options  Object containing options to be passed to jQueryUI's dialog
2483          */
2484         var button_options = {};
2485         button_options[PMA_messages.strGo] = function () {
2487             event.preventDefault();
2489             /**
2490              * @var $the_form    Object referring to the change password form
2491              */
2492             var $the_form = $("#change_password_form");
2494             if (! PMA_checkPassword($the_form)) {
2495                 return false;
2496             }
2498             /**
2499              * @var this_value  String containing the value of the submit button.
2500              * Need to append this for the change password form on Server Privileges
2501              * page to work
2502              */
2503             var this_value = $(this).val();
2505             var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2506             $the_form.append('<input type="hidden" name="ajax_request" value="true" />');
2508             $.post($the_form.attr('action'), $the_form.serialize() + '&change_pw=' + this_value, function (data) {
2509                 if (data.success === true) {
2510                     $("#page_content").prepend(data.message);
2511                     PMA_highlightSQL($('#page_content'));
2512                     $("#change_password_dialog").hide().remove();
2513                     $("#edit_user_dialog").dialog("close").remove();
2514                     PMA_ajaxRemoveMessage($msgbox);
2515                 }
2516                 else {
2517                     PMA_ajaxShowMessage(data.error, false);
2518                 }
2519             }); // end $.post()
2520         };
2522         button_options[PMA_messages.strCancel] = function () {
2523             $(this).dialog('close');
2524         };
2525         $.get($(this).attr('href'), {'ajax_request': true}, function (data) {
2526             if (data.success) {
2527                 $('<div id="change_password_dialog"></div>')
2528                 .dialog({
2529                     title: PMA_messages.strChangePassword,
2530                     width: 600,
2531                     close: function (ev, ui) {
2532                         $(this).remove();
2533                     },
2534                     buttons : button_options,
2535                     modal: true
2536                 })
2537                 .append(data.message);
2538                 // for this dialog, we remove the fieldset wrapping due to double headings
2539                 $("fieldset#fieldset_change_password")
2540                 .find("legend").remove().end()
2541                 .find("table.noclick").unwrap().addClass("some-margin")
2542                 .find("input#text_pma_pw").focus();
2543                 displayPasswordGenerateButton();
2544                 $('#fieldset_change_password_footer').hide();
2545                 PMA_ajaxRemoveMessage($msgbox);
2546                 $('#change_password_form').bind('submit', function (e) {
2547                     e.preventDefault();
2548                     $(this)
2549                         .closest('.ui-dialog')
2550                         .find('.ui-dialog-buttonpane .ui-button')
2551                         .first()
2552                         .click();
2553                 });
2554             } else {
2555                 PMA_ajaxShowMessage(data.error, false);
2556             }
2557         }); // end $.get()
2558     }); // end handler for change password anchor
2559 }); // end $() for Change Password
2562  * Unbind all event handlers before tearing down a page
2563  */
2564 AJAX.registerTeardown('functions.js', function () {
2565     $("select.column_type").die('change');
2566     $("select.default_type").die('change');
2567     $('input.allow_null').die('change');
2570  * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when
2571  * the page loads and when the selected data type changes
2572  */
2573 AJAX.registerOnload('functions.js', function () {
2574     // is called here for normal page loads and also when opening
2575     // the Create table dialog
2576     PMA_verifyColumnsProperties();
2577     //
2578     // needs live() to work also in the Create Table dialog
2579     $("select.column_type").live('change', function () {
2580         PMA_showNoticeForEnum($(this));
2581     });
2582     $("select.default_type").live('change', function () {
2583         PMA_hideShowDefaultValue($(this));
2584     });
2585     $('input.allow_null').live('change', function () {
2586         PMA_validateDefaultValue($(this));
2587     });
2590 function PMA_verifyColumnsProperties()
2592     $("select.column_type").each(function () {
2593         PMA_showNoticeForEnum($(this));
2594     });
2595     $("select.default_type").each(function () {
2596         PMA_hideShowDefaultValue($(this));
2597     });
2601  * Hides/shows the default value input field, depending on the default type
2602  * Ticks the NULL checkbox if NULL is chosen as default value.
2603  */
2604 function PMA_hideShowDefaultValue($default_type)
2606     if ($default_type.val() == 'USER_DEFINED') {
2607         $default_type.siblings('.default_value').show().focus();
2608     } else {
2609         $default_type.siblings('.default_value').hide();
2610         if ($default_type.val() == 'NULL') {
2611             var $null_checkbox = $default_type.closest('tr').find('.allow_null');
2612             $null_checkbox.prop('checked', true);
2613         }
2614     }
2618  * If the column does not allow NULL values, makes sure that default is not NULL
2619  */
2620 function PMA_validateDefaultValue($null_checkbox)
2622     if (! $null_checkbox.prop('checked')) {
2623         var $default = $null_checkbox.closest('tr').find('.default_type');
2624         if ($default.val() == 'NULL') {
2625             $default.val('NONE');
2626         }
2627     }
2632  * Unbind all event handlers before tearing down a page
2633  */
2634 AJAX.registerTeardown('functions.js', function () {
2635     $("a.open_enum_editor").die('click');
2636     $("input.add_value").die('click');
2637     $("#enum_editor td.drop").die('click');
2640  * @var $enum_editor_dialog An object that points to the jQuery
2641  *                          dialog of the ENUM/SET editor
2642  */
2643 var $enum_editor_dialog = null;
2645  * Opens the ENUM/SET editor and controls its functions
2646  */
2647 AJAX.registerOnload('functions.js', function () {
2648     $("a.open_enum_editor").live('click', function () {
2649         // Get the name of the column that is being edited
2650         var colname = $(this).closest('tr').find('input:first').val();
2651         var title;
2652         var i;
2653         // And use it to make up a title for the page
2654         if (colname.length < 1) {
2655             title = PMA_messages.enum_newColumnVals;
2656         } else {
2657             title = PMA_messages.enum_columnVals.replace(
2658                 /%s/,
2659                 '"' + decodeURIComponent(colname) + '"'
2660             );
2661         }
2662         // Get the values as a string
2663         var inputstring = $(this)
2664             .closest('td')
2665             .find("input")
2666             .val();
2667         // Escape html entities
2668         inputstring = $('<div/>')
2669             .text(inputstring)
2670             .html();
2671         // Parse the values, escaping quotes and
2672         // slashes on the fly, into an array
2673         var values = [];
2674         var in_string = false;
2675         var curr, next, buffer = '';
2676         for (i = 0; i < inputstring.length; i++) {
2677             curr = inputstring.charAt(i);
2678             next = i == inputstring.length ? '' : inputstring.charAt(i + 1);
2679             if (! in_string && curr == "'") {
2680                 in_string = true;
2681             } else if (in_string && curr == "\\" && next == "\\") {
2682                 buffer += "&#92;";
2683                 i++;
2684             } else if (in_string && next == "'" && (curr == "'" || curr == "\\")) {
2685                 buffer += "&#39;";
2686                 i++;
2687             } else if (in_string && curr == "'") {
2688                 in_string = false;
2689                 values.push(buffer);
2690                 buffer = '';
2691             } else if (in_string) {
2692                 buffer += curr;
2693             }
2694         }
2695         if (buffer.length > 0) {
2696             // The leftovers in the buffer are the last value (if any)
2697             values.push(buffer);
2698         }
2699         var fields = '';
2700         // If there are no values, maybe the user is about to make a
2701         // new list so we add a few for him/her to get started with.
2702         if (values.length === 0) {
2703             values.push('', '', '', '');
2704         }
2705         // Add the parsed values to the editor
2706         var drop_icon = PMA_getImage('b_drop.png');
2707         for (i = 0; i < values.length; i++) {
2708             fields += "<tr><td>" +
2709                    "<input type='text' value='" + values[i] + "'/>" +
2710                    "</td><td class='drop'>" +
2711                    drop_icon +
2712                    "</td></tr>";
2713         }
2714         /**
2715          * @var dialog HTML code for the ENUM/SET dialog
2716          */
2717         var dialog = "<div id='enum_editor'>" +
2718                    "<fieldset>" +
2719                     "<legend>" + title + "</legend>" +
2720                     "<p>" + PMA_getImage('s_notice.png') +
2721                     PMA_messages.enum_hint + "</p>" +
2722                     "<table class='values'>" + fields + "</table>" +
2723                     "</fieldset><fieldset class='tblFooters'>" +
2724                     "<table class='add'><tr><td>" +
2725                     "<div class='slider'></div>" +
2726                     "</td><td>" +
2727                     "<form><div><input type='submit' class='add_value' value='" +
2728                     $.sprintf(PMA_messages.enum_addValue, 1) +
2729                     "'/></div></form>" +
2730                     "</td></tr></table>" +
2731                     "<input type='hidden' value='" + // So we know which column's data is being edited
2732                     $(this).closest('td').find("input").attr("id") +
2733                     "' />" +
2734                     "</fieldset>" +
2735                     "</div>";
2736         /**
2737          * @var  Defines functions to be called when the buttons in
2738          * the buttonOptions jQuery dialog bar are pressed
2739          */
2740         var buttonOptions = {};
2741         buttonOptions[PMA_messages.strGo] = function () {
2742             // When the submit button is clicked,
2743             // put the data back into the original form
2744             var value_array = [];
2745             $(this).find(".values input").each(function (index, elm) {
2746                 var val = elm.value.replace(/\\/g, '\\\\').replace(/'/g, "''");
2747                 value_array.push("'" + val + "'");
2748             });
2749             // get the Length/Values text field where this value belongs
2750             var values_id = $(this).find("input[type='hidden']").val();
2751             $("input#" + values_id).val(value_array.join(","));
2752             $(this).dialog("close");
2753         };
2754         buttonOptions[PMA_messages.strClose] = function () {
2755             $(this).dialog("close");
2756         };
2757         // Show the dialog
2758         var width = parseInt(
2759             (parseInt($('html').css('font-size'), 10) / 13) * 340,
2760             10
2761         );
2762         if (! width) {
2763             width = 340;
2764         }
2765         $enum_editor_dialog = $(dialog).dialog({
2766             minWidth: width,
2767             modal: true,
2768             title: PMA_messages.enum_editor,
2769             buttons: buttonOptions,
2770             open: function () {
2771                 // Focus the "Go" button after opening the dialog
2772                 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus();
2773             },
2774             close: function () {
2775                 $(this).remove();
2776             }
2777         });
2778         // slider for choosing how many fields to add
2779         $enum_editor_dialog.find(".slider").slider({
2780             animate: true,
2781             range: "min",
2782             value: 1,
2783             min: 1,
2784             max: 9,
2785             slide: function (event, ui) {
2786                 $(this).closest('table').find('input[type=submit]').val(
2787                     $.sprintf(PMA_messages.enum_addValue, ui.value)
2788                 );
2789             }
2790         });
2791         // Focus the slider, otherwise it looks nearly transparent
2792         $('a.ui-slider-handle').addClass('ui-state-focus');
2793         return false;
2794     });
2796     // When "add a new value" is clicked, append an empty text field
2797     $("input.add_value").live('click', function (e) {
2798         e.preventDefault();
2799         var num_new_rows = $enum_editor_dialog.find("div.slider").slider('value');
2800         while (num_new_rows--) {
2801             $enum_editor_dialog.find('.values')
2802                 .append(
2803                     "<tr style='display: none;'><td>" +
2804                     "<input type='text' />" +
2805                     "</td><td class='drop'>" +
2806                     PMA_getImage('b_drop.png') +
2807                     "</td></tr>"
2808                 )
2809                 .find('tr:last')
2810                 .show('fast');
2811         }
2812     });
2814     // Removes the specified row from the enum editor
2815     $("#enum_editor td.drop").live('click', function () {
2816         $(this).closest('tr').hide('fast', function () {
2817             $(this).remove();
2818         });
2819     });
2823  * Ensures indexes names are valid according to their type and, for a primary
2824  * key, lock index name to 'PRIMARY'
2825  * @param string   form_id  Variable which parses the form name as
2826  *                            the input
2827  * @return boolean  false    if there is no index form, true else
2828  */
2829 function checkIndexName(form_id)
2831     if ($("#" + form_id).length === 0) {
2832         return false;
2833     }
2835     // Gets the elements pointers
2836     var $the_idx_name = $("#input_index_name");
2837     var $the_idx_type = $("#select_index_type");
2839     // Index is a primary key
2840     if ($the_idx_type.find("option:selected").val() == 'PRIMARY') {
2841         $the_idx_name.val('PRIMARY');
2842         $the_idx_name.prop("disabled", true);
2843     }
2845     // Other cases
2846     else {
2847         if ($the_idx_name.val() == 'PRIMARY') {
2848             $the_idx_name.val("");
2849         }
2850         $the_idx_name.prop("disabled", false);
2851     }
2853     return true;
2854 } // end of the 'checkIndexName()' function
2856 AJAX.registerTeardown('functions.js', function () {
2857     $('#index_frm input[type=submit]').die('click');
2859 AJAX.registerOnload('functions.js', function () {
2860     /**
2861      * Handler for adding more columns to an index in the editor
2862      */
2863     $('#index_frm input[type=submit]').live('click', function (event) {
2864         event.preventDefault();
2865         var rows_to_add = $(this)
2866             .closest('fieldset')
2867             .find('.slider')
2868             .slider('value');
2869         while (rows_to_add--) {
2870             var $newrow = $('#index_columns')
2871                 .find('tbody > tr:first')
2872                 .clone()
2873                 .appendTo(
2874                     $('#index_columns').find('tbody')
2875                 );
2876             $newrow.find(':input').each(function () {
2877                 $(this).val('');
2878             });
2879             // focus index size input on column picked
2880             $newrow.find('select').change(function () {
2881                 if ($(this).find("option:selected").val() === '') {
2882                     return true;
2883                 }
2884                 $(this).closest("tr").find("input").focus();
2885             });
2886         }
2887     });
2890 function indexEditorDialog(url, title, callback_success, callback_failure)
2892     /*Remove the hidden dialogs if there are*/
2893     if ($('#edit_index_dialog').length !== 0) {
2894         $('#edit_index_dialog').remove();
2895     }
2896     var $div = $('<div id="edit_index_dialog"></div>');
2898     /**
2899      * @var button_options Object that stores the options
2900      *                     passed to jQueryUI dialog
2901      */
2902     var button_options = {};
2903     button_options[PMA_messages.strGo] = function () {
2904         /**
2905          * @var    the_form    object referring to the export form
2906          */
2907         var $form = $("#index_frm");
2908         var $msgbox = PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
2909         PMA_prepareForAjaxRequest($form);
2910         //User wants to submit the form
2911         $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
2912             if ($("#sqlqueryresults").length !== 0) {
2913                 $("#sqlqueryresults").remove();
2914             }
2915             if (data.success === true) {
2916                 PMA_ajaxShowMessage(data.message);
2917                 if ($('#result_query').length) {
2918                     $('#result_query').remove();
2919                 }
2920                 if (data.sql_query) {
2921                     $('<div id="result_query"></div>')
2922                         .html(data.sql_query)
2923                         .prependTo('#page_content');
2924                     PMA_highlightSQL($('#page_content'));
2925                 }
2926                 $("#result_query .notice").remove();
2927                 $("#result_query").prepend(data.message);
2928                 /*Reload the field form*/
2929                 $("#table_index").remove();
2930                 var $temp_div = $("<div id='temp_div'><div>").append(data.index_table);
2931                 $temp_div.find("#table_index").insertAfter("#index_header");
2932                 if ($("#edit_index_dialog").length > 0) {
2933                     $("#edit_index_dialog").dialog("close");
2934                 }
2935                 $('div.no_indexes_defined').hide();
2936                 if (callback_success) {
2937                     callback_success();
2938                 }
2939                 PMA_reloadNavigation();
2940             } else {
2941                 var $temp_div = $("<div id='temp_div'><div>").append(data.error);
2942                 var $error;
2943                 if ($temp_div.find(".error code").length !== 0) {
2944                     $error = $temp_div.find(".error code").addClass("error");
2945                 } else {
2946                     $error = $temp_div;
2947                 }
2948                 if (callback_failure) {
2949                     callback_failure();
2950                 }
2951                 PMA_ajaxShowMessage($error, false);
2952             }
2953         }); // end $.post()
2954     };
2955     button_options[PMA_messages.strCancel] = function () {
2956         $(this).dialog('close');
2957     };
2958     var $msgbox = PMA_ajaxShowMessage();
2959     $.get("tbl_indexes.php", url, function (data) {
2960         if (data.success === false) {
2961             //in the case of an error, show the error message returned.
2962             PMA_ajaxShowMessage(data.error, false);
2963         } else {
2964             PMA_ajaxRemoveMessage($msgbox);
2965             // Show dialog if the request was successful
2966             $div
2967             .append(data.message)
2968             .dialog({
2969                 title: title,
2970                 width: 450,
2971                 open: PMA_verifyColumnsProperties,
2972                 modal: true,
2973                 buttons: button_options,
2974                 close: function () {
2975                     $(this).remove();
2976                 }
2977             });
2978             checkIndexType();
2979             checkIndexName("index_frm");
2980             PMA_showHints($div);
2981             // Add a slider for selecting how many columns to add to the index
2982             $div.find('.slider').slider({
2983                 animate: true,
2984                 value: 1,
2985                 min: 1,
2986                 max: 16,
2987                 slide: function (event, ui) {
2988                     $(this).closest('fieldset').find('input[type=submit]').val(
2989                         $.sprintf(PMA_messages.strAddToIndex, ui.value)
2990                     );
2991                 }
2992             });
2993             // focus index size input on column picked
2994             $div.find('table#index_columns select').change(function () {
2995                 if ($(this).find("option:selected").val() === '') {
2996                     return true;
2997                 }
2998                 $(this).closest("tr").find("input").focus();
2999             });
3000             // Focus the slider, otherwise it looks nearly transparent
3001             $('a.ui-slider-handle').addClass('ui-state-focus');
3002             // set focus on index name input, if empty
3003             var input = $div.find('input#input_index_name');
3004             input.val() || input.focus();
3005         }
3006     }); // end $.get()
3010  * Function to display tooltips that were
3011  * generated on the PHP side by PMA_Util::showHint()
3013  * @param object $div a div jquery object which specifies the
3014  *                    domain for searching for tooltips. If we
3015  *                    omit this parameter the function searches
3016  *                    in the whole body
3017  **/
3018 function PMA_showHints($div)
3020     if ($div === undefined || ! $div instanceof jQuery || $div.length === 0) {
3021         $div = $("body");
3022     }
3023     $div.find('.pma_hint').each(function () {
3024         PMA_tooltip(
3025             $(this).children('img'),
3026             'img',
3027             $(this).children('span').html()
3028         );
3029     });
3032 AJAX.registerOnload('functions.js', function () {
3033     PMA_showHints();
3036 function PMA_mainMenuResizerCallback() {
3037     // 5 px margin for jumping menu in Chrome
3038     return $(document.body).width() - 5;
3040 // This must be fired only once after the inital page load
3041 $(function () {
3042     // Initialise the menu resize plugin
3043     $('#topmenu').menuResizer(PMA_mainMenuResizerCallback);
3044     // register resize event
3045     $(window).resize(function () {
3046         $('#topmenu').menuResizer('resize');
3047     });
3051  * Get the row number from the classlist (for example, row_1)
3052  */
3053 function PMA_getRowNumber(classlist)
3055     return parseInt(classlist.split(/\s+row_/)[1], 10);
3059  * Changes status of slider
3060  */
3061 function PMA_set_status_label($element)
3063     var text;
3064     if ($element.css('display') == 'none') {
3065         text = '+ ';
3066     } else {
3067         text = '- ';
3068     }
3069     $element.closest('.slide-wrapper').prev().find('span').text(text);
3073  * var  toggleButton  This is a function that creates a toggle
3074  *                    sliding button given a jQuery reference
3075  *                    to the correct DOM element
3076  */
3077 var toggleButton = function ($obj) {
3078     // In rtl mode the toggle switch is flipped horizontally
3079     // so we need to take that into account
3080     var right;
3081     if ($('span.text_direction', $obj).text() == 'ltr') {
3082         right = 'right';
3083     } else {
3084         right = 'left';
3085     }
3086     /**
3087      *  var  h  Height of the button, used to scale the
3088      *          background image and position the layers
3089      */
3090     var h = $obj.height();
3091     $('img', $obj).height(h);
3092     $('table', $obj).css('bottom', h - 1);
3093     /**
3094      *  var  on   Width of the "ON" part of the toggle switch
3095      *  var  off  Width of the "OFF" part of the toggle switch
3096      */
3097     var on  = $('td.toggleOn', $obj).width();
3098     var off = $('td.toggleOff', $obj).width();
3099     // Make the "ON" and "OFF" parts of the switch the same size
3100     // + 2 pixels to avoid overflowed
3101     $('td.toggleOn > div', $obj).width(Math.max(on, off) + 2);
3102     $('td.toggleOff > div', $obj).width(Math.max(on, off) + 2);
3103     /**
3104      *  var  w  Width of the central part of the switch
3105      */
3106     var w = parseInt(($('img', $obj).height() / 16) * 22, 10);
3107     // Resize the central part of the switch on the top
3108     // layer to match the background
3109     $('table td:nth-child(2) > div', $obj).width(w);
3110     /**
3111      *  var  imgw    Width of the background image
3112      *  var  tblw    Width of the foreground layer
3113      *  var  offset  By how many pixels to move the background
3114      *               image, so that it matches the top layer
3115      */
3116     var imgw = $('img', $obj).width();
3117     var tblw = $('table', $obj).width();
3118     var offset = parseInt(((imgw - tblw) / 2), 10);
3119     // Move the background to match the layout of the top layer
3120     $obj.find('img').css(right, offset);
3121     /**
3122      *  var  offw    Outer width of the "ON" part of the toggle switch
3123      *  var  btnw    Outer width of the central part of the switch
3124      */
3125     var offw = $('td.toggleOff', $obj).outerWidth();
3126     var btnw = $('table td:nth-child(2)', $obj).outerWidth();
3127     // Resize the main div so that exactly one side of
3128     // the switch plus the central part fit into it.
3129     $obj.width(offw + btnw + 2);
3130     /**
3131      *  var  move  How many pixels to move the
3132      *             switch by when toggling
3133      */
3134     var move = $('td.toggleOff', $obj).outerWidth();
3135     // If the switch is initialized to the
3136     // OFF state we need to move it now.
3137     if ($('div.container', $obj).hasClass('off')) {
3138         if (right == 'right') {
3139             $('div.container', $obj).animate({'left': '-=' + move + 'px'}, 0);
3140         } else {
3141             $('div.container', $obj).animate({'left': '+=' + move + 'px'}, 0);
3142         }
3143     }
3144     // Attach an 'onclick' event to the switch
3145     $('div.container', $obj).click(function () {
3146         if ($(this).hasClass('isActive')) {
3147             return false;
3148         } else {
3149             $(this).addClass('isActive');
3150         }
3151         var $msg = PMA_ajaxShowMessage();
3152         var $container = $(this);
3153         var callback = $('span.callback', this).text();
3154         var operator, url, removeClass, addClass;
3155         // Perform the actual toggle
3156         if ($(this).hasClass('on')) {
3157             if (right == 'right') {
3158                 operator = '-=';
3159             } else {
3160                 operator = '+=';
3161             }
3162             url = $(this).find('td.toggleOff > span').text();
3163             removeClass = 'on';
3164             addClass = 'off';
3165         } else {
3166             if (right == 'right') {
3167                 operator = '+=';
3168             } else {
3169                 operator = '-=';
3170             }
3171             url = $(this).find('td.toggleOn > span').text();
3172             removeClass = 'off';
3173             addClass = 'on';
3174         }
3175         $.post(url, {'ajax_request': true}, function (data) {
3176             if (data.success === true) {
3177                 PMA_ajaxRemoveMessage($msg);
3178                 $container
3179                 .removeClass(removeClass)
3180                 .addClass(addClass)
3181                 .animate({'left': operator + move + 'px'}, function () {
3182                     $container.removeClass('isActive');
3183                 });
3184                 eval(callback);
3185             } else {
3186                 PMA_ajaxShowMessage(data.error, false);
3187                 $container.removeClass('isActive');
3188             }
3189         });
3190     });
3194  * Unbind all event handlers before tearing down a page
3195  */
3196 AJAX.registerTeardown('functions.js', function () {
3197     $('div.container').unbind('click');
3200  * Initialise all toggle buttons
3201  */
3202 AJAX.registerOnload('functions.js', function () {
3203     $('div.toggleAjax').each(function () {
3204         var $button = $(this).show();
3205         $button.find('img').each(function () {
3206             if (this.complete) {
3207                 toggleButton($button);
3208             } else {
3209                 $(this).load(function () {
3210                     toggleButton($button);
3211                 });
3212             }
3213         });
3214     });
3218  * Unbind all event handlers before tearing down a page
3219  */
3220 AJAX.registerTeardown('functions.js', function () {
3221     $('.vpointer').die('hover');
3222     $('.vmarker').die('click');
3223     $('#pageselector').die('change');
3224     $('a.formLinkSubmit').die('click');
3225     $('#update_recent_tables').unbind('ready');
3228  * Vertical pointer
3229  */
3230 AJAX.registerOnload('functions.js', function () {
3231     $('.vpointer').live('hover',
3232         //handlerInOut
3233         function (e) {
3234             var $this_td = $(this);
3235             var row_num = PMA_getRowNumber($this_td.attr('class'));
3236             // for all td of the same vertical row, toggle hover
3237             $('.vpointer').filter('.row_' + row_num).toggleClass('hover');
3238         }
3239     );
3242     /**
3243      * Vertical marker
3244      */
3245     $('.vmarker').live('click', function (e) {
3246         // do not trigger when clicked on anchor
3247         if ($(e.target).is('a, img, a *')) {
3248             return;
3249         }
3251         var $this_td = $(this);
3252         var row_num = PMA_getRowNumber($this_td.attr('class'));
3254         // XXX: FF fires two click events for <label> (label and checkbox), so we need to handle this differently
3255         var $checkbox = $('.vmarker').filter('.row_' + row_num + ':first').find(':checkbox');
3256         if ($checkbox.length) {
3257             // checkbox in a row, add or remove class depending on checkbox state
3258             var checked = $checkbox.prop('checked');
3259             if (!$(e.target).is(':checkbox, label')) {
3260                 checked = !checked;
3261                 $checkbox.prop('checked', checked);
3262             }
3263             // for all td of the same vertical row, toggle the marked class
3264             if (checked) {
3265                 $('.vmarker').filter('.row_' + row_num).addClass('marked');
3266             } else {
3267                 $('.vmarker').filter('.row_' + row_num).removeClass('marked');
3268             }
3269         } else {
3270             // normaln data table, just toggle class
3271             $('.vmarker').filter('.row_' + row_num).toggleClass('marked');
3272         }
3273     });
3275     /**
3276      * Autosubmit page selector
3277      */
3278     $('select.pageselector').live('change', function (event) {
3279         event.stopPropagation();
3280         // Check where to load the new content
3281         if ($(this).closest("#pma_navigation").length === 0) {
3282             // For the main page we don't need to do anything,
3283             $(this).closest("form").submit();
3284         } else {
3285             // but for the navigation we need to manually replace the content
3286             PMA_navigationTreePagination($(this));
3287         }
3288     });
3290     /**
3291      * Load version information asynchronously.
3292      */
3293     if ($('li.jsversioncheck').length > 0) {
3294         $.getJSON('version_check.php', {}, PMA_current_version);
3295     }
3297     if ($('#is_git_revision').length > 0) {
3298         setTimeout(PMA_display_git_revision, 10);
3299     }
3301     /**
3302      * Slider effect.
3303      */
3304     PMA_init_slider();
3306     /**
3307      * Enables the text generated by PMA_Util::linkOrButton() to be clickable
3308      */
3309     $('a.formLinkSubmit').live('click', function (e) {
3311         if ($(this).attr('href').indexOf('=') != -1) {
3312             var data = $(this).attr('href').substr($(this).attr('href').indexOf('#') + 1).split('=', 2);
3313             $(this).parents('form').append('<input type="hidden" name="' + data[0] + '" value="' + data[1] + '"/>');
3314         }
3315         $(this).parents('form').submit();
3316         return false;
3317     });
3319     if ($('#update_recent_tables').length) {
3320         $.get(
3321             $('#update_recent_tables').attr('href'),
3322             function (data) {
3323                 if (data.success === true) {
3324                     $('#recentTable').html(data.options);
3325                 }
3326             }
3327         );
3328     }
3330 }); // end of $()
3334  * Initializes slider effect.
3335  */
3336 function PMA_init_slider()
3338     $('div.pma_auto_slider').each(function () {
3339         var $this = $(this);
3340         if ($this.data('slider_init_done')) {
3341             return;
3342         }
3343         var $wrapper = $('<div>', {'class': 'slide-wrapper'});
3344         $wrapper.toggle($this.is(':visible'));
3345         $('<a>', {href: '#' + this.id, "class": 'ajax'})
3346             .text(this.title)
3347             .prepend($('<span>'))
3348             .insertBefore($this)
3349             .click(function () {
3350                 var $wrapper = $this.closest('.slide-wrapper');
3351                 var visible = $this.is(':visible');
3352                 if (!visible) {
3353                     $wrapper.show();
3354                 }
3355                 $this[visible ? 'hide' : 'show']('blind', function () {
3356                     $wrapper.toggle(!visible);
3357                     PMA_set_status_label($this);
3358                 });
3359                 return false;
3360             });
3361         $this.wrap($wrapper);
3362         PMA_set_status_label($this);
3363         $this.data('slider_init_done', 1);
3364     });
3368  * Initializes slider effect.
3369  */
3370 AJAX.registerOnload('functions.js', function () {
3371     PMA_init_slider();
3375  * Restores sliders to the state they were in before initialisation.
3376  */
3377 AJAX.registerTeardown('functions.js', function () {
3378     $('div.pma_auto_slider').each(function () {
3379         var $this = $(this);
3380         $this.removeData();
3381         $this.parent().replaceWith($this);
3382         $this.parent().children('a').remove();
3383     });
3387  * Creates a message inside an object with a sliding effect
3389  * @param msg    A string containing the text to display
3390  * @param $obj   a jQuery object containing the reference
3391  *                 to the element where to put the message
3392  *                 This is optional, if no element is
3393  *                 provided, one will be created below the
3394  *                 navigation links at the top of the page
3396  * @return bool   True on success, false on failure
3397  */
3398 function PMA_slidingMessage(msg, $obj)
3400     if (msg === undefined || msg.length === 0) {
3401         // Don't show an empty message
3402         return false;
3403     }
3404     if ($obj === undefined || ! $obj instanceof jQuery || $obj.length === 0) {
3405         // If the second argument was not supplied,
3406         // we might have to create a new DOM node.
3407         if ($('#PMA_slidingMessage').length === 0) {
3408             $('#page_content').prepend(
3409                 '<span id="PMA_slidingMessage" ' +
3410                 'style="display: inline-block;"></span>'
3411             );
3412         }
3413         $obj = $('#PMA_slidingMessage');
3414     }
3415     if ($obj.has('div').length > 0) {
3416         // If there already is a message inside the
3417         // target object, we must get rid of it
3418         $obj
3419         .find('div')
3420         .first()
3421         .fadeOut(function () {
3422             $obj
3423             .children()
3424             .remove();
3425             $obj
3426             .append('<div style="display: none;">' + msg + '</div>')
3427             .animate({
3428                 height: $obj.find('div').first().height()
3429             })
3430             .find('div')
3431             .first()
3432             .fadeIn();
3433         });
3434     } else {
3435         // Object does not already have a message
3436         // inside it, so we simply slide it down
3437         var h = $obj
3438                 .width('100%')
3439                 .html('<div style="display: none;">' + msg + '</div>')
3440                 .find('div')
3441                 .first()
3442                 .height();
3443         $obj
3444         .find('div')
3445         .first()
3446         .css('height', 0)
3447         .show()
3448         .animate({
3449                 height: h
3450             }, function () {
3451             // Set the height of the parent
3452             // to the height of the child
3453                 $obj
3454                 .height(
3455                     $obj
3456                     .find('div')
3457                     .first()
3458                     .height()
3459                 );
3460             });
3461     }
3462     return true;
3463 } // end PMA_slidingMessage()
3466  * Unbind all event handlers before tearing down a page
3467  */
3468 AJAX.registerTeardown('functions.js', function () {
3469     $("#drop_tbl_anchor.ajax").die('click');
3470     $("#drop_view_anchor.ajax").die('click');
3471     $("#truncate_tbl_anchor.ajax").die('click');
3474  * Attach Ajax event handlers for Drop Table.
3475  */
3476 AJAX.registerOnload('functions.js', function () {
3477     $("#drop_tbl_anchor.ajax").live('click', function (event) {
3478         event.preventDefault();
3479         /**
3480          * @var question    String containing the question to be asked for confirmation
3481          */
3482         var question = PMA_messages.strDropTableStrongWarning + ' ';
3483         question += $.sprintf(
3484             PMA_messages.strDoYouReally,
3485             'DROP TABLE ' + PMA_commonParams.get('table')
3486         );
3488         $(this).PMA_confirm(question, $(this).attr('href'), function (url) {
3490             var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3491             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function (data) {
3492                 if (data.success === true) {
3493                     PMA_ajaxRemoveMessage($msgbox);
3494                     // Table deleted successfully, refresh both the frames
3495                     PMA_reloadNavigation();
3496                     PMA_commonParams.set('table', '');
3497                     PMA_commonActions.refreshMain(
3498                         PMA_commonParams.get('opendb_url'),
3499                         function () {
3500                             PMA_ajaxShowMessage(data.message);
3501                         }
3502                     );
3503                 } else {
3504                     PMA_ajaxShowMessage(data.error, false);
3505                 }
3506             }); // end $.get()
3507         }); // end $.PMA_confirm()
3508     }); //end of Drop Table Ajax action
3510     $("#drop_view_anchor.ajax").live('click', function (event) {
3511         event.preventDefault();
3512         /**
3513          * @var question    String containing the question to be asked for confirmation
3514          */
3515         var question = PMA_messages.strDropTableStrongWarning + ' ';
3516         question += $.sprintf(
3517             PMA_messages.strDoYouReally,
3518             'DROP VIEW ' + PMA_commonParams.get('table')
3519         );
3521         $(this).PMA_confirm(question, $(this).attr('href'), function (url) {
3523             var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3524             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function (data) {
3525                 if (data.success === true) {
3526                     PMA_ajaxRemoveMessage($msgbox);
3527                     // Table deleted successfully, refresh both the frames
3528                     PMA_reloadNavigation();
3529                     PMA_commonParams.set('table', '');
3530                     PMA_commonActions.refreshMain(
3531                         PMA_commonParams.get('opendb_url'),
3532                         function () {
3533                             PMA_ajaxShowMessage(data.message);
3534                         }
3535                     );
3536                 } else {
3537                     PMA_ajaxShowMessage(data.error, false);
3538                 }
3539             }); // end $.get()
3540         }); // end $.PMA_confirm()
3541     }); //end of Drop View Ajax action
3543     $("#truncate_tbl_anchor.ajax").live('click', function (event) {
3544         event.preventDefault();
3545         /**
3546          * @var question    String containing the question to be asked for confirmation
3547          */
3548         var question = PMA_messages.strTruncateTableStrongWarning + ' ';
3549         question += $.sprintf(
3550             PMA_messages.strDoYouReally,
3551             'TRUNCATE ' + PMA_commonParams.get('table')
3552         );
3553         $(this).PMA_confirm(question, $(this).attr('href'), function (url) {
3554             PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3555             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function (data) {
3556                 if ($("#sqlqueryresults").length !== 0) {
3557                     $("#sqlqueryresults").remove();
3558                 }
3559                 if ($("#result_query").length !== 0) {
3560                     $("#result_query").remove();
3561                 }
3562                 if (data.success === true) {
3563                     PMA_ajaxShowMessage(data.message);
3564                     $("<div id='sqlqueryresults'></div>").prependTo("#page_content");
3565                     $("#sqlqueryresults").html(data.sql_query);
3566                     PMA_highlightSQL($('#page_content'));
3567                 } else {
3568                     PMA_ajaxShowMessage(data.error, false);
3569                 }
3570             }); // end $.get()
3571         }); // end $.PMA_confirm()
3572     }); //end of Truncate Table Ajax action
3573 }); // end of $() for Truncate Table
3576  * Attach CodeMirror2 editor to SQL edit area.
3577  */
3578 AJAX.registerOnload('functions.js', function () {
3579     var $elm = $('#sqlquery');
3580     if ($elm.length > 0) {
3581         if (typeof CodeMirror != 'undefined') {
3582             // for codemirror
3583             codemirror_editor = CodeMirror.fromTextArea($elm[0], {
3584                 lineNumbers: true,
3585                 matchBrackets: true,
3586                 indentUnit: 4,
3587                 mode: "text/x-mysql",
3588                 lineWrapping: true
3589             });
3590             codemirror_editor.focus();
3591             $(codemirror_editor.getWrapperElement()).bind(
3592                 'keydown',
3593                 catchKeypressesFromSqlTextboxes
3594             );
3595         } else {
3596             // without codemirror
3597             $elm.focus().bind('keydown', catchKeypressesFromSqlTextboxes);
3598         }
3599     }
3600     PMA_highlightSQL($('body'));
3602 AJAX.registerTeardown('functions.js', function () {
3603     if (codemirror_editor) {
3604         $('#sqlquery').text(codemirror_editor.getValue());
3605         codemirror_editor.toTextArea();
3606         codemirror_editor = false;
3607     }
3611  * jQuery plugin to cancel selection in HTML code.
3612  */
3613 (function ($) {
3614     $.fn.noSelect = function (p) { //no select plugin by Paulo P.Marinas
3615         var prevent = (p === null) ? true : p;
3616         if (prevent) {
3617             return this.each(function () {
3618                 if ($.browser.msie || $.browser.safari) {
3619                     $(this).bind('selectstart', function () {
3620                         return false;
3621                     });
3622                 } else if ($.browser.mozilla) {
3623                     $(this).css('MozUserSelect', 'none');
3624                     $('body').trigger('focus');
3625                 } else if ($.browser.opera) {
3626                     $(this).bind('mousedown', function () {
3627                         return false;
3628                     });
3629                 } else {
3630                     $(this).attr('unselectable', 'on');
3631                 }
3632             });
3633         } else {
3634             return this.each(function () {
3635                 if ($.browser.msie || $.browser.safari) {
3636                     $(this).unbind('selectstart');
3637                 } else if ($.browser.mozilla) {
3638                     $(this).css('MozUserSelect', 'inherit');
3639                 } else if ($.browser.opera) {
3640                     $(this).unbind('mousedown');
3641                 } else {
3642                     $(this).removeAttr('unselectable');
3643                 }
3644             });
3645         }
3646     }; //end noSelect
3647 })(jQuery);
3650  * jQuery plugin to correctly filter input fields by value, needed
3651  * because some nasty values may break selector syntax
3652  */
3653 (function ($) {
3654     $.fn.filterByValue = function (value) {
3655         return this.filter(function () {
3656             return $(this).val() === value;
3657         });
3658     };
3659 })(jQuery);
3662  * Create a jQuery UI tooltip
3664  * @param $elements     jQuery object representing the elements
3665  * @param item          the item
3666  *                      (see http://api.jqueryui.com/tooltip/#option-items)
3667  * @param myContent     content of the tooltip
3668  * @param additionalOptions to override the default options
3670  */
3671 function PMA_tooltip($elements, item, myContent, additionalOptions)
3673     if ($('#no_hint').length > 0) {
3674         return;
3675     }
3677     var defaultOptions = {
3678         content: myContent,
3679         items:  item,
3680         tooltipClass: "tooltip",
3681         track: true,
3682         show: false,
3683         hide: false
3684     };
3686     $elements.tooltip($.extend(true, defaultOptions, additionalOptions));
3690  * Return value of a cell in a table.
3691  */
3692 function PMA_getCellValue(td) {
3693     var $td = $(td);
3694     if ($td.is('.null')) {
3695         return '';
3696     } else if (! $td.is('.to_be_saved') && $td.data('original_data')) {
3697         return $td.data('original_data');
3698     } else {
3699         return $td.text();
3700     }
3704  * Unbind all event handlers before tearing down a page
3705  */
3706 AJAX.registerTeardown('functions.js', function () {
3707     $('a.themeselect').die('click');
3708     $('.autosubmit').die('change');
3709     $('a.take_theme').unbind('click');
3712 AJAX.registerOnload('functions.js', function () {
3713     /**
3714      * Theme selector.
3715      */
3716     $('a.themeselect').live('click', function (e) {
3717         window.open(
3718             e.target,
3719             'themes',
3720             'left=10,top=20,width=510,height=350,scrollbars=yes,status=yes,resizable=yes'
3721             );
3722         return false;
3723     });
3725     /**
3726      * Automatic form submission on change.
3727      */
3728     $('.autosubmit').live('change', function (e) {
3729         $(this).closest('form').submit();
3730     });
3732     /**
3733      * Theme changer.
3734      */
3735     $('a.take_theme').click(function (e) {
3736         var what = this.name;
3737         if (window.opener && window.opener.document.forms['setTheme'].elements['set_theme']) {
3738             window.opener.document.forms['setTheme'].elements['set_theme'].value = what;
3739             window.opener.document.forms['setTheme'].submit();
3740             window.close();
3741             return false;
3742         }
3743         return true;
3744     });
3748  * Clear text selection
3749  */
3750 function PMA_clearSelection() {
3751     if (document.selection && document.selection.empty) {
3752         document.selection.empty();
3753     } else if (window.getSelection) {
3754         var sel = window.getSelection();
3755         if (sel.empty) {
3756             sel.empty();
3757         }
3758         if (sel.removeAllRanges) {
3759             sel.removeAllRanges();
3760         }
3761     }
3765  * HTML escaping
3766  */
3767 function escapeHtml(unsafe) {
3768     return unsafe
3769         .replace(/&/g, "&amp;")
3770         .replace(/</g, "&lt;")
3771         .replace(/>/g, "&gt;")
3772         .replace(/"/g, "&quot;")
3773         .replace(/'/g, "&#039;");
3777  * Print button
3778  */
3779 function printPage()
3781     // Do print the page
3782     if (typeof(window.print) != 'undefined') {
3783         window.print();
3784     }
3788  * Unbind all event handlers before tearing down a page
3789  */
3790 AJAX.registerTeardown('functions.js', function () {
3791     $('input#print').unbind('click');
3792     $('a.create_view.ajax').die('click');
3793     $('#createViewDialog').find('input, select').die('keydown');
3796 AJAX.registerOnload('functions.js', function () {
3797     $('input#print').click(printPage);
3798     /**
3799      * Ajaxification for the "Create View" action
3800      */
3801     $('a.create_view.ajax').live('click', function (e) {
3802         e.preventDefault();
3803         PMA_createViewDialog($(this));
3804     });
3805     /**
3806      * Attach Ajax event handlers for input fields in the editor
3807      * and used to submit the Ajax request when the ENTER key is pressed.
3808      */
3809     $('#createViewDialog').find('input, select').live('keydown', function (e) {
3810         if (e.which === 13) { // 13 is the ENTER key
3811             e.preventDefault();
3812             $(this).closest('.ui-dialog').find('.ui-button:first').click();
3813         }
3814     }); // end $.live()
3816     var $elm = $('textarea[name="view[as]"]');
3817     if ($elm.length > 0) {
3818         if (typeof CodeMirror != 'undefined') {
3819             syntaxHighlighter = CodeMirror.fromTextArea(
3820                 $elm[0],
3821                 {
3822                     lineNumbers: true,
3823                     matchBrackets: true,
3824                     indentUnit: 4,
3825                     mode: "text/x-mysql",
3826                     lineWrapping: true
3827                 }
3828             );
3829         }
3830     }
3833 function PMA_createViewDialog($this)
3835     var $msg = PMA_ajaxShowMessage();
3836     var syntaxHighlighter = null;
3837     $.get($this.attr('href') + '&ajax_request=1&ajax_dialog=1', function (data) {
3838         if (data.success === true) {
3839             PMA_ajaxRemoveMessage($msg);
3840             var buttonOptions = {};
3841             buttonOptions[PMA_messages.strGo] = function () {
3842                 if (typeof CodeMirror !== 'undefined') {
3843                     syntaxHighlighter.save();
3844                 }
3845                 $msg = PMA_ajaxShowMessage();
3846                 $.get('view_create.php', $('#createViewDialog').find('form').serialize(), function (data) {
3847                     PMA_ajaxRemoveMessage($msg);
3848                     if (data.success === true) {
3849                         $('#createViewDialog').dialog("close");
3850                         $('#result_query').html(data.message);
3851                         PMA_reloadNavigation();
3852                     } else {
3853                         PMA_ajaxShowMessage(data.error, false);
3854                     }
3855                 });
3856             };
3857             buttonOptions[PMA_messages.strClose] = function () {
3858                 $(this).dialog("close");
3859             };
3860             var $dialog = $('<div/>').attr('id', 'createViewDialog').append(data.message).dialog({
3861                 width: 600,
3862                 minWidth: 400,
3863                 modal: true,
3864                 buttons: buttonOptions,
3865                 title: PMA_messages.strCreateView,
3866                 close: function () {
3867                     $(this).remove();
3868                 }
3869             });
3870             // Attach syntax highlited editor
3871             if (typeof CodeMirror !== 'undefined') {
3872                 var $elm = $dialog.find('textarea');
3873                 var opts = {lineNumbers: true, matchBrackets: true, indentUnit: 4, mode: "text/x-mysql", lineWrapping: true};
3874                 syntaxHighlighter = CodeMirror.fromTextArea($elm[0], opts);
3875             }
3876             $('input:visible[type=text]', $dialog).first().focus();
3877         } else {
3878             PMA_ajaxShowMessage(data.error);
3879         }
3880     });
3884  * Makes the breadcrumbs and the menu bar float at the top of the viewport
3885  */
3886 $(function () {
3887     if ($("#floating_menubar").length && $('#PMA_disable_floating_menubar').length === 0) {
3888         var left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
3889         $("#floating_menubar")
3890             .css('margin-' + left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width())
3891             .css(left, 0)
3892             .css({
3893                 'position': 'fixed',
3894                 'top': 0,
3895                 'width': '100%',
3896                 'z-index': 500
3897             })
3898             .append($('#serverinfo'))
3899             .append($('#topmenucontainer'));
3900         // Allow the DOM to render, then adjust the padding on the body
3901         setTimeout(function () {
3902             $('body').css(
3903                 'padding-top',
3904                 $('#floating_menubar').outerHeight(true)
3905             );
3906             $('#topmenu').menuResizer('resize');
3907         }, 4);
3908     }
3912  * Scrolls the page to the top if clicking the serverinfo bar
3913  */
3914 $(function () {
3915     $(document).delegate("#serverinfo, #goto_pagetop", "click", function (event) {
3916         event.preventDefault();
3917         $('html, body').animate({scrollTop: 0}, 'fast');
3918     });
3921 var checkboxes_sel = "input.checkall:checkbox:enabled";
3923  * Watches checkboxes in a form to set the checkall box accordingly
3924  */
3925 var checkboxes_changed = function () {
3926     var $form = $(this.form);
3927     // total number of checkboxes in current form
3928     var total_boxes = $form.find(checkboxes_sel).length;
3929     // number of checkboxes checked in current form
3930     var checked_boxes = $form.find(checkboxes_sel + ":checked").length;
3931     var $checkall = $form.find("input.checkall_box");
3932     if (total_boxes == checked_boxes) {
3933         $checkall.prop({checked: true, indeterminate: false});
3934     }
3935     else if (checked_boxes > 0) {
3936         $checkall.prop({checked: true, indeterminate: true});
3937     }
3938     else {
3939         $checkall.prop({checked: false, indeterminate: false});
3940     }
3942 $(checkboxes_sel).live("change", checkboxes_changed);
3944 $("input.checkall_box").live("change", function () {
3945     var is_checked = $(this).is(":checked");
3946     $(this.form).find(checkboxes_sel).prop("checked", is_checked)
3947     .parents("tr").toggleClass("marked", is_checked);
3951  * Toggles row colors of a set of 'tr' elements starting from a given element
3953  * @param $start Starting element
3954  */
3955 function toggleRowColors($start)
3957     for (var $curr_row = $start; $curr_row.length > 0; $curr_row = $curr_row.next()) {
3958         if ($curr_row.hasClass('odd')) {
3959             $curr_row.removeClass('odd').addClass('even');
3960         } else if ($curr_row.hasClass('even')) {
3961             $curr_row.removeClass('even').addClass('odd');
3962         }
3963     }
3967  * Formats a byte number to human-readable form
3969  * @param bytes the bytes to format
3970  * @param optional subdecimals the number of digits after the point
3971  * @param optional pointchar the char to use as decimal point
3972  */
3973 function formatBytes(bytes, subdecimals, pointchar) {
3974     if (!subdecimals) {
3975         subdecimals = 0;
3976     }
3977     if (!pointchar) {
3978         pointchar = '.';
3979     }
3980     var units = ['B', 'KiB', 'MiB', 'GiB'];
3981     for (var i = 0; bytes > 1024 && i < units.length; i++) {
3982         bytes /= 1024;
3983     }
3984     var factor = Math.pow(10, subdecimals);
3985     bytes = Math.round(bytes * factor) / factor;
3986     bytes = bytes.toString().split('.').join(pointchar);
3987     return bytes + ' ' + units[i];
3990 AJAX.registerOnload('functions.js', function () {
3991     /**
3992      * Opens pma more themes link in themes browser, in new window instead of popup
3993      * This way, we don't break HTML validity
3994      */
3995     $("a._blank").prop("target", "_blank");
3996     /**
3997      * Reveal the login form to users with JS enabled
3998      * and focus the appropriate input field
3999      */
4000     var $loginform = $('#loginform');
4001     if ($loginform.length) {
4002         $loginform.find('.js-show').show();
4003         if ($('#input_username').val()) {
4004             $('#input_password').focus();
4005         } else {
4006             $('#input_username').focus();
4007         }
4008     }
4012  * When user gets an ajax session expiry message, we show a login link
4013  */
4014 $('a.login-link').live('click', function (e) {
4015     e.preventDefault();
4016     window.location.reload(true);
4020  * Dynamically adjust the width of the boxes
4021  * on the table and db operations pages
4022  */
4023 (function () {
4024     function DynamicBoxes() {
4025         var $boxContainer = $('#boxContainer');
4026         if ($boxContainer.length) {
4027             var minWidth = $boxContainer.data('box-width');
4028             var viewport = $(window).width() - $('#pma_navigation').width();
4029             var slots = Math.floor(viewport / minWidth);
4030             $boxContainer.children()
4031             .each(function () {
4032                 if (viewport < minWidth) {
4033                     $(this).width(minWidth);
4034                 } else {
4035                     $(this).css('width', ((1 /  slots) * 100) + "%");
4036                 }
4037             })
4038             .removeClass('clearfloat')
4039             .filter(':nth-child(' + slots + 'n+1)')
4040             .addClass('clearfloat');
4041         }
4042     }
4043     AJAX.registerOnload('functions.js', function () {
4044         DynamicBoxes();
4045     });
4046     $(function () {
4047         $(window).resize(DynamicBoxes);
4048     });
4049 })();
4052  * Formats timestamp for display
4053  */
4054 function PMA_formatDateTime(date, seconds) {
4055     var result = $.datepicker.formatDate('yy-mm-dd', date);
4056     var timefmt = 'HH:mm';
4057     if (seconds) {
4058         timefmt = 'HH:mm:ss';
4059     }
4060     return result + ' ' + $.datepicker.formatTime(
4061         timefmt, {
4062             hour: date.getHours(),
4063             minute: date.getMinutes(),
4064             second: date.getSeconds()
4065         }
4066     );