Merge remote-tracking branch 'origin/QA_4_7' into QA_4_7
[phpmyadmin.git] / js / functions.js
blobe415820faa1e67426791095c7ae4608745b10928
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * general function, usually for data manipulation pages
4  *
5  */
7 /**
8  * @var sql_box_locked lock for the sqlbox textarea in the querybox
9  */
10 var sql_box_locked = false;
12 /**
13  * @var array holds elements which content should only selected once
14  */
15 var only_once_elements = [];
17 /**
18  * @var   int   ajax_message_count   Number of AJAX messages shown since page load
19  */
20 var ajax_message_count = 0;
22 /**
23  * @var codemirror_editor object containing CodeMirror editor of the query editor in SQL tab
24  */
25 var codemirror_editor = false;
27 /**
28  * @var codemirror_editor object containing CodeMirror editor of the inline query editor
29  */
30 var codemirror_inline_editor = false;
32 /**
33  * @var sql_autocomplete_in_progress bool shows if Table/Column name autocomplete AJAX is in progress
34  */
35 var sql_autocomplete_in_progress = false;
37 /**
38  * @var sql_autocomplete object containing list of columns in each table
39  */
40 var sql_autocomplete = false;
42 /**
43  * @var sql_autocomplete_default_table string containing default table to autocomplete columns
44  */
45 var sql_autocomplete_default_table = '';
47 /**
48  * @var central_column_list array to hold the columns in central list per db.
49  */
50 var central_column_list = [];
52 /**
53  * @var primary_indexes array to hold 'Primary' index columns.
54  */
55 var primary_indexes = [];
57 /**
58  * @var unique_indexes array to hold 'Unique' index columns.
59  */
60 var unique_indexes = [];
62 /**
63  * @var indexes array to hold 'Index' columns.
64  */
65 var indexes = [];
67 /**
68  * @var fulltext_indexes array to hold 'Fulltext' columns.
69  */
70 var fulltext_indexes = [];
72 /**
73  * @var spatial_indexes array to hold 'Spatial' columns.
74  */
75 var spatial_indexes = [];
77 /**
78  * Make sure that ajax requests will not be cached
79  * by appending a random variable to their parameters
80  */
81 $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
82     var nocache = new Date().getTime() + "" + Math.floor(Math.random() * 1000000);
83     if (typeof options.data == "string") {
84         options.data += "&_nocache=" + nocache;
85     } else if (typeof options.data == "object") {
86         options.data = $.extend(originalOptions.data, {'_nocache' : nocache});
87     }
88 });
91  * Adds a date/time picker to an element
92  *
93  * @param object  $this_element   a jQuery object pointing to the element
94  */
95 function PMA_addDatepicker($this_element, type, options)
97     var showTimepicker = true;
98     if (type=="date") {
99         showTimepicker = false;
100     }
102     var defaultOptions = {
103         showOn: 'button',
104         buttonImage: themeCalendarImage, // defined in js/messages.php
105         buttonImageOnly: true,
106         stepMinutes: 1,
107         stepHours: 1,
108         showSecond: true,
109         showMillisec: true,
110         showMicrosec: true,
111         showTimepicker: showTimepicker,
112         showButtonPanel: false,
113         dateFormat: 'yy-mm-dd', // yy means year with four digits
114         timeFormat: 'HH:mm:ss.lc',
115         constrainInput: false,
116         altFieldTimeOnly: false,
117         showAnim: '',
118         beforeShow: function (input, inst) {
119             // Remember that we came from the datepicker; this is used
120             // in tbl_change.js by verificationsAfterFieldChange()
121             $this_element.data('comes_from', 'datepicker');
122             if ($(input).closest('.cEdit').length > 0) {
123                 setTimeout(function () {
124                     inst.dpDiv.css({
125                         top: 0,
126                         left: 0,
127                         position: 'relative'
128                     });
129                 }, 0);
130             }
131             // Fix wrong timepicker z-index, doesn't work without timeout
132             setTimeout(function () {
133                 $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index'));
134             }, 0);
135         },
136         onSelect: function() {
137             $this_element.data('datepicker').inline = true;
138         },
139         onClose: function (dateText, dp_inst) {
140             // The value is no more from the date picker
141             $this_element.data('comes_from', '');
142             if (typeof $this_element.data('datepicker') !== 'undefined') {
143                 $this_element.data('datepicker').inline = false;
144             }
145         }
146     };
147     if (type == "datetime" || type == "timestamp") {
148         $this_element.datetimepicker($.extend(defaultOptions, options));
149     }
150     else if (type == "date") {
151         $this_element.datetimepicker($.extend(defaultOptions, options));
152     }
153     else if (type == "time") {
154         $this_element.timepicker($.extend(defaultOptions, options));
155         // Add a tip regarding entering MySQL allowed-values for TIME data-type
156         PMA_tooltip($this_element, 'input', PMA_messages.strMysqlAllowedValuesTipTime);
157     }
161  * Add a date/time picker to each element that needs it
162  * (only when jquery-ui-timepicker-addon.js is loaded)
163  */
164 function addDateTimePicker() {
165     if ($.timepicker !== undefined) {
166         $('input.timefield, input.datefield, input.datetimefield').each(function () {
168             var decimals = $(this).parent().attr('data-decimals');
169             var type = $(this).parent().attr('data-type');
171             var showMillisec = false;
172             var showMicrosec = false;
173             var timeFormat = 'HH:mm:ss';
174             // check for decimal places of seconds
175             if (decimals > 0 && type.indexOf('time') != -1){
176                 if (decimals > 3) {
177                     showMillisec = true;
178                     showMicrosec = true;
179                     timeFormat = 'HH:mm:ss.lc';
180                 } else {
181                     showMillisec = true;
182                     timeFormat = 'HH:mm:ss.l';
183                 }
184             }
185             PMA_addDatepicker($(this), type, {
186                 showMillisec: showMillisec,
187                 showMicrosec: showMicrosec,
188                 timeFormat: timeFormat
189             });
191             // Add a tip regarding entering MySQL allowed-values
192             // for TIME and DATE data-type
193             if ($(this).hasClass('timefield')) {
194                 PMA_tooltip($(this), 'input', PMA_messages.strMysqlAllowedValuesTipTime);
195             } else if ($(this).hasClass('datefield')) {
196                 PMA_tooltip($(this), 'input', PMA_messages.strMysqlAllowedValuesTipDate);
197             }
198         });
199     }
203  * Handle redirect and reload flags sent as part of AJAX requests
205  * @param data ajax response data
206  */
207 function PMA_handleRedirectAndReload(data) {
208     if (parseInt(data.redirect_flag) == 1) {
209         // add one more GET param to display session expiry msg
210         if (window.location.href.indexOf('?') === -1) {
211             window.location.href += '?session_expired=1';
212         } else {
213             window.location.href += '&session_expired=1';
214         }
215         window.location.reload();
216     } else if (parseInt(data.reload_flag) == 1) {
217         // remove the token param and reload
218         window.location.href = window.location.href.replace(/&?token=[^&#]*/g, "");
219         window.location.reload();
220     }
224  * Creates an SQL editor which supports auto completing etc.
226  * @param $textarea   jQuery object wrapping the textarea to be made the editor
227  * @param options     optional options for CodeMirror
228  * @param resize      optional resizing ('vertical', 'horizontal', 'both')
229  * @param lintOptions additional options for lint
230  */
231 function PMA_getSQLEditor($textarea, options, resize, lintOptions) {
232     if ($textarea.length > 0 && typeof CodeMirror !== 'undefined') {
234         // merge options for CodeMirror
235         var defaults = {
236             lineNumbers: true,
237             matchBrackets: true,
238             extraKeys: {"Ctrl-Space": "autocomplete"},
239             hintOptions: {"completeSingle": false, "completeOnSingleClick": true},
240             indentUnit: 4,
241             mode: "text/x-mysql",
242             lineWrapping: true
243         };
245         if (CodeMirror.sqlLint) {
246             $.extend(defaults, {
247                 gutters: ["CodeMirror-lint-markers"],
248                 lint: {
249                     "getAnnotations": CodeMirror.sqlLint,
250                     "async": true,
251                     "lintOptions": lintOptions
252                 }
253             });
254         }
256         $.extend(true, defaults, options);
258         // create CodeMirror editor
259         var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults);
260         // allow resizing
261         if (! resize) {
262             resize = 'vertical';
263         }
264         var handles = '';
265         if (resize == 'vertical') {
266             handles = 's';
267         }
268         if (resize == 'both') {
269             handles = 'all';
270         }
271         if (resize == 'horizontal') {
272             handles = 'e, w';
273         }
274         $(codemirrorEditor.getWrapperElement())
275             .css('resize', resize)
276             .resizable({
277                 handles: handles,
278                 resize: function() {
279                     codemirrorEditor.setSize($(this).width(), $(this).height());
280                 }
281             });
282         // enable autocomplete
283         codemirrorEditor.on("inputRead", codemirrorAutocompleteOnInputRead);
285         return codemirrorEditor;
286     }
287     return null;
291  * Clear text selection
292  */
293 function PMA_clearSelection() {
294     if (document.selection && document.selection.empty) {
295         document.selection.empty();
296     } else if (window.getSelection) {
297         var sel = window.getSelection();
298         if (sel.empty) {
299             sel.empty();
300         }
301         if (sel.removeAllRanges) {
302             sel.removeAllRanges();
303         }
304     }
308  * Create a jQuery UI tooltip
310  * @param $elements     jQuery object representing the elements
311  * @param item          the item
312  *                      (see https://api.jqueryui.com/tooltip/#option-items)
313  * @param myContent     content of the tooltip
314  * @param additionalOptions to override the default options
316  */
317 function PMA_tooltip($elements, item, myContent, additionalOptions)
319     if ($('#no_hint').length > 0) {
320         return;
321     }
323     var defaultOptions = {
324         content: myContent,
325         items:  item,
326         tooltipClass: "tooltip",
327         track: true,
328         show: false,
329         hide: false
330     };
332     $elements.tooltip($.extend(true, defaultOptions, additionalOptions));
336  * HTML escaping
337  */
339 function escapeHtml(unsafe) {
340     if (typeof(unsafe) != 'undefined') {
341         return unsafe
342             .toString()
343             .replace(/&/g, "&")
344             .replace(/</g, "&lt;")
345             .replace(/>/g, "&gt;")
346             .replace(/"/g, "&quot;")
347             .replace(/'/g, "&#039;");
348     } else {
349         return false;
350     }
353 function escapeJsString(unsafe) {
354     if (typeof(unsafe) != 'undefined') {
355         return unsafe
356             .toString()
357             .replace("\000", '')
358             .replace('\\', '\\\\')
359             .replace('\'', '\\\'')
360             .replace("&#039;", "\\\&#039;")
361             .replace('"', '\"')
362             .replace("&quot;", "\&quot;")
363             .replace("\n", '\n')
364             .replace("\r", '\r')
365             .replace(/<\/script/gi, '</\' + \'script')
366     } else {
367         return false;
368     }
371 function PMA_sprintf() {
372     return sprintf.apply(this, arguments);
376  * Hides/shows the default value input field, depending on the default type
377  * Ticks the NULL checkbox if NULL is chosen as default value.
378  */
379 function PMA_hideShowDefaultValue($default_type)
381     if ($default_type.val() == 'USER_DEFINED') {
382         $default_type.siblings('.default_value').show().focus();
383     } else {
384         $default_type.siblings('.default_value').hide();
385         if ($default_type.val() == 'NULL') {
386             var $null_checkbox = $default_type.closest('tr').find('.allow_null');
387             $null_checkbox.prop('checked', true);
388         }
389     }
393  * Hides/shows the input field for column expression based on whether
394  * VIRTUAL/PERSISTENT is selected
396  * @param $virtuality virtuality dropdown
397  */
398 function PMA_hideShowExpression($virtuality)
400     if ($virtuality.val() === '') {
401         $virtuality.siblings('.expression').hide();
402     } else {
403         $virtuality.siblings('.expression').show();
404     }
408  * Show notices for ENUM columns; add/hide the default value
410  */
411 function PMA_verifyColumnsProperties()
413     $("select.column_type").each(function () {
414         PMA_showNoticeForEnum($(this));
415     });
416     $("select.default_type").each(function () {
417         PMA_hideShowDefaultValue($(this));
418     });
419     $('select.virtuality').each(function () {
420         PMA_hideShowExpression($(this));
421     });
425  * Add a hidden field to the form to indicate that this will be an
426  * Ajax request (only if this hidden field does not exist)
428  * @param $form object   the form
429  */
430 function PMA_prepareForAjaxRequest($form)
432     if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
433         $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
434     }
438  * Generate a new password and copy it to the password input areas
440  * @param passwd_form object   the form that holds the password fields
442  * @return boolean  always true
443  */
444 function suggestPassword(passwd_form)
446     // restrict the password to just letters and numbers to avoid problems:
447     // "editors and viewers regard the password as multiple words and
448     // things like double click no longer work"
449     var pwchars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZ";
450     var passwordlength = 16;    // do we want that to be dynamic?  no, keep it simple :)
451     var passwd = passwd_form.generated_pw;
452     var randomWords = new Int32Array(passwordlength);
454     passwd.value = '';
456     // First we're going to try to use a built-in CSPRNG
457     if (window.crypto && window.crypto.getRandomValues) {
458         window.crypto.getRandomValues(randomWords);
459     }
460     // Because of course IE calls it msCrypto instead of being standard
461     else if (window.msCrypto && window.msCrypto.getRandomValues) {
462         window.msCrypto.getRandomValues(randomWords);
463     } else {
464         // Fallback to Math.random
465         for (var i = 0; i < passwordlength; i++) {
466             randomWords[i] = Math.floor(Math.random() * pwchars.length);
467         }
468     }
470     for (var i = 0; i < passwordlength; i++) {
471         passwd.value += pwchars.charAt(Math.abs(randomWords[i]) % pwchars.length);
472     }
474     $jquery_passwd_form = $(passwd_form);
476     passwd_form.elements['pma_pw'].value = passwd.value;
477     passwd_form.elements['pma_pw2'].value = passwd.value;
478     meter_obj = $jquery_passwd_form.find('meter[name="pw_meter"]').first();
479     meter_obj_label = $jquery_passwd_form.find('span[name="pw_strength"]').first();
480     checkPasswordStrength(passwd.value, meter_obj, meter_obj_label);
481     return true;
485  * Version string to integer conversion.
486  */
487 function parseVersionString(str)
489     if (typeof(str) != 'string') { return false; }
490     var add = 0;
491     // Parse possible alpha/beta/rc/
492     var state = str.split('-');
493     if (state.length >= 2) {
494         if (state[1].substr(0, 2) == 'rc') {
495             add = - 20 - parseInt(state[1].substr(2), 10);
496         } else if (state[1].substr(0, 4) == 'beta') {
497             add =  - 40 - parseInt(state[1].substr(4), 10);
498         } else if (state[1].substr(0, 5) == 'alpha') {
499             add =  - 60 - parseInt(state[1].substr(5), 10);
500         } else if (state[1].substr(0, 3) == 'dev') {
501             /* We don't handle dev, it's git snapshot */
502             add = 0;
503         }
504     }
505     // Parse version
506     var x = str.split('.');
507     // Use 0 for non existing parts
508     var maj = parseInt(x[0], 10) || 0;
509     var min = parseInt(x[1], 10) || 0;
510     var pat = parseInt(x[2], 10) || 0;
511     var hotfix = parseInt(x[3], 10) || 0;
512     return  maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
516  * Indicates current available version on main page.
517  */
518 function PMA_current_version(data)
520     if (data && data.version && data.date) {
521         var current = parseVersionString($('span.version').text());
522         var latest = parseVersionString(data.version);
523         var url = 'https://www.phpmyadmin.net/files/' + escapeHtml(encodeURIComponent(data.version)) + '/';
524         var version_information_message = document.createElement('span');
525         version_information_message.className = 'latest';
526         var version_information_message_link = document.createElement('a');
527         version_information_message_link.href = url;
528         version_information_message_link.className = 'disableAjax';
529         version_information_message_link_text = document.createTextNode(data.version);
530         version_information_message_link.appendChild(version_information_message_link_text);
531         var prefix_message = document.createTextNode(PMA_messages.strLatestAvailable + ' ');
532         version_information_message.appendChild(prefix_message);
533         version_information_message.appendChild(version_information_message_link);
534         if (latest > current) {
535             var message = PMA_sprintf(
536                 PMA_messages.strNewerVersion,
537                 escapeHtml(data.version),
538                 escapeHtml(data.date)
539             );
540             var htmlClass = 'notice';
541             if (Math.floor(latest / 10000) === Math.floor(current / 10000)) {
542                 /* Security update */
543                 htmlClass = 'error';
544             }
545             $('#newer_version_notice').remove();
546             var maincontainer_div = document.createElement('div');
547             maincontainer_div.id = 'newer_version_notice';
548             maincontainer_div.className = htmlClass;
549             var maincontainer_div_link = document.createElement('a');
550             maincontainer_div_link.href = url;
551             maincontainer_div_link.className = 'disableAjax';
552             maincontainer_div_link_text = document.createTextNode(message);
553             maincontainer_div_link.appendChild(maincontainer_div_link_text);
554             maincontainer_div.appendChild(maincontainer_div_link);
555             $('#maincontainer').append($(maincontainer_div));
556         }
557         if (latest === current) {
558             version_information_message = document.createTextNode(' (' + PMA_messages.strUpToDate + ')');
559         }
560         /* Remove extra whitespace */
561         var version_info = $('#li_pma_version').contents().get(2);
562         version_info.textContent = $.trim(version_info.textContent);
563         var $liPmaVersion = $('#li_pma_version');
564         $liPmaVersion.find('span.latest').remove();
565         $liPmaVersion.append($(version_information_message));
566     }
570  * Loads Git revision data from ajax for index.php
571  */
572 function PMA_display_git_revision()
574     $('#is_git_revision').remove();
575     $('#li_pma_version_git').remove();
576     $.get(
577         "index.php",
578         {
579             "server": PMA_commonParams.get('server'),
580             "token": PMA_commonParams.get('token'),
581             "git_revision": true,
582             "ajax_request": true,
583             "no_debug": true
584         },
585         function (data) {
586             if (typeof data !== 'undefined' && data.success === true) {
587                 $(data.message).insertAfter('#li_pma_version');
588             }
589         }
590     );
594  * for libraries/display_change_password.lib.php
595  *     libraries/user_password.php
597  */
599 function displayPasswordGenerateButton()
601     $('#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>');
602     $('#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>');
606  * selects the content of a given object, f.e. a textarea
608  * @param element     object  element of which the content will be selected
609  * @param lock        var     variable which holds the lock for this element
610  *                              or true, if no lock exists
611  * @param only_once   boolean if true this is only done once
612  *                              f.e. only on first focus
613  */
614 function selectContent(element, lock, only_once)
616     if (only_once && only_once_elements[element.name]) {
617         return;
618     }
620     only_once_elements[element.name] = true;
622     if (lock) {
623         return;
624     }
626     element.select();
630  * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query.
631  * This function is called while clicking links
633  * @param theLink     object the link
634  * @param theSqlQuery object the sql query to submit
636  * @return boolean  whether to run the query or not
637  */
638 function confirmLink(theLink, theSqlQuery)
640     // Confirmation is not required in the configuration file
641     // or browser is Opera (crappy js implementation)
642     if (PMA_messages.strDoYouReally === '' || typeof(window.opera) != 'undefined') {
643         return true;
644     }
646     var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, theSqlQuery));
647     if (is_confirmed) {
648         if ($(theLink).hasClass('formLinkSubmit')) {
649             var name = 'is_js_confirmed';
651             if ($(theLink).attr('href').indexOf('usesubform') != -1) {
652                 var matches = $(theLink).attr('href').substr('#').match(/usesubform\[(\d+)\]/i);
653                 if (matches != null) {
654                     name = 'subform[' + matches[1] + '][is_js_confirmed]';
655                 }
656             }
658             $(theLink).parents('form').append('<input type="hidden" name="' + name + '" value="1" />');
659         } else if (typeof(theLink.href) != 'undefined') {
660             theLink.href += '&is_js_confirmed=1';
661         } else if (typeof(theLink.form) != 'undefined') {
662             theLink.form.action += '?is_js_confirmed=1';
663         }
664     }
666     return is_confirmed;
667 } // end of the 'confirmLink()' function
670  * Confirms a "DROP/DELETE/ALTER" query before
671  * submitting it if required.
672  * This function is called by the 'checkSqlQuery()' js function.
674  * @param theForm1 object   the form
675  * @param sqlQuery1 string  the sql query string
677  * @return boolean  whether to run the query or not
679  * @see     checkSqlQuery()
680  */
681 function confirmQuery(theForm1, sqlQuery1)
683     // Confirmation is not required in the configuration file
684     if (PMA_messages.strDoYouReally === '') {
685         return true;
686     }
688     // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
689     //
690     // TODO: find a way (if possible) to use the parser-analyser
691     // for this kind of verification
692     // For now, I just added a ^ to check for the statement at
693     // beginning of expression
695     var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i');
696     var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
697     var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
698     var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
700     if (do_confirm_re_0.test(sqlQuery1) ||
701         do_confirm_re_1.test(sqlQuery1) ||
702         do_confirm_re_2.test(sqlQuery1) ||
703         do_confirm_re_3.test(sqlQuery1)) {
704         var message;
705         if (sqlQuery1.length > 100) {
706             message = sqlQuery1.substr(0, 100) + '\n    ...';
707         } else {
708             message = sqlQuery1;
709         }
710         var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, message));
711         // statement is confirmed -> update the
712         // "is_js_confirmed" form field so the confirm test won't be
713         // run on the server side and allows to submit the form
714         if (is_confirmed) {
715             theForm1.elements.is_js_confirmed.value = 1;
716             return true;
717         }
718         // statement is rejected -> do not submit the form
719         else {
720             window.focus();
721             return false;
722         } // end if (handle confirm box result)
723     } // end if (display confirm box)
725     return true;
726 } // end of the 'confirmQuery()' function
729  * Displays an error message if the user submitted the sql query form with no
730  * sql query, else checks for "DROP/DELETE/ALTER" statements
732  * @param theForm object the form
734  * @return boolean  always false
736  * @see     confirmQuery()
737  */
738 function checkSqlQuery(theForm)
740     // get the textarea element containing the query
741     var sqlQuery;
742     if (codemirror_editor) {
743         codemirror_editor.save();
744         sqlQuery = codemirror_editor.getValue();
745     } else {
746         sqlQuery = theForm.elements.sql_query.value;
747     }
748     var space_re = new RegExp('\\s+');
749     if (typeof(theForm.elements.sql_file) != 'undefined' &&
750             theForm.elements.sql_file.value.replace(space_re, '') !== '') {
751         return true;
752     }
753     if (typeof(theForm.elements.id_bookmark) != 'undefined' &&
754             (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') &&
755             theForm.elements.id_bookmark.selectedIndex !== 0) {
756         return true;
757     }
758     var result = false;
759     // Checks for "DROP/DELETE/ALTER" statements
760     if (sqlQuery.replace(space_re, '') !== '') {
761         result = confirmQuery(theForm, sqlQuery);
762     } else {
763         alert(PMA_messages.strFormEmpty);
764     }
766     if (codemirror_editor) {
767         codemirror_editor.focus();
768     } else if (codemirror_inline_editor) {
769         codemirror_inline_editor.focus();
770     }
771     return result;
772 } // end of the 'checkSqlQuery()' function
775  * Check if a form's element is empty.
776  * An element containing only spaces is also considered empty
778  * @param object   the form
779  * @param string   the name of the form field to put the focus on
781  * @return boolean  whether the form field is empty or not
782  */
783 function emptyCheckTheField(theForm, theFieldName)
785     var theField = theForm.elements[theFieldName];
786     var space_re = new RegExp('\\s+');
787     return theField.value.replace(space_re, '') === '';
788 } // end of the 'emptyCheckTheField()' function
791  * Ensures a value submitted in a form is numeric and is in a range
793  * @param object   the form
794  * @param string   the name of the form field to check
795  * @param integer  the minimum authorized value
796  * @param integer  the maximum authorized value
798  * @return boolean  whether a valid number has been submitted or not
799  */
800 function checkFormElementInRange(theForm, theFieldName, message, min, max)
802     var theField         = theForm.elements[theFieldName];
803     var val              = parseInt(theField.value, 10);
805     if (typeof(min) == 'undefined') {
806         min = 0;
807     }
808     if (typeof(max) == 'undefined') {
809         max = Number.MAX_VALUE;
810     }
812     // It's not a number
813     if (isNaN(val)) {
814         theField.select();
815         alert(PMA_messages.strEnterValidNumber);
816         theField.focus();
817         return false;
818     }
819     // It's a number but it is not between min and max
820     else if (val < min || val > max) {
821         theField.select();
822         alert(PMA_sprintf(message, val));
823         theField.focus();
824         return false;
825     }
826     // It's a valid number
827     else {
828         theField.value = val;
829     }
830     return true;
832 } // end of the 'checkFormElementInRange()' function
835 function checkTableEditForm(theForm, fieldsCnt)
837     // TODO: avoid sending a message if user just wants to add a line
838     // on the form but has not completed at least one field name
840     var atLeastOneField = 0;
841     var i, elm, elm2, elm3, val, id;
843     for (i = 0; i < fieldsCnt; i++) {
844         id = "#field_" + i + "_2";
845         elm = $(id);
846         val = elm.val();
847         if (val == 'VARCHAR' || val == 'CHAR' || val == 'BIT' || val == 'VARBINARY' || val == 'BINARY') {
848             elm2 = $("#field_" + i + "_3");
849             val = parseInt(elm2.val(), 10);
850             elm3 = $("#field_" + i + "_1");
851             if (isNaN(val) && elm3.val() !== "") {
852                 elm2.select();
853                 alert(PMA_messages.strEnterValidLength);
854                 elm2.focus();
855                 return false;
856             }
857         }
859         if (atLeastOneField === 0) {
860             id = "field_" + i + "_1";
861             if (!emptyCheckTheField(theForm, id)) {
862                 atLeastOneField = 1;
863             }
864         }
865     }
866     if (atLeastOneField === 0) {
867         var theField = theForm.elements.field_0_1;
868         alert(PMA_messages.strFormEmpty);
869         theField.focus();
870         return false;
871     }
873     // at least this section is under jQuery
874     var $input = $("input.textfield[name='table']");
875     if ($input.val() === "") {
876         alert(PMA_messages.strFormEmpty);
877         $input.focus();
878         return false;
879     }
881     return true;
882 } // enf of the 'checkTableEditForm()' function
885  * True if last click is to check a row.
886  */
887 var last_click_checked = false;
890  * Zero-based index of last clicked row.
891  * Used to handle the shift + click event in the code above.
892  */
893 var last_clicked_row = -1;
896  * Zero-based index of last shift clicked row.
897  */
898 var last_shift_clicked_row = -1;
900 var _idleSecondsCounter = 0;
901 var IncInterval;
902 var updateTimeout;
903 AJAX.registerTeardown('functions.js', function () {
904     clearTimeout(updateTimeout);
905     clearInterval(IncInterval);
906     $(document).off('mousemove');
909 AJAX.registerOnload('functions.js', function () {
910     document.onclick = function() {
911         _idleSecondsCounter = 0;
912     };
913     $(document).on('mousemove',function() {
914         _idleSecondsCounter = 0;
915     });
916     document.onkeypress = function() {
917         _idleSecondsCounter = 0;
918     };
919     function guid() {
920         function s4() {
921             return Math.floor((1 + Math.random()) * 0x10000)
922                 .toString(16)
923             .substring(1);
924         }
925         return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
926             s4() + '-' + s4() + s4() + s4();
927     }
929     function SetIdleTime() {
930         _idleSecondsCounter++;
931     }
932     function UpdateIdleTime() {
933         var href = 'index.php';
934         var guid = 'default';
935         if (isStorageSupported('sessionStorage')) {
936             guid = window.sessionStorage.guid;
937         }
938         var params = {
939                 'ajax_request' : true,
940                 'token' : PMA_commonParams.get('token'),
941                 'server' : PMA_commonParams.get('server'),
942                 'db' : PMA_commonParams.get('db'),
943                 'guid': guid,
944                 'access_time':_idleSecondsCounter
945             };
946         $.ajax({
947                 type: 'POST',
948                 url: href,
949                 data: params,
950                 success: function (data) {
951                     if (data.success) {
952                         if (PMA_commonParams.get('LoginCookieValidity') - _idleSecondsCounter < 0) {
953                             /* There is other active window, let's reset counter */
954                             _idleSecondsCounter = 0;
955                         }
956                         var remaining = Math.min(
957                             /* Remaining login validity */
958                             PMA_commonParams.get('LoginCookieValidity') - _idleSecondsCounter,
959                             /* Remaining time till session GC */
960                             PMA_commonParams.get('session_gc_maxlifetime')
961                         );
962                         var interval = 1000;
963                         if (remaining > 5) {
964                             // max value for setInterval() function
965                             interval = Math.min((remaining - 1) * 1000, Math.pow(2, 31) - 1);
966                         }
967                         updateTimeout = window.setTimeout(UpdateIdleTime, interval);
968                     } else { //timeout occurred
969                         clearInterval(IncInterval);
970                         if (isStorageSupported('sessionStorage')){
971                             window.sessionStorage.clear();
972                         }
973                         window.location.reload(true);
974                     }
975                 }
976             });
977     }
978     if (PMA_commonParams.get('logged_in')) {
979         IncInterval = window.setInterval(SetIdleTime, 1000);
980         var session_timeout = Math.min(
981             PMA_commonParams.get('LoginCookieValidity'),
982             PMA_commonParams.get('session_gc_maxlifetime')
983         );
984         if (isStorageSupported('sessionStorage')) {
985             window.sessionStorage.setItem('guid', guid());
986         }
987         var interval = (session_timeout - 5) * 1000;
988         if (interval > Math.pow(2, 31) - 1) { // max value for setInterval() function
989             interval = Math.pow(2, 31) - 1;
990         }
991         updateTimeout = window.setTimeout(UpdateIdleTime, interval);
992     }
995  * Unbind all event handlers before tearing down a page
996  */
997 AJAX.registerTeardown('functions.js', function () {
998     $(document).off('click', 'input:checkbox.checkall');
1000 AJAX.registerOnload('functions.js', function () {
1001     /**
1002      * Row marking in horizontal mode (use "on" so that it works also for
1003      * next pages reached via AJAX); a tr may have the class noclick to remove
1004      * this behavior.
1005      */
1007     $(document).on('click', 'input:checkbox.checkall', function (e) {
1008         $this = $(this);
1009         var $tr = $this.closest('tr');
1010         var $table = $this.closest('table');
1012         if (!e.shiftKey || last_clicked_row == -1) {
1013             // usual click
1015             var $checkbox = $tr.find(':checkbox.checkall');
1016             var checked = $this.prop('checked');
1017             $checkbox.prop('checked', checked).trigger('change');
1018             if (checked) {
1019                 $tr.addClass('marked');
1020             } else {
1021                 $tr.removeClass('marked');
1022             }
1023             last_click_checked = checked;
1025             // remember the last clicked row
1026             last_clicked_row = last_click_checked ? $table.find('tr:not(.noclick)').index($tr) : -1;
1027             last_shift_clicked_row = -1;
1028         } else {
1029             // handle the shift click
1030             PMA_clearSelection();
1031             var start, end;
1033             // clear last shift click result
1034             if (last_shift_clicked_row >= 0) {
1035                 if (last_shift_clicked_row >= last_clicked_row) {
1036                     start = last_clicked_row;
1037                     end = last_shift_clicked_row;
1038                 } else {
1039                     start = last_shift_clicked_row;
1040                     end = last_clicked_row;
1041                 }
1042                 $tr.parent().find('tr:not(.noclick)')
1043                     .slice(start, end + 1)
1044                     .removeClass('marked')
1045                     .find(':checkbox')
1046                     .prop('checked', false)
1047                     .trigger('change');
1048             }
1050             // handle new shift click
1051             var curr_row = $table.find('tr:not(.noclick)').index($tr);
1052             if (curr_row >= last_clicked_row) {
1053                 start = last_clicked_row;
1054                 end = curr_row;
1055             } else {
1056                 start = curr_row;
1057                 end = last_clicked_row;
1058             }
1059             $tr.parent().find('tr:not(.noclick)')
1060                 .slice(start, end + 1)
1061                 .addClass('marked')
1062                 .find(':checkbox')
1063                 .prop('checked', true)
1064                 .trigger('change');
1066             // remember the last shift clicked row
1067             last_shift_clicked_row = curr_row;
1068         }
1069     });
1071     addDateTimePicker();
1073     /**
1074      * Add attribute to text boxes for iOS devices (based on bugID: 3508912)
1075      */
1076     if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) {
1077         $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off');
1078     }
1082  * Row highlighting in horizontal mode (use "on"
1083  * so that it works also for pages reached via AJAX)
1084  */
1085 /*AJAX.registerOnload('functions.js', function () {
1086     $(document).on('hover', 'tr',function (event) {
1087         var $tr = $(this);
1088         $tr.toggleClass('hover',event.type=='mouseover');
1089         $tr.children().toggleClass('hover',event.type=='mouseover');
1090     });
1091 })*/
1094  * marks all rows and selects its first checkbox inside the given element
1095  * the given element is usually a table or a div containing the table or tables
1097  * @param container    DOM element
1098  */
1099 function markAllRows(container_id)
1102     $("#" + container_id).find("input:checkbox:enabled").prop('checked', true)
1103     .trigger("change")
1104     .parents("tr").addClass("marked");
1105     return true;
1109  * marks all rows and selects its first checkbox inside the given element
1110  * the given element is usually a table or a div containing the table or tables
1112  * @param container    DOM element
1113  */
1114 function unMarkAllRows(container_id)
1117     $("#" + container_id).find("input:checkbox:enabled").prop('checked', false)
1118     .trigger("change")
1119     .parents("tr").removeClass("marked");
1120     return true;
1124   * Checks/unchecks all options of a <select> element
1125   *
1126   * @param string   the form name
1127   * @param string   the element name
1128   * @param boolean  whether to check or to uncheck options
1129   *
1130   * @return boolean  always true
1131   */
1132 function setSelectOptions(the_form, the_select, do_check)
1134     $("form[name='" + the_form + "'] select[name='" + the_select + "']").find("option").prop('selected', do_check);
1135     return true;
1136 } // end of the 'setSelectOptions()' function
1139  * Sets current value for query box.
1140  */
1141 function setQuery(query)
1143     if (codemirror_editor) {
1144         codemirror_editor.setValue(query);
1145         codemirror_editor.focus();
1146     } else {
1147         document.sqlform.sql_query.value = query;
1148         document.sqlform.sql_query.focus();
1149     }
1153  * Handles 'Simulate query' button on SQL query box.
1155  * @return void
1156  */
1157 function PMA_handleSimulateQueryButton()
1159     var update_re = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i');
1160     var delete_re = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
1161     var query = '';
1163     if (codemirror_editor) {
1164         query = codemirror_editor.getValue();
1165     } else {
1166         query = $('#sqlquery').val();
1167     }
1169     var $simulateDml = $('#simulate_dml');
1170     if (update_re.test(query) || delete_re.test(query)) {
1171         if (! $simulateDml.length) {
1172             $('#button_submit_query')
1173             .before('<input type="button" id="simulate_dml"' +
1174                 'tabindex="199" value="' +
1175                 PMA_messages.strSimulateDML +
1176                 '" />');
1177         }
1178     } else {
1179         if ($simulateDml.length) {
1180             $simulateDml.remove();
1181         }
1182     }
1186   * Create quick sql statements.
1187   *
1188   */
1189 function insertQuery(queryType)
1191     if (queryType == "clear") {
1192         setQuery('');
1193         return;
1194     } else if (queryType == "format") {
1195         if (codemirror_editor) {
1196             $('#querymessage').html(PMA_messages.strFormatting +
1197                 '&nbsp;<img class="ajaxIcon" src="' +
1198                 pmaThemeImage + 'ajax_clock_small.gif" alt="">');
1199             var href = 'db_sql_format.php';
1200             var params = {
1201                 'ajax_request': true,
1202                 'token': PMA_commonParams.get('token'),
1203                 'sql': codemirror_editor.getValue()
1204             };
1205             $.ajax({
1206                 type: 'POST',
1207                 url: href,
1208                 data: params,
1209                 success: function (data) {
1210                     if (data.success) {
1211                         codemirror_editor.setValue(data.sql);
1212                     }
1213                     $('#querymessage').html('');
1214                 }
1215             });
1216         }
1217         return;
1218     } else if (queryType == "saved") {
1219         if (isStorageSupported('localStorage') && typeof window.localStorage.auto_saved_sql != 'undefined') {
1220             setQuery(window.localStorage.auto_saved_sql);
1221         } else if ($.cookie('auto_saved_sql')) {
1222             setQuery($.cookie('auto_saved_sql'));
1223         } else {
1224             PMA_ajaxShowMessage(PMA_messages.strNoAutoSavedQuery);
1225         }
1226         return;
1227     }
1229     var query = "";
1230     var myListBox = document.sqlform.dummy;
1231     var table = document.sqlform.table.value;
1233     if (myListBox.options.length > 0) {
1234         sql_box_locked = true;
1235         var columnsList = "";
1236         var valDis = "";
1237         var editDis = "";
1238         var NbSelect = 0;
1239         for (var i = 0; i < myListBox.options.length; i++) {
1240             NbSelect++;
1241             if (NbSelect > 1) {
1242                 columnsList += ", ";
1243                 valDis += ",";
1244                 editDis += ",";
1245             }
1246             columnsList += myListBox.options[i].value;
1247             valDis += "[value-" + NbSelect + "]";
1248             editDis += myListBox.options[i].value + "=[value-" + NbSelect + "]";
1249         }
1250         if (queryType == "selectall") {
1251             query = "SELECT * FROM `" + table + "` WHERE 1";
1252         } else if (queryType == "select") {
1253             query = "SELECT " + columnsList + " FROM `" + table + "` WHERE 1";
1254         } else if (queryType == "insert") {
1255             query = "INSERT INTO `" + table + "`(" + columnsList + ") VALUES (" + valDis + ")";
1256         } else if (queryType == "update") {
1257             query = "UPDATE `" + table + "` SET " + editDis + " WHERE 1";
1258         } else if (queryType == "delete") {
1259             query = "DELETE FROM `" + table + "` WHERE 0";
1260         }
1261         setQuery(query);
1262         sql_box_locked = false;
1263     }
1268   * Inserts multiple fields.
1269   *
1270   */
1271 function insertValueQuery()
1273     var myQuery = document.sqlform.sql_query;
1274     var myListBox = document.sqlform.dummy;
1276     if (myListBox.options.length > 0) {
1277         sql_box_locked = true;
1278         var columnsList = "";
1279         var NbSelect = 0;
1280         for (var i = 0; i < myListBox.options.length; i++) {
1281             if (myListBox.options[i].selected) {
1282                 NbSelect++;
1283                 if (NbSelect > 1) {
1284                     columnsList += ", ";
1285                 }
1286                 columnsList += myListBox.options[i].value;
1287             }
1288         }
1290         /* CodeMirror support */
1291         if (codemirror_editor) {
1292             codemirror_editor.replaceSelection(columnsList);
1293             codemirror_editor.focus();
1294         //IE support
1295         } else if (document.selection) {
1296             myQuery.focus();
1297             var sel = document.selection.createRange();
1298             sel.text = columnsList;
1299         }
1300         //MOZILLA/NETSCAPE support
1301         else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart == "0") {
1302             var startPos = document.sqlform.sql_query.selectionStart;
1303             var endPos = document.sqlform.sql_query.selectionEnd;
1304             var SqlString = document.sqlform.sql_query.value;
1306             myQuery.value = SqlString.substring(0, startPos) + columnsList + SqlString.substring(endPos, SqlString.length);
1307             myQuery.focus();
1308         } else {
1309             myQuery.value += columnsList;
1310         }
1311         sql_box_locked = false;
1312     }
1316  * Updates the input fields for the parameters based on the query
1317  */
1318 function updateQueryParameters() {
1320     if ($('#parameterized').is(':checked')) {
1321         var query = codemirror_editor ? codemirror_editor.getValue() : $('#sqlquery').val();
1323         var allParameters = query.match(/:[a-zA-Z0-9_]+/g);
1324         var parameters = [];
1325         // get unique parameters
1326         if (allParameters) {
1327             $.each(allParameters, function(i, parameter){
1328                 if ($.inArray(parameter, parameters) === -1) {
1329                     parameters.push(parameter);
1330                 }
1331             });
1332         } else {
1333             $('#parametersDiv').text(PMA_messages.strNoParam);
1334             return;
1335         }
1337         var $temp = $('<div />');
1338         $temp.append($('#parametersDiv').children());
1339         $('#parametersDiv').empty();
1341         $.each(parameters, function (i, parameter) {
1342             var paramName = parameter.substring(1);
1343             var $param = $temp.find('#paramSpan_' + paramName );
1344             if (! $param.length) {
1345                 $param = $('<span class="parameter" id="paramSpan_' + paramName + '" />');
1346                 $('<label for="param_' + paramName + '" />').text(parameter).appendTo($param);
1347                 $('<input type="text" name="parameters[' + parameter + ']" id="param_' + paramName + '" />').appendTo($param);
1348             }
1349             $('#parametersDiv').append($param);
1350         });
1351     } else {
1352         $('#parametersDiv').empty();
1353     }
1357   * Refresh/resize the WYSIWYG scratchboard
1358   */
1359 function refreshLayout()
1361     var $elm = $('#pdflayout');
1362     var orientation = $('#orientation_opt').val();
1363     var paper = 'A4';
1364     var $paperOpt = $('#paper_opt');
1365     if ($paperOpt.length == 1) {
1366         paper = $paperOpt.val();
1367     }
1368     var posa = 'y';
1369     var posb = 'x';
1370     if (orientation == 'P') {
1371         posa = 'x';
1372         posb = 'y';
1373     }
1374     $elm.css('width', pdfPaperSize(paper, posa) + 'px');
1375     $elm.css('height', pdfPaperSize(paper, posb) + 'px');
1379  * Initializes positions of elements.
1380  */
1381 function TableDragInit() {
1382     $('.pdflayout_table').each(function () {
1383         var $this = $(this);
1384         var number = $this.data('number');
1385         var x = $('#c_table_' + number + '_x').val();
1386         var y = $('#c_table_' + number + '_y').val();
1387         $this.css('left', x + 'px');
1388         $this.css('top', y + 'px');
1389         /* Make elements draggable */
1390         $this.draggable({
1391             containment: "parent",
1392             drag: function (evt, ui) {
1393                 var number = $this.data('number');
1394                 $('#c_table_' + number + '_x').val(parseInt(ui.position.left, 10));
1395                 $('#c_table_' + number + '_y').val(parseInt(ui.position.top, 10));
1396             }
1397         });
1398     });
1402  * Resets drag and drop positions.
1403  */
1404 function resetDrag() {
1405     $('.pdflayout_table').each(function () {
1406         var $this = $(this);
1407         var x = $this.data('x');
1408         var y = $this.data('y');
1409         $this.css('left', x + 'px');
1410         $this.css('top', y + 'px');
1411     });
1415  * User schema handlers.
1416  */
1417 $(function () {
1418     /* Move in scratchboard on manual change */
1419     $(document).on('change', '.position-change', function () {
1420         var $this = $(this);
1421         var $elm = $('#table_' + $this.data('number'));
1422         $elm.css($this.data('axis'), $this.val() + 'px');
1423     });
1424     /* Refresh on paper size/orientation change */
1425     $(document).on('change', '.paper-change', function () {
1426         var $elm = $('#pdflayout');
1427         if ($elm.css('visibility') == 'visible') {
1428             refreshLayout();
1429             TableDragInit();
1430         }
1431     });
1432     /* Show/hide the WYSIWYG scratchboard */
1433     $(document).on('click', '#toggle-dragdrop', function () {
1434         var $elm = $('#pdflayout');
1435         if ($elm.css('visibility') == 'hidden') {
1436             refreshLayout();
1437             TableDragInit();
1438             $elm.css('visibility', 'visible');
1439             $elm.css('display', 'block');
1440             $('#showwysiwyg').val('1');
1441         } else {
1442             $elm.css('visibility', 'hidden');
1443             $elm.css('display', 'none');
1444             $('#showwysiwyg').val('0');
1445         }
1446     });
1447     /* Reset scratchboard */
1448     $(document).on('click', '#reset-dragdrop', function () {
1449         resetDrag();
1450     });
1454  * Returns paper sizes for a given format
1455  */
1456 function pdfPaperSize(format, axis)
1458     switch (format.toUpperCase()) {
1459     case '4A0':
1460         if (axis == 'x') {
1461             return 4767.87;
1462         } else {
1463             return 6740.79;
1464         }
1465         break;
1466     case '2A0':
1467         if (axis == 'x') {
1468             return 3370.39;
1469         } else {
1470             return 4767.87;
1471         }
1472         break;
1473     case 'A0':
1474         if (axis == 'x') {
1475             return 2383.94;
1476         } else {
1477             return 3370.39;
1478         }
1479         break;
1480     case 'A1':
1481         if (axis == 'x') {
1482             return 1683.78;
1483         } else {
1484             return 2383.94;
1485         }
1486         break;
1487     case 'A2':
1488         if (axis == 'x') {
1489             return 1190.55;
1490         } else {
1491             return 1683.78;
1492         }
1493         break;
1494     case 'A3':
1495         if (axis == 'x') {
1496             return 841.89;
1497         } else {
1498             return 1190.55;
1499         }
1500         break;
1501     case 'A4':
1502         if (axis == 'x') {
1503             return 595.28;
1504         } else {
1505             return 841.89;
1506         }
1507         break;
1508     case 'A5':
1509         if (axis == 'x') {
1510             return 419.53;
1511         } else {
1512             return 595.28;
1513         }
1514         break;
1515     case 'A6':
1516         if (axis == 'x') {
1517             return 297.64;
1518         } else {
1519             return 419.53;
1520         }
1521         break;
1522     case 'A7':
1523         if (axis == 'x') {
1524             return 209.76;
1525         } else {
1526             return 297.64;
1527         }
1528         break;
1529     case 'A8':
1530         if (axis == 'x') {
1531             return 147.40;
1532         } else {
1533             return 209.76;
1534         }
1535         break;
1536     case 'A9':
1537         if (axis == 'x') {
1538             return 104.88;
1539         } else {
1540             return 147.40;
1541         }
1542         break;
1543     case 'A10':
1544         if (axis == 'x') {
1545             return 73.70;
1546         } else {
1547             return 104.88;
1548         }
1549         break;
1550     case 'B0':
1551         if (axis == 'x') {
1552             return 2834.65;
1553         } else {
1554             return 4008.19;
1555         }
1556         break;
1557     case 'B1':
1558         if (axis == 'x') {
1559             return 2004.09;
1560         } else {
1561             return 2834.65;
1562         }
1563         break;
1564     case 'B2':
1565         if (axis == 'x') {
1566             return 1417.32;
1567         } else {
1568             return 2004.09;
1569         }
1570         break;
1571     case 'B3':
1572         if (axis == 'x') {
1573             return 1000.63;
1574         } else {
1575             return 1417.32;
1576         }
1577         break;
1578     case 'B4':
1579         if (axis == 'x') {
1580             return 708.66;
1581         } else {
1582             return 1000.63;
1583         }
1584         break;
1585     case 'B5':
1586         if (axis == 'x') {
1587             return 498.90;
1588         } else {
1589             return 708.66;
1590         }
1591         break;
1592     case 'B6':
1593         if (axis == 'x') {
1594             return 354.33;
1595         } else {
1596             return 498.90;
1597         }
1598         break;
1599     case 'B7':
1600         if (axis == 'x') {
1601             return 249.45;
1602         } else {
1603             return 354.33;
1604         }
1605         break;
1606     case 'B8':
1607         if (axis == 'x') {
1608             return 175.75;
1609         } else {
1610             return 249.45;
1611         }
1612         break;
1613     case 'B9':
1614         if (axis == 'x') {
1615             return 124.72;
1616         } else {
1617             return 175.75;
1618         }
1619         break;
1620     case 'B10':
1621         if (axis == 'x') {
1622             return 87.87;
1623         } else {
1624             return 124.72;
1625         }
1626         break;
1627     case 'C0':
1628         if (axis == 'x') {
1629             return 2599.37;
1630         } else {
1631             return 3676.54;
1632         }
1633         break;
1634     case 'C1':
1635         if (axis == 'x') {
1636             return 1836.85;
1637         } else {
1638             return 2599.37;
1639         }
1640         break;
1641     case 'C2':
1642         if (axis == 'x') {
1643             return 1298.27;
1644         } else {
1645             return 1836.85;
1646         }
1647         break;
1648     case 'C3':
1649         if (axis == 'x') {
1650             return 918.43;
1651         } else {
1652             return 1298.27;
1653         }
1654         break;
1655     case 'C4':
1656         if (axis == 'x') {
1657             return 649.13;
1658         } else {
1659             return 918.43;
1660         }
1661         break;
1662     case 'C5':
1663         if (axis == 'x') {
1664             return 459.21;
1665         } else {
1666             return 649.13;
1667         }
1668         break;
1669     case 'C6':
1670         if (axis == 'x') {
1671             return 323.15;
1672         } else {
1673             return 459.21;
1674         }
1675         break;
1676     case 'C7':
1677         if (axis == 'x') {
1678             return 229.61;
1679         } else {
1680             return 323.15;
1681         }
1682         break;
1683     case 'C8':
1684         if (axis == 'x') {
1685             return 161.57;
1686         } else {
1687             return 229.61;
1688         }
1689         break;
1690     case 'C9':
1691         if (axis == 'x') {
1692             return 113.39;
1693         } else {
1694             return 161.57;
1695         }
1696         break;
1697     case 'C10':
1698         if (axis == 'x') {
1699             return 79.37;
1700         } else {
1701             return 113.39;
1702         }
1703         break;
1704     case 'RA0':
1705         if (axis == 'x') {
1706             return 2437.80;
1707         } else {
1708             return 3458.27;
1709         }
1710         break;
1711     case 'RA1':
1712         if (axis == 'x') {
1713             return 1729.13;
1714         } else {
1715             return 2437.80;
1716         }
1717         break;
1718     case 'RA2':
1719         if (axis == 'x') {
1720             return 1218.90;
1721         } else {
1722             return 1729.13;
1723         }
1724         break;
1725     case 'RA3':
1726         if (axis == 'x') {
1727             return 864.57;
1728         } else {
1729             return 1218.90;
1730         }
1731         break;
1732     case 'RA4':
1733         if (axis == 'x') {
1734             return 609.45;
1735         } else {
1736             return 864.57;
1737         }
1738         break;
1739     case 'SRA0':
1740         if (axis == 'x') {
1741             return 2551.18;
1742         } else {
1743             return 3628.35;
1744         }
1745         break;
1746     case 'SRA1':
1747         if (axis == 'x') {
1748             return 1814.17;
1749         } else {
1750             return 2551.18;
1751         }
1752         break;
1753     case 'SRA2':
1754         if (axis == 'x') {
1755             return 1275.59;
1756         } else {
1757             return 1814.17;
1758         }
1759         break;
1760     case 'SRA3':
1761         if (axis == 'x') {
1762             return 907.09;
1763         } else {
1764             return 1275.59;
1765         }
1766         break;
1767     case 'SRA4':
1768         if (axis == 'x') {
1769             return 637.80;
1770         } else {
1771             return 907.09;
1772         }
1773         break;
1774     case 'LETTER':
1775         if (axis == 'x') {
1776             return 612.00;
1777         } else {
1778             return 792.00;
1779         }
1780         break;
1781     case 'LEGAL':
1782         if (axis == 'x') {
1783             return 612.00;
1784         } else {
1785             return 1008.00;
1786         }
1787         break;
1788     case 'EXECUTIVE':
1789         if (axis == 'x') {
1790             return 521.86;
1791         } else {
1792             return 756.00;
1793         }
1794         break;
1795     case 'FOLIO':
1796         if (axis == 'x') {
1797             return 612.00;
1798         } else {
1799             return 936.00;
1800         }
1801         break;
1802     } // end switch
1804     return 0;
1808  * Get checkbox for foreign key checks
1810  * @return string
1811  */
1812 function getForeignKeyCheckboxLoader() {
1813     var html = '';
1814     html    += '<div>';
1815     html    += '<div class="load-default-fk-check-value">';
1816     html    += PMA_getImage('ajax_clock_small.gif');
1817     html    += '</div>';
1818     html    += '</div>';
1819     return html;
1822 function loadForeignKeyCheckbox() {
1823     // Load default foreign key check value
1824     var params = {
1825         'ajax_request': true,
1826         'token': PMA_commonParams.get('token'),
1827         'server': PMA_commonParams.get('server'),
1828         'get_default_fk_check_value': true
1829     };
1830     $.get('sql.php', params, function (data) {
1831         var html = '<input type="hidden" name="fk_checks" value="0" />' +
1832             '<input type="checkbox" name="fk_checks" id="fk_checks"' +
1833             (data.default_fk_check_value ? ' checked="checked"' : '') + ' />' +
1834             '<label for="fk_checks">' + PMA_messages.strForeignKeyCheck + '</label>';
1835         $('.load-default-fk-check-value').replaceWith(html);
1836     });
1839 function getJSConfirmCommonParam(elem) {
1840     return {
1841         'is_js_confirmed' : 1,
1842         'ajax_request' : true,
1843         'fk_checks': $(elem).find('#fk_checks').is(':checked') ? 1 : 0
1844     };
1848  * Unbind all event handlers before tearing down a page
1849  */
1850 AJAX.registerTeardown('functions.js', function () {
1851     $(document).off('click', "a.inline_edit_sql");
1852     $(document).off('click', "input#sql_query_edit_save");
1853     $(document).off('click', "input#sql_query_edit_discard");
1854     $('input.sqlbutton').unbind('click');
1855     if (codemirror_editor) {
1856         codemirror_editor.off('blur');
1857     } else {
1858         $(document).off('blur', '#sqlquery');
1859     }
1860     $(document).off('change', '#parameterized');
1861     $('#sqlquery').unbind('keydown');
1862     $('#sql_query_edit').unbind('keydown');
1864     if (codemirror_inline_editor) {
1865         // Copy the sql query to the text area to preserve it.
1866         $('#sql_query_edit').text(codemirror_inline_editor.getValue());
1867         $(codemirror_inline_editor.getWrapperElement()).unbind('keydown');
1868         codemirror_inline_editor.toTextArea();
1869         codemirror_inline_editor = false;
1870     }
1871     if (codemirror_editor) {
1872         $(codemirror_editor.getWrapperElement()).unbind('keydown');
1873     }
1877  * Jquery Coding for inline editing SQL_QUERY
1878  */
1879 AJAX.registerOnload('functions.js', function () {
1880     // If we are coming back to the page by clicking forward button
1881     // of the browser, bind the code mirror to inline query editor.
1882     bindCodeMirrorToInlineEditor();
1883     $(document).on('click', "a.inline_edit_sql", function () {
1884         if ($('#sql_query_edit').length) {
1885             // An inline query editor is already open,
1886             // we don't want another copy of it
1887             return false;
1888         }
1890         var $form = $(this).prev('form');
1891         var sql_query  = $form.find("input[name='sql_query']").val().trim();
1892         var $inner_sql = $(this).parent().prev().find('code.sql');
1893         var old_text   = $inner_sql.html();
1895         var new_content = "<textarea name=\"sql_query_edit\" id=\"sql_query_edit\">" + escapeHtml(sql_query) + "</textarea>\n";
1896         new_content    += getForeignKeyCheckboxLoader();
1897         new_content    += "<input type=\"submit\" id=\"sql_query_edit_save\" class=\"button btnSave\" value=\"" + PMA_messages.strGo + "\"/>\n";
1898         new_content    += "<input type=\"button\" id=\"sql_query_edit_discard\" class=\"button btnDiscard\" value=\"" + PMA_messages.strCancel + "\"/>\n";
1899         var $editor_area = $('div#inline_editor');
1900         if ($editor_area.length === 0) {
1901             $editor_area = $('<div id="inline_editor_outer"></div>');
1902             $editor_area.insertBefore($inner_sql);
1903         }
1904         $editor_area.html(new_content);
1905         loadForeignKeyCheckbox();
1906         $inner_sql.hide();
1908         bindCodeMirrorToInlineEditor();
1909         return false;
1910     });
1912     $(document).on('click', "input#sql_query_edit_save", function () {
1913         //hide already existing success message
1914         var sql_query;
1915         if (codemirror_inline_editor) {
1916             codemirror_inline_editor.save();
1917             sql_query = codemirror_inline_editor.getValue();
1918         } else {
1919             sql_query = $(this).parent().find('#sql_query_edit').val();
1920         }
1921         var fk_check = $(this).parent().find('#fk_checks').is(':checked');
1923         var $form = $("a.inline_edit_sql").prev('form');
1924         var $fake_form = $('<form>', {action: 'import.php', method: 'post'})
1925                 .append($form.find("input[name=server], input[name=db], input[name=table], input[name=token]").clone())
1926                 .append($('<input/>', {type: 'hidden', name: 'show_query', value: 1}))
1927                 .append($('<input/>', {type: 'hidden', name: 'is_js_confirmed', value: 0}))
1928                 .append($('<input/>', {type: 'hidden', name: 'sql_query', value: sql_query}))
1929                 .append($('<input/>', {type: 'hidden', name: 'fk_checks', value: fk_check ? 1 : 0}));
1930         if (! checkSqlQuery($fake_form[0])) {
1931             return false;
1932         }
1933         $(".success").hide();
1934         $fake_form.appendTo($('body')).submit();
1935     });
1937     $(document).on('click', "input#sql_query_edit_discard", function () {
1938         var $divEditor = $('div#inline_editor_outer');
1939         $divEditor.siblings('code.sql').show();
1940         $divEditor.remove();
1941     });
1943     $('input.sqlbutton').click(function (evt) {
1944         insertQuery(evt.target.id);
1945         PMA_handleSimulateQueryButton();
1946         return false;
1947     });
1949     $(document).on('change', '#parameterized', updateQueryParameters);
1951     var $inputUsername = $('#input_username');
1952     if ($inputUsername) {
1953         if ($inputUsername.val() === '') {
1954             $inputUsername.focus();
1955         } else {
1956             $('#input_password').focus();
1957         }
1958     }
1962  * "inputRead" event handler for CodeMirror SQL query editors for autocompletion
1963  */
1964 function codemirrorAutocompleteOnInputRead(instance) {
1965     if (!sql_autocomplete_in_progress
1966         && (!instance.options.hintOptions.tables || !sql_autocomplete)) {
1968         if (!sql_autocomplete) {
1969             // Reset after teardown
1970             instance.options.hintOptions.tables = false;
1971             instance.options.hintOptions.defaultTable = '';
1973             sql_autocomplete_in_progress = true;
1975             var href = 'db_sql_autocomplete.php';
1976             var params = {
1977                 'ajax_request': true,
1978                 'token': PMA_commonParams.get('token'),
1979                 'server': PMA_commonParams.get('server'),
1980                 'db': PMA_commonParams.get('db'),
1981                 'no_debug': true
1982             };
1984             var columnHintRender = function(elem, self, data) {
1985                 $('<div class="autocomplete-column-name">')
1986                     .text(data.columnName)
1987                     .appendTo(elem);
1988                 $('<div class="autocomplete-column-hint">')
1989                     .text(data.columnHint)
1990                     .appendTo(elem);
1991             };
1993             $.ajax({
1994                 type: 'POST',
1995                 url: href,
1996                 data: params,
1997                 success: function (data) {
1998                     if (data.success) {
1999                         var tables = JSON.parse(data.tables);
2000                         sql_autocomplete_default_table = PMA_commonParams.get('table');
2001                         sql_autocomplete = [];
2002                         for (var table in tables) {
2003                             if (tables.hasOwnProperty(table)) {
2004                                 var columns = tables[table];
2005                                 table = {
2006                                     text: table,
2007                                     columns: []
2008                                 };
2009                                 for (var column in columns) {
2010                                     if (columns.hasOwnProperty(column)) {
2011                                         var displayText = columns[column].Type;
2012                                         if (columns[column].Key == 'PRI') {
2013                                             displayText += ' | Primary';
2014                                         } else if (columns[column].Key == 'UNI') {
2015                                             displayText += ' | Unique';
2016                                         }
2017                                         table.columns.push({
2018                                             text: column,
2019                                             displayText: column + " | " +  displayText,
2020                                             columnName: column,
2021                                             columnHint: displayText,
2022                                             render: columnHintRender
2023                                         });
2024                                     }
2025                                 }
2026                             }
2027                             sql_autocomplete.push(table);
2028                         }
2029                         instance.options.hintOptions.tables = sql_autocomplete;
2030                         instance.options.hintOptions.defaultTable = sql_autocomplete_default_table;
2031                     }
2032                 },
2033                 complete: function () {
2034                     sql_autocomplete_in_progress = false;
2035                 }
2036             });
2037         }
2038         else {
2039             instance.options.hintOptions.tables = sql_autocomplete;
2040             instance.options.hintOptions.defaultTable = sql_autocomplete_default_table;
2041         }
2042     }
2043     if (instance.state.completionActive) {
2044         return;
2045     }
2046     var cur = instance.getCursor();
2047     var token = instance.getTokenAt(cur);
2048     var string = '';
2049     if (token.string.match(/^[.`\w@]\w*$/)) {
2050         string = token.string;
2051     }
2052     if (string.length > 0) {
2053         CodeMirror.commands.autocomplete(instance);
2054     }
2058  * Remove autocomplete information before tearing down a page
2059  */
2060 AJAX.registerTeardown('functions.js', function () {
2061     sql_autocomplete = false;
2062     sql_autocomplete_default_table = '';
2066  * Binds the CodeMirror to the text area used to inline edit a query.
2067  */
2068 function bindCodeMirrorToInlineEditor() {
2069     var $inline_editor = $('#sql_query_edit');
2070     if ($inline_editor.length > 0) {
2071         if (typeof CodeMirror !== 'undefined') {
2072             var height = $inline_editor.css('height');
2073             codemirror_inline_editor = PMA_getSQLEditor($inline_editor);
2074             codemirror_inline_editor.getWrapperElement().style.height = height;
2075             codemirror_inline_editor.refresh();
2076             codemirror_inline_editor.focus();
2077             $(codemirror_inline_editor.getWrapperElement())
2078                 .bind('keydown', catchKeypressesFromSqlInlineEdit);
2079         } else {
2080             $inline_editor
2081                 .focus()
2082                 .bind('keydown', catchKeypressesFromSqlInlineEdit);
2083         }
2084     }
2087 function catchKeypressesFromSqlInlineEdit(event) {
2088     // ctrl-enter is 10 in chrome and ie, but 13 in ff
2089     if ((event.ctrlKey || event.metaKey) && (event.keyCode == 13 || event.keyCode == 10)) {
2090         $("#sql_query_edit_save").trigger('click');
2091     }
2095  * Adds doc link to single highlighted SQL element
2096  */
2097 function PMA_doc_add($elm, params)
2099     if (typeof mysql_doc_template == 'undefined') {
2100         return;
2101     }
2103     var url = PMA_sprintf(
2104         decodeURIComponent(mysql_doc_template),
2105         params[0]
2106     );
2107     if (params.length > 1) {
2108         url += '#' + params[1];
2109     }
2110     var content = $elm.text();
2111     $elm.text('');
2112     $elm.append('<a target="mysql_doc" class="cm-sql-doc" href="' + url + '">' + content + '</a>');
2116  * Generates doc links for keywords inside highlighted SQL
2117  */
2118 function PMA_doc_keyword(idx, elm)
2120     var $elm = $(elm);
2121     /* Skip already processed ones */
2122     if ($elm.find('a').length > 0) {
2123         return;
2124     }
2125     var keyword = $elm.text().toUpperCase();
2126     var $next = $elm.next('.cm-keyword');
2127     if ($next) {
2128         var next_keyword = $next.text().toUpperCase();
2129         var full = keyword + ' ' + next_keyword;
2131         var $next2 = $next.next('.cm-keyword');
2132         if ($next2) {
2133             var next2_keyword = $next2.text().toUpperCase();
2134             var full2 = full + ' ' + next2_keyword;
2135             if (full2 in mysql_doc_keyword) {
2136                 PMA_doc_add($elm, mysql_doc_keyword[full2]);
2137                 PMA_doc_add($next, mysql_doc_keyword[full2]);
2138                 PMA_doc_add($next2, mysql_doc_keyword[full2]);
2139                 return;
2140             }
2141         }
2142         if (full in mysql_doc_keyword) {
2143             PMA_doc_add($elm, mysql_doc_keyword[full]);
2144             PMA_doc_add($next, mysql_doc_keyword[full]);
2145             return;
2146         }
2147     }
2148     if (keyword in mysql_doc_keyword) {
2149         PMA_doc_add($elm, mysql_doc_keyword[keyword]);
2150     }
2154  * Generates doc links for builtins inside highlighted SQL
2155  */
2156 function PMA_doc_builtin(idx, elm)
2158     var $elm = $(elm);
2159     var builtin = $elm.text().toUpperCase();
2160     if (builtin in mysql_doc_builtin) {
2161         PMA_doc_add($elm, mysql_doc_builtin[builtin]);
2162     }
2166  * Higlights SQL using CodeMirror.
2167  */
2168 function PMA_highlightSQL($base)
2170     var $elm = $base.find('code.sql');
2171     $elm.each(function () {
2172         var $sql = $(this);
2173         var $pre = $sql.find('pre');
2174         /* We only care about visible elements to avoid double processing */
2175         if ($pre.is(":visible")) {
2176             var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
2177             $sql.append($highlight);
2178             if (typeof CodeMirror != 'undefined') {
2179                 CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]);
2180                 $pre.hide();
2181                 $highlight.find('.cm-keyword').each(PMA_doc_keyword);
2182                 $highlight.find('.cm-builtin').each(PMA_doc_builtin);
2183             }
2184         }
2185     });
2189  * Updates an element containing code.
2191  * @param jQuery Object $base base element which contains the raw and the
2192  *                            highlighted code.
2194  * @param string htmlValue    code in HTML format, displayed if code cannot be
2195  *                            highlighted
2197  * @param string rawValue     raw code, used as a parameter for highlighter
2199  * @return bool               whether content was updated or not
2200  */
2201 function PMA_updateCode($base, htmlValue, rawValue)
2203     var $code = $base.find('code');
2204     if ($code.length === 0) {
2205         return false;
2206     }
2208     // Determines the type of the content and appropriate CodeMirror mode.
2209     var type = '', mode = '';
2210     if  ($code.hasClass('json')) {
2211         type = 'json';
2212         mode = 'application/json';
2213     } else if ($code.hasClass('sql')) {
2214         type = 'sql';
2215         mode = 'text/x-mysql';
2216     } else if ($code.hasClass('xml')) {
2217         type = 'xml';
2218         mode = 'application/xml';
2219     } else {
2220         return false;
2221     }
2223     // Element used to display unhighlighted code.
2224     var $notHighlighted = $('<pre>' + htmlValue + '</pre>');
2226     // Tries to highlight code using CodeMirror.
2227     if (typeof CodeMirror != 'undefined') {
2228         var $highlighted = $('<div class="' + type + '-highlight cm-s-default"></div>');
2229         CodeMirror.runMode(rawValue, mode, $highlighted[0]);
2230         $notHighlighted.hide();
2231         $code.html('').append($notHighlighted, $highlighted[0]);
2232     } else {
2233         $code.html('').append($notHighlighted);
2234     }
2236     return true;
2240  * Show a message on the top of the page for an Ajax request
2242  * Sample usage:
2244  * 1) var $msg = PMA_ajaxShowMessage();
2245  * This will show a message that reads "Loading...". Such a message will not
2246  * disappear automatically and cannot be dismissed by the user. To remove this
2247  * message either the PMA_ajaxRemoveMessage($msg) function must be called or
2248  * another message must be show with PMA_ajaxShowMessage() function.
2250  * 2) var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2251  * This is a special case. The behaviour is same as above,
2252  * just with a different message
2254  * 3) var $msg = PMA_ajaxShowMessage('The operation was successful');
2255  * This will show a message that will disappear automatically and it can also
2256  * be dismissed by the user.
2258  * 4) var $msg = PMA_ajaxShowMessage('Some error', false);
2259  * This will show a message that will not disappear automatically, but it
2260  * can be dismissed by the user after he has finished reading it.
2262  * @param string  message     string containing the message to be shown.
2263  *                              optional, defaults to 'Loading...'
2264  * @param mixed   timeout     number of milliseconds for the message to be visible
2265  *                              optional, defaults to 5000. If set to 'false', the
2266  *                              notification will never disappear
2267  * @param string  type        string to dictate the type of message shown.
2268  *                              optional, defaults to normal notification.
2269  *                              If set to 'error', the notification will show message
2270  *                              with red background.
2271  *                              If set to 'success', the notification will show with
2272  *                              a green background.
2273  * @return jQuery object       jQuery Element that holds the message div
2274  *                              this object can be passed to PMA_ajaxRemoveMessage()
2275  *                              to remove the notification
2276  */
2277 function PMA_ajaxShowMessage(message, timeout, type)
2279     /**
2280      * @var self_closing Whether the notification will automatically disappear
2281      */
2282     var self_closing = true;
2283     /**
2284      * @var dismissable Whether the user will be able to remove
2285      *                  the notification by clicking on it
2286      */
2287     var dismissable = true;
2288     // Handle the case when a empty data.message is passed.
2289     // We don't want the empty message
2290     if (message === '') {
2291         return true;
2292     } else if (! message) {
2293         // If the message is undefined, show the default
2294         message = PMA_messages.strLoading;
2295         dismissable = false;
2296         self_closing = false;
2297     } else if (message == PMA_messages.strProcessingRequest) {
2298         // This is another case where the message should not disappear
2299         dismissable = false;
2300         self_closing = false;
2301     }
2302     // Figure out whether (or after how long) to remove the notification
2303     if (timeout === undefined) {
2304         timeout = 5000;
2305     } else if (timeout === false) {
2306         self_closing = false;
2307     }
2308     // Determine type of message, add styling as required
2309     if (type === "error") {
2310       message = "<div class=\"error\">" + message + "</div>";
2311     } else if (type === "success") {
2312       message = "<div class=\"success\">" + message + "</div>";
2313     }
2314     // Create a parent element for the AJAX messages, if necessary
2315     if ($('#loading_parent').length === 0) {
2316         $('<div id="loading_parent"></div>')
2317         .prependTo("#page_content");
2318     }
2319     // Update message count to create distinct message elements every time
2320     ajax_message_count++;
2321     // Remove all old messages, if any
2322     $("span.ajax_notification[id^=ajax_message_num]").remove();
2323     /**
2324      * @var    $retval    a jQuery object containing the reference
2325      *                    to the created AJAX message
2326      */
2327     var $retval = $(
2328             '<span class="ajax_notification" id="ajax_message_num_' +
2329             ajax_message_count +
2330             '"></span>'
2331     )
2332     .hide()
2333     .appendTo("#loading_parent")
2334     .html(message)
2335     .show();
2336     // If the notification is self-closing we should create a callback to remove it
2337     if (self_closing) {
2338         $retval
2339         .delay(timeout)
2340         .fadeOut('medium', function () {
2341             if ($(this).is(':data(tooltip)')) {
2342                 $(this).tooltip('destroy');
2343             }
2344             // Remove the notification
2345             $(this).remove();
2346         });
2347     }
2348     // If the notification is dismissable we need to add the relevant class to it
2349     // and add a tooltip so that the users know that it can be removed
2350     if (dismissable) {
2351         $retval.addClass('dismissable').css('cursor', 'pointer');
2352         /**
2353          * Add a tooltip to the notification to let the user know that (s)he
2354          * can dismiss the ajax notification by clicking on it.
2355          */
2356         PMA_tooltip(
2357             $retval,
2358             'span',
2359             PMA_messages.strDismiss
2360         );
2361     }
2362     PMA_highlightSQL($retval);
2364     return $retval;
2368  * Removes the message shown for an Ajax operation when it's completed
2370  * @param jQuery object   jQuery Element that holds the notification
2372  * @return nothing
2373  */
2374 function PMA_ajaxRemoveMessage($this_msgbox)
2376     if ($this_msgbox !== undefined && $this_msgbox instanceof jQuery) {
2377         $this_msgbox
2378         .stop(true, true)
2379         .fadeOut('medium');
2380         if ($this_msgbox.is(':data(tooltip)')) {
2381             $this_msgbox.tooltip('destroy');
2382         } else {
2383             $this_msgbox.remove();
2384         }
2385     }
2389  * Requests SQL for previewing before executing.
2391  * @param jQuery Object $form Form containing query data
2393  * @return void
2394  */
2395 function PMA_previewSQL($form)
2397     var form_url = $form.attr('action');
2398     var form_data = $form.serialize() +
2399         '&do_save_data=1' +
2400         '&preview_sql=1' +
2401         '&ajax_request=1';
2402     var $msgbox = PMA_ajaxShowMessage();
2403     $.ajax({
2404         type: 'POST',
2405         url: form_url,
2406         data: form_data,
2407         success: function (response) {
2408             PMA_ajaxRemoveMessage($msgbox);
2409             if (response.success) {
2410                 var $dialog_content = $('<div/>')
2411                     .append(response.sql_data);
2412                 var button_options = {};
2413                 button_options[PMA_messages.strClose] = function () {
2414                     $(this).dialog('close');
2415                 };
2416                 var $response_dialog = $dialog_content.dialog({
2417                     minWidth: 550,
2418                     maxHeight: 400,
2419                     modal: true,
2420                     buttons: button_options,
2421                     title: PMA_messages.strPreviewSQL,
2422                     close: function () {
2423                         $(this).remove();
2424                     },
2425                     open: function () {
2426                         // Pretty SQL printing.
2427                         PMA_highlightSQL($(this));
2428                     }
2429                 });
2430             } else {
2431                 PMA_ajaxShowMessage(response.message);
2432             }
2433         },
2434         error: function () {
2435             PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest);
2436         }
2437     });
2441  * check for reserved keyword column name
2443  * @param jQuery Object $form Form
2445  * @returns true|false
2446  */
2448 function PMA_checkReservedWordColumns($form) {
2449     var is_confirmed = true;
2450     $.ajax({
2451         type: 'POST',
2452         url: "tbl_structure.php",
2453         data: $form.serialize() + '&reserved_word_check=1',
2454         success: function (data) {
2455             if (typeof data.success != 'undefined' && data.success === true) {
2456                 is_confirmed = confirm(data.message);
2457             }
2458         },
2459         async:false
2460     });
2461     return is_confirmed;
2464 // This event only need to be fired once after the initial page load
2465 $(function () {
2466     /**
2467      * Allows the user to dismiss a notification
2468      * created with PMA_ajaxShowMessage()
2469      */
2470     $(document).on('click', 'span.ajax_notification.dismissable', function () {
2471         PMA_ajaxRemoveMessage($(this));
2472     });
2473     /**
2474      * The below two functions hide the "Dismiss notification" tooltip when a user
2475      * is hovering a link or button that is inside an ajax message
2476      */
2477     $(document).on('mouseover', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
2478         if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
2479             $(this).parents('span.ajax_notification').tooltip('disable');
2480         }
2481     });
2482     $(document).on('mouseout', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
2483         if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
2484             $(this).parents('span.ajax_notification').tooltip('enable');
2485         }
2486     });
2490  * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected
2491  */
2492 function PMA_showNoticeForEnum(selectElement)
2494     var enum_notice_id = selectElement.attr("id").split("_")[1];
2495     enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2], 10) + 1);
2496     var selectedType = selectElement.val();
2497     if (selectedType == "ENUM" || selectedType == "SET") {
2498         $("p#enum_notice_" + enum_notice_id).show();
2499     } else {
2500         $("p#enum_notice_" + enum_notice_id).hide();
2501     }
2505  * Creates a Profiling Chart. Used in sql.js
2506  * and in server_status_monitor.js
2507  */
2508 function PMA_createProfilingChart(target, data)
2510     // create the chart
2511     var factory = new JQPlotChartFactory();
2512     var chart = factory.createChart(ChartType.PIE, target);
2514     // create the data table and add columns
2515     var dataTable = new DataTable();
2516     dataTable.addColumn(ColumnType.STRING, '');
2517     dataTable.addColumn(ColumnType.NUMBER, '');
2518     dataTable.setData(data);
2520     // draw the chart and return the chart object
2521     chart.draw(dataTable, {
2522         seriesDefaults: {
2523             rendererOptions: {
2524                 showDataLabels:  true
2525             }
2526         },
2527         highlighter: {
2528             tooltipLocation: 'se',
2529             sizeAdjust: 0,
2530             tooltipAxes: 'pieref',
2531             formatString: '%s, %.9Ps'
2532         },
2533         legend: {
2534             show: true,
2535             location: 'e',
2536             rendererOptions: {
2537                 numberColumns: 2
2538             }
2539         },
2540         // from http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines#Color_Palette
2541         seriesColors: [
2542             '#fce94f',
2543             '#fcaf3e',
2544             '#e9b96e',
2545             '#8ae234',
2546             '#729fcf',
2547             '#ad7fa8',
2548             '#ef2929',
2549             '#eeeeec',
2550             '#888a85',
2551             '#c4a000',
2552             '#ce5c00',
2553             '#8f5902',
2554             '#4e9a06',
2555             '#204a87',
2556             '#5c3566',
2557             '#a40000',
2558             '#babdb6',
2559             '#2e3436'
2560         ]
2561     });
2562     return chart;
2566  * Formats a profiling duration nicely (in us and ms time).
2567  * Used in server_status_monitor.js
2569  * @param  integer    Number to be formatted, should be in the range of microsecond to second
2570  * @param  integer    Accuracy, how many numbers right to the comma should be
2571  * @return string     The formatted number
2572  */
2573 function PMA_prettyProfilingNum(num, acc)
2575     if (!acc) {
2576         acc = 2;
2577     }
2578     acc = Math.pow(10, acc);
2579     if (num * 1000 < 0.1) {
2580         num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ';
2581     } else if (num < 0.1) {
2582         num = Math.round(acc * (num * 1000)) / acc + 'm';
2583     } else {
2584         num = Math.round(acc * num) / acc;
2585     }
2587     return num + 's';
2592  * Formats a SQL Query nicely with newlines and indentation. Depends on Codemirror and MySQL Mode!
2594  * @param string      Query to be formatted
2595  * @return string      The formatted query
2596  */
2597 function PMA_SQLPrettyPrint(string)
2599     if (typeof CodeMirror == 'undefined') {
2600         return string;
2601     }
2603     var mode = CodeMirror.getMode({}, "text/x-mysql");
2604     var stream = new CodeMirror.StringStream(string);
2605     var state = mode.startState();
2606     var token, tokens = [];
2607     var output = '';
2608     var tabs = function (cnt) {
2609         var ret = '';
2610         for (var i = 0; i < 4 * cnt; i++) {
2611             ret += " ";
2612         }
2613         return ret;
2614     };
2616     // "root-level" statements
2617     var statements = {
2618         'select': ['select', 'from', 'on', 'where', 'having', 'limit', 'order by', 'group by'],
2619         'update': ['update', 'set', 'where'],
2620         'insert into': ['insert into', 'values']
2621     };
2622     // don't put spaces before these tokens
2623     var spaceExceptionsBefore = {';': true, ',': true, '.': true, '(': true};
2624     // don't put spaces after these tokens
2625     var spaceExceptionsAfter = {'.': true};
2627     // Populate tokens array
2628     var str = '';
2629     while (! stream.eol()) {
2630         stream.start = stream.pos;
2631         token = mode.token(stream, state);
2632         if (token !== null) {
2633             tokens.push([token, stream.current().toLowerCase()]);
2634         }
2635     }
2637     var currentStatement = tokens[0][1];
2639     if (! statements[currentStatement]) {
2640         return string;
2641     }
2642     // Holds all currently opened code blocks (statement, function or generic)
2643     var blockStack = [];
2644     // Holds the type of block from last iteration (the current is in blockStack[0])
2645     var previousBlock;
2646     // If a new code block is found, newBlock contains its type for one iteration and vice versa for endBlock
2647     var newBlock, endBlock;
2648     // How much to indent in the current line
2649     var indentLevel = 0;
2650     // Holds the "root-level" statements
2651     var statementPart, lastStatementPart = statements[currentStatement][0];
2653     blockStack.unshift('statement');
2655     // Iterate through every token and format accordingly
2656     for (var i = 0; i < tokens.length; i++) {
2657         previousBlock = blockStack[0];
2659         // New block => push to stack
2660         if (tokens[i][1] == '(') {
2661             if (i < tokens.length - 1 && tokens[i + 1][0] == 'statement-verb') {
2662                 blockStack.unshift(newBlock = 'statement');
2663             } else if (i > 0 && tokens[i - 1][0] == 'builtin') {
2664                 blockStack.unshift(newBlock = 'function');
2665             } else {
2666                 blockStack.unshift(newBlock = 'generic');
2667             }
2668         } else {
2669             newBlock = null;
2670         }
2672         // Block end => pop from stack
2673         if (tokens[i][1] == ')') {
2674             endBlock = blockStack[0];
2675             blockStack.shift();
2676         } else {
2677             endBlock = null;
2678         }
2680         // A subquery is starting
2681         if (i > 0 && newBlock == 'statement') {
2682             indentLevel++;
2683             output += "\n" + tabs(indentLevel) + tokens[i][1] + ' ' + tokens[i + 1][1].toUpperCase() + "\n" + tabs(indentLevel + 1);
2684             currentStatement = tokens[i + 1][1];
2685             i++;
2686             continue;
2687         }
2689         // A subquery is ending
2690         if (endBlock == 'statement' && indentLevel > 0) {
2691             output += "\n" + tabs(indentLevel);
2692             indentLevel--;
2693         }
2695         // One less indentation for statement parts (from, where, order by, etc.) and a newline
2696         statementPart = statements[currentStatement].indexOf(tokens[i][1]);
2697         if (statementPart != -1) {
2698             if (i > 0) {
2699                 output += "\n";
2700             }
2701             output += tabs(indentLevel) + tokens[i][1].toUpperCase();
2702             output += "\n" + tabs(indentLevel + 1);
2703             lastStatementPart = tokens[i][1];
2704         }
2705         // Normal indentation and spaces for everything else
2706         else {
2707             if (! spaceExceptionsBefore[tokens[i][1]] &&
2708                ! (i > 0 && spaceExceptionsAfter[tokens[i - 1][1]]) &&
2709                output.charAt(output.length - 1) != ' ') {
2710                 output += " ";
2711             }
2712             if (tokens[i][0] == 'keyword') {
2713                 output += tokens[i][1].toUpperCase();
2714             } else {
2715                 output += tokens[i][1];
2716             }
2717         }
2719         // split columns in select and 'update set' clauses, but only inside statements blocks
2720         if ((lastStatementPart == 'select' || lastStatementPart == 'where'  || lastStatementPart == 'set') &&
2721             tokens[i][1] == ',' && blockStack[0] == 'statement') {
2723             output += "\n" + tabs(indentLevel + 1);
2724         }
2726         // split conditions in where clauses, but only inside statements blocks
2727         if (lastStatementPart == 'where' &&
2728             (tokens[i][1] == 'and' || tokens[i][1] == 'or' || tokens[i][1] == 'xor')) {
2730             if (blockStack[0] == 'statement') {
2731                 output += "\n" + tabs(indentLevel + 1);
2732             }
2733             // Todo: Also split and or blocks in newlines & indentation++
2734             //if (blockStack[0] == 'generic')
2735              //   output += ...
2736         }
2737     }
2738     return output;
2742  * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not
2743  *  return a jQuery object yet and hence cannot be chained
2745  * @param string      question
2746  * @param string      url           URL to be passed to the callbackFn to make
2747  *                                  an Ajax call to
2748  * @param function    callbackFn    callback to execute after user clicks on OK
2749  * @param function    openCallback  optional callback to run when dialog is shown
2750  */
2752 jQuery.fn.PMA_confirm = function (question, url, callbackFn, openCallback) {
2753     var confirmState = PMA_commonParams.get('confirm');
2754     if (! confirmState) {
2755         // user does not want to confirm
2756         if ($.isFunction(callbackFn)) {
2757             callbackFn.call(this, url);
2758             return true;
2759         }
2760     }
2761     if (PMA_messages.strDoYouReally === '') {
2762         return true;
2763     }
2765     /**
2766      * @var    button_options  Object that stores the options passed to jQueryUI
2767      *                          dialog
2768      */
2769     var button_options = [
2770         {
2771             text: PMA_messages.strOK,
2772             'class': 'submitOK',
2773             click: function () {
2774                 $(this).dialog("close");
2775                 if ($.isFunction(callbackFn)) {
2776                     callbackFn.call(this, url);
2777                 }
2778             }
2779         },
2780         {
2781             text: PMA_messages.strCancel,
2782             'class': 'submitCancel',
2783             click: function () {
2784                 $(this).dialog("close");
2785             }
2786         }
2787     ];
2789     $('<div/>', {'id': 'confirm_dialog', 'title': PMA_messages.strConfirm})
2790     .prepend(question)
2791     .dialog({
2792         buttons: button_options,
2793         close: function () {
2794             $(this).remove();
2795         },
2796         open: openCallback,
2797         modal: true
2798     });
2802  * jQuery function to sort a table's body after a new row has been appended to it.
2804  * @param string      text_selector   string to select the sortKey's text
2806  * @return jQuery Object for chaining purposes
2807  */
2808 jQuery.fn.PMA_sort_table = function (text_selector) {
2809     return this.each(function () {
2811         /**
2812          * @var table_body  Object referring to the table's <tbody> element
2813          */
2814         var table_body = $(this);
2815         /**
2816          * @var rows    Object referring to the collection of rows in {@link table_body}
2817          */
2818         var rows = $(this).find('tr').get();
2820         //get the text of the field that we will sort by
2821         $.each(rows, function (index, row) {
2822             row.sortKey = $.trim($(row).find(text_selector).text().toLowerCase());
2823         });
2825         //get the sorted order
2826         rows.sort(function (a, b) {
2827             if (a.sortKey < b.sortKey) {
2828                 return -1;
2829             }
2830             if (a.sortKey > b.sortKey) {
2831                 return 1;
2832             }
2833             return 0;
2834         });
2836         //pull out each row from the table and then append it according to it's order
2837         $.each(rows, function (index, row) {
2838             $(table_body).append(row);
2839             row.sortKey = null;
2840         });
2841     });
2845  * Unbind all event handlers before tearing down a page
2846  */
2847 AJAX.registerTeardown('functions.js', function () {
2848     $(document).off('submit', "#create_table_form_minimal.ajax");
2849     $(document).off('submit', "form.create_table_form.ajax");
2850     $(document).off('click', "form.create_table_form.ajax input[name=submit_num_fields]");
2851     $(document).off('keyup', "form.create_table_form.ajax input");
2852     $(document).off('change', "input[name=partition_count],input[name=subpartition_count],select[name=partition_by]");
2856  * jQuery coding for 'Create Table'.  Used on db_operations.php,
2857  * db_structure.php and db_tracking.php (i.e., wherever
2858  * libraries/display_create_table.lib.php is used)
2860  * Attach Ajax Event handlers for Create Table
2861  */
2862 AJAX.registerOnload('functions.js', function () {
2863     /**
2864      * Attach event handler for submission of create table form (save)
2865      */
2866     $(document).on('submit', "form.create_table_form.ajax", function (event) {
2867         event.preventDefault();
2869         /**
2870          * @var    the_form    object referring to the create table form
2871          */
2872         var $form = $(this);
2874         /*
2875          * First validate the form; if there is a problem, avoid submitting it
2876          *
2877          * checkTableEditForm() needs a pure element and not a jQuery object,
2878          * this is why we pass $form[0] as a parameter (the jQuery object
2879          * is actually an array of DOM elements)
2880          */
2882         if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) {
2883             PMA_prepareForAjaxRequest($form);
2884             if (PMA_checkReservedWordColumns($form)) {
2885                 PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2886                 //User wants to submit the form
2887                 $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
2888                     if (typeof data !== 'undefined' && data.success === true) {
2889                         $('#properties_message')
2890                          .removeClass('error')
2891                          .html('');
2892                         PMA_ajaxShowMessage(data.message);
2893                         // Only if the create table dialog (distinct panel) exists
2894                         var $createTableDialog = $("#create_table_dialog");
2895                         if ($createTableDialog.length > 0) {
2896                             $createTableDialog.dialog("close").remove();
2897                         }
2898                         $('#tableslistcontainer').before(data.formatted_sql);
2900                         /**
2901                          * @var tables_table    Object referring to the <tbody> element that holds the list of tables
2902                          */
2903                         var tables_table = $("#tablesForm").find("tbody").not("#tbl_summary_row");
2904                         // this is the first table created in this db
2905                         if (tables_table.length === 0) {
2906                             PMA_commonActions.refreshMain(
2907                                 PMA_commonParams.get('opendb_url')
2908                             );
2909                         } else {
2910                             /**
2911                              * @var curr_last_row   Object referring to the last <tr> element in {@link tables_table}
2912                              */
2913                             var curr_last_row = $(tables_table).find('tr:last');
2914                             /**
2915                              * @var curr_last_row_index_string   String containing the index of {@link curr_last_row}
2916                              */
2917                             var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0];
2918                             /**
2919                              * @var curr_last_row_index Index of {@link curr_last_row}
2920                              */
2921                             var curr_last_row_index = parseFloat(curr_last_row_index_string);
2922                             /**
2923                              * @var new_last_row_index   Index of the new row to be appended to {@link tables_table}
2924                              */
2925                             var new_last_row_index = curr_last_row_index + 1;
2926                             /**
2927                              * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table}
2928                              */
2929                             var new_last_row_id = 'checkbox_tbl_' + new_last_row_index;
2931                             data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id);
2932                             //append to table
2933                             $(data.new_table_string)
2934                              .appendTo(tables_table);
2936                             //Sort the table
2937                             $(tables_table).PMA_sort_table('th');
2939                             // Adjust summary row
2940                             PMA_adjustTotals();
2941                         }
2943                         //Refresh navigation as a new table has been added
2944                         PMA_reloadNavigation();
2945                         // Redirect to table structure page on creation of new table
2946                         var params_12 = 'ajax_request=true&ajax_page_request=true';
2947                         if (! (history && history.pushState)) {
2948                             params_12 += PMA_MicroHistory.menus.getRequestParam();
2949                         }
2950                         tblStruct_url = 'tbl_structure.php?server=' + data._params.server +
2951                             '&db='+ data._params.db + '&token=' + data._params.token +
2952                             '&goto=db_structure.php&table=' + data._params.table + '';
2953                         $.get(tblStruct_url, params_12, AJAX.responseHandler);
2954                     } else {
2955                         PMA_ajaxShowMessage(
2956                             '<div class="error">' + data.error + '</div>',
2957                             false
2958                         );
2959                     }
2960                 }); // end $.post()
2961             }
2962         } // end if (checkTableEditForm() )
2963     }); // end create table form (save)
2965     /**
2966      * Submits the intermediate changes in the table creation form
2967      * to refresh the UI accordingly
2968      */
2969     function submitChangesInCreateTableForm (actionParam) {
2971         /**
2972          * @var    the_form    object referring to the create table form
2973          */
2974         var $form = $('form.create_table_form.ajax');
2976         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2977         PMA_prepareForAjaxRequest($form);
2979         //User wants to add more fields to the table
2980         $.post($form.attr('action'), $form.serialize() + "&" + actionParam, function (data) {
2981             if (typeof data !== 'undefined' && data.success) {
2982                 var $pageContent = $("#page_content");
2983                 $pageContent.html(data.message);
2984                 PMA_highlightSQL($pageContent);
2985                 PMA_verifyColumnsProperties();
2986                 PMA_hideShowConnection($('.create_table_form select[name=tbl_storage_engine]'));
2987                 PMA_ajaxRemoveMessage($msgbox);
2988             } else {
2989                 PMA_ajaxShowMessage(data.error);
2990             }
2991         }); //end $.post()
2992     }
2994     /**
2995      * Attach event handler for create table form (add fields)
2996      */
2997     $(document).on('click', "form.create_table_form.ajax input[name=submit_num_fields]", function (event) {
2998         event.preventDefault();
3000         if (!checkFormElementInRange(this.form, 'added_fields', PMA_messages.strLeastColumnError, 1)) {
3001             return;
3002         }
3004         submitChangesInCreateTableForm('submit_num_fields=1');
3005     }); // end create table form (add fields)
3007     $(document).on('keydown', "form.create_table_form.ajax input[name=added_fields]", function (event) {
3008         if (event.keyCode == 13) {
3009             event.preventDefault();
3010             event.stopImmediatePropagation();
3011             $(this)
3012                 .closest('form')
3013                 .find('input[name=submit_num_fields]')
3014                 .click();
3015         }
3016     });
3018     /**
3019      * Attach event handler to manage changes in number of partitions and subpartitions
3020      */
3021     $(document).on('change', "input[name=partition_count],input[name=subpartition_count],select[name=partition_by]", function (event) {
3022         $this = $(this);
3023         $form = $this.parents('form');
3024         if ($form.is(".create_table_form.ajax")) {
3025             submitChangesInCreateTableForm('submit_partition_change=1');
3026         } else {
3027             $form.submit();
3028         }
3029     });
3031     $(document).on('change', "input[value=AUTO_INCREMENT]", function() {
3032         if (this.checked) {
3033             var col = /\d/.exec($(this).attr('name'));
3034             col = col[0];
3035             var $selectFieldKey = $('select[name="field_key[' + col + ']"]');
3036             if ($selectFieldKey.val() === 'none_'+col) {
3037                 $selectFieldKey.val('primary_'+col).change();
3038             }
3039         }
3040     });
3041     $('body')
3042     .off('click', 'input.preview_sql')
3043     .on('click', 'input.preview_sql', function () {
3044         var $form = $(this).closest('form');
3045         PMA_previewSQL($form);
3046     });
3051  * Validates the password field in a form
3053  * @see    PMA_messages.strPasswordEmpty
3054  * @see    PMA_messages.strPasswordNotSame
3055  * @param  object $the_form The form to be validated
3056  * @return bool
3057  */
3058 function PMA_checkPassword($the_form)
3060     // Did the user select 'no password'?
3061     if ($the_form.find('#nopass_1').is(':checked')) {
3062         return true;
3063     } else {
3064         var $pred = $the_form.find('#select_pred_password');
3065         if ($pred.length && ($pred.val() == 'none' || $pred.val() == 'keep')) {
3066             return true;
3067         }
3068     }
3070     var $password = $the_form.find('input[name=pma_pw]');
3071     var $password_repeat = $the_form.find('input[name=pma_pw2]');
3072     var alert_msg = false;
3074     if ($password.val() === '') {
3075         alert_msg = PMA_messages.strPasswordEmpty;
3076     } else if ($password.val() != $password_repeat.val()) {
3077         alert_msg = PMA_messages.strPasswordNotSame;
3078     }
3080     if (alert_msg) {
3081         alert(alert_msg);
3082         $password.val('');
3083         $password_repeat.val('');
3084         $password.focus();
3085         return false;
3086     }
3087     return true;
3091  * Unbind all event handlers before tearing down a page
3092  */
3093 AJAX.registerTeardown('functions.js', function () {
3094     $(document).off('click', '#change_password_anchor.ajax');
3097  * Attach Ajax event handlers for 'Change Password' on index.php
3098  */
3099 AJAX.registerOnload('functions.js', function () {
3101     /* Handler for hostname type */
3102     $(document).on('change', '#select_pred_hostname', function () {
3103         var hostname = $('#pma_hostname');
3104         if (this.value == 'any') {
3105             hostname.val('%');
3106         } else if (this.value == 'localhost') {
3107             hostname.val('localhost');
3108         } else if (this.value == 'thishost' && $(this).data('thishost')) {
3109             hostname.val($(this).data('thishost'));
3110         } else if (this.value == 'hosttable') {
3111             hostname.val('').prop('required', false);
3112         } else if (this.value == 'userdefined') {
3113             hostname.focus().select().prop('required', true);
3114         }
3115     });
3117     /* Handler for editing hostname */
3118     $(document).on('change', '#pma_hostname', function () {
3119         $('#select_pred_hostname').val('userdefined');
3120         $('#pma_hostname').prop('required', true);
3121     });
3123     /* Handler for username type */
3124     $(document).on('change', '#select_pred_username', function () {
3125         if (this.value == 'any') {
3126             $('#pma_username').val('').prop('required', false);
3127             $('#user_exists_warning').css('display', 'none');
3128         } else if (this.value == 'userdefined') {
3129             $('#pma_username').focus().select().prop('required', true);
3130         }
3131     });
3133     /* Handler for editing username */
3134     $(document).on('change', '#pma_username', function () {
3135         $('#select_pred_username').val('userdefined');
3136         $('#pma_username').prop('required', true);
3137     });
3139     /* Handler for password type */
3140     $(document).on('change', '#select_pred_password', function () {
3141         if (this.value == 'none') {
3142             $('#text_pma_pw2').prop('required', false).val('');
3143             $('#text_pma_pw').prop('required', false).val('');
3144         } else if (this.value == 'userdefined') {
3145             $('#text_pma_pw2').prop('required', true);
3146             $('#text_pma_pw').prop('required', true).focus().select();
3147         } else {
3148             $('#text_pma_pw2').prop('required', false);
3149             $('#text_pma_pw').prop('required', false);
3150         }
3151     });
3153     /* Handler for editing password */
3154     $(document).on('change', '#text_pma_pw,#text_pma_pw2', function () {
3155         $('#select_pred_password').val('userdefined');
3156         $('#text_pma_pw2').prop('required', true);
3157         $('#text_pma_pw').prop('required', true);
3158     });
3160     /**
3161      * Attach Ajax event handler on the change password anchor
3162      */
3163     $(document).on('click', '#change_password_anchor.ajax', function (event) {
3164         event.preventDefault();
3166         var $msgbox = PMA_ajaxShowMessage();
3168         /**
3169          * @var button_options  Object containing options to be passed to jQueryUI's dialog
3170          */
3171         var button_options = {};
3172         button_options[PMA_messages.strGo] = function () {
3174             event.preventDefault();
3176             /**
3177              * @var $the_form    Object referring to the change password form
3178              */
3179             var $the_form = $("#change_password_form");
3181             if (! PMA_checkPassword($the_form)) {
3182                 return false;
3183             }
3185             /**
3186              * @var this_value  String containing the value of the submit button.
3187              * Need to append this for the change password form on Server Privileges
3188              * page to work
3189              */
3190             var this_value = $(this).val();
3192             var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3193             $the_form.append('<input type="hidden" name="ajax_request" value="true" />');
3195             $.post($the_form.attr('action'), $the_form.serialize() + '&change_pw=' + this_value, function (data) {
3196                 if (typeof data === 'undefined' || data.success !== true) {
3197                     PMA_ajaxShowMessage(data.error, false);
3198                     return;
3199                 }
3201                 var $pageContent = $("#page_content");
3202                 $pageContent.prepend(data.message);
3203                 PMA_highlightSQL($pageContent);
3204                 $("#change_password_dialog").hide().remove();
3205                 $("#edit_user_dialog").dialog("close").remove();
3206                 PMA_ajaxRemoveMessage($msgbox);
3207             }); // end $.post()
3208         };
3210         button_options[PMA_messages.strCancel] = function () {
3211             $(this).dialog('close');
3212         };
3213         $.get($(this).attr('href'), {'ajax_request': true}, function (data) {
3214             if (typeof data === 'undefined' || !data.success) {
3215                 PMA_ajaxShowMessage(data.error, false);
3216                 return;
3217             }
3219             if (data._scripts) {
3220                 AJAX.scriptHandler.load(data._scripts);
3221             }
3223             $('<div id="change_password_dialog"></div>')
3224                 .dialog({
3225                     title: PMA_messages.strChangePassword,
3226                     width: 600,
3227                     close: function (ev, ui) {
3228                         $(this).remove();
3229                     },
3230                     buttons: button_options,
3231                     modal: true
3232                 })
3233                 .append(data.message);
3234             // for this dialog, we remove the fieldset wrapping due to double headings
3235             $("fieldset#fieldset_change_password")
3236                 .find("legend").remove().end()
3237                 .find("table.noclick").unwrap().addClass("some-margin")
3238                 .find("input#text_pma_pw").focus();
3239             $('#fieldset_change_password_footer').hide();
3240             PMA_ajaxRemoveMessage($msgbox);
3241             $('#change_password_form').bind('submit', function (e) {
3242                 e.preventDefault();
3243                 $(this)
3244                     .closest('.ui-dialog')
3245                     .find('.ui-dialog-buttonpane .ui-button')
3246                     .first()
3247                     .click();
3248             });
3249         }); // end $.get()
3250     }); // end handler for change password anchor
3251 }); // end $() for Change Password
3254  * Unbind all event handlers before tearing down a page
3255  */
3256 AJAX.registerTeardown('functions.js', function () {
3257     $(document).off('change', "select.column_type");
3258     $(document).off('change', "select.default_type");
3259     $(document).off('change', "select.virtuality");
3260     $(document).off('change', 'input.allow_null');
3261     $(document).off('change', '.create_table_form select[name=tbl_storage_engine]');
3264  * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when
3265  * the page loads and when the selected data type changes
3266  */
3267 AJAX.registerOnload('functions.js', function () {
3268     // is called here for normal page loads and also when opening
3269     // the Create table dialog
3270     PMA_verifyColumnsProperties();
3271     //
3272     // needs on() to work also in the Create Table dialog
3273     $(document).on('change', "select.column_type", function () {
3274         PMA_showNoticeForEnum($(this));
3275     });
3276     $(document).on('change', "select.default_type", function () {
3277         PMA_hideShowDefaultValue($(this));
3278     });
3279     $(document).on('change', "select.virtuality", function () {
3280         PMA_hideShowExpression($(this));
3281     });
3282     $(document).on('change', 'input.allow_null', function () {
3283         PMA_validateDefaultValue($(this));
3284     });
3285     $(document).on('change', '.create_table_form select[name=tbl_storage_engine]', function () {
3286         PMA_hideShowConnection($(this));
3287     });
3291  * If the chosen storage engine is FEDERATED show connection field. Hide otherwise
3293  * @param $engine_selector storage engine selector
3294  */
3295 function PMA_hideShowConnection($engine_selector)
3297     var $connection = $('.create_table_form input[name=connection]');
3298     var index = $connection.parent('td').index() + 1;
3299     var $labelTh = $connection.parents('tr').prev('tr').children('th:nth-child(' + index + ')');
3300     if ($engine_selector.val() != 'FEDERATED') {
3301         $connection
3302             .prop('disabled', true)
3303             .parent('td').hide();
3304         $labelTh.hide();
3305     } else {
3306         $connection
3307             .prop('disabled', false)
3308             .parent('td').show();
3309         $labelTh.show();
3310     }
3314  * If the column does not allow NULL values, makes sure that default is not NULL
3315  */
3316 function PMA_validateDefaultValue($null_checkbox)
3318     if (! $null_checkbox.prop('checked')) {
3319         var $default = $null_checkbox.closest('tr').find('.default_type');
3320         if ($default.val() == 'NULL') {
3321             $default.val('NONE');
3322         }
3323     }
3327  * function to populate the input fields on picking a column from central list
3329  * @param string  input_id input id of the name field for the column to be populated
3330  * @param integer offset of the selected column in central list of columns
3331  */
3332 function autoPopulate(input_id, offset)
3334     var db = PMA_commonParams.get('db');
3335     var table = PMA_commonParams.get('table');
3336     input_id = input_id.substring(0, input_id.length - 1);
3337     $('#' + input_id + '1').val(central_column_list[db + '_' + table][offset].col_name);
3338     var col_type = central_column_list[db + '_' + table][offset].col_type.toUpperCase();
3339     $('#' + input_id + '2').val(col_type);
3340     var $input3 = $('#' + input_id + '3');
3341     $input3.val(central_column_list[db + '_' + table][offset].col_length);
3342     if(col_type === 'ENUM' || col_type === 'SET') {
3343         $input3.next().show();
3344     } else {
3345         $input3.next().hide();
3346     }
3347     var col_default = central_column_list[db + '_' + table][offset].col_default.toUpperCase();
3348     var $input4 = $('#' + input_id + '4');
3349     if (col_default !== '' && col_default !== 'NULL' && col_default !== 'CURRENT_TIMESTAMP') {
3350         $input4.val("USER_DEFINED");
3351         $input4.next().next().show();
3352         $input4.next().next().val(central_column_list[db + '_' + table][offset].col_default);
3353     } else {
3354         $input4.val(central_column_list[db + '_' + table][offset].col_default);
3355         $input4.next().next().hide();
3356     }
3357     $('#' + input_id + '5').val(central_column_list[db + '_' + table][offset].col_collation);
3358     var $input6 = $('#' + input_id + '6');
3359     $input6.val(central_column_list[db + '_' + table][offset].col_attribute);
3360     if(central_column_list[db + '_' + table][offset].col_extra === 'on update CURRENT_TIMESTAMP') {
3361         $input6.val(central_column_list[db + '_' + table][offset].col_extra);
3362     }
3363     if(central_column_list[db + '_' + table][offset].col_extra.toUpperCase() === 'AUTO_INCREMENT') {
3364         $('#' + input_id + '9').prop("checked",true).change();
3365     } else {
3366         $('#' + input_id + '9').prop("checked",false);
3367     }
3368     if(central_column_list[db + '_' + table][offset].col_isNull !== '0') {
3369         $('#' + input_id + '7').prop("checked",true);
3370     } else {
3371         $('#' + input_id + '7').prop("checked",false);
3372     }
3376  * Unbind all event handlers before tearing down a page
3377  */
3378 AJAX.registerTeardown('functions.js', function () {
3379     $(document).off('click', "a.open_enum_editor");
3380     $(document).off('click', "input.add_value");
3381     $(document).off('click', "#enum_editor td.drop");
3382     $(document).off('click', 'a.central_columns_dialog');
3385  * @var $enum_editor_dialog An object that points to the jQuery
3386  *                          dialog of the ENUM/SET editor
3387  */
3388 var $enum_editor_dialog = null;
3390  * Opens the ENUM/SET editor and controls its functions
3391  */
3392 AJAX.registerOnload('functions.js', function () {
3393     $(document).on('click', "a.open_enum_editor", function () {
3394         // Get the name of the column that is being edited
3395         var colname = $(this).closest('tr').find('input:first').val();
3396         var title;
3397         var i;
3398         // And use it to make up a title for the page
3399         if (colname.length < 1) {
3400             title = PMA_messages.enum_newColumnVals;
3401         } else {
3402             title = PMA_messages.enum_columnVals.replace(
3403                 /%s/,
3404                 '"' + escapeHtml(decodeURIComponent(colname)) + '"'
3405             );
3406         }
3407         // Get the values as a string
3408         var inputstring = $(this)
3409             .closest('td')
3410             .find("input")
3411             .val();
3412         // Escape html entities
3413         inputstring = $('<div/>')
3414             .text(inputstring)
3415             .html();
3416         // Parse the values, escaping quotes and
3417         // slashes on the fly, into an array
3418         var values = [];
3419         var in_string = false;
3420         var curr, next, buffer = '';
3421         for (i = 0; i < inputstring.length; i++) {
3422             curr = inputstring.charAt(i);
3423             next = i == inputstring.length ? '' : inputstring.charAt(i + 1);
3424             if (! in_string && curr == "'") {
3425                 in_string = true;
3426             } else if (in_string && curr == "\\" && next == "\\") {
3427                 buffer += "&#92;";
3428                 i++;
3429             } else if (in_string && next == "'" && (curr == "'" || curr == "\\")) {
3430                 buffer += "&#39;";
3431                 i++;
3432             } else if (in_string && curr == "'") {
3433                 in_string = false;
3434                 values.push(buffer);
3435                 buffer = '';
3436             } else if (in_string) {
3437                 buffer += curr;
3438             }
3439         }
3440         if (buffer.length > 0) {
3441             // The leftovers in the buffer are the last value (if any)
3442             values.push(buffer);
3443         }
3444         var fields = '';
3445         // If there are no values, maybe the user is about to make a
3446         // new list so we add a few for him/her to get started with.
3447         if (values.length === 0) {
3448             values.push('', '', '', '');
3449         }
3450         // Add the parsed values to the editor
3451         var drop_icon = PMA_getImage('b_drop.png');
3452         for (i = 0; i < values.length; i++) {
3453             fields += "<tr><td>" +
3454                    "<input type='text' value='" + values[i] + "'/>" +
3455                    "</td><td class='drop'>" +
3456                    drop_icon +
3457                    "</td></tr>";
3458         }
3459         /**
3460          * @var dialog HTML code for the ENUM/SET dialog
3461          */
3462         var dialog = "<div id='enum_editor'>" +
3463                    "<fieldset>" +
3464                     "<legend>" + title + "</legend>" +
3465                     "<p>" + PMA_getImage('s_notice.png') +
3466                     PMA_messages.enum_hint + "</p>" +
3467                     "<table class='values'>" + fields + "</table>" +
3468                     "</fieldset><fieldset class='tblFooters'>" +
3469                     "<table class='add'><tr><td>" +
3470                     "<div class='slider'></div>" +
3471                     "</td><td>" +
3472                     "<form><div><input type='submit' class='add_value' value='" +
3473                     PMA_sprintf(PMA_messages.enum_addValue, 1) +
3474                     "'/></div></form>" +
3475                     "</td></tr></table>" +
3476                     "<input type='hidden' value='" + // So we know which column's data is being edited
3477                     $(this).closest('td').find("input").attr("id") +
3478                     "' />" +
3479                     "</fieldset>" +
3480                     "</div>";
3481         /**
3482          * @var  Defines functions to be called when the buttons in
3483          * the buttonOptions jQuery dialog bar are pressed
3484          */
3485         var buttonOptions = {};
3486         buttonOptions[PMA_messages.strGo] = function () {
3487             // When the submit button is clicked,
3488             // put the data back into the original form
3489             var value_array = [];
3490             $(this).find(".values input").each(function (index, elm) {
3491                 var val = elm.value.replace(/\\/g, '\\\\').replace(/'/g, "''");
3492                 value_array.push("'" + val + "'");
3493             });
3494             // get the Length/Values text field where this value belongs
3495             var values_id = $(this).find("input[type='hidden']").val();
3496             $("input#" + values_id).val(value_array.join(","));
3497             $(this).dialog("close");
3498         };
3499         buttonOptions[PMA_messages.strClose] = function () {
3500             $(this).dialog("close");
3501         };
3502         // Show the dialog
3503         var width = parseInt(
3504             (parseInt($('html').css('font-size'), 10) / 13) * 340,
3505             10
3506         );
3507         if (! width) {
3508             width = 340;
3509         }
3510         $enum_editor_dialog = $(dialog).dialog({
3511             minWidth: width,
3512             maxHeight: 450,
3513             modal: true,
3514             title: PMA_messages.enum_editor,
3515             buttons: buttonOptions,
3516             open: function () {
3517                 // Focus the "Go" button after opening the dialog
3518                 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus();
3519             },
3520             close: function () {
3521                 $(this).remove();
3522             }
3523         });
3524         // slider for choosing how many fields to add
3525         $enum_editor_dialog.find(".slider").slider({
3526             animate: true,
3527             range: "min",
3528             value: 1,
3529             min: 1,
3530             max: 9,
3531             slide: function (event, ui) {
3532                 $(this).closest('table').find('input[type=submit]').val(
3533                     PMA_sprintf(PMA_messages.enum_addValue, ui.value)
3534                 );
3535             }
3536         });
3537         // Focus the slider, otherwise it looks nearly transparent
3538         $('a.ui-slider-handle').addClass('ui-state-focus');
3539         return false;
3540     });
3542     $(document).on('click', 'a.central_columns_dialog', function (e) {
3543         var href = "db_central_columns.php";
3544         var db = PMA_commonParams.get('db');
3545         var table = PMA_commonParams.get('table');
3546         var maxRows = $(this).data('maxrows');
3547         var pick = $(this).data('pick');
3548         if (pick !== false) {
3549             pick = true;
3550         }
3551         var params = {
3552             'ajax_request' : true,
3553             'token' : PMA_commonParams.get('token'),
3554             'server' : PMA_commonParams.get('server'),
3555             'db' : PMA_commonParams.get('db'),
3556             'cur_table' : PMA_commonParams.get('table'),
3557             'getColumnList':true
3558         };
3559         var colid = $(this).closest('td').find("input").attr("id");
3560         var fields = '';
3561         if (! (db + '_' + table in central_column_list)) {
3562             central_column_list.push(db + '_' + table);
3563             $.ajax({
3564                 type: 'POST',
3565                 url: href,
3566                 data: params,
3567                 success: function (data) {
3568                     central_column_list[db + '_' + table] = JSON.parse(data.message);
3569                 },
3570                 async:false
3571             });
3572         }
3573         var i = 0;
3574         var list_size = central_column_list[db + '_' + table].length;
3575         var min = (list_size <= maxRows) ? list_size : maxRows;
3576         for (i = 0; i < min; i++) {
3578             fields += '<tr><td><div><span style="font-weight:bold">' +
3579                 escapeHtml(central_column_list[db + '_' + table][i].col_name) +
3580                 '</span><br><span style="color:gray">' + central_column_list[db + '_' + table][i].col_type;
3582             if (central_column_list[db + '_' + table][i].col_attribute !== '') {
3583                 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_attribute) + ') ';
3584             }
3585             if (central_column_list[db + '_' + table][i].col_length !== '') {
3586                 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_length) +') ';
3587             }
3588             fields += escapeHtml(central_column_list[db + '_' + table][i].col_extra) + '</span>' +
3589                 '</div></td>';
3590             if (pick) {
3591                 fields += '<td><input class="pick" style="width:100%" type="submit" value="' +
3592                     PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>';
3593             }
3594             fields += '</tr>';
3595         }
3596         var result_pointer = i;
3597         var search_in = '<input type="text" class="filter_rows" placeholder="' + PMA_messages.searchList + '">';
3598         if (fields === '') {
3599             fields = PMA_sprintf(PMA_messages.strEmptyCentralList, "'" + escapeHtml(db) + "'");
3600             search_in = '';
3601         }
3602         var seeMore = '';
3603         if (list_size > maxRows) {
3604             seeMore = "<fieldset class='tblFooters' style='text-align:center;font-weight:bold'>" +
3605                 "<a href='#' id='seeMore'>" + PMA_messages.seeMore + "</a></fieldset>";
3606         }
3607         var central_columns_dialog = "<div style='max-height:400px'>" +
3608             "<fieldset>" +
3609             search_in +
3610             "<table id='col_list' style='width:100%' class='values'>" + fields + "</table>" +
3611             "</fieldset>" +
3612             seeMore +
3613             "</div>";
3615         var width = parseInt(
3616             (parseInt($('html').css('font-size'), 10) / 13) * 500,
3617             10
3618         );
3619         if (! width) {
3620             width = 500;
3621         }
3622         var buttonOptions = {};
3623         var $central_columns_dialog = $(central_columns_dialog).dialog({
3624             minWidth: width,
3625             maxHeight: 450,
3626             modal: true,
3627             title: PMA_messages.pickColumnTitle,
3628             buttons: buttonOptions,
3629             open: function () {
3630                 $('#col_list').on("click", ".pick", function (){
3631                     $central_columns_dialog.remove();
3632                 });
3633                 $(".filter_rows").on("keyup", function () {
3634                     $.uiTableFilter($("#col_list"), $(this).val());
3635                 });
3636                 $("#seeMore").click(function() {
3637                     fields = '';
3638                     min = (list_size <= maxRows + result_pointer) ? list_size : maxRows + result_pointer;
3639                     for (i = result_pointer; i < min; i++) {
3641                         fields += '<tr><td><div><span style="font-weight:bold">' +
3642                             central_column_list[db + '_' + table][i].col_name +
3643                             '</span><br><span style="color:gray">' +
3644                             central_column_list[db + '_' + table][i].col_type;
3646                         if (central_column_list[db + '_' + table][i].col_attribute !== '') {
3647                             fields += '(' + central_column_list[db + '_' + table][i].col_attribute + ') ';
3648                         }
3649                         if (central_column_list[db + '_' + table][i].col_length !== '') {
3650                             fields += '(' + central_column_list[db + '_' + table][i].col_length + ') ';
3651                         }
3652                         fields += central_column_list[db + '_' + table][i].col_extra + '</span>' +
3653                             '</div></td>';
3654                         if (pick) {
3655                             fields += '<td><input class="pick" style="width:100%" type="submit" value="' +
3656                                 PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>';
3657                         }
3658                         fields += '</tr>';
3659                     }
3660                     $("#col_list").append(fields);
3661                     result_pointer = i;
3662                     if (result_pointer === list_size) {
3663                         $('.tblFooters').hide();
3664                     }
3665                     return false;
3666                 });
3667                 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus();
3668             },
3669             close: function () {
3670                 $('#col_list').off("click", ".pick");
3671                 $(".filter_rows").off("keyup");
3672                 $(this).remove();
3673             }
3674         });
3675         return false;
3676     });
3678    // $(document).on('click', 'a.show_central_list',function(e) {
3680    // });
3681     // When "add a new value" is clicked, append an empty text field
3682     $(document).on('click', "input.add_value", function (e) {
3683         e.preventDefault();
3684         var num_new_rows = $enum_editor_dialog.find("div.slider").slider('value');
3685         while (num_new_rows--) {
3686             $enum_editor_dialog.find('.values')
3687                 .append(
3688                     "<tr style='display: none;'><td>" +
3689                     "<input type='text' />" +
3690                     "</td><td class='drop'>" +
3691                     PMA_getImage('b_drop.png') +
3692                     "</td></tr>"
3693                 )
3694                 .find('tr:last')
3695                 .show('fast');
3696         }
3697     });
3699     // Removes the specified row from the enum editor
3700     $(document).on('click', "#enum_editor td.drop", function () {
3701         $(this).closest('tr').hide('fast', function () {
3702             $(this).remove();
3703         });
3704     });
3708  * Ensures indexes names are valid according to their type and, for a primary
3709  * key, lock index name to 'PRIMARY'
3710  * @param string   form_id  Variable which parses the form name as
3711  *                            the input
3712  * @return boolean  false    if there is no index form, true else
3713  */
3714 function checkIndexName(form_id)
3716     if ($("#" + form_id).length === 0) {
3717         return false;
3718     }
3720     // Gets the elements pointers
3721     var $the_idx_name = $("#input_index_name");
3722     var $the_idx_choice = $("#select_index_choice");
3724     // Index is a primary key
3725     if ($the_idx_choice.find("option:selected").val() == 'PRIMARY') {
3726         $the_idx_name.val('PRIMARY');
3727         $the_idx_name.prop("disabled", true);
3728     }
3730     // Other cases
3731     else {
3732         if ($the_idx_name.val() == 'PRIMARY') {
3733             $the_idx_name.val("");
3734         }
3735         $the_idx_name.prop("disabled", false);
3736     }
3738     return true;
3739 } // end of the 'checkIndexName()' function
3741 AJAX.registerTeardown('functions.js', function () {
3742     $(document).off('click', '#index_frm input[type=submit]');
3744 AJAX.registerOnload('functions.js', function () {
3745     /**
3746      * Handler for adding more columns to an index in the editor
3747      */
3748     $(document).on('click', '#index_frm input[type=submit]', function (event) {
3749         event.preventDefault();
3750         var rows_to_add = $(this)
3751             .closest('fieldset')
3752             .find('.slider')
3753             .slider('value');
3755         var tempEmptyVal = function () {
3756             $(this).val('');
3757         };
3759         var tempSetFocus = function () {
3760             if ($(this).find("option:selected").val() === '') {
3761                 return true;
3762             }
3763             $(this).closest("tr").find("input").focus();
3764         };
3766         while (rows_to_add--) {
3767             var $indexColumns = $('#index_columns');
3768             var $newrow = $indexColumns
3769                 .find('tbody > tr:first')
3770                 .clone()
3771                 .appendTo(
3772                     $indexColumns.find('tbody')
3773                 );
3774             $newrow.find(':input').each(tempEmptyVal);
3775             // focus index size input on column picked
3776             $newrow.find('select').change(tempSetFocus);
3777         }
3778     });
3781 function indexEditorDialog(url, title, callback_success, callback_failure)
3783     /*Remove the hidden dialogs if there are*/
3784     var $editIndexDialog = $('#edit_index_dialog');
3785     if ($editIndexDialog.length !== 0) {
3786         $editIndexDialog.remove();
3787     }
3788     var $div = $('<div id="edit_index_dialog"></div>');
3790     /**
3791      * @var button_options Object that stores the options
3792      *                     passed to jQueryUI dialog
3793      */
3794     var button_options = {};
3795     button_options[PMA_messages.strGo] = function () {
3796         /**
3797          * @var    the_form    object referring to the export form
3798          */
3799         var $form = $("#index_frm");
3800         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3801         PMA_prepareForAjaxRequest($form);
3802         //User wants to submit the form
3803         $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
3804             var $sqlqueryresults = $(".sqlqueryresults");
3805             if ($sqlqueryresults.length !== 0) {
3806                 $sqlqueryresults.remove();
3807             }
3808             if (typeof data !== 'undefined' && data.success === true) {
3809                 PMA_ajaxShowMessage(data.message);
3810                 var $resultQuery = $('.result_query');
3811                 if ($resultQuery.length) {
3812                     $resultQuery.remove();
3813                 }
3814                 if (data.sql_query) {
3815                     $('<div class="result_query"></div>')
3816                         .html(data.sql_query)
3817                         .prependTo('#page_content');
3818                     PMA_highlightSQL($('#page_content'));
3819                 }
3820                 $(".result_query .notice").remove();
3821                 $resultQuery.prepend(data.message);
3822                 /*Reload the field form*/
3823                 $("#table_index").remove();
3824                 $("<div id='temp_div'><div>")
3825                     .append(data.index_table)
3826                     .find("#table_index")
3827                     .insertAfter("#index_header");
3828                 var $editIndexDialog = $("#edit_index_dialog");
3829                 if ($editIndexDialog.length > 0) {
3830                     $editIndexDialog.dialog("close");
3831                 }
3832                 $('div.no_indexes_defined').hide();
3833                 if (callback_success) {
3834                     callback_success();
3835                 }
3836                 PMA_reloadNavigation();
3837             } else {
3838                 var $temp_div = $("<div id='temp_div'><div>").append(data.error);
3839                 var $error;
3840                 if ($temp_div.find(".error code").length !== 0) {
3841                     $error = $temp_div.find(".error code").addClass("error");
3842                 } else {
3843                     $error = $temp_div;
3844                 }
3845                 if (callback_failure) {
3846                     callback_failure();
3847                 }
3848                 PMA_ajaxShowMessage($error, false);
3849             }
3850         }); // end $.post()
3851     };
3852     button_options[PMA_messages.strPreviewSQL] = function () {
3853         // Function for Previewing SQL
3854         var $form = $('#index_frm');
3855         PMA_previewSQL($form);
3856     };
3857     button_options[PMA_messages.strCancel] = function () {
3858         $(this).dialog('close');
3859     };
3860     var $msgbox = PMA_ajaxShowMessage();
3861     $.get("tbl_indexes.php", url, function (data) {
3862         if (typeof data !== 'undefined' && data.success === false) {
3863             //in the case of an error, show the error message returned.
3864             PMA_ajaxShowMessage(data.error, false);
3865         } else {
3866             PMA_ajaxRemoveMessage($msgbox);
3867             // Show dialog if the request was successful
3868             $div
3869             .append(data.message)
3870             .dialog({
3871                 title: title,
3872                 width: 'auto',
3873                 open: PMA_verifyColumnsProperties,
3874                 modal: true,
3875                 buttons: button_options,
3876                 close: function () {
3877                     $(this).remove();
3878                 }
3879             });
3880             $div.find('.tblFooters').remove();
3881             showIndexEditDialog($div);
3882         }
3883     }); // end $.get()
3886 function showIndexEditDialog($outer)
3888     checkIndexType();
3889     checkIndexName("index_frm");
3890     var $indexColumns = $('#index_columns');
3891     $indexColumns.find('td').each(function () {
3892         $(this).css("width", $(this).width() + 'px');
3893     });
3894     $indexColumns.find('tbody').sortable({
3895         axis: 'y',
3896         containment: $indexColumns.find("tbody"),
3897         tolerance: 'pointer'
3898     });
3899     PMA_showHints($outer);
3900     PMA_init_slider();
3901     // Add a slider for selecting how many columns to add to the index
3902     $outer.find('.slider').slider({
3903         animate: true,
3904         value: 1,
3905         min: 1,
3906         max: 16,
3907         slide: function (event, ui) {
3908             $(this).closest('fieldset').find('input[type=submit]').val(
3909                 PMA_sprintf(PMA_messages.strAddToIndex, ui.value)
3910             );
3911         }
3912     });
3913     $('div.add_fields').removeClass('hide');
3914     // focus index size input on column picked
3915     $outer.find('table#index_columns select').change(function () {
3916         if ($(this).find("option:selected").val() === '') {
3917             return true;
3918         }
3919         $(this).closest("tr").find("input").focus();
3920     });
3921     // Focus the slider, otherwise it looks nearly transparent
3922     $('a.ui-slider-handle').addClass('ui-state-focus');
3923     // set focus on index name input, if empty
3924     var input = $outer.find('input#input_index_name');
3925     if (! input.val()) {
3926         input.focus();
3927     }
3931  * Function to display tooltips that were
3932  * generated on the PHP side by PMA\libraries\Util::showHint()
3934  * @param object $div a div jquery object which specifies the
3935  *                    domain for searching for tooltips. If we
3936  *                    omit this parameter the function searches
3937  *                    in the whole body
3938  **/
3939 function PMA_showHints($div)
3941     if ($div === undefined || ! $div instanceof jQuery || $div.length === 0) {
3942         $div = $("body");
3943     }
3944     $div.find('.pma_hint').each(function () {
3945         PMA_tooltip(
3946             $(this).children('img'),
3947             'img',
3948             $(this).children('span').html()
3949         );
3950     });
3953 AJAX.registerOnload('functions.js', function () {
3954     PMA_showHints();
3957 function PMA_mainMenuResizerCallback() {
3958     // 5 px margin for jumping menu in Chrome
3959     return $(document.body).width() - 5;
3961 // This must be fired only once after the initial page load
3962 $(function () {
3963     // Initialise the menu resize plugin
3964     $('#topmenu').menuResizer(PMA_mainMenuResizerCallback);
3965     // register resize event
3966     $(window).resize(function () {
3967         $('#topmenu').menuResizer('resize');
3968     });
3972  * Get the row number from the classlist (for example, row_1)
3973  */
3974 function PMA_getRowNumber(classlist)
3976     return parseInt(classlist.split(/\s+row_/)[1], 10);
3980  * Changes status of slider
3981  */
3982 function PMA_set_status_label($element)
3984     var text;
3985     if ($element.css('display') == 'none') {
3986         text = '+ ';
3987     } else {
3988         text = '- ';
3989     }
3990     $element.closest('.slide-wrapper').prev().find('span').text(text);
3994  * var  toggleButton  This is a function that creates a toggle
3995  *                    sliding button given a jQuery reference
3996  *                    to the correct DOM element
3997  */
3998 var toggleButton = function ($obj) {
3999     // In rtl mode the toggle switch is flipped horizontally
4000     // so we need to take that into account
4001     var right;
4002     if ($('span.text_direction', $obj).text() == 'ltr') {
4003         right = 'right';
4004     } else {
4005         right = 'left';
4006     }
4007     /**
4008      *  var  h  Height of the button, used to scale the
4009      *          background image and position the layers
4010      */
4011     var h = $obj.height();
4012     $('img', $obj).height(h);
4013     $('table', $obj).css('bottom', h - 1);
4014     /**
4015      *  var  on   Width of the "ON" part of the toggle switch
4016      *  var  off  Width of the "OFF" part of the toggle switch
4017      */
4018     var on  = $('td.toggleOn', $obj).width();
4019     var off = $('td.toggleOff', $obj).width();
4020     // Make the "ON" and "OFF" parts of the switch the same size
4021     // + 2 pixels to avoid overflowed
4022     $('td.toggleOn > div', $obj).width(Math.max(on, off) + 2);
4023     $('td.toggleOff > div', $obj).width(Math.max(on, off) + 2);
4024     /**
4025      *  var  w  Width of the central part of the switch
4026      */
4027     var w = parseInt(($('img', $obj).height() / 16) * 22, 10);
4028     // Resize the central part of the switch on the top
4029     // layer to match the background
4030     $('table td:nth-child(2) > div', $obj).width(w);
4031     /**
4032      *  var  imgw    Width of the background image
4033      *  var  tblw    Width of the foreground layer
4034      *  var  offset  By how many pixels to move the background
4035      *               image, so that it matches the top layer
4036      */
4037     var imgw = $('img', $obj).width();
4038     var tblw = $('table', $obj).width();
4039     var offset = parseInt(((imgw - tblw) / 2), 10);
4040     // Move the background to match the layout of the top layer
4041     $obj.find('img').css(right, offset);
4042     /**
4043      *  var  offw    Outer width of the "ON" part of the toggle switch
4044      *  var  btnw    Outer width of the central part of the switch
4045      */
4046     var offw = $('td.toggleOff', $obj).outerWidth();
4047     var btnw = $('table td:nth-child(2)', $obj).outerWidth();
4048     // Resize the main div so that exactly one side of
4049     // the switch plus the central part fit into it.
4050     $obj.width(offw + btnw + 2);
4051     /**
4052      *  var  move  How many pixels to move the
4053      *             switch by when toggling
4054      */
4055     var move = $('td.toggleOff', $obj).outerWidth();
4056     // If the switch is initialized to the
4057     // OFF state we need to move it now.
4058     if ($('div.container', $obj).hasClass('off')) {
4059         if (right == 'right') {
4060             $('div.container', $obj).animate({'left': '-=' + move + 'px'}, 0);
4061         } else {
4062             $('div.container', $obj).animate({'left': '+=' + move + 'px'}, 0);
4063         }
4064     }
4065     // Attach an 'onclick' event to the switch
4066     $('div.container', $obj).click(function () {
4067         if ($(this).hasClass('isActive')) {
4068             return false;
4069         } else {
4070             $(this).addClass('isActive');
4071         }
4072         var $msg = PMA_ajaxShowMessage();
4073         var $container = $(this);
4074         var callback = $('span.callback', this).text();
4075         var operator, url, removeClass, addClass;
4076         // Perform the actual toggle
4077         if ($(this).hasClass('on')) {
4078             if (right == 'right') {
4079                 operator = '-=';
4080             } else {
4081                 operator = '+=';
4082             }
4083             url = $(this).find('td.toggleOff > span').text();
4084             removeClass = 'on';
4085             addClass = 'off';
4086         } else {
4087             if (right == 'right') {
4088                 operator = '+=';
4089             } else {
4090                 operator = '-=';
4091             }
4092             url = $(this).find('td.toggleOn > span').text();
4093             removeClass = 'off';
4094             addClass = 'on';
4095         }
4097         var params = {'ajax_request': true, 'token': PMA_commonParams.get('token')};
4098         $.post(url, params, function (data) {
4099             if (typeof data !== 'undefined' && data.success === true) {
4100                 PMA_ajaxRemoveMessage($msg);
4101                 $container
4102                 .removeClass(removeClass)
4103                 .addClass(addClass)
4104                 .animate({'left': operator + move + 'px'}, function () {
4105                     $container.removeClass('isActive');
4106                 });
4107                 eval(callback);
4108             } else {
4109                 PMA_ajaxShowMessage(data.error, false);
4110                 $container.removeClass('isActive');
4111             }
4112         });
4113     });
4117  * Unbind all event handlers before tearing down a page
4118  */
4119 AJAX.registerTeardown('functions.js', function () {
4120     $('div.container').unbind('click');
4123  * Initialise all toggle buttons
4124  */
4125 AJAX.registerOnload('functions.js', function () {
4126     $('div.toggleAjax').each(function () {
4127         var $button = $(this).show();
4128         $button.find('img').each(function () {
4129             if (this.complete) {
4130                 toggleButton($button);
4131             } else {
4132                 $(this).load(function () {
4133                     toggleButton($button);
4134                 });
4135             }
4136         });
4137     });
4141  * Unbind all event handlers before tearing down a page
4142  */
4143 AJAX.registerTeardown('functions.js', function () {
4144     $(document).off('change', 'select.pageselector');
4145     $(document).off('click', 'a.formLinkSubmit');
4146     $('#update_recent_tables').unbind('ready');
4147     $('#sync_favorite_tables').unbind('ready');
4150 AJAX.registerOnload('functions.js', function () {
4152     /**
4153      * Autosubmit page selector
4154      */
4155     $(document).on('change', 'select.pageselector', function (event) {
4156         event.stopPropagation();
4157         // Check where to load the new content
4158         if ($(this).closest("#pma_navigation").length === 0) {
4159             // For the main page we don't need to do anything,
4160             $(this).closest("form").submit();
4161         } else {
4162             // but for the navigation we need to manually replace the content
4163             PMA_navigationTreePagination($(this));
4164         }
4165     });
4167     /**
4168      * Load version information asynchronously.
4169      */
4170     if ($('li.jsversioncheck').length > 0) {
4171         $.ajax({
4172             dataType: "json",
4173             url: 'version_check.php',
4174             method: "POST",
4175             data: {
4176                 "server": PMA_commonParams.get('server'),
4177                 "token": PMA_commonParams.get('token'),
4178             },
4179             success: PMA_current_version
4180         });
4181     }
4183     if ($('#is_git_revision').length > 0) {
4184         setTimeout(PMA_display_git_revision, 10);
4185     }
4187     /**
4188      * Slider effect.
4189      */
4190     PMA_init_slider();
4192     /**
4193      * Enables the text generated by PMA\libraries\Util::linkOrButton() to be clickable
4194      */
4195     $(document).on('click', 'a.formLinkSubmit', function (e) {
4196         if (! $(this).hasClass('requireConfirm')) {
4197             submitFormLink($(this));
4198             return false;
4199         }
4200     });
4202     var $updateRecentTables = $('#update_recent_tables');
4203     if ($updateRecentTables.length) {
4204         $.get(
4205             $updateRecentTables.attr('href'),
4206             {no_debug: true},
4207             function (data) {
4208                 if (typeof data !== 'undefined' && data.success === true) {
4209                     $('#pma_recent_list').html(data.list);
4210                 }
4211             }
4212         );
4213     }
4215     // Sync favorite tables from localStorage to pmadb.
4216     if ($('#sync_favorite_tables').length) {
4217         $.ajax({
4218             url: $('#sync_favorite_tables').attr("href"),
4219             cache: false,
4220             type: 'POST',
4221             data: {
4222                 favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined')
4223                     ? window.localStorage.favorite_tables
4224                     : '',
4225                 token: PMA_commonParams.get('token'),
4226                 server: PMA_commonParams.get('server'),
4227                 no_debug: true
4228             },
4229             success: function (data) {
4230                 // Update localStorage.
4231                 if (isStorageSupported('localStorage')) {
4232                     window.localStorage.favorite_tables = data.favorite_tables;
4233                 }
4234                 $('#pma_favorite_list').html(data.list);
4235             }
4236         });
4237     }
4238 }); // end of $()
4241  * Submits the form placed in place of a link due to the excessive url length
4243  * @param $link anchor
4244  * @returns {Boolean}
4245  */
4246 function submitFormLink($link)
4248     if ($link.attr('href').indexOf('=') != -1) {
4249         var data = $link.attr('href').substr($link.attr('href').indexOf('#') + 1).split('=', 2);
4250         $link.parents('form').append('<input type="hidden" name="' + data[0] + '" value="' + data[1] + '"/>');
4251     }
4252     $link.parents('form').submit();
4256  * Initializes slider effect.
4257  */
4258 function PMA_init_slider()
4260     $('div.pma_auto_slider').each(function () {
4261         var $this = $(this);
4262         if ($this.data('slider_init_done')) {
4263             return;
4264         }
4265         var $wrapper = $('<div>', {'class': 'slide-wrapper'});
4266         $wrapper.toggle($this.is(':visible'));
4267         $('<a>', {href: '#' + this.id, "class": 'ajax'})
4268             .text($this.attr('title'))
4269             .prepend($('<span>'))
4270             .insertBefore($this)
4271             .click(function () {
4272                 var $wrapper = $this.closest('.slide-wrapper');
4273                 var visible = $this.is(':visible');
4274                 if (!visible) {
4275                     $wrapper.show();
4276                 }
4277                 $this[visible ? 'hide' : 'show']('blind', function () {
4278                     $wrapper.toggle(!visible);
4279                     $wrapper.parent().toggleClass("print_ignore", visible);
4280                     PMA_set_status_label($this);
4281                 });
4282                 return false;
4283             });
4284         $this.wrap($wrapper);
4285         $this.removeAttr('title');
4286         PMA_set_status_label($this);
4287         $this.data('slider_init_done', 1);
4288     });
4292  * Initializes slider effect.
4293  */
4294 AJAX.registerOnload('functions.js', function () {
4295     PMA_init_slider();
4299  * Restores sliders to the state they were in before initialisation.
4300  */
4301 AJAX.registerTeardown('functions.js', function () {
4302     $('div.pma_auto_slider').each(function () {
4303         var $this = $(this);
4304         $this.removeData();
4305         $this.parent().replaceWith($this);
4306         $this.parent().children('a').remove();
4307     });
4311  * Creates a message inside an object with a sliding effect
4313  * @param msg    A string containing the text to display
4314  * @param $obj   a jQuery object containing the reference
4315  *                 to the element where to put the message
4316  *                 This is optional, if no element is
4317  *                 provided, one will be created below the
4318  *                 navigation links at the top of the page
4320  * @return bool   True on success, false on failure
4321  */
4322 function PMA_slidingMessage(msg, $obj)
4324     if (msg === undefined || msg.length === 0) {
4325         // Don't show an empty message
4326         return false;
4327     }
4328     if ($obj === undefined || ! $obj instanceof jQuery || $obj.length === 0) {
4329         // If the second argument was not supplied,
4330         // we might have to create a new DOM node.
4331         if ($('#PMA_slidingMessage').length === 0) {
4332             $('#page_content').prepend(
4333                 '<span id="PMA_slidingMessage" ' +
4334                 'style="display: inline-block;"></span>'
4335             );
4336         }
4337         $obj = $('#PMA_slidingMessage');
4338     }
4339     if ($obj.has('div').length > 0) {
4340         // If there already is a message inside the
4341         // target object, we must get rid of it
4342         $obj
4343         .find('div')
4344         .first()
4345         .fadeOut(function () {
4346             $obj
4347             .children()
4348             .remove();
4349             $obj
4350             .append('<div>' + msg + '</div>');
4351             // highlight any sql before taking height;
4352             PMA_highlightSQL($obj);
4353             $obj.find('div')
4354                 .first()
4355                 .hide();
4356             $obj
4357             .animate({
4358                 height: $obj.find('div').first().height()
4359             })
4360             .find('div')
4361             .first()
4362             .fadeIn();
4363         });
4364     } else {
4365         // Object does not already have a message
4366         // inside it, so we simply slide it down
4367         $obj.width('100%')
4368             .html('<div>' + msg + '</div>');
4369         // highlight any sql before taking height;
4370         PMA_highlightSQL($obj);
4371         var h = $obj
4372             .find('div')
4373             .first()
4374             .hide()
4375             .height();
4376         $obj
4377         .find('div')
4378         .first()
4379         .css('height', 0)
4380         .show()
4381         .animate({
4382                 height: h
4383             }, function () {
4384             // Set the height of the parent
4385             // to the height of the child
4386                 $obj
4387                 .height(
4388                     $obj
4389                     .find('div')
4390                     .first()
4391                     .height()
4392                 );
4393             });
4394     }
4395     return true;
4396 } // end PMA_slidingMessage()
4399  * Attach CodeMirror2 editor to SQL edit area.
4400  */
4401 AJAX.registerOnload('functions.js', function () {
4402     var $elm = $('#sqlquery');
4403     if ($elm.length > 0) {
4404         if (typeof CodeMirror != 'undefined') {
4405             codemirror_editor = PMA_getSQLEditor($elm);
4406             codemirror_editor.focus();
4407             codemirror_editor.on("blur", updateQueryParameters);
4408         } else {
4409             // without codemirror
4410             $elm.focus()
4411                 .bind('blur', updateQueryParameters);
4412         }
4413     }
4414     PMA_highlightSQL($('body'));
4416 AJAX.registerTeardown('functions.js', function () {
4417     if (codemirror_editor) {
4418         $('#sqlquery').text(codemirror_editor.getValue());
4419         codemirror_editor.toTextArea();
4420         codemirror_editor = false;
4421     }
4423 AJAX.registerOnload('functions.js', function () {
4424     // initializes all lock-page elements lock-id and
4425     // val-hash data property
4426     $('#page_content form.lock-page textarea, ' +
4427             '#page_content form.lock-page input[type="text"], '+
4428             '#page_content form.lock-page input[type="number"], '+
4429             '#page_content form.lock-page select').each(function (i) {
4430         $(this).data('lock-id', i);
4431         // val-hash is the hash of default value of the field
4432         // so that it can be compared with new value hash
4433         // to check whether field was modified or not.
4434         $(this).data('val-hash', AJAX.hash($(this).val()));
4435     });
4437     // initializes lock-page elements (input types checkbox and radio buttons)
4438     // lock-id and val-hash data property
4439     $('#page_content form.lock-page input[type="checkbox"], ' +
4440             '#page_content form.lock-page input[type="radio"]').each(function (i) {
4441         $(this).data('lock-id', i);
4442         $(this).data('val-hash', AJAX.hash($(this).is(":checked")));
4443     });
4447  * jQuery plugin to correctly filter input fields by value, needed
4448  * because some nasty values may break selector syntax
4449  */
4450 (function ($) {
4451     $.fn.filterByValue = function (value) {
4452         return this.filter(function () {
4453             return $(this).val() === value;
4454         });
4455     };
4456 })(jQuery);
4459  * Return value of a cell in a table.
4460  */
4461 function PMA_getCellValue(td) {
4462     var $td = $(td);
4463     if ($td.is('.null')) {
4464         return '';
4465     } else if ((! $td.is('.to_be_saved')
4466         || $td.is('.set'))
4467         && $td.data('original_data')
4468     ) {
4469         return $td.data('original_data');
4470     } else {
4471         return $td.text();
4472     }
4475 $(window).on('popstate', function (event, data) {
4476     $('#printcss').attr('media','print');
4477     return true;
4481  * Unbind all event handlers before tearing down a page
4482  */
4483 AJAX.registerTeardown('functions.js', function () {
4484     $(document).off('click', 'a.themeselect');
4485     $(document).off('change', '.autosubmit');
4486     $('a.take_theme').unbind('click');
4489 AJAX.registerOnload('functions.js', function () {
4490     /**
4491      * Theme selector.
4492      */
4493     $(document).on('click', 'a.themeselect', function (e) {
4494         window.open(
4495             e.target,
4496             'themes',
4497             'left=10,top=20,width=510,height=350,scrollbars=yes,status=yes,resizable=yes'
4498             );
4499         return false;
4500     });
4502     /**
4503      * Automatic form submission on change.
4504      */
4505     $(document).on('change', '.autosubmit', function (e) {
4506         $(this).closest('form').submit();
4507     });
4509     /**
4510      * Theme changer.
4511      */
4512     $('a.take_theme').click(function (e) {
4513         var what = this.name;
4514         if (window.opener && window.opener.document.forms.setTheme.elements.set_theme) {
4515             window.opener.document.forms.setTheme.elements.set_theme.value = what;
4516             window.opener.document.forms.setTheme.submit();
4517             window.close();
4518             return false;
4519         }
4520         return true;
4521     });
4525  * Produce print preview
4526  */
4527 function printPreview()
4529     $('#printcss').attr('media','all');
4530     createPrintAndBackButtons();
4534  * Create print and back buttons in preview page
4535  */
4536 function createPrintAndBackButtons() {
4538     var back_button = $("<input/>",{
4539         type: 'button',
4540         value: PMA_messages.back,
4541         id: 'back_button_print_view'
4542     });
4543     back_button.click(removePrintAndBackButton);
4544     back_button.appendTo('#page_content');
4545     var print_button = $("<input/>",{
4546         type: 'button',
4547         value: PMA_messages.print,
4548         id: 'print_button_print_view'
4549     });
4550     print_button.click(printPage);
4551     print_button.appendTo('#page_content');
4555  * Remove print and back buttons and revert to normal view
4556  */
4557 function removePrintAndBackButton(){
4558     $('#printcss').attr('media','print');
4559     $('#back_button_print_view').remove();
4560     $('#print_button_print_view').remove();
4564  * Print page
4565  */
4566 function printPage(){
4567     if (typeof(window.print) != 'undefined') {
4568         window.print();
4569     }
4573  * Unbind all event handlers before tearing down a page
4574  */
4575 AJAX.registerTeardown('functions.js', function () {
4576     $('input#print').unbind('click');
4577     $(document).off('click', 'a.create_view.ajax');
4578     $(document).off('keydown', '#createViewDialog input, #createViewDialog select');
4579     $(document).off('change', '#fkc_checkbox');
4582 AJAX.registerOnload('functions.js', function () {
4583     $('input#print').click(printPage);
4584     $('.logout').click(function() {
4585         var form = $(
4586             '<form method="POST" action="' + $(this).attr('href') + '" class="disableAjax">' +
4587             '<input type="hidden" name="token" value="' + PMA_commonParams.get('token') + '"/>' +
4588             '</form>'
4589         );
4590         $('body').append(form);
4591         form.submit();
4592         return false;
4593     });
4594     /**
4595      * Ajaxification for the "Create View" action
4596      */
4597     $(document).on('click', 'a.create_view.ajax', function (e) {
4598         e.preventDefault();
4599         PMA_createViewDialog($(this));
4600     });
4601     /**
4602      * Attach Ajax event handlers for input fields in the editor
4603      * and used to submit the Ajax request when the ENTER key is pressed.
4604      */
4605     if ($('#createViewDialog').length !== 0) {
4606         $(document).on('keydown', '#createViewDialog input, #createViewDialog select', function (e) {
4607             if (e.which === 13) { // 13 is the ENTER key
4608                 e.preventDefault();
4610                 // with preventing default, selection by <select> tag
4611                 // was also prevented in IE
4612                 $(this).blur();
4614                 $(this).closest('.ui-dialog').find('.ui-button:first').click();
4615             }
4616         }); // end $(document).on()
4617     }
4619     syntaxHighlighter = PMA_getSQLEditor($('textarea[name="view[as]"]'));
4623 function PMA_createViewDialog($this)
4625     var $msg = PMA_ajaxShowMessage();
4626     var syntaxHighlighter = null;
4627     $.get($this.attr('href') + '&ajax_request=1&ajax_dialog=1', function (data) {
4628         if (typeof data !== 'undefined' && data.success === true) {
4629             PMA_ajaxRemoveMessage($msg);
4630             var buttonOptions = {};
4631             buttonOptions[PMA_messages.strGo] = function () {
4632                 if (typeof CodeMirror !== 'undefined') {
4633                     syntaxHighlighter.save();
4634                 }
4635                 $msg = PMA_ajaxShowMessage();
4636                 $.post('view_create.php', $('#createViewDialog').find('form').serialize(), function (data) {
4637                     PMA_ajaxRemoveMessage($msg);
4638                     if (typeof data !== 'undefined' && data.success === true) {
4639                         $('#createViewDialog').dialog("close");
4640                         $('.result_query').html(data.message);
4641                         PMA_reloadNavigation();
4642                     } else {
4643                         PMA_ajaxShowMessage(data.error, false);
4644                     }
4645                 });
4646             };
4647             buttonOptions[PMA_messages.strClose] = function () {
4648                 $(this).dialog("close");
4649             };
4650             var $dialog = $('<div/>').attr('id', 'createViewDialog').append(data.message).dialog({
4651                 width: 600,
4652                 minWidth: 400,
4653                 modal: true,
4654                 buttons: buttonOptions,
4655                 title: PMA_messages.strCreateView,
4656                 close: function () {
4657                     $(this).remove();
4658                 }
4659             });
4660             // Attach syntax highlighted editor
4661             syntaxHighlighter = PMA_getSQLEditor($dialog.find('textarea'));
4662             $('input:visible[type=text]', $dialog).first().focus();
4663         } else {
4664             PMA_ajaxShowMessage(data.error);
4665         }
4666     });
4670  * Makes the breadcrumbs and the menu bar float at the top of the viewport
4671  */
4672 $(function () {
4673     if ($("#floating_menubar").length && $('#PMA_disable_floating_menubar').length === 0) {
4674         var left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
4675         $("#floating_menubar")
4676             .css('margin-' + left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width())
4677             .css(left, 0)
4678             .css({
4679                 'position': 'fixed',
4680                 'top': 0,
4681                 'width': '100%',
4682                 'z-index': 99
4683             })
4684             .append($('#serverinfo'))
4685             .append($('#topmenucontainer'));
4686         // Allow the DOM to render, then adjust the padding on the body
4687         setTimeout(function () {
4688             $('body').css(
4689                 'padding-top',
4690                 $('#floating_menubar').outerHeight(true)
4691             );
4692             $('#topmenu').menuResizer('resize');
4693         }, 4);
4694     }
4698  * Scrolls the page to the top if clicking the serverinfo bar
4699  */
4700 $(function () {
4701     $(document).delegate("#serverinfo, #goto_pagetop", "click", function (event) {
4702         event.preventDefault();
4703         $('html, body').animate({scrollTop: 0}, 'fast');
4704     });
4707 var checkboxes_sel = "input.checkall:checkbox:enabled";
4709  * Watches checkboxes in a form to set the checkall box accordingly
4710  */
4711 var checkboxes_changed = function () {
4712     var $form = $(this.form);
4713     // total number of checkboxes in current form
4714     var total_boxes = $form.find(checkboxes_sel).length;
4715     // number of checkboxes checked in current form
4716     var checked_boxes = $form.find(checkboxes_sel + ":checked").length;
4717     var $checkall = $form.find("input.checkall_box");
4718     if (total_boxes == checked_boxes) {
4719         $checkall.prop({checked: true, indeterminate: false});
4720     }
4721     else if (checked_boxes > 0) {
4722         $checkall.prop({checked: true, indeterminate: true});
4723     }
4724     else {
4725         $checkall.prop({checked: false, indeterminate: false});
4726     }
4728 $(document).on("change", checkboxes_sel, checkboxes_changed);
4730 $(document).on("change", "input.checkall_box", function () {
4731     var is_checked = $(this).is(":checked");
4732     $(this.form).find(checkboxes_sel).prop("checked", is_checked)
4733     .parents("tr").toggleClass("marked", is_checked);
4737  * Watches checkboxes in a sub form to set the sub checkall box accordingly
4738  */
4739 var sub_checkboxes_changed = function () {
4740     var $form = $(this).parent().parent();
4741     // total number of checkboxes in current sub form
4742     var total_boxes = $form.find(checkboxes_sel).length;
4743     // number of checkboxes checked in current sub form
4744     var checked_boxes = $form.find(checkboxes_sel + ":checked").length;
4745     var $checkall = $form.find("input.sub_checkall_box");
4746     if (total_boxes == checked_boxes) {
4747         $checkall.prop({checked: true, indeterminate: false});
4748     }
4749     else if (checked_boxes > 0) {
4750         $checkall.prop({checked: true, indeterminate: true});
4751     }
4752     else {
4753         $checkall.prop({checked: false, indeterminate: false});
4754     }
4756 $(document).on("change", checkboxes_sel + ", input.checkall_box:checkbox:enabled", sub_checkboxes_changed);
4758 $(document).on("change", "input.sub_checkall_box", function () {
4759     var is_checked = $(this).is(":checked");
4760     var $form = $(this).parent().parent();
4761     $form.find(checkboxes_sel).prop("checked", is_checked)
4762     .parents("tr").toggleClass("marked", is_checked);
4766  * Formats a byte number to human-readable form
4768  * @param bytes the bytes to format
4769  * @param optional subdecimals the number of digits after the point
4770  * @param optional pointchar the char to use as decimal point
4771  */
4772 function formatBytes(bytes, subdecimals, pointchar) {
4773     if (!subdecimals) {
4774         subdecimals = 0;
4775     }
4776     if (!pointchar) {
4777         pointchar = '.';
4778     }
4779     var units = ['B', 'KiB', 'MiB', 'GiB'];
4780     for (var i = 0; bytes > 1024 && i < units.length; i++) {
4781         bytes /= 1024;
4782     }
4783     var factor = Math.pow(10, subdecimals);
4784     bytes = Math.round(bytes * factor) / factor;
4785     bytes = bytes.toString().split('.').join(pointchar);
4786     return bytes + ' ' + units[i];
4789 AJAX.registerOnload('functions.js', function () {
4790     /**
4791      * Reveal the login form to users with JS enabled
4792      * and focus the appropriate input field
4793      */
4794     var $loginform = $('#loginform');
4795     if ($loginform.length) {
4796         $loginform.find('.js-show').show();
4797         if ($('#input_username').val()) {
4798             $('#input_password').focus();
4799         } else {
4800             $('#input_username').focus();
4801         }
4802     }
4806  * Dynamically adjust the width of the boxes
4807  * on the table and db operations pages
4808  */
4809 (function () {
4810     function DynamicBoxes() {
4811         var $boxContainer = $('#boxContainer');
4812         if ($boxContainer.length) {
4813             var minWidth = $boxContainer.data('box-width');
4814             var viewport = $(window).width() - $('#pma_navigation').width();
4815             var slots = Math.floor(viewport / minWidth);
4816             $boxContainer.children()
4817             .each(function () {
4818                 if (viewport < minWidth) {
4819                     $(this).width(minWidth);
4820                 } else {
4821                     $(this).css('width', ((1 /  slots) * 100) + "%");
4822                 }
4823             })
4824             .removeClass('clearfloat')
4825             .filter(':nth-child(' + slots + 'n+1)')
4826             .addClass('clearfloat');
4827         }
4828     }
4829     AJAX.registerOnload('functions.js', function () {
4830         DynamicBoxes();
4831     });
4832     $(function () {
4833         $(window).resize(DynamicBoxes);
4834     });
4835 })();
4838  * Formats timestamp for display
4839  */
4840 function PMA_formatDateTime(date, seconds) {
4841     var result = $.datepicker.formatDate('yy-mm-dd', date);
4842     var timefmt = 'HH:mm';
4843     if (seconds) {
4844         timefmt = 'HH:mm:ss';
4845     }
4846     return result + ' ' + $.datepicker.formatTime(
4847         timefmt, {
4848             hour: date.getHours(),
4849             minute: date.getMinutes(),
4850             second: date.getSeconds()
4851         }
4852     );
4856  * Check than forms have less fields than max allowed by PHP.
4857  */
4858 function checkNumberOfFields() {
4859     if (typeof maxInputVars === 'undefined') {
4860         return false;
4861     }
4862     if (false === maxInputVars) {
4863         return false;
4864     }
4865     $('form').each(function() {
4866         var nbInputs = $(this).find(':input').length;
4867         if (nbInputs > maxInputVars) {
4868             var warning = PMA_sprintf(PMA_messages.strTooManyInputs, maxInputVars);
4869             PMA_ajaxShowMessage(warning);
4870             return false;
4871         }
4872         return true;
4873     });
4875     return true;
4879  * Ignore the displayed php errors.
4880  * Simply removes the displayed errors.
4882  * @param  clearPrevErrors whether to clear errors stored
4883  *             in $_SESSION['prev_errors'] at server
4885  */
4886 function PMA_ignorePhpErrors(clearPrevErrors){
4887     if (typeof(clearPrevErrors) === "undefined" ||
4888         clearPrevErrors === null
4889     ) {
4890         str = false;
4891     }
4892     // send AJAX request to error_report.php with send_error_report=0, exception_type=php & token.
4893     // It clears the prev_errors stored in session.
4894     if(clearPrevErrors){
4895         var $pmaReportErrorsForm = $('#pma_report_errors_form');
4896         $pmaReportErrorsForm.find('input[name="send_error_report"]').val(0); // change send_error_report to '0'
4897         $pmaReportErrorsForm.submit();
4898     }
4900     // remove displayed errors
4901     var $pmaErrors = $('#pma_errors');
4902     $pmaErrors.fadeOut( "slow");
4903     $pmaErrors.remove();
4907  * Toggle the Datetimepicker UI if the date value entered
4908  * by the user in the 'text box' is not going to be accepted
4909  * by the Datetimepicker plugin (but is accepted by MySQL)
4910  */
4911 function toggleDatepickerIfInvalid($td, $input_field) {
4912     // Regex allowed by the Datetimepicker UI
4913     var dtexpDate = new RegExp(['^([0-9]{4})',
4914         '-(((01|03|05|07|08|10|12)-((0[1-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)',
4915         '-((0[1-9])|([1-2][0-9])|30)))$'].join(''));
4916     var dtexpTime = new RegExp(['^(([0-1][0-9])|(2[0-3]))',
4917         ':((0[0-9])|([1-5][0-9]))',
4918         ':((0[0-9])|([1-5][0-9]))(\.[0-9]{1,6}){0,1}$'].join(''));
4920     // If key-ed in Time or Date values are unsupported by the UI, close it
4921     if ($td.attr('data-type') === 'date' && ! dtexpDate.test($input_field.val())) {
4922         $input_field.datepicker('hide');
4923     } else if ($td.attr('data-type') === 'time' && ! dtexpTime.test($input_field.val())) {
4924         $input_field.datepicker('hide');
4925     } else {
4926         $input_field.datepicker('show');
4927     }
4931  * Unbind all event handlers before tearing down a page
4932  */
4933 AJAX.registerTeardown('functions.js', function(){
4934     $(document).off('keydown', 'form input, form textarea, form select');
4937 AJAX.registerOnload('functions.js', function () {
4938     /**
4939      * Handle 'Ctrl/Alt + Enter' form submits
4940      */
4941     $('form input, form textarea, form select').on('keydown', function(e){
4942         if((e.ctrlKey && e.which == 13) || (e.altKey && e.which == 13)) {
4943             $form = $(this).closest('form');
4944             if (! $form.find('input[type="submit"]') ||
4945                 ! $form.find('input[type="submit"]').click()
4946             ) {
4947                 $form.submit();
4948             }
4949         }
4950     });
4954  * Unbind all event handlers before tearing down a page
4955  */
4956 AJAX.registerTeardown('functions.js', function(){
4957     $(document).off('change', 'input[type=radio][name="pw_hash"]');
4960 AJAX.registerOnload('functions.js', function(){
4961     /*
4962      * Display warning regarding SSL when sha256_password
4963      * method is selected
4964      * Used in user_password.php (Change Password link on index.php)
4965      */
4966     $(document).on("change", 'select#select_authentication_plugin_cp', function() {
4967         if (this.value === 'sha256_password') {
4968             $('#ssl_reqd_warning_cp').show();
4969         } else {
4970             $('#ssl_reqd_warning_cp').hide();
4971         }
4972     });