updated adodb package to work with php 7.1
[openemr.git] / phpmyadmin / js / functions.js
blob1cf52831abb0d8688aebb621abd1fc45b5becc6d
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 chart_activeTimeouts object active timeouts that refresh the charts. When disabling a realtime chart, this can be used to stop the continuous ajax requests
49  */
50 var chart_activeTimeouts = {};
52 /**
53  * @var central_column_list array to hold the columns in central list per db.
54  */
55 var central_column_list = [];
57 /**
58  * @var primary_indexes array to hold 'Primary' index columns.
59  */
60 var primary_indexes = [];
62 /**
63  * @var unique_indexes array to hold 'Unique' index columns.
64  */
65 var unique_indexes = [];
67 /**
68  * @var indexes array to hold 'Index' columns.
69  */
70 var indexes = [];
72 /**
73  * @var fulltext_indexes array to hold 'Fulltext' columns.
74  */
75 var fulltext_indexes = [];
77 /**
78  * @var spatial_indexes array to hold 'Spatial' columns.
79  */
80 var spatial_indexes = [];
82 /**
83  * Make sure that ajax requests will not be cached
84  * by appending a random variable to their parameters
85  */
86 $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
87     var nocache = new Date().getTime() + "" + Math.floor(Math.random() * 1000000);
88     if (typeof options.data == "string") {
89         options.data += "&_nocache=" + nocache;
90     } else if (typeof options.data == "object") {
91         options.data = $.extend(originalOptions.data, {'_nocache' : nocache});
92     }
93 });
95 /**
96  * Hanle redirect and reload flags send as part of AJAX requests
97  *
98  * @param data ajax response data
99  */
100 function PMA_handleRedirectAndReload(data) {
101     if (parseInt(data.redirect_flag) == 1) {
102         // add one more GET param to display session expiry msg
103         if (window.location.href.indexOf('?') === -1) {
104             window.location.href += '?session_expired=1';
105         } else {
106             window.location.href += '&session_expired=1';
107         }
108         window.location.reload();
109     } else if (parseInt(data.reload_flag) == 1) {
110         // remove the token param and reload
111         window.location.href = window.location.href.replace(/&?token=[^&#]*/g, "");
112         window.location.reload();
113     }
117  * Creates an SQL editor which supports auto completing etc.
119  * @param $textarea jQuery object wrapping the textarea to be made the editor
120  * @param options   optional options for CodeMirror
121  * @param resize    optional resizing ('vertical', 'horizontal', 'both')
122  */
123 function PMA_getSQLEditor($textarea, options, resize) {
124     if ($textarea.length > 0 && typeof CodeMirror !== 'undefined') {
126         // merge options for CodeMirror
127         var defaults = {
128             lineNumbers: true,
129             matchBrackets: true,
130             extraKeys: {"Ctrl-Space": "autocomplete"},
131             hintOptions: {"completeSingle": false, "completeOnSingleClick": true},
132             indentUnit: 4,
133             mode: "text/x-mysql",
134             lineWrapping: true
135         };
137         if (CodeMirror.sqlLint) {
138             $.extend(defaults, {
139                 gutters: ["CodeMirror-lint-markers"],
140                 lint: {
141                     "getAnnotations": CodeMirror.sqlLint,
142                     "async": true,
143                 }
144             });
145         }
147         $.extend(true, defaults, options);
149         // create CodeMirror editor
150         var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults);
151         // allow resizing
152         if (! resize) {
153             resize = 'vertical';
154         }
155         var handles = '';
156         if (resize == 'vertical') {
157             handles = 'n, s';
158         }
159         if (resize == 'both') {
160             handles = 'all';
161         }
162         if (resize == 'horizontal') {
163             handles = 'e, w';
164         }
165         $(codemirrorEditor.getWrapperElement())
166             .css('resize', resize)
167             .resizable({
168                 handles: handles,
169                 resize: function() {
170                     codemirrorEditor.setSize($(this).width(), $(this).height());
171                 }
172             });
173         // enable autocomplete
174         codemirrorEditor.on("inputRead", codemirrorAutocompleteOnInputRead);
176         return codemirrorEditor;
177     }
178     return null;
182  * Clear text selection
183  */
184 function PMA_clearSelection() {
185     if (document.selection && document.selection.empty) {
186         document.selection.empty();
187     } else if (window.getSelection) {
188         var sel = window.getSelection();
189         if (sel.empty) {
190             sel.empty();
191         }
192         if (sel.removeAllRanges) {
193             sel.removeAllRanges();
194         }
195     }
199  * Create a jQuery UI tooltip
201  * @param $elements     jQuery object representing the elements
202  * @param item          the item
203  *                      (see http://api.jqueryui.com/tooltip/#option-items)
204  * @param myContent     content of the tooltip
205  * @param additionalOptions to override the default options
207  */
208 function PMA_tooltip($elements, item, myContent, additionalOptions)
210     if ($('#no_hint').length > 0) {
211         return;
212     }
214     var defaultOptions = {
215         content: myContent,
216         items:  item,
217         tooltipClass: "tooltip",
218         track: true,
219         show: false,
220         hide: false
221     };
223     $elements.tooltip($.extend(true, defaultOptions, additionalOptions));
227  * HTML escaping
228  */
230 function escapeHtml(unsafe) {
231     if (typeof(unsafe) != 'undefined') {
232         return unsafe
233             .toString()
234             .replace(/&/g, "&")
235             .replace(/</g, "&lt;")
236             .replace(/>/g, "&gt;")
237             .replace(/"/g, "&quot;")
238             .replace(/'/g, "&#039;");
239     } else {
240         return false;
241     }
244 function PMA_sprintf() {
245     return sprintf.apply(this, arguments);
249  * Hides/shows the default value input field, depending on the default type
250  * Ticks the NULL checkbox if NULL is chosen as default value.
251  */
252 function PMA_hideShowDefaultValue($default_type)
254     if ($default_type.val() == 'USER_DEFINED') {
255         $default_type.siblings('.default_value').show().focus();
256     } else {
257         $default_type.siblings('.default_value').hide();
258         if ($default_type.val() == 'NULL') {
259             var $null_checkbox = $default_type.closest('tr').find('.allow_null');
260             $null_checkbox.prop('checked', true);
261         }
262     }
266  * Hides/shows the input field for column expression based on whether
267  * VIRTUAL/PERSISTENT is selected
269  * @param $virtuality virtuality dropdown
270  */
271 function PMA_hideShowExpression($virtuality)
273     if ($virtuality.val() == '') {
274         $virtuality.siblings('.expression').hide();
275     } else {
276         $virtuality.siblings('.expression').show();
277     }
281  * Show notices for ENUM columns; add/hide the default value
283  */
284 function PMA_verifyColumnsProperties()
286     $("select.column_type").each(function () {
287         PMA_showNoticeForEnum($(this));
288     });
289     $("select.default_type").each(function () {
290         PMA_hideShowDefaultValue($(this));
291     });
292     $('select.virtuality').each(function () {
293         PMA_hideShowExpression($(this));
294     });
298  * Add a hidden field to the form to indicate that this will be an
299  * Ajax request (only if this hidden field does not exist)
301  * @param $form object   the form
302  */
303 function PMA_prepareForAjaxRequest($form)
305     if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
306         $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
307     }
311  * Generate a new password and copy it to the password input areas
313  * @param passwd_form object   the form that holds the password fields
315  * @return boolean  always true
316  */
317 function suggestPassword(passwd_form)
319     // restrict the password to just letters and numbers to avoid problems:
320     // "editors and viewers regard the password as multiple words and
321     // things like double click no longer work"
322     var pwchars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZ";
323     var passwordlength = 16;    // do we want that to be dynamic?  no, keep it simple :)
324     var passwd = passwd_form.generated_pw;
325     var randomWords = new Int32Array(passwordlength);
327     passwd.value = '';
329     // First we're going to try to use a built-in CSPRNG
330     if (window.crypto && window.crypto.getRandomValues) {
331         window.crypto.getRandomValues(randomWords);
332     }
333     // Because of course IE calls it msCrypto instead of being standard
334     else if (window.msCrypto && window.msCrypto.getRandomValues) {
335         window.msCrypto.getRandomValues(randomWords);
336     } else {
337         // Fallback to Math.random
338         for (var i = 0; i < passwordlength; i++) {
339             randomWords[i] = Math.floor(Math.random() * pwchars.length);
340         }
341     }
343     for (var i = 0; i < passwordlength; i++) {
344         passwd.value += pwchars.charAt(Math.abs(randomWords[i]) % pwchars.length);
345     }
347     passwd_form.text_pma_pw.value = passwd.value;
348     passwd_form.text_pma_pw2.value = passwd.value;
349     return true;
353  * Version string to integer conversion.
354  */
355 function parseVersionString(str)
357     if (typeof(str) != 'string') { return false; }
358     var add = 0;
359     // Parse possible alpha/beta/rc/
360     var state = str.split('-');
361     if (state.length >= 2) {
362         if (state[1].substr(0, 2) == 'rc') {
363             add = - 20 - parseInt(state[1].substr(2), 10);
364         } else if (state[1].substr(0, 4) == 'beta') {
365             add =  - 40 - parseInt(state[1].substr(4), 10);
366         } else if (state[1].substr(0, 5) == 'alpha') {
367             add =  - 60 - parseInt(state[1].substr(5), 10);
368         } else if (state[1].substr(0, 3) == 'dev') {
369             /* We don't handle dev, it's git snapshot */
370             add = 0;
371         }
372     }
373     // Parse version
374     var x = str.split('.');
375     // Use 0 for non existing parts
376     var maj = parseInt(x[0], 10) || 0;
377     var min = parseInt(x[1], 10) || 0;
378     var pat = parseInt(x[2], 10) || 0;
379     var hotfix = parseInt(x[3], 10) || 0;
380     return  maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
384  * Indicates current available version on main page.
385  */
386 function PMA_current_version(data)
388     if (data && data.version && data.date) {
389         var current = parseVersionString($('span.version').text());
390         var latest = parseVersionString(data.version);
391         var version_information_message = '<span class="latest">' +
392             PMA_messages.strLatestAvailable +
393             ' ' + escapeHtml(data.version) +
394             '</span>';
395         if (latest > current) {
396             var message = PMA_sprintf(
397                 PMA_messages.strNewerVersion,
398                 escapeHtml(data.version),
399                 escapeHtml(data.date)
400             );
401             var htmlClass = 'notice';
402             if (Math.floor(latest / 10000) === Math.floor(current / 10000)) {
403                 /* Security update */
404                 htmlClass = 'error';
405             }
406             $('#newer_version_notice').remove();
407             $('#maincontainer').after('<div id="newer_version_notice" class="' + htmlClass + '">' + message + '</div>');
408         }
409         if (latest === current) {
410             version_information_message = ' (' + PMA_messages.strUpToDate + ')';
411         }
412         var $liPmaVersion = $('#li_pma_version');
413         $liPmaVersion.find('span.latest').remove();
414         $liPmaVersion.append(version_information_message);
415     }
419  * Loads Git revision data from ajax for index.php
420  */
421 function PMA_display_git_revision()
423     $('#is_git_revision').remove();
424     $('#li_pma_version_git').remove();
425     $.get(
426         "index.php",
427         {
428             "server": PMA_commonParams.get('server'),
429             "token": PMA_commonParams.get('token'),
430             "git_revision": true,
431             "ajax_request": true,
432             "no_debug": true
433         },
434         function (data) {
435             if (typeof data !== 'undefined' && data.success === true) {
436                 $(data.message).insertAfter('#li_pma_version');
437             }
438         }
439     );
443  * for libraries/display_change_password.lib.php
444  *     libraries/user_password.php
446  */
448 function displayPasswordGenerateButton()
450     $('#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>');
451     $('#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>');
455  * Adds a date/time picker to an element
457  * @param object  $this_element   a jQuery object pointing to the element
458  */
459 function PMA_addDatepicker($this_element, type, options)
461     var showTimepicker = true;
462     if (type=="date") {
463         showTimepicker = false;
464     }
466     var defaultOptions = {
467         showOn: 'button',
468         buttonImage: themeCalendarImage, // defined in js/messages.php
469         buttonImageOnly: true,
470         stepMinutes: 1,
471         stepHours: 1,
472         showSecond: true,
473         showMillisec: true,
474         showMicrosec: true,
475         showTimepicker: showTimepicker,
476         showButtonPanel: false,
477         dateFormat: 'yy-mm-dd', // yy means year with four digits
478         timeFormat: 'HH:mm:ss.lc',
479         constrainInput: false,
480         altFieldTimeOnly: false,
481         showAnim: '',
482         beforeShow: function (input, inst) {
483             // Remember that we came from the datepicker; this is used
484             // in tbl_change.js by verificationsAfterFieldChange()
485             $this_element.data('comes_from', 'datepicker');
486             if ($(input).closest('.cEdit').length > 0) {
487                 setTimeout(function () {
488                     inst.dpDiv.css({
489                         top: 0,
490                         left: 0,
491                         position: 'relative'
492                     });
493                 }, 0);
494             }
495             // Fix wrong timepicker z-index, doesn't work without timeout
496             setTimeout(function () {
497                 $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index'));
498             }, 0);
499         },
500         onSelect: function() {
501             $this_element.data('datepicker').inline = true;
502         },
503         onClose: function (dateText, dp_inst) {
504             // The value is no more from the date picker
505             $this_element.data('comes_from', '');
506             if (typeof $this_element.data('datepicker') !== 'undefined') {
507                 $this_element.data('datepicker').inline = false;
508             }
509         }
510     };
511     if (type == "datetime" || type == "timestamp") {
512         $this_element.datetimepicker($.extend(defaultOptions, options));
513     }
514     else if (type == "date") {
515         $this_element.datetimepicker($.extend(defaultOptions, options));
516     }
517     else if (type == "time") {
518         $this_element.timepicker($.extend(defaultOptions, options));
519     }
523  * selects the content of a given object, f.e. a textarea
525  * @param element     object  element of which the content will be selected
526  * @param lock        var     variable which holds the lock for this element
527  *                              or true, if no lock exists
528  * @param only_once   boolean if true this is only done once
529  *                              f.e. only on first focus
530  */
531 function selectContent(element, lock, only_once)
533     if (only_once && only_once_elements[element.name]) {
534         return;
535     }
537     only_once_elements[element.name] = true;
539     if (lock) {
540         return;
541     }
543     element.select();
547  * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query.
548  * This function is called while clicking links
550  * @param theLink     object the link
551  * @param theSqlQuery object the sql query to submit
553  * @return boolean  whether to run the query or not
554  */
555 function confirmLink(theLink, theSqlQuery)
557     // Confirmation is not required in the configuration file
558     // or browser is Opera (crappy js implementation)
559     if (PMA_messages.strDoYouReally === '' || typeof(window.opera) != 'undefined') {
560         return true;
561     }
563     var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, theSqlQuery));
564     if (is_confirmed) {
565         if ($(theLink).hasClass('formLinkSubmit')) {
566             var name = 'is_js_confirmed';
567             if ($(theLink).attr('href').indexOf('usesubform') != -1) {
568                 name = 'subform[' + $(theLink).attr('href').substr('#').match(/usesubform\[(\d+)\]/i)[1] + '][is_js_confirmed]';
569             }
571             $(theLink).parents('form').append('<input type="hidden" name="' + name + '" value="1" />');
572         } else if (typeof(theLink.href) != 'undefined') {
573             theLink.href += '&is_js_confirmed=1';
574         } else if (typeof(theLink.form) != 'undefined') {
575             theLink.form.action += '?is_js_confirmed=1';
576         }
577     }
579     return is_confirmed;
580 } // end of the 'confirmLink()' function
583  * Displays an error message if a "DROP DATABASE" statement is submitted
584  * while it isn't allowed, else confirms a "DROP/DELETE/ALTER" query before
585  * submitting it if required.
586  * This function is called by the 'checkSqlQuery()' js function.
588  * @param theForm1 object   the form
589  * @param sqlQuery1 object  the sql query textarea
591  * @return boolean  whether to run the query or not
593  * @see     checkSqlQuery()
594  */
595 function confirmQuery(theForm1, sqlQuery1)
597     // Confirmation is not required in the configuration file
598     if (PMA_messages.strDoYouReally === '') {
599         return true;
600     }
602     // "DROP DATABASE" statement isn't allowed
603     if (PMA_messages.strNoDropDatabases !== '') {
604         var drop_re = new RegExp('(^|;)\\s*DROP\\s+(IF EXISTS\\s+)?DATABASE\\s', 'i');
605         if (drop_re.test(sqlQuery1.value)) {
606             alert(PMA_messages.strNoDropDatabases);
607             theForm1.reset();
608             sqlQuery1.focus();
609             return false;
610         } // end if
611     } // end if
613     // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
614     //
615     // TODO: find a way (if possible) to use the parser-analyser
616     // for this kind of verification
617     // For now, I just added a ^ to check for the statement at
618     // beginning of expression
620     var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|DATABASE|PROCEDURE)\\s', 'i');
621     var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
622     var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
623     var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
625     if (do_confirm_re_0.test(sqlQuery1.value) ||
626         do_confirm_re_1.test(sqlQuery1.value) ||
627         do_confirm_re_2.test(sqlQuery1.value) ||
628         do_confirm_re_3.test(sqlQuery1.value)) {
629         var message;
630         if (sqlQuery1.value.length > 100) {
631             message = sqlQuery1.value.substr(0, 100) + '\n    ...';
632         } else {
633             message = sqlQuery1.value;
634         }
635         var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, message));
636         // statement is confirmed -> update the
637         // "is_js_confirmed" form field so the confirm test won't be
638         // run on the server side and allows to submit the form
639         if (is_confirmed) {
640             theForm1.elements.is_js_confirmed.value = 1;
641             return true;
642         }
643         // statement is rejected -> do not submit the form
644         else {
645             window.focus();
646             sqlQuery1.focus();
647             return false;
648         } // end if (handle confirm box result)
649     } // end if (display confirm box)
651     return true;
652 } // end of the 'confirmQuery()' function
655  * Displays an error message if the user submitted the sql query form with no
656  * sql query, else checks for "DROP/DELETE/ALTER" statements
658  * @param theForm object the form
660  * @return boolean  always false
662  * @see     confirmQuery()
663  */
664 function checkSqlQuery(theForm)
666     // get the textarea element containing the query
667     var sqlQuery;
668     if (codemirror_editor) {
669         codemirror_editor.save();
670         sqlQuery = codemirror_editor.getValue();
671     } else {
672         sqlQuery = theForm.elements.sql_query.value;
673     }
674     var isEmpty  = 1;
675     var space_re = new RegExp('\\s+');
676     if (typeof(theForm.elements.sql_file) != 'undefined' &&
677             theForm.elements.sql_file.value.replace(space_re, '') !== '') {
678         return true;
679     }
680     if (isEmpty && typeof(theForm.elements.id_bookmark) != 'undefined' &&
681             (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') &&
682             theForm.elements.id_bookmark.selectedIndex !== 0) {
683         return true;
684     }
685     // Checks for "DROP/DELETE/ALTER" statements
686     if (sqlQuery.replace(space_re, '') !== '') {
687         return confirmQuery(theForm, sqlQuery);
688     }
689     theForm.reset();
690     isEmpty = 1;
692     if (isEmpty) {
693         alert(PMA_messages.strFormEmpty);
694         codemirror_editor.focus();
695         return false;
696     }
698     return true;
699 } // end of the 'checkSqlQuery()' function
702  * Check if a form's element is empty.
703  * An element containing only spaces is also considered empty
705  * @param object   the form
706  * @param string   the name of the form field to put the focus on
708  * @return boolean  whether the form field is empty or not
709  */
710 function emptyCheckTheField(theForm, theFieldName)
712     var theField = theForm.elements[theFieldName];
713     var space_re = new RegExp('\\s+');
714     return theField.value.replace(space_re, '') === '';
715 } // end of the 'emptyCheckTheField()' function
718  * Ensures a value submitted in a form is numeric and is in a range
720  * @param object   the form
721  * @param string   the name of the form field to check
722  * @param integer  the minimum authorized value
723  * @param integer  the maximum authorized value
725  * @return boolean  whether a valid number has been submitted or not
726  */
727 function checkFormElementInRange(theForm, theFieldName, message, min, max)
729     var theField         = theForm.elements[theFieldName];
730     var val              = parseInt(theField.value, 10);
732     if (typeof(min) == 'undefined') {
733         min = 0;
734     }
735     if (typeof(max) == 'undefined') {
736         max = Number.MAX_VALUE;
737     }
739     // It's not a number
740     if (isNaN(val)) {
741         theField.select();
742         alert(PMA_messages.strEnterValidNumber);
743         theField.focus();
744         return false;
745     }
746     // It's a number but it is not between min and max
747     else if (val < min || val > max) {
748         theField.select();
749         alert(PMA_sprintf(message, val));
750         theField.focus();
751         return false;
752     }
753     // It's a valid number
754     else {
755         theField.value = val;
756     }
757     return true;
759 } // end of the 'checkFormElementInRange()' function
762 function checkTableEditForm(theForm, fieldsCnt)
764     // TODO: avoid sending a message if user just wants to add a line
765     // on the form but has not completed at least one field name
767     var atLeastOneField = 0;
768     var i, elm, elm2, elm3, val, id;
770     for (i = 0; i < fieldsCnt; i++) {
771         id = "#field_" + i + "_2";
772         elm = $(id);
773         val = elm.val();
774         if (val == 'VARCHAR' || val == 'CHAR' || val == 'BIT' || val == 'VARBINARY' || val == 'BINARY') {
775             elm2 = $("#field_" + i + "_3");
776             val = parseInt(elm2.val(), 10);
777             elm3 = $("#field_" + i + "_1");
778             if (isNaN(val) && elm3.val() !== "") {
779                 elm2.select();
780                 alert(PMA_messages.strEnterValidLength);
781                 elm2.focus();
782                 return false;
783             }
784         }
786         if (atLeastOneField === 0) {
787             id = "field_" + i + "_1";
788             if (!emptyCheckTheField(theForm, id)) {
789                 atLeastOneField = 1;
790             }
791         }
792     }
793     if (atLeastOneField === 0) {
794         var theField = theForm.elements.field_0_1;
795         alert(PMA_messages.strFormEmpty);
796         theField.focus();
797         return false;
798     }
800     // at least this section is under jQuery
801     var $input = $("input.textfield[name='table']");
802     if ($input.val() === "") {
803         alert(PMA_messages.strFormEmpty);
804         $input.focus();
805         return false;
806     }
808     return true;
809 } // enf of the 'checkTableEditForm()' function
812  * True if last click is to check a row.
813  */
814 var last_click_checked = false;
817  * Zero-based index of last clicked row.
818  * Used to handle the shift + click event in the code above.
819  */
820 var last_clicked_row = -1;
823  * Zero-based index of last shift clicked row.
824  */
825 var last_shift_clicked_row = -1;
827 var _idleSecondsCounter = 0;
828 var IncInterval;
829 var updateTimeout;
830 AJAX.registerTeardown('functions.js', function () {
831     clearTimeout(updateTimeout);
832     clearInterval(IncInterval);
833     $(document).off('mousemove');
836 AJAX.registerOnload('functions.js', function () {
837     document.onclick = function() {
838         _idleSecondsCounter = 0;
839     };
840     $(document).on('mousemove',function() {
841         _idleSecondsCounter = 0;
842     });
843     document.onkeypress = function() {
844         _idleSecondsCounter = 0;
845     };
847     function SetIdleTime() {
848         _idleSecondsCounter++;
849     }
850     function UpdateIdleTime() {
851         var href = 'index.php';
852         var params = {
853                 'ajax_request' : true,
854                 'token' : PMA_commonParams.get('token'),
855                 'server' : PMA_commonParams.get('server'),
856                 'db' : PMA_commonParams.get('db'),
857                 'access_time':_idleSecondsCounter
858             };
859         $.ajax({
860                 type: 'POST',
861                 url: href,
862                 data: params,
863                 success: function (data) {
864                     if (data.success) {
865                         if (PMA_commonParams.get('LoginCookieValidity')-_idleSecondsCounter > 5) {
866                             var interval = (PMA_commonParams.get('LoginCookieValidity') - _idleSecondsCounter - 5) * 1000;
867                             if (interval > Math.pow(2, 31) - 1) { // max value for setInterval() function
868                                 interval = Math.pow(2, 31) - 1;
869                             }
870                             updateTimeout = window.setTimeout(UpdateIdleTime, interval);
871                         } else {
872                             updateTimeout = window.setTimeout(UpdateIdleTime, 2000);
873                         }
874                     } else { //timeout occurred
875                         window.location.reload(true);
876                         clearInterval(IncInterval);
877                     }
878                 }
879             });
880     }
881     if (PMA_commonParams.get('logged_in') && PMA_commonParams.get('auth_type') == 'cookie') {
882         IncInterval = window.setInterval(SetIdleTime, 1000);
883         var interval = (PMA_commonParams.get('LoginCookieValidity') - 5) * 1000;
884         if (interval > Math.pow(2, 31) - 1) { // max value for setInterval() function
885             interval = Math.pow(2, 31) - 1;
886         }
887         updateTimeout = window.setTimeout(UpdateIdleTime, interval);
888     }
891  * Unbind all event handlers before tearing down a page
892  */
893 AJAX.registerTeardown('functions.js', function () {
894     $(document).off('click', 'input:checkbox.checkall');
896 AJAX.registerOnload('functions.js', function () {
897     /**
898      * Row marking in horizontal mode (use "on" so that it works also for
899      * next pages reached via AJAX); a tr may have the class noclick to remove
900      * this behavior.
901      */
903     $(document).on('click', 'input:checkbox.checkall', function (e) {
904         $this = $(this);
905         var $tr = $this.closest('tr');
906         var $table = $this.closest('table');
908         if (!e.shiftKey || last_clicked_row == -1) {
909             // usual click
911             var $checkbox = $tr.find(':checkbox.checkall');
912             var checked = $this.prop('checked');
913             $checkbox.prop('checked', checked).trigger('change');
914             if (checked) {
915                 $tr.addClass('marked');
916             } else {
917                 $tr.removeClass('marked');
918             }
919             last_click_checked = checked;
921             // remember the last clicked row
922             last_clicked_row = last_click_checked ? $table.find('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr) : -1;
923             last_shift_clicked_row = -1;
924         } else {
925             // handle the shift click
926             PMA_clearSelection();
927             var start, end;
929             // clear last shift click result
930             if (last_shift_clicked_row >= 0) {
931                 if (last_shift_clicked_row >= last_clicked_row) {
932                     start = last_clicked_row;
933                     end = last_shift_clicked_row;
934                 } else {
935                     start = last_shift_clicked_row;
936                     end = last_clicked_row;
937                 }
938                 $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
939                     .slice(start, end + 1)
940                     .removeClass('marked')
941                     .find(':checkbox')
942                     .prop('checked', false)
943                     .trigger('change');
944             }
946             // handle new shift click
947             var curr_row = $table.find('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr);
948             if (curr_row >= last_clicked_row) {
949                 start = last_clicked_row;
950                 end = curr_row;
951             } else {
952                 start = curr_row;
953                 end = last_clicked_row;
954             }
955             $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
956                 .slice(start, end + 1)
957                 .addClass('marked')
958                 .find(':checkbox')
959                 .prop('checked', true)
960                 .trigger('change');
962             // remember the last shift clicked row
963             last_shift_clicked_row = curr_row;
964         }
965     });
967     addDateTimePicker();
969     /**
970      * Add attribute to text boxes for iOS devices (based on bugID: 3508912)
971      */
972     if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) {
973         $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off');
974     }
978  * Row highlighting in horizontal mode (use "on"
979  * so that it works also for pages reached via AJAX)
980  */
981 /*AJAX.registerOnload('functions.js', function () {
982     $(document).on('hover', 'tr.odd, tr.even',function (event) {
983         var $tr = $(this);
984         $tr.toggleClass('hover',event.type=='mouseover');
985         $tr.children().toggleClass('hover',event.type=='mouseover');
986     });
987 })*/
990  * This array is used to remember mark status of rows in browse mode
991  */
992 var marked_row = [];
995  * marks all rows and selects its first checkbox inside the given element
996  * the given element is usually a table or a div containing the table or tables
998  * @param container    DOM element
999  */
1000 function markAllRows(container_id)
1003     $("#" + container_id).find("input:checkbox:enabled").prop('checked', true)
1004     .trigger("change")
1005     .parents("tr").addClass("marked");
1006     return true;
1010  * marks all rows and selects its first checkbox inside the given element
1011  * the given element is usually a table or a div containing the table or tables
1013  * @param container    DOM element
1014  */
1015 function unMarkAllRows(container_id)
1018     $("#" + container_id).find("input:checkbox:enabled").prop('checked', false)
1019     .trigger("change")
1020     .parents("tr").removeClass("marked");
1021     return true;
1025  * Checks/unchecks all checkbox in given container (f.e. a form, fieldset or div)
1027  * @param string   container_id  the container id
1028  * @param boolean  state         new value for checkbox (true or false)
1029  * @return boolean  always true
1030  */
1031 function setCheckboxes(container_id, state)
1034     $("#" + container_id).find("input:checkbox").prop('checked', state);
1035     return true;
1036 } // end of the 'setCheckboxes()' function
1039   * Checks/unchecks all options of a <select> element
1040   *
1041   * @param string   the form name
1042   * @param string   the element name
1043   * @param boolean  whether to check or to uncheck options
1044   *
1045   * @return boolean  always true
1046   */
1047 function setSelectOptions(the_form, the_select, do_check)
1049     $("form[name='" + the_form + "'] select[name='" + the_select + "']").find("option").prop('selected', do_check);
1050     return true;
1051 } // end of the 'setSelectOptions()' function
1054  * Sets current value for query box.
1055  */
1056 function setQuery(query)
1058     if (codemirror_editor) {
1059         codemirror_editor.setValue(query);
1060         codemirror_editor.focus();
1061     } else {
1062         document.sqlform.sql_query.value = query;
1063         document.sqlform.sql_query.focus();
1064     }
1068  * Handles 'Simulate query' button on SQL query box.
1070  * @return void
1071  */
1072 function PMA_handleSimulateQueryButton()
1074     var update_re = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i');
1075     var delete_re = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
1076     var query = '';
1078     if (codemirror_editor) {
1079         query = codemirror_editor.getValue();
1080     } else {
1081         query = $('#sqlquery').val();
1082     }
1084     var $simulateDml = $('#simulate_dml');
1085     if (update_re.test(query) || delete_re.test(query)) {
1086         if (! $simulateDml.length) {
1087             $('#button_submit_query')
1088             .before('<input type="button" id="simulate_dml"' +
1089                 'tabindex="199" value="' +
1090                 PMA_messages.strSimulateDML +
1091                 '" />');
1092         }
1093     } else {
1094         if ($simulateDml.length) {
1095             $simulateDml.remove();
1096         }
1097     }
1101   * Create quick sql statements.
1102   *
1103   */
1104 function insertQuery(queryType)
1106     if (queryType == "clear") {
1107         setQuery('');
1108         return;
1109     } else if (queryType == "format") {
1110         if (codemirror_editor) {
1111             $('#querymessage').html(PMA_messages.strFormatting +
1112                 '&nbsp;<img class="ajaxIcon" src="' +
1113                 pmaThemeImage + 'ajax_clock_small.gif" alt="">');
1114             var href = 'db_sql_format.php';
1115             var params = {
1116                 'ajax_request': true,
1117                 'token': PMA_commonParams.get('token'),
1118                 'sql': codemirror_editor.getValue()
1119             };
1120             $.ajax({
1121                 type: 'POST',
1122                 url: href,
1123                 data: params,
1124                 success: function (data) {
1125                     if (data.success) {
1126                         codemirror_editor.setValue(data.sql);
1127                     }
1128                     $('#querymessage').html('');
1129                 }
1130             });
1131         }
1132         return;
1133     } else if (queryType == "saved") {
1134         if ($.cookie('auto_saved_sql')) {
1135             setQuery($.cookie('auto_saved_sql'));
1136         } else {
1137             PMA_ajaxShowMessage(PMA_messages.strNoAutoSavedQuery);
1138         }
1139         return;
1140     }
1142     var query = "";
1143     var myListBox = document.sqlform.dummy;
1144     var table = document.sqlform.table.value;
1146     if (myListBox.options.length > 0) {
1147         sql_box_locked = true;
1148         var columnsList = "";
1149         var valDis = "";
1150         var editDis = "";
1151         var NbSelect = 0;
1152         for (var i = 0; i < myListBox.options.length; i++) {
1153             NbSelect++;
1154             if (NbSelect > 1) {
1155                 columnsList += ", ";
1156                 valDis += ",";
1157                 editDis += ",";
1158             }
1159             columnsList += myListBox.options[i].value;
1160             valDis += "[value-" + NbSelect + "]";
1161             editDis += myListBox.options[i].value + "=[value-" + NbSelect + "]";
1162         }
1163         if (queryType == "selectall") {
1164             query = "SELECT * FROM `" + table + "` WHERE 1";
1165         } else if (queryType == "select") {
1166             query = "SELECT " + columnsList + " FROM `" + table + "` WHERE 1";
1167         } else if (queryType == "insert") {
1168             query = "INSERT INTO `" + table + "`(" + columnsList + ") VALUES (" + valDis + ")";
1169         } else if (queryType == "update") {
1170             query = "UPDATE `" + table + "` SET " + editDis + " WHERE 1";
1171         } else if (queryType == "delete") {
1172             query = "DELETE FROM `" + table + "` WHERE 1";
1173         }
1174         setQuery(query);
1175         sql_box_locked = false;
1176     }
1181   * Inserts multiple fields.
1182   *
1183   */
1184 function insertValueQuery()
1186     var myQuery = document.sqlform.sql_query;
1187     var myListBox = document.sqlform.dummy;
1189     if (myListBox.options.length > 0) {
1190         sql_box_locked = true;
1191         var columnsList = "";
1192         var NbSelect = 0;
1193         for (var i = 0; i < myListBox.options.length; i++) {
1194             if (myListBox.options[i].selected) {
1195                 NbSelect++;
1196                 if (NbSelect > 1) {
1197                     columnsList += ", ";
1198                 }
1199                 columnsList += myListBox.options[i].value;
1200             }
1201         }
1203         /* CodeMirror support */
1204         if (codemirror_editor) {
1205             codemirror_editor.replaceSelection(columnsList);
1206         //IE support
1207         } else if (document.selection) {
1208             myQuery.focus();
1209             var sel = document.selection.createRange();
1210             sel.text = columnsList;
1211             document.sqlform.insert.focus();
1212         }
1213         //MOZILLA/NETSCAPE support
1214         else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart == "0") {
1215             var startPos = document.sqlform.sql_query.selectionStart;
1216             var endPos = document.sqlform.sql_query.selectionEnd;
1217             var SqlString = document.sqlform.sql_query.value;
1219             myQuery.value = SqlString.substring(0, startPos) + columnsList + SqlString.substring(endPos, SqlString.length);
1220         } else {
1221             myQuery.value += columnsList;
1222         }
1223         sql_box_locked = false;
1224     }
1228  * Updates the input fields for the parameters based on the query
1229  */
1230 function updateQueryParameters() {
1232     if ($('#parameterized').is(':checked')) {
1233         var query = codemirror_editor ? codemirror_editor.getValue() : $('#sqlquery').val();
1235         var allParameters = query.match(/:[a-zA-Z0-9_]+/g);
1236          var parameters = [];
1237          // get unique parameters
1238          if (allParameters) {
1239              $.each(allParameters, function(i, parameter){
1240                  if ($.inArray(parameter, parameters) === -1) {
1241                      parameters.push(parameter);
1242                  }
1243              });
1244          }
1246          var $temp = $('<div />');
1247          $temp.append($('#parametersDiv').children());
1248          $('#parametersDiv').empty();
1250          $.each(parameters, function (i, parameter) {
1251              var paramName = parameter.substring(1);
1252              var $param = $temp.find('#paramSpan_' + paramName );
1253              if (! $param.length) {
1254                  $param = $('<span class="parameter" id="paramSpan_' + paramName + '" />');
1255                  $('<label for="param_' + paramName + '" />').text(parameter).appendTo($param);
1256                  $('<input type="text" name="parameters[' + parameter + ']" id="param_' + paramName + '" />').appendTo($param);
1257              }
1258              $('#parametersDiv').append($param);
1259          });
1260     } else {
1261         $('#parametersDiv').empty();
1262     }
1266  * Add a date/time picker to each element that needs it
1267  * (only when jquery-ui-timepicker-addon.js is loaded)
1268  */
1269 function addDateTimePicker() {
1270     if ($.timepicker !== undefined) {
1271         $('input.timefield, input.datefield, input.datetimefield').each(function () {
1273             var decimals = $(this).parent().attr('data-decimals');
1274             var type = $(this).parent().attr('data-type');
1276             var showMillisec = false;
1277             var showMicrosec = false;
1278             var timeFormat = 'HH:mm:ss';
1279             // check for decimal places of seconds
1280             if (decimals > 0 && type.indexOf('time') != -1){
1281                 if (decimals > 3) {
1282                     showMillisec = true;
1283                     showMicrosec = true;
1284                     timeFormat = 'HH:mm:ss.lc';
1285                 } else {
1286                     showMillisec = true;
1287                     timeFormat = 'HH:mm:ss.l';
1288                 }
1289             }
1290             PMA_addDatepicker($(this), type, {
1291                 showMillisec: showMillisec,
1292                 showMicrosec: showMicrosec,
1293                 timeFormat: timeFormat
1294             });
1295         });
1296     }
1300   * Refresh/resize the WYSIWYG scratchboard
1301   */
1302 function refreshLayout()
1304     var $elm = $('#pdflayout');
1305     var orientation = $('#orientation_opt').val();
1306     var paper = 'A4';
1307     var $paperOpt = $('#paper_opt');
1308     if ($paperOpt.length == 1) {
1309         paper = $paperOpt.val();
1310     }
1311     var posa = 'y';
1312     var posb = 'x';
1313     if (orientation == 'P') {
1314         posa = 'x';
1315         posb = 'y';
1316     }
1317     $elm.css('width', pdfPaperSize(paper, posa) + 'px');
1318     $elm.css('height', pdfPaperSize(paper, posb) + 'px');
1322  * Initializes positions of elements.
1323  */
1324 function TableDragInit() {
1325     $('.pdflayout_table').each(function () {
1326         var $this = $(this);
1327         var number = $this.data('number');
1328         var x = $('#c_table_' + number + '_x').val();
1329         var y = $('#c_table_' + number + '_y').val();
1330         $this.css('left', x + 'px');
1331         $this.css('top', y + 'px');
1332         /* Make elements draggable */
1333         $this.draggable({
1334             containment: "parent",
1335             drag: function (evt, ui) {
1336                 var number = $this.data('number');
1337                 $('#c_table_' + number + '_x').val(parseInt(ui.position.left, 10));
1338                 $('#c_table_' + number + '_y').val(parseInt(ui.position.top, 10));
1339             }
1340         });
1341     });
1345  * Resets drag and drop positions.
1346  */
1347 function resetDrag() {
1348     $('.pdflayout_table').each(function () {
1349         var $this = $(this);
1350         var x = $this.data('x');
1351         var y = $this.data('y');
1352         $this.css('left', x + 'px');
1353         $this.css('top', y + 'px');
1354     });
1358  * User schema handlers.
1359  */
1360 $(function () {
1361     /* Move in scratchboard on manual change */
1362     $(document).on('change', '.position-change', function () {
1363         var $this = $(this);
1364         var $elm = $('#table_' + $this.data('number'));
1365         $elm.css($this.data('axis'), $this.val() + 'px');
1366     });
1367     /* Refresh on paper size/orientation change */
1368     $(document).on('change', '.paper-change', function () {
1369         var $elm = $('#pdflayout');
1370         if ($elm.css('visibility') == 'visible') {
1371             refreshLayout();
1372             TableDragInit();
1373         }
1374     });
1375     /* Show/hide the WYSIWYG scratchboard */
1376     $(document).on('click', '#toggle-dragdrop', function () {
1377         var $elm = $('#pdflayout');
1378         if ($elm.css('visibility') == 'hidden') {
1379             refreshLayout();
1380             TableDragInit();
1381             $elm.css('visibility', 'visible');
1382             $elm.css('display', 'block');
1383             $('#showwysiwyg').val('1');
1384         } else {
1385             $elm.css('visibility', 'hidden');
1386             $elm.css('display', 'none');
1387             $('#showwysiwyg').val('0');
1388         }
1389     });
1390     /* Reset scratchboard */
1391     $(document).on('click', '#reset-dragdrop', function () {
1392         resetDrag();
1393     });
1397  * Returns paper sizes for a given format
1398  */
1399 function pdfPaperSize(format, axis)
1401     switch (format.toUpperCase()) {
1402     case '4A0':
1403         if (axis == 'x') {
1404             return 4767.87;
1405         } else {
1406             return 6740.79;
1407         }
1408         break;
1409     case '2A0':
1410         if (axis == 'x') {
1411             return 3370.39;
1412         } else {
1413             return 4767.87;
1414         }
1415         break;
1416     case 'A0':
1417         if (axis == 'x') {
1418             return 2383.94;
1419         } else {
1420             return 3370.39;
1421         }
1422         break;
1423     case 'A1':
1424         if (axis == 'x') {
1425             return 1683.78;
1426         } else {
1427             return 2383.94;
1428         }
1429         break;
1430     case 'A2':
1431         if (axis == 'x') {
1432             return 1190.55;
1433         } else {
1434             return 1683.78;
1435         }
1436         break;
1437     case 'A3':
1438         if (axis == 'x') {
1439             return 841.89;
1440         } else {
1441             return 1190.55;
1442         }
1443         break;
1444     case 'A4':
1445         if (axis == 'x') {
1446             return 595.28;
1447         } else {
1448             return 841.89;
1449         }
1450         break;
1451     case 'A5':
1452         if (axis == 'x') {
1453             return 419.53;
1454         } else {
1455             return 595.28;
1456         }
1457         break;
1458     case 'A6':
1459         if (axis == 'x') {
1460             return 297.64;
1461         } else {
1462             return 419.53;
1463         }
1464         break;
1465     case 'A7':
1466         if (axis == 'x') {
1467             return 209.76;
1468         } else {
1469             return 297.64;
1470         }
1471         break;
1472     case 'A8':
1473         if (axis == 'x') {
1474             return 147.40;
1475         } else {
1476             return 209.76;
1477         }
1478         break;
1479     case 'A9':
1480         if (axis == 'x') {
1481             return 104.88;
1482         } else {
1483             return 147.40;
1484         }
1485         break;
1486     case 'A10':
1487         if (axis == 'x') {
1488             return 73.70;
1489         } else {
1490             return 104.88;
1491         }
1492         break;
1493     case 'B0':
1494         if (axis == 'x') {
1495             return 2834.65;
1496         } else {
1497             return 4008.19;
1498         }
1499         break;
1500     case 'B1':
1501         if (axis == 'x') {
1502             return 2004.09;
1503         } else {
1504             return 2834.65;
1505         }
1506         break;
1507     case 'B2':
1508         if (axis == 'x') {
1509             return 1417.32;
1510         } else {
1511             return 2004.09;
1512         }
1513         break;
1514     case 'B3':
1515         if (axis == 'x') {
1516             return 1000.63;
1517         } else {
1518             return 1417.32;
1519         }
1520         break;
1521     case 'B4':
1522         if (axis == 'x') {
1523             return 708.66;
1524         } else {
1525             return 1000.63;
1526         }
1527         break;
1528     case 'B5':
1529         if (axis == 'x') {
1530             return 498.90;
1531         } else {
1532             return 708.66;
1533         }
1534         break;
1535     case 'B6':
1536         if (axis == 'x') {
1537             return 354.33;
1538         } else {
1539             return 498.90;
1540         }
1541         break;
1542     case 'B7':
1543         if (axis == 'x') {
1544             return 249.45;
1545         } else {
1546             return 354.33;
1547         }
1548         break;
1549     case 'B8':
1550         if (axis == 'x') {
1551             return 175.75;
1552         } else {
1553             return 249.45;
1554         }
1555         break;
1556     case 'B9':
1557         if (axis == 'x') {
1558             return 124.72;
1559         } else {
1560             return 175.75;
1561         }
1562         break;
1563     case 'B10':
1564         if (axis == 'x') {
1565             return 87.87;
1566         } else {
1567             return 124.72;
1568         }
1569         break;
1570     case 'C0':
1571         if (axis == 'x') {
1572             return 2599.37;
1573         } else {
1574             return 3676.54;
1575         }
1576         break;
1577     case 'C1':
1578         if (axis == 'x') {
1579             return 1836.85;
1580         } else {
1581             return 2599.37;
1582         }
1583         break;
1584     case 'C2':
1585         if (axis == 'x') {
1586             return 1298.27;
1587         } else {
1588             return 1836.85;
1589         }
1590         break;
1591     case 'C3':
1592         if (axis == 'x') {
1593             return 918.43;
1594         } else {
1595             return 1298.27;
1596         }
1597         break;
1598     case 'C4':
1599         if (axis == 'x') {
1600             return 649.13;
1601         } else {
1602             return 918.43;
1603         }
1604         break;
1605     case 'C5':
1606         if (axis == 'x') {
1607             return 459.21;
1608         } else {
1609             return 649.13;
1610         }
1611         break;
1612     case 'C6':
1613         if (axis == 'x') {
1614             return 323.15;
1615         } else {
1616             return 459.21;
1617         }
1618         break;
1619     case 'C7':
1620         if (axis == 'x') {
1621             return 229.61;
1622         } else {
1623             return 323.15;
1624         }
1625         break;
1626     case 'C8':
1627         if (axis == 'x') {
1628             return 161.57;
1629         } else {
1630             return 229.61;
1631         }
1632         break;
1633     case 'C9':
1634         if (axis == 'x') {
1635             return 113.39;
1636         } else {
1637             return 161.57;
1638         }
1639         break;
1640     case 'C10':
1641         if (axis == 'x') {
1642             return 79.37;
1643         } else {
1644             return 113.39;
1645         }
1646         break;
1647     case 'RA0':
1648         if (axis == 'x') {
1649             return 2437.80;
1650         } else {
1651             return 3458.27;
1652         }
1653         break;
1654     case 'RA1':
1655         if (axis == 'x') {
1656             return 1729.13;
1657         } else {
1658             return 2437.80;
1659         }
1660         break;
1661     case 'RA2':
1662         if (axis == 'x') {
1663             return 1218.90;
1664         } else {
1665             return 1729.13;
1666         }
1667         break;
1668     case 'RA3':
1669         if (axis == 'x') {
1670             return 864.57;
1671         } else {
1672             return 1218.90;
1673         }
1674         break;
1675     case 'RA4':
1676         if (axis == 'x') {
1677             return 609.45;
1678         } else {
1679             return 864.57;
1680         }
1681         break;
1682     case 'SRA0':
1683         if (axis == 'x') {
1684             return 2551.18;
1685         } else {
1686             return 3628.35;
1687         }
1688         break;
1689     case 'SRA1':
1690         if (axis == 'x') {
1691             return 1814.17;
1692         } else {
1693             return 2551.18;
1694         }
1695         break;
1696     case 'SRA2':
1697         if (axis == 'x') {
1698             return 1275.59;
1699         } else {
1700             return 1814.17;
1701         }
1702         break;
1703     case 'SRA3':
1704         if (axis == 'x') {
1705             return 907.09;
1706         } else {
1707             return 1275.59;
1708         }
1709         break;
1710     case 'SRA4':
1711         if (axis == 'x') {
1712             return 637.80;
1713         } else {
1714             return 907.09;
1715         }
1716         break;
1717     case 'LETTER':
1718         if (axis == 'x') {
1719             return 612.00;
1720         } else {
1721             return 792.00;
1722         }
1723         break;
1724     case 'LEGAL':
1725         if (axis == 'x') {
1726             return 612.00;
1727         } else {
1728             return 1008.00;
1729         }
1730         break;
1731     case 'EXECUTIVE':
1732         if (axis == 'x') {
1733             return 521.86;
1734         } else {
1735             return 756.00;
1736         }
1737         break;
1738     case 'FOLIO':
1739         if (axis == 'x') {
1740             return 612.00;
1741         } else {
1742             return 936.00;
1743         }
1744         break;
1745     } // end switch
1747     return 0;
1751  * Get checkbox for foreign key checks
1753  * @return string
1754  */
1755 function getForeignKeyCheckboxLoader() {
1756     var html = '';
1757     html    += '<div>';
1758     html    += '<div class="load-default-fk-check-value">';
1759     html    += PMA_getImage('ajax_clock_small.gif');
1760     html    += '</div>';
1761     html    += '</div>';
1762     return html;
1765 function loadForeignKeyCheckbox() {
1766     // Load default foreign key check value
1767     var params = {
1768         'ajax_request': true,
1769         'token': PMA_commonParams.get('token'),
1770         'server': PMA_commonParams.get('server'),
1771         'get_default_fk_check_value': true
1772     };
1773     $.get('sql.php', params, function (data) {
1774         var html = '<input type="hidden" name="fk_checks" value="0" />' +
1775             '<input type="checkbox" name="fk_checks" id="fk_checks"' +
1776             (data.default_fk_check_value ? ' checked="checked"' : '') + ' />' +
1777             '<label for="fk_checks">' + PMA_messages.strForeignKeyCheck + '</label>';
1778         $('.load-default-fk-check-value').replaceWith(html);
1779     });
1782 function getJSConfirmCommonParam(elem) {
1783     return {
1784         'is_js_confirmed' : 1,
1785         'ajax_request' : true,
1786         'fk_checks': $(elem).find('#fk_checks').is(':checked') ? 1 : 0
1787     };
1791  * Unbind all event handlers before tearing down a page
1792  */
1793 AJAX.registerTeardown('functions.js', function () {
1794     $(document).off('click', "a.inline_edit_sql");
1795     $(document).off('click', "input#sql_query_edit_save");
1796     $(document).off('click', "input#sql_query_edit_discard");
1797     $('input.sqlbutton').unbind('click');
1798     if (codemirror_editor) {
1799         codemirror_editor.off('blur');
1800     } else {
1801         $(document).off('blur', '#sqlquery');
1802     }
1803     $(document).off('change', '#parameterized');
1804     $('#sqlquery').unbind('keydown');
1805     $('#sql_query_edit').unbind('keydown');
1807     if (codemirror_inline_editor) {
1808         // Copy the sql query to the text area to preserve it.
1809         $('#sql_query_edit').text(codemirror_inline_editor.getValue());
1810         $(codemirror_inline_editor.getWrapperElement()).unbind('keydown');
1811         codemirror_inline_editor.toTextArea();
1812         codemirror_inline_editor = false;
1813     }
1814     if (codemirror_editor) {
1815         $(codemirror_editor.getWrapperElement()).unbind('keydown');
1816     }
1820  * Jquery Coding for inline editing SQL_QUERY
1821  */
1822 AJAX.registerOnload('functions.js', function () {
1823     // If we are coming back to the page by clicking forward button
1824     // of the browser, bind the code mirror to inline query editor.
1825     bindCodeMirrorToInlineEditor();
1826     $(document).on('click', "a.inline_edit_sql", function () {
1827         if ($('#sql_query_edit').length) {
1828             // An inline query editor is already open,
1829             // we don't want another copy of it
1830             return false;
1831         }
1833         var $form = $(this).prev('form');
1834         var sql_query  = $form.find("input[name='sql_query']").val().trim();
1835         var $inner_sql = $(this).parent().prev().find('code.sql');
1836         var old_text   = $inner_sql.html();
1838         var new_content = "<textarea name=\"sql_query_edit\" id=\"sql_query_edit\">" + sql_query + "</textarea>\n";
1839         new_content    += getForeignKeyCheckboxLoader();
1840         new_content    += "<input type=\"submit\" id=\"sql_query_edit_save\" class=\"button btnSave\" value=\"" + PMA_messages.strGo + "\"/>\n";
1841         new_content    += "<input type=\"button\" id=\"sql_query_edit_discard\" class=\"button btnDiscard\" value=\"" + PMA_messages.strCancel + "\"/>\n";
1842         var $editor_area = $('div#inline_editor');
1843         if ($editor_area.length === 0) {
1844             $editor_area = $('<div id="inline_editor_outer"></div>');
1845             $editor_area.insertBefore($inner_sql);
1846         }
1847         $editor_area.html(new_content);
1848         loadForeignKeyCheckbox();
1849         $inner_sql.hide();
1851         bindCodeMirrorToInlineEditor();
1852         return false;
1853     });
1855     $(document).on('click', "input#sql_query_edit_save", function () {
1856         $(".success").hide();
1857         //hide already existing success message
1858         var sql_query;
1859         if (codemirror_inline_editor) {
1860             codemirror_inline_editor.save();
1861             sql_query = codemirror_inline_editor.getValue();
1862         } else {
1863             sql_query = $(this).parent().find('#sql_query_edit').val();
1864         }
1865         var fk_check = $(this).parent().find('#fk_checks').is(':checked');
1867         var $form = $("a.inline_edit_sql").prev('form');
1868         var $fake_form = $('<form>', {action: 'import.php', method: 'post'})
1869                 .append($form.find("input[name=server], input[name=db], input[name=table], input[name=token]").clone())
1870                 .append($('<input/>', {type: 'hidden', name: 'show_query', value: 1}))
1871                 .append($('<input/>', {type: 'hidden', name: 'is_js_confirmed', value: 0}))
1872                 .append($('<input/>', {type: 'hidden', name: 'sql_query', value: sql_query}))
1873                 .append($('<input/>', {type: 'hidden', name: 'fk_checks', value: fk_check ? 1 : 0}));
1874         if (! checkSqlQuery($fake_form[0])) {
1875             return false;
1876         }
1877         $fake_form.appendTo($('body')).submit();
1878     });
1880     $(document).on('click', "input#sql_query_edit_discard", function () {
1881         var $divEditor = $('div#inline_editor_outer');
1882         $divEditor.siblings('code.sql').show();
1883         $divEditor.remove();
1884     });
1886     $('input.sqlbutton').click(function (evt) {
1887         insertQuery(evt.target.id);
1888         PMA_handleSimulateQueryButton();
1889         return false;
1890     });
1892     $(document).on('change', '#parameterized', updateQueryParameters);
1894     var $inputUsername = $('#input_username');
1895     if ($inputUsername) {
1896         if ($inputUsername.val() === '') {
1897             $inputUsername.focus();
1898         } else {
1899             $('#input_password').focus();
1900         }
1901     }
1905  * "inputRead" event handler for CodeMirror SQL query editors for autocompletion
1906  */
1907 function codemirrorAutocompleteOnInputRead(instance) {
1908     if (!sql_autocomplete_in_progress
1909         && (!instance.options.hintOptions.tables || !sql_autocomplete)) {
1911         if (!sql_autocomplete) {
1912             // Reset after teardown
1913             instance.options.hintOptions.tables = false;
1914             instance.options.hintOptions.defaultTable = '';
1916             sql_autocomplete_in_progress = true;
1918             var href = 'db_sql_autocomplete.php';
1919             var params = {
1920                 'ajax_request': true,
1921                 'token': PMA_commonParams.get('token'),
1922                 'server': PMA_commonParams.get('server'),
1923                 'db': PMA_commonParams.get('db'),
1924                 'no_debug': true
1925             };
1927             var columnHintRender = function(elem, self, data) {
1928                 $('<div class="autocomplete-column-name">')
1929                     .text(data.columnName)
1930                     .appendTo(elem);
1931                 $('<div class="autocomplete-column-hint">')
1932                     .text(data.columnHint)
1933                     .appendTo(elem);
1934             };
1936             $.ajax({
1937                 type: 'POST',
1938                 url: href,
1939                 data: params,
1940                 success: function (data) {
1941                     if (data.success) {
1942                         var tables = $.parseJSON(data.tables);
1943                         sql_autocomplete_default_table = PMA_commonParams.get('table');
1944                         sql_autocomplete = [];
1945                         for (var table in tables) {
1946                             if (tables.hasOwnProperty(table)) {
1947                                 var columns = tables[table];
1948                                 table = {
1949                                     text: table,
1950                                     columns: []
1951                                 };
1952                                 for (var column in columns) {
1953                                     if (columns.hasOwnProperty(column)) {
1954                                         var displayText = columns[column].Type;
1955                                         if (columns[column].Key == 'PRI') {
1956                                             displayText += ' | Primary';
1957                                         } else if (columns[column].Key == 'UNI') {
1958                                             displayText += ' | Unique';
1959                                         }
1960                                         table.columns.push({
1961                                             text: column,
1962                                             displayText: column + " | " +  displayText,
1963                                             columnName: column,
1964                                             columnHint: displayText,
1965                                             render: columnHintRender
1966                                         });
1967                                     }
1968                                 }
1969                             }
1970                             sql_autocomplete.push(table);
1971                         }
1972                         instance.options.hintOptions.tables = sql_autocomplete;
1973                         instance.options.hintOptions.defaultTable = sql_autocomplete_default_table;
1974                     }
1975                 },
1976                 complete: function () {
1977                     sql_autocomplete_in_progress = false;
1978                 }
1979             });
1980         }
1981         else {
1982             instance.options.hintOptions.tables = sql_autocomplete;
1983             instance.options.hintOptions.defaultTable = sql_autocomplete_default_table;
1984         }
1985     }
1986     if (instance.state.completionActive) {
1987         return;
1988     }
1989     var cur = instance.getCursor();
1990     var token = instance.getTokenAt(cur);
1991     var string = '';
1992     if (token.string.match(/^[.`\w@]\w*$/)) {
1993         string = token.string;
1994     }
1995     if (string.length > 0) {
1996         CodeMirror.commands.autocomplete(instance);
1997     }
2001  * Remove autocomplete information before tearing down a page
2002  */
2003 AJAX.registerTeardown('functions.js', function () {
2004     sql_autocomplete = false;
2005     sql_autocomplete_default_table = '';
2009  * Binds the CodeMirror to the text area used to inline edit a query.
2010  */
2011 function bindCodeMirrorToInlineEditor() {
2012     var $inline_editor = $('#sql_query_edit');
2013     if ($inline_editor.length > 0) {
2014         if (typeof CodeMirror !== 'undefined') {
2015             var height = $inline_editor.css('height');
2016             codemirror_inline_editor = PMA_getSQLEditor($inline_editor);
2017             codemirror_inline_editor.getWrapperElement().style.height = height;
2018             codemirror_inline_editor.refresh();
2019             codemirror_inline_editor.focus();
2020             $(codemirror_inline_editor.getWrapperElement())
2021                 .bind('keydown', catchKeypressesFromSqlInlineEdit);
2022         } else {
2023             $inline_editor
2024                 .focus()
2025                 .bind('keydown', catchKeypressesFromSqlInlineEdit);
2026         }
2027     }
2030 function catchKeypressesFromSqlInlineEdit(event) {
2031     // ctrl-enter is 10 in chrome and ie, but 13 in ff
2032     if (event.ctrlKey && (event.keyCode == 13 || event.keyCode == 10)) {
2033         $("#sql_query_edit_save").trigger('click');
2034     }
2038  * Adds doc link to single highlighted SQL element
2039  */
2040 function PMA_doc_add($elm, params)
2042     if (typeof mysql_doc_template == 'undefined') {
2043         return;
2044     }
2046     var url = PMA_sprintf(
2047         decodeURIComponent(mysql_doc_template),
2048         params[0]
2049     );
2050     if (params.length > 1) {
2051         url += '#' + params[1];
2052     }
2053     var content = $elm.text();
2054     $elm.text('');
2055     $elm.append('<a target="mysql_doc" class="cm-sql-doc" href="' + url + '">' + content + '</a>');
2059  * Generates doc links for keywords inside highlighted SQL
2060  */
2061 function PMA_doc_keyword(idx, elm)
2063     var $elm = $(elm);
2064     /* Skip already processed ones */
2065     if ($elm.find('a').length > 0) {
2066         return;
2067     }
2068     var keyword = $elm.text().toUpperCase();
2069     var $next = $elm.next('.cm-keyword');
2070     if ($next) {
2071         var next_keyword = $next.text().toUpperCase();
2072         var full = keyword + ' ' + next_keyword;
2074         var $next2 = $next.next('.cm-keyword');
2075         if ($next2) {
2076             var next2_keyword = $next2.text().toUpperCase();
2077             var full2 = full + ' ' + next2_keyword;
2078             if (full2 in mysql_doc_keyword) {
2079                 PMA_doc_add($elm, mysql_doc_keyword[full2]);
2080                 PMA_doc_add($next, mysql_doc_keyword[full2]);
2081                 PMA_doc_add($next2, mysql_doc_keyword[full2]);
2082                 return;
2083             }
2084         }
2085         if (full in mysql_doc_keyword) {
2086             PMA_doc_add($elm, mysql_doc_keyword[full]);
2087             PMA_doc_add($next, mysql_doc_keyword[full]);
2088             return;
2089         }
2090     }
2091     if (keyword in mysql_doc_keyword) {
2092         PMA_doc_add($elm, mysql_doc_keyword[keyword]);
2093     }
2097  * Generates doc links for builtins inside highlighted SQL
2098  */
2099 function PMA_doc_builtin(idx, elm)
2101     var $elm = $(elm);
2102     var builtin = $elm.text().toUpperCase();
2103     if (builtin in mysql_doc_builtin) {
2104         PMA_doc_add($elm, mysql_doc_builtin[builtin]);
2105     }
2109  * Higlights SQL using CodeMirror.
2110  */
2111 function PMA_highlightSQL($base)
2113     var $elm = $base.find('code.sql');
2114     $elm.each(function () {
2115         var $sql = $(this);
2116         var $pre = $sql.find('pre');
2117         /* We only care about visible elements to avoid double processing */
2118         if ($pre.is(":visible")) {
2119             var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
2120             $sql.append($highlight);
2121             if (typeof CodeMirror != 'undefined') {
2122                 CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]);
2123                 $pre.hide();
2124                 $highlight.find('.cm-keyword').each(PMA_doc_keyword);
2125                 $highlight.find('.cm-builtin').each(PMA_doc_builtin);
2126             }
2127         }
2128     });
2132  * Updates an element containing code.
2134  * @param jQuery Object $base base element which contains the raw and the
2135  *                            highlighted code.
2137  * @param string htmlValue    code in HTML format, displayed if code cannot be
2138  *                            highlighted
2140  * @param string rawValue     raw code, used as a parameter for highlighter
2142  * @return bool               whether content was updated or not
2143  */
2144 function PMA_updateCode($base, htmlValue, rawValue)
2146     var $code = $base.find('code');
2147     if ($code.length == 0) {
2148         return false;
2149     }
2151     // Determines the type of the content and appropriate CodeMirror mode.
2152     var type = '', mode = '';
2153     if  ($code.hasClass('json')) {
2154         type = 'json';
2155         mode = 'application/json';
2156     } else if ($code.hasClass('sql')) {
2157         type = 'sql';
2158         mode = 'text/x-mysql';
2159     } else if ($code.hasClass('xml')) {
2160         type = 'xml';
2161         mode = 'application/xml';
2162     } else {
2163         return false;
2164     }
2166     // Element used to display unhighlighted code.
2167     var $notHighlighted = $('<pre>' + htmlValue + '</pre>');
2169     // Tries to highlight code using CodeMirror.
2170     if (typeof CodeMirror != 'undefined') {
2171         var $highlighted = $('<div class="' + type + '-highlight cm-s-default"></div>');
2172         CodeMirror.runMode(rawValue, mode, $highlighted[0]);
2173         $notHighlighted.hide();
2174         $code.html('').append($notHighlighted, $highlighted[0]);
2175     } else {
2176         $code.html('').append($notHighlighted);
2177     }
2179     return true;
2183  * Show a message on the top of the page for an Ajax request
2185  * Sample usage:
2187  * 1) var $msg = PMA_ajaxShowMessage();
2188  * This will show a message that reads "Loading...". Such a message will not
2189  * disappear automatically and cannot be dismissed by the user. To remove this
2190  * message either the PMA_ajaxRemoveMessage($msg) function must be called or
2191  * another message must be show with PMA_ajaxShowMessage() function.
2193  * 2) var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2194  * This is a special case. The behaviour is same as above,
2195  * just with a different message
2197  * 3) var $msg = PMA_ajaxShowMessage('The operation was successful');
2198  * This will show a message that will disappear automatically and it can also
2199  * be dismissed by the user.
2201  * 4) var $msg = PMA_ajaxShowMessage('Some error', false);
2202  * This will show a message that will not disappear automatically, but it
2203  * can be dismissed by the user after he has finished reading it.
2205  * @param string  message     string containing the message to be shown.
2206  *                              optional, defaults to 'Loading...'
2207  * @param mixed   timeout     number of milliseconds for the message to be visible
2208  *                              optional, defaults to 5000. If set to 'false', the
2209  *                              notification will never disappear
2210  * @return jQuery object       jQuery Element that holds the message div
2211  *                              this object can be passed to PMA_ajaxRemoveMessage()
2212  *                              to remove the notification
2213  */
2214 function PMA_ajaxShowMessage(message, timeout)
2216     /**
2217      * @var self_closing Whether the notification will automatically disappear
2218      */
2219     var self_closing = true;
2220     /**
2221      * @var dismissable Whether the user will be able to remove
2222      *                  the notification by clicking on it
2223      */
2224     var dismissable = true;
2225     // Handle the case when a empty data.message is passed.
2226     // We don't want the empty message
2227     if (message === '') {
2228         return true;
2229     } else if (! message) {
2230         // If the message is undefined, show the default
2231         message = PMA_messages.strLoading;
2232         dismissable = false;
2233         self_closing = false;
2234     } else if (message == PMA_messages.strProcessingRequest) {
2235         // This is another case where the message should not disappear
2236         dismissable = false;
2237         self_closing = false;
2238     }
2239     // Figure out whether (or after how long) to remove the notification
2240     if (timeout === undefined) {
2241         timeout = 5000;
2242     } else if (timeout === false) {
2243         self_closing = false;
2244     }
2245     // Create a parent element for the AJAX messages, if necessary
2246     if ($('#loading_parent').length === 0) {
2247         $('<div id="loading_parent"></div>')
2248         .prependTo("#page_content");
2249     }
2250     // Update message count to create distinct message elements every time
2251     ajax_message_count++;
2252     // Remove all old messages, if any
2253     $("span.ajax_notification[id^=ajax_message_num]").remove();
2254     /**
2255      * @var    $retval    a jQuery object containing the reference
2256      *                    to the created AJAX message
2257      */
2258     var $retval = $(
2259             '<span class="ajax_notification" id="ajax_message_num_' +
2260             ajax_message_count +
2261             '"></span>'
2262     )
2263     .hide()
2264     .appendTo("#loading_parent")
2265     .html(message)
2266     .show();
2267     // If the notification is self-closing we should create a callback to remove it
2268     if (self_closing) {
2269         $retval
2270         .delay(timeout)
2271         .fadeOut('medium', function () {
2272             if ($(this).is(':data(tooltip)')) {
2273                 $(this).tooltip('destroy');
2274             }
2275             // Remove the notification
2276             $(this).remove();
2277         });
2278     }
2279     // If the notification is dismissable we need to add the relevant class to it
2280     // and add a tooltip so that the users know that it can be removed
2281     if (dismissable) {
2282         $retval.addClass('dismissable').css('cursor', 'pointer');
2283         /**
2284          * Add a tooltip to the notification to let the user know that (s)he
2285          * can dismiss the ajax notification by clicking on it.
2286          */
2287         PMA_tooltip(
2288             $retval,
2289             'span',
2290             PMA_messages.strDismiss
2291         );
2292     }
2293     PMA_highlightSQL($retval);
2295     return $retval;
2299  * Removes the message shown for an Ajax operation when it's completed
2301  * @param jQuery object   jQuery Element that holds the notification
2303  * @return nothing
2304  */
2305 function PMA_ajaxRemoveMessage($this_msgbox)
2307     if ($this_msgbox !== undefined && $this_msgbox instanceof jQuery) {
2308         $this_msgbox
2309         .stop(true, true)
2310         .fadeOut('medium');
2311         if ($this_msgbox.is(':data(tooltip)')) {
2312             $this_msgbox.tooltip('destroy');
2313         } else {
2314             $this_msgbox.remove();
2315         }
2316     }
2320  * Requests SQL for previewing before executing.
2322  * @param jQuery Object $form Form containing query data
2324  * @return void
2325  */
2326 function PMA_previewSQL($form)
2328     var form_url = $form.attr('action');
2329     var form_data = $form.serialize() +
2330         '&do_save_data=1' +
2331         '&preview_sql=1' +
2332         '&ajax_request=1';
2333     var $msgbox = PMA_ajaxShowMessage();
2334     $.ajax({
2335         type: 'POST',
2336         url: form_url,
2337         data: form_data,
2338         success: function (response) {
2339             PMA_ajaxRemoveMessage($msgbox);
2340             if (response.success) {
2341                 var $dialog_content = $('<div/>')
2342                     .append(response.sql_data);
2343                 var button_options = {};
2344                 button_options[PMA_messages.strClose] = function () {
2345                     $(this).dialog('close');
2346                 };
2347                 var $response_dialog = $dialog_content.dialog({
2348                     minWidth: 550,
2349                     maxHeight: 400,
2350                     modal: true,
2351                     buttons: button_options,
2352                     title: PMA_messages.strPreviewSQL,
2353                     close: function () {
2354                         $(this).remove();
2355                     },
2356                     open: function () {
2357                         // Pretty SQL printing.
2358                         PMA_highlightSQL($(this));
2359                     }
2360                 });
2361             } else {
2362                 PMA_ajaxShowMessage(response.message);
2363             }
2364         },
2365         error: function () {
2366             PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest);
2367         }
2368     });
2372  * check for reserved keyword column name
2374  * @param jQuery Object $form Form
2376  * @returns true|false
2377  */
2379 function PMA_checkReservedWordColumns($form) {
2380     var is_confirmed = true;
2381     $.ajax({
2382         type: 'POST',
2383         url: "tbl_structure.php",
2384         data: $form.serialize() + '&reserved_word_check=1',
2385         success: function (data) {
2386             if (typeof data.success != 'undefined' && data.success === true) {
2387                 is_confirmed = confirm(data.message);
2388             }
2389         },
2390         async:false
2391     });
2392     return is_confirmed;
2395 // This event only need to be fired once after the initial page load
2396 $(function () {
2397     /**
2398      * Allows the user to dismiss a notification
2399      * created with PMA_ajaxShowMessage()
2400      */
2401     $(document).on('click', 'span.ajax_notification.dismissable', function () {
2402         PMA_ajaxRemoveMessage($(this));
2403     });
2404     /**
2405      * The below two functions hide the "Dismiss notification" tooltip when a user
2406      * is hovering a link or button that is inside an ajax message
2407      */
2408     $(document).on('mouseover', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
2409         if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
2410             $(this).parents('span.ajax_notification').tooltip('disable');
2411         }
2412     });
2413     $(document).on('mouseout', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
2414         if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
2415             $(this).parents('span.ajax_notification').tooltip('enable');
2416         }
2417     });
2421  * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected
2422  */
2423 function PMA_showNoticeForEnum(selectElement)
2425     var enum_notice_id = selectElement.attr("id").split("_")[1];
2426     enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2], 10) + 1);
2427     var selectedType = selectElement.val();
2428     if (selectedType == "ENUM" || selectedType == "SET") {
2429         $("p#enum_notice_" + enum_notice_id).show();
2430     } else {
2431         $("p#enum_notice_" + enum_notice_id).hide();
2432     }
2436  * Creates a Profiling Chart with jqplot. Used in sql.js
2437  * and in server_status_monitor.js
2438  */
2439 function PMA_createProfilingChartJqplot(target, data)
2441     return $.jqplot(target, [data],
2442         {
2443             seriesDefaults: {
2444                 renderer: $.jqplot.PieRenderer,
2445                 rendererOptions: {
2446                     showDataLabels:  true
2447                 }
2448             },
2449             highlighter: {
2450                 show: true,
2451                 tooltipLocation: 'se',
2452                 sizeAdjust: 0,
2453                 tooltipAxes: 'pieref',
2454                 useAxesFormatters: false,
2455                 formatString: '%s, %.9Ps'
2456             },
2457             legend: {
2458                 show: true,
2459                 location: 'e',
2460                 rendererOptions: {numberColumns: 2}
2461             },
2462             // from http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines#Color_Palette
2463             seriesColors: [
2464                 '#fce94f',
2465                 '#fcaf3e',
2466                 '#e9b96e',
2467                 '#8ae234',
2468                 '#729fcf',
2469                 '#ad7fa8',
2470                 '#ef2929',
2471                 '#eeeeec',
2472                 '#888a85',
2473                 '#c4a000',
2474                 '#ce5c00',
2475                 '#8f5902',
2476                 '#4e9a06',
2477                 '#204a87',
2478                 '#5c3566',
2479                 '#a40000',
2480                 '#babdb6',
2481                 '#2e3436'
2482             ]
2483         }
2484     );
2488  * Formats a profiling duration nicely (in us and ms time).
2489  * Used in server_status_monitor.js
2491  * @param  integer    Number to be formatted, should be in the range of microsecond to second
2492  * @param  integer    Accuracy, how many numbers right to the comma should be
2493  * @return string     The formatted number
2494  */
2495 function PMA_prettyProfilingNum(num, acc)
2497     if (!acc) {
2498         acc = 2;
2499     }
2500     acc = Math.pow(10, acc);
2501     if (num * 1000 < 0.1) {
2502         num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ';
2503     } else if (num < 0.1) {
2504         num = Math.round(acc * (num * 1000)) / acc + 'm';
2505     } else {
2506         num = Math.round(acc * num) / acc;
2507     }
2509     return num + 's';
2514  * Formats a SQL Query nicely with newlines and indentation. Depends on Codemirror and MySQL Mode!
2516  * @param string      Query to be formatted
2517  * @return string      The formatted query
2518  */
2519 function PMA_SQLPrettyPrint(string)
2521     if (typeof CodeMirror == 'undefined') {
2522         return string;
2523     }
2525     var mode = CodeMirror.getMode({}, "text/x-mysql");
2526     var stream = new CodeMirror.StringStream(string);
2527     var state = mode.startState();
2528     var token, tokens = [];
2529     var output = '';
2530     var tabs = function (cnt) {
2531         var ret = '';
2532         for (var i = 0; i < 4 * cnt; i++) {
2533             ret += " ";
2534         }
2535         return ret;
2536     };
2538     // "root-level" statements
2539     var statements = {
2540         'select': ['select', 'from', 'on', 'where', 'having', 'limit', 'order by', 'group by'],
2541         'update': ['update', 'set', 'where'],
2542         'insert into': ['insert into', 'values']
2543     };
2544     // don't put spaces before these tokens
2545     var spaceExceptionsBefore = {';': true, ',': true, '.': true, '(': true};
2546     // don't put spaces after these tokens
2547     var spaceExceptionsAfter = {'.': true};
2549     // Populate tokens array
2550     var str = '';
2551     while (! stream.eol()) {
2552         stream.start = stream.pos;
2553         token = mode.token(stream, state);
2554         if (token !== null) {
2555             tokens.push([token, stream.current().toLowerCase()]);
2556         }
2557     }
2559     var currentStatement = tokens[0][1];
2561     if (! statements[currentStatement]) {
2562         return string;
2563     }
2564     // Holds all currently opened code blocks (statement, function or generic)
2565     var blockStack = [];
2566     // Holds the type of block from last iteration (the current is in blockStack[0])
2567     var previousBlock;
2568     // If a new code block is found, newBlock contains its type for one iteration and vice versa for endBlock
2569     var newBlock, endBlock;
2570     // How much to indent in the current line
2571     var indentLevel = 0;
2572     // Holds the "root-level" statements
2573     var statementPart, lastStatementPart = statements[currentStatement][0];
2575     blockStack.unshift('statement');
2577     // Iterate through every token and format accordingly
2578     for (var i = 0; i < tokens.length; i++) {
2579         previousBlock = blockStack[0];
2581         // New block => push to stack
2582         if (tokens[i][1] == '(') {
2583             if (i < tokens.length - 1 && tokens[i + 1][0] == 'statement-verb') {
2584                 blockStack.unshift(newBlock = 'statement');
2585             } else if (i > 0 && tokens[i - 1][0] == 'builtin') {
2586                 blockStack.unshift(newBlock = 'function');
2587             } else {
2588                 blockStack.unshift(newBlock = 'generic');
2589             }
2590         } else {
2591             newBlock = null;
2592         }
2594         // Block end => pop from stack
2595         if (tokens[i][1] == ')') {
2596             endBlock = blockStack[0];
2597             blockStack.shift();
2598         } else {
2599             endBlock = null;
2600         }
2602         // A subquery is starting
2603         if (i > 0 && newBlock == 'statement') {
2604             indentLevel++;
2605             output += "\n" + tabs(indentLevel) + tokens[i][1] + ' ' + tokens[i + 1][1].toUpperCase() + "\n" + tabs(indentLevel + 1);
2606             currentStatement = tokens[i + 1][1];
2607             i++;
2608             continue;
2609         }
2611         // A subquery is ending
2612         if (endBlock == 'statement' && indentLevel > 0) {
2613             output += "\n" + tabs(indentLevel);
2614             indentLevel--;
2615         }
2617         // One less indentation for statement parts (from, where, order by, etc.) and a newline
2618         statementPart = statements[currentStatement].indexOf(tokens[i][1]);
2619         if (statementPart != -1) {
2620             if (i > 0) {
2621                 output += "\n";
2622             }
2623             output += tabs(indentLevel) + tokens[i][1].toUpperCase();
2624             output += "\n" + tabs(indentLevel + 1);
2625             lastStatementPart = tokens[i][1];
2626         }
2627         // Normal indentation and spaces for everything else
2628         else {
2629             if (! spaceExceptionsBefore[tokens[i][1]] &&
2630                ! (i > 0 && spaceExceptionsAfter[tokens[i - 1][1]]) &&
2631                output.charAt(output.length - 1) != ' ') {
2632                 output += " ";
2633             }
2634             if (tokens[i][0] == 'keyword') {
2635                 output += tokens[i][1].toUpperCase();
2636             } else {
2637                 output += tokens[i][1];
2638             }
2639         }
2641         // split columns in select and 'update set' clauses, but only inside statements blocks
2642         if ((lastStatementPart == 'select' || lastStatementPart == 'where'  || lastStatementPart == 'set') &&
2643             tokens[i][1] == ',' && blockStack[0] == 'statement') {
2645             output += "\n" + tabs(indentLevel + 1);
2646         }
2648         // split conditions in where clauses, but only inside statements blocks
2649         if (lastStatementPart == 'where' &&
2650             (tokens[i][1] == 'and' || tokens[i][1] == 'or' || tokens[i][1] == 'xor')) {
2652             if (blockStack[0] == 'statement') {
2653                 output += "\n" + tabs(indentLevel + 1);
2654             }
2655             // Todo: Also split and or blocks in newlines & indentation++
2656             //if (blockStack[0] == 'generic')
2657              //   output += ...
2658         }
2659     }
2660     return output;
2664  * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not
2665  *  return a jQuery object yet and hence cannot be chained
2667  * @param string      question
2668  * @param string      url           URL to be passed to the callbackFn to make
2669  *                                  an Ajax call to
2670  * @param function    callbackFn    callback to execute after user clicks on OK
2671  * @param function    openCallback  optional callback to run when dialog is shown
2672  */
2674 jQuery.fn.PMA_confirm = function (question, url, callbackFn, openCallback) {
2675     var confirmState = PMA_commonParams.get('confirm');
2676     if (! confirmState) {
2677         // user does not want to confirm
2678         if ($.isFunction(callbackFn)) {
2679             callbackFn.call(this, url);
2680             return true;
2681         }
2682     }
2683     if (PMA_messages.strDoYouReally === '') {
2684         return true;
2685     }
2687     /**
2688      * @var    button_options  Object that stores the options passed to jQueryUI
2689      *                          dialog
2690      */
2691     var button_options = [
2692         {
2693             text: PMA_messages.strOK,
2694             'class': 'submitOK',
2695             click: function () {
2696                 $(this).dialog("close");
2697                 if ($.isFunction(callbackFn)) {
2698                     callbackFn.call(this, url);
2699                 }
2700             }
2701         },
2702         {
2703             text: PMA_messages.strCancel,
2704             'class': 'submitCancel',
2705             click: function () {
2706                 $(this).dialog("close");
2707             }
2708         }
2709     ];
2711     $('<div/>', {'id': 'confirm_dialog', 'title': PMA_messages.strConfirm})
2712     .prepend(question)
2713     .dialog({
2714         buttons: button_options,
2715         close: function () {
2716             $(this).remove();
2717         },
2718         open: openCallback,
2719         modal: true
2720     });
2724  * jQuery function to sort a table's body after a new row has been appended to it.
2725  * Also fixes the even/odd classes of the table rows at the end.
2727  * @param string      text_selector   string to select the sortKey's text
2729  * @return jQuery Object for chaining purposes
2730  */
2731 jQuery.fn.PMA_sort_table = function (text_selector) {
2732     return this.each(function () {
2734         /**
2735          * @var table_body  Object referring to the table's <tbody> element
2736          */
2737         var table_body = $(this);
2738         /**
2739          * @var rows    Object referring to the collection of rows in {@link table_body}
2740          */
2741         var rows = $(this).find('tr').get();
2743         //get the text of the field that we will sort by
2744         $.each(rows, function (index, row) {
2745             row.sortKey = $.trim($(row).find(text_selector).text().toLowerCase());
2746         });
2748         //get the sorted order
2749         rows.sort(function (a, b) {
2750             if (a.sortKey < b.sortKey) {
2751                 return -1;
2752             }
2753             if (a.sortKey > b.sortKey) {
2754                 return 1;
2755             }
2756             return 0;
2757         });
2759         //pull out each row from the table and then append it according to it's order
2760         $.each(rows, function (index, row) {
2761             $(table_body).append(row);
2762             row.sortKey = null;
2763         });
2765         //Re-check the classes of each row
2766         $(this).find('tr:odd')
2767         .removeClass('even').addClass('odd')
2768         .end()
2769         .find('tr:even')
2770         .removeClass('odd').addClass('even');
2771     });
2775  * Unbind all event handlers before tearing down a page
2776  */
2777 AJAX.registerTeardown('functions.js', function () {
2778     $(document).off('submit', "#create_table_form_minimal.ajax");
2779     $(document).off('submit', "form.create_table_form.ajax");
2780     $(document).off('click', "form.create_table_form.ajax input[name=submit_num_fields]");
2781     $(document).off('keyup', "form.create_table_form.ajax input");
2785  * jQuery coding for 'Create Table'.  Used on db_operations.php,
2786  * db_structure.php and db_tracking.php (i.e., wherever
2787  * libraries/display_create_table.lib.php is used)
2789  * Attach Ajax Event handlers for Create Table
2790  */
2791 AJAX.registerOnload('functions.js', function () {
2792     /**
2793      * Attach event handler for submission of create table form (save)
2794      */
2795     $(document).on('submit', "form.create_table_form.ajax", function (event) {
2796         event.preventDefault();
2798         /**
2799          * @var    the_form    object referring to the create table form
2800          */
2801         var $form = $(this);
2803         /*
2804          * First validate the form; if there is a problem, avoid submitting it
2805          *
2806          * checkTableEditForm() needs a pure element and not a jQuery object,
2807          * this is why we pass $form[0] as a parameter (the jQuery object
2808          * is actually an array of DOM elements)
2809          */
2811         if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) {
2812             PMA_prepareForAjaxRequest($form);
2813             if (PMA_checkReservedWordColumns($form)) {
2814                 PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2815                 //User wants to submit the form
2816                 $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
2817                     if (typeof data !== 'undefined' && data.success === true) {
2818                         $('#properties_message')
2819                          .removeClass('error')
2820                          .html('');
2821                         PMA_ajaxShowMessage(data.message);
2822                         // Only if the create table dialog (distinct panel) exists
2823                         var $createTableDialog = $("#create_table_dialog");
2824                         if ($createTableDialog.length > 0) {
2825                             $createTableDialog.dialog("close").remove();
2826                         }
2827                         $('#tableslistcontainer').before(data.formatted_sql);
2829                         /**
2830                          * @var tables_table    Object referring to the <tbody> element that holds the list of tables
2831                          */
2832                         var tables_table = $("#tablesForm").find("tbody").not("#tbl_summary_row");
2833                         // this is the first table created in this db
2834                         if (tables_table.length === 0) {
2835                             PMA_commonActions.refreshMain(
2836                                 PMA_commonParams.get('opendb_url')
2837                             );
2838                         } else {
2839                             /**
2840                              * @var curr_last_row   Object referring to the last <tr> element in {@link tables_table}
2841                              */
2842                             var curr_last_row = $(tables_table).find('tr:last');
2843                             /**
2844                              * @var curr_last_row_index_string   String containing the index of {@link curr_last_row}
2845                              */
2846                             var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0];
2847                             /**
2848                              * @var curr_last_row_index Index of {@link curr_last_row}
2849                              */
2850                             var curr_last_row_index = parseFloat(curr_last_row_index_string);
2851                             /**
2852                              * @var new_last_row_index   Index of the new row to be appended to {@link tables_table}
2853                              */
2854                             var new_last_row_index = curr_last_row_index + 1;
2855                             /**
2856                              * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table}
2857                              */
2858                             var new_last_row_id = 'checkbox_tbl_' + new_last_row_index;
2860                             data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id);
2861                             //append to table
2862                             $(data.new_table_string)
2863                              .appendTo(tables_table);
2865                             //Sort the table
2866                             $(tables_table).PMA_sort_table('th');
2868                             // Adjust summary row
2869                             PMA_adjustTotals();
2870                         }
2872                         //Refresh navigation as a new table has been added
2873                         PMA_reloadNavigation();
2874                         // Redirect to table structure page on creation of new table
2875                         var params_12 = 'ajax_request=true&ajax_page_request=true';
2876                         if (! (history && history.pushState)) {
2877                             params_12 += PMA_MicroHistory.menus.getRequestParam();
2878                         }
2879                         tblStruct_url = 'tbl_structure.php?server=' + data._params.server +
2880                             '&db='+ data._params.db + '&token=' + data._params.token +
2881                             '&goto=db_structure.php&table=' + data._params.table + '';
2882                         $.get(tblStruct_url, params_12, AJAX.responseHandler);
2883                     } else {
2884                         PMA_ajaxShowMessage(
2885                             '<div class="error">' + data.error + '</div>',
2886                             false
2887                         );
2888                     }
2889                 }); // end $.post()
2890             }
2891         } // end if (checkTableEditForm() )
2892     }); // end create table form (save)
2894     /**
2895      * Attach event handler for create table form (add fields)
2896      */
2897     $(document).on('click', "form.create_table_form.ajax input[name=submit_num_fields]", function (event) {
2898         event.preventDefault();
2899         /**
2900          * @var    the_form    object referring to the create table form
2901          */
2902         var $form = $(this).closest('form');
2904         if (!checkFormElementInRange(this.form, 'added_fields', PMA_messages.strLeastColumnError, 1)) {
2905             return;
2906         }
2908         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
2909         PMA_prepareForAjaxRequest($form);
2911         //User wants to add more fields to the table
2912         $.post($form.attr('action'), $form.serialize() + "&submit_num_fields=1", function (data) {
2913             if (typeof data !== 'undefined' && data.success) {
2914                 var $pageContent = $("#page_content");
2915                 $pageContent.html(data.message);
2916                 PMA_highlightSQL($pageContent);
2917                 PMA_verifyColumnsProperties();
2918                 PMA_hideShowConnection($('.create_table_form select[name=tbl_storage_engine]'));
2919                 PMA_ajaxRemoveMessage($msgbox);
2920             } else {
2921                 PMA_ajaxShowMessage(data.error);
2922             }
2923         }); //end $.post()
2924     }); // end create table form (add fields)
2926     $(document).on('keydown', "form.create_table_form.ajax input[name=added_fields]", function (event) {
2927         if (event.keyCode == 13) {
2928             event.preventDefault();
2929             event.stopImmediatePropagation();
2930             $(this)
2931                 .closest('form')
2932                 .find('input[name=submit_num_fields]')
2933                 .click();
2934         }
2935     });
2936     $("input[value=AUTO_INCREMENT]").change(function(){
2937         if (this.checked) {
2938             var col = /\d/.exec($(this).attr('name'));
2939             col = col[0];
2940             var $selectFieldKey = $('select[name="field_key[' + col + ']"]');
2941             if ($selectFieldKey.val() === 'none_'+col) {
2942                 $selectFieldKey.val('primary_'+col).change();
2943             }
2944         }
2945     });
2946     $('body')
2947     .off('click', 'input.preview_sql')
2948     .on('click', 'input.preview_sql', function () {
2949         var $form = $(this).closest('form');
2950         PMA_previewSQL($form);
2951     });
2956  * Validates the password field in a form
2958  * @see    PMA_messages.strPasswordEmpty
2959  * @see    PMA_messages.strPasswordNotSame
2960  * @param  object $the_form The form to be validated
2961  * @return bool
2962  */
2963 function PMA_checkPassword($the_form)
2965     // Did the user select 'no password'?
2966     if ($the_form.find('#nopass_1').is(':checked')) {
2967         return true;
2968     } else {
2969         var $pred = $the_form.find('#select_pred_password');
2970         if ($pred.length && ($pred.val() == 'none' || $pred.val() == 'keep')) {
2971             return true;
2972         }
2973     }
2975     var $password = $the_form.find('input[name=pma_pw]');
2976     var $password_repeat = $the_form.find('input[name=pma_pw2]');
2977     var alert_msg = false;
2979     if ($password.val() === '') {
2980         alert_msg = PMA_messages.strPasswordEmpty;
2981     } else if ($password.val() != $password_repeat.val()) {
2982         alert_msg = PMA_messages.strPasswordNotSame;
2983     }
2985     if (alert_msg) {
2986         alert(alert_msg);
2987         $password.val('');
2988         $password_repeat.val('');
2989         $password.focus();
2990         return false;
2991     }
2992     return true;
2996  * Unbind all event handlers before tearing down a page
2997  */
2998 AJAX.registerTeardown('functions.js', function () {
2999     $(document).off('click', '#change_password_anchor.ajax');
3002  * Attach Ajax event handlers for 'Change Password' on index.php
3003  */
3004 AJAX.registerOnload('functions.js', function () {
3006     /**
3007      * Attach Ajax event handler on the change password anchor
3008      */
3009     $(document).on('click', '#change_password_anchor.ajax', function (event) {
3010         event.preventDefault();
3012         var $msgbox = PMA_ajaxShowMessage();
3014         /**
3015          * @var button_options  Object containing options to be passed to jQueryUI's dialog
3016          */
3017         var button_options = {};
3018         button_options[PMA_messages.strGo] = function () {
3020             event.preventDefault();
3022             /**
3023              * @var $the_form    Object referring to the change password form
3024              */
3025             var $the_form = $("#change_password_form");
3027             if (! PMA_checkPassword($the_form)) {
3028                 return false;
3029             }
3031             /**
3032              * @var this_value  String containing the value of the submit button.
3033              * Need to append this for the change password form on Server Privileges
3034              * page to work
3035              */
3036             var this_value = $(this).val();
3038             var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3039             $the_form.append('<input type="hidden" name="ajax_request" value="true" />');
3041             $.post($the_form.attr('action'), $the_form.serialize() + '&change_pw=' + this_value, function (data) {
3042                 if (typeof data === 'undefined' || data.success !== true) {
3043                     PMA_ajaxShowMessage(data.error, false);
3044                     return;
3045                 }
3047                 var $pageContent = $("#page_content");
3048                 $pageContent.prepend(data.message);
3049                 PMA_highlightSQL($pageContent);
3050                 $("#change_password_dialog").hide().remove();
3051                 $("#edit_user_dialog").dialog("close").remove();
3052                 PMA_ajaxRemoveMessage($msgbox);
3053             }); // end $.post()
3054         };
3056         button_options[PMA_messages.strCancel] = function () {
3057             $(this).dialog('close');
3058         };
3059         $.get($(this).attr('href'), {'ajax_request': true}, function (data) {
3060             if (typeof data === 'undefined' || !data.success) {
3061                 PMA_ajaxShowMessage(data.error, false);
3062                 return;
3063             }
3065             $('<div id="change_password_dialog"></div>')
3066                 .dialog({
3067                     title: PMA_messages.strChangePassword,
3068                     width: 600,
3069                     close: function (ev, ui) {
3070                         $(this).remove();
3071                     },
3072                     buttons: button_options,
3073                     modal: true
3074                 })
3075                 .append(data.message);
3076             // for this dialog, we remove the fieldset wrapping due to double headings
3077             $("fieldset#fieldset_change_password")
3078                 .find("legend").remove().end()
3079                 .find("table.noclick").unwrap().addClass("some-margin")
3080                 .find("input#text_pma_pw").focus();
3081             displayPasswordGenerateButton();
3082             $('#fieldset_change_password_footer').hide();
3083             PMA_ajaxRemoveMessage($msgbox);
3084             $('#change_password_form').bind('submit', function (e) {
3085                 e.preventDefault();
3086                 $(this)
3087                     .closest('.ui-dialog')
3088                     .find('.ui-dialog-buttonpane .ui-button')
3089                     .first()
3090                     .click();
3091             });
3092         }); // end $.get()
3093     }); // end handler for change password anchor
3094 }); // end $() for Change Password
3097  * Unbind all event handlers before tearing down a page
3098  */
3099 AJAX.registerTeardown('functions.js', function () {
3100     $(document).off('change', "select.column_type");
3101     $(document).off('change', "select.default_type");
3102     $(document).off('change', "select.virtuality");
3103     $(document).off('change', 'input.allow_null');
3104     $(document).off('change', '.create_table_form select[name=tbl_storage_engine]');
3107  * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when
3108  * the page loads and when the selected data type changes
3109  */
3110 AJAX.registerOnload('functions.js', function () {
3111     // is called here for normal page loads and also when opening
3112     // the Create table dialog
3113     PMA_verifyColumnsProperties();
3114     //
3115     // needs on() to work also in the Create Table dialog
3116     $(document).on('change', "select.column_type", function () {
3117         PMA_showNoticeForEnum($(this));
3118     });
3119     $(document).on('change', "select.default_type", function () {
3120         PMA_hideShowDefaultValue($(this));
3121     });
3122     $(document).on('change', "select.virtuality", function () {
3123         PMA_hideShowExpression($(this));
3124     });
3125     $(document).on('change', 'input.allow_null', function () {
3126         PMA_validateDefaultValue($(this));
3127     });
3128     $(document).on('change', '.create_table_form select[name=tbl_storage_engine]', function () {
3129         PMA_hideShowConnection($(this));
3130     });
3134  * If the chosen storage engine is FEDERATED show connection field. Hide otherwise
3136  * @param $engine_selector storage engine selector
3137  */
3138 function PMA_hideShowConnection($engine_selector)
3140     var $connection = $('.create_table_form input[name=connection]');
3141     var index = $connection.parent('td').index() + 1;
3142     var $labelTh = $connection.parents('tr').prev('tr').children('th:nth-child(' + index + ')');
3143     if ($engine_selector.val() != 'FEDERATED') {
3144         $connection
3145             .prop('disabled', true)
3146             .parent('td').hide();
3147         $labelTh.hide();
3148     } else {
3149         $connection
3150             .prop('disabled', false)
3151             .parent('td').show();
3152         $labelTh.show();
3153     }
3157  * If the column does not allow NULL values, makes sure that default is not NULL
3158  */
3159 function PMA_validateDefaultValue($null_checkbox)
3161     if (! $null_checkbox.prop('checked')) {
3162         var $default = $null_checkbox.closest('tr').find('.default_type');
3163         if ($default.val() == 'NULL') {
3164             $default.val('NONE');
3165         }
3166     }
3170  * function to populate the input fields on picking a column from central list
3172  * @param string  input_id input id of the name field for the column to be populated
3173  * @param integer offset of the selected column in central list of columns
3174  */
3175 function autoPopulate(input_id, offset)
3177     var db = PMA_commonParams.get('db');
3178     var table = PMA_commonParams.get('table');
3179     input_id = input_id.substring(0, input_id.length - 1);
3180     $('#' + input_id + '1').val(central_column_list[db + '_' + table][offset].col_name);
3181     var col_type = central_column_list[db + '_' + table][offset].col_type.toUpperCase();
3182     $('#' + input_id + '2').val(col_type);
3183     var $input3 = $('#' + input_id + '3');
3184     $input3.val(central_column_list[db + '_' + table][offset].col_length);
3185     if(col_type === 'ENUM' || col_type === 'SET') {
3186         $input3.next().show();
3187     } else {
3188         $input3.next().hide();
3189     }
3190     var col_default = central_column_list[db + '_' + table][offset].col_default.toUpperCase();
3191     var $input4 = $('#' + input_id + '4');
3192     if (col_default !== '' && col_default !== 'NULL' && col_default !== 'CURRENT_TIMESTAMP') {
3193         $input4.val("USER_DEFINED");
3194         $input4.next().next().show();
3195         $input4.next().next().val(central_column_list[db + '_' + table][offset].col_default);
3196     } else {
3197         $input4.val(central_column_list[db + '_' + table][offset].col_default);
3198         $input4.next().next().hide();
3199     }
3200     $('#' + input_id + '5').val(central_column_list[db + '_' + table][offset].col_collation);
3201     var $input6 = $('#' + input_id + '6');
3202     $input6.val(central_column_list[db + '_' + table][offset].col_attribute);
3203     if(central_column_list[db + '_' + table][offset].col_extra === 'on update CURRENT_TIMESTAMP') {
3204         $input6.val(central_column_list[db + '_' + table][offset].col_extra);
3205     }
3206     if(central_column_list[db + '_' + table][offset].col_extra.toUpperCase() === 'AUTO_INCREMENT') {
3207         $('#' + input_id + '9').prop("checked",true).change();
3208     } else {
3209         $('#' + input_id + '9').prop("checked",false);
3210     }
3211     if(central_column_list[db + '_' + table][offset].col_isNull !== '0') {
3212         $('#' + input_id + '7').prop("checked",true);
3213     } else {
3214         $('#' + input_id + '7').prop("checked",false);
3215     }
3219  * Unbind all event handlers before tearing down a page
3220  */
3221 AJAX.registerTeardown('functions.js', function () {
3222     $(document).off('click', "a.open_enum_editor");
3223     $(document).off('click', "input.add_value");
3224     $(document).off('click', "#enum_editor td.drop");
3225     $(document).off('click', 'a.central_columns_dialog');
3228  * @var $enum_editor_dialog An object that points to the jQuery
3229  *                          dialog of the ENUM/SET editor
3230  */
3231 var $enum_editor_dialog = null;
3233  * Opens the ENUM/SET editor and controls its functions
3234  */
3235 AJAX.registerOnload('functions.js', function () {
3236     $(document).on('click', "a.open_enum_editor", function () {
3237         // Get the name of the column that is being edited
3238         var colname = $(this).closest('tr').find('input:first').val();
3239         var title;
3240         var i;
3241         // And use it to make up a title for the page
3242         if (colname.length < 1) {
3243             title = PMA_messages.enum_newColumnVals;
3244         } else {
3245             title = PMA_messages.enum_columnVals.replace(
3246                 /%s/,
3247                 '"' + escapeHtml(decodeURIComponent(colname)) + '"'
3248             );
3249         }
3250         // Get the values as a string
3251         var inputstring = $(this)
3252             .closest('td')
3253             .find("input")
3254             .val();
3255         // Escape html entities
3256         inputstring = $('<div/>')
3257             .text(inputstring)
3258             .html();
3259         // Parse the values, escaping quotes and
3260         // slashes on the fly, into an array
3261         var values = [];
3262         var in_string = false;
3263         var curr, next, buffer = '';
3264         for (i = 0; i < inputstring.length; i++) {
3265             curr = inputstring.charAt(i);
3266             next = i == inputstring.length ? '' : inputstring.charAt(i + 1);
3267             if (! in_string && curr == "'") {
3268                 in_string = true;
3269             } else if (in_string && curr == "\\" && next == "\\") {
3270                 buffer += "&#92;";
3271                 i++;
3272             } else if (in_string && next == "'" && (curr == "'" || curr == "\\")) {
3273                 buffer += "&#39;";
3274                 i++;
3275             } else if (in_string && curr == "'") {
3276                 in_string = false;
3277                 values.push(buffer);
3278                 buffer = '';
3279             } else if (in_string) {
3280                 buffer += curr;
3281             }
3282         }
3283         if (buffer.length > 0) {
3284             // The leftovers in the buffer are the last value (if any)
3285             values.push(buffer);
3286         }
3287         var fields = '';
3288         // If there are no values, maybe the user is about to make a
3289         // new list so we add a few for him/her to get started with.
3290         if (values.length === 0) {
3291             values.push('', '', '', '');
3292         }
3293         // Add the parsed values to the editor
3294         var drop_icon = PMA_getImage('b_drop.png');
3295         for (i = 0; i < values.length; i++) {
3296             fields += "<tr><td>" +
3297                    "<input type='text' value='" + values[i] + "'/>" +
3298                    "</td><td class='drop'>" +
3299                    drop_icon +
3300                    "</td></tr>";
3301         }
3302         /**
3303          * @var dialog HTML code for the ENUM/SET dialog
3304          */
3305         var dialog = "<div id='enum_editor'>" +
3306                    "<fieldset>" +
3307                     "<legend>" + title + "</legend>" +
3308                     "<p>" + PMA_getImage('s_notice.png') +
3309                     PMA_messages.enum_hint + "</p>" +
3310                     "<table class='values'>" + fields + "</table>" +
3311                     "</fieldset><fieldset class='tblFooters'>" +
3312                     "<table class='add'><tr><td>" +
3313                     "<div class='slider'></div>" +
3314                     "</td><td>" +
3315                     "<form><div><input type='submit' class='add_value' value='" +
3316                     PMA_sprintf(PMA_messages.enum_addValue, 1) +
3317                     "'/></div></form>" +
3318                     "</td></tr></table>" +
3319                     "<input type='hidden' value='" + // So we know which column's data is being edited
3320                     $(this).closest('td').find("input").attr("id") +
3321                     "' />" +
3322                     "</fieldset>" +
3323                     "</div>";
3324         /**
3325          * @var  Defines functions to be called when the buttons in
3326          * the buttonOptions jQuery dialog bar are pressed
3327          */
3328         var buttonOptions = {};
3329         buttonOptions[PMA_messages.strGo] = function () {
3330             // When the submit button is clicked,
3331             // put the data back into the original form
3332             var value_array = [];
3333             $(this).find(".values input").each(function (index, elm) {
3334                 var val = elm.value.replace(/\\/g, '\\\\').replace(/'/g, "''");
3335                 value_array.push("'" + val + "'");
3336             });
3337             // get the Length/Values text field where this value belongs
3338             var values_id = $(this).find("input[type='hidden']").val();
3339             $("input#" + values_id).val(value_array.join(","));
3340             $(this).dialog("close");
3341         };
3342         buttonOptions[PMA_messages.strClose] = function () {
3343             $(this).dialog("close");
3344         };
3345         // Show the dialog
3346         var width = parseInt(
3347             (parseInt($('html').css('font-size'), 10) / 13) * 340,
3348             10
3349         );
3350         if (! width) {
3351             width = 340;
3352         }
3353         $enum_editor_dialog = $(dialog).dialog({
3354             minWidth: width,
3355             maxHeight: 450,
3356             modal: true,
3357             title: PMA_messages.enum_editor,
3358             buttons: buttonOptions,
3359             open: function () {
3360                 // Focus the "Go" button after opening the dialog
3361                 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus();
3362             },
3363             close: function () {
3364                 $(this).remove();
3365             }
3366         });
3367         // slider for choosing how many fields to add
3368         $enum_editor_dialog.find(".slider").slider({
3369             animate: true,
3370             range: "min",
3371             value: 1,
3372             min: 1,
3373             max: 9,
3374             slide: function (event, ui) {
3375                 $(this).closest('table').find('input[type=submit]').val(
3376                     PMA_sprintf(PMA_messages.enum_addValue, ui.value)
3377                 );
3378             }
3379         });
3380         // Focus the slider, otherwise it looks nearly transparent
3381         $('a.ui-slider-handle').addClass('ui-state-focus');
3382         return false;
3383     });
3385     $(document).on('click', 'a.central_columns_dialog', function (e) {
3386         var href = "db_central_columns.php";
3387         var db = PMA_commonParams.get('db');
3388         var table = PMA_commonParams.get('table');
3389         var maxRows = $(this).data('maxrows');
3390         var pick = $(this).data('pick');
3391         if (pick !== false) {
3392             pick = true;
3393         }
3394         var params = {
3395             'ajax_request' : true,
3396             'token' : PMA_commonParams.get('token'),
3397             'server' : PMA_commonParams.get('server'),
3398             'db' : PMA_commonParams.get('db'),
3399             'cur_table' : PMA_commonParams.get('table'),
3400             'getColumnList':true
3401         };
3402         var colid = $(this).closest('td').find("input").attr("id");
3403         var fields = '';
3404         if (! (db + '_' + table in central_column_list)) {
3405             central_column_list.push(db + '_' + table);
3406             $.ajax({
3407                 type: 'POST',
3408                 url: href,
3409                 data: params,
3410                 success: function (data) {
3411                     central_column_list[db + '_' + table] = $.parseJSON(data.message);
3412                 },
3413                 async:false
3414             });
3415         }
3416         var i = 0;
3417         var list_size = central_column_list[db + '_' + table].length;
3418         var min = (list_size <= maxRows) ? list_size : maxRows;
3419         for (i = 0; i < min; i++) {
3421             fields += '<tr><td><div><span style="font-weight:bold">' +
3422                 escapeHtml(central_column_list[db + '_' + table][i].col_name) +
3423                 '</span><br><span style="color:gray">' + central_column_list[db + '_' + table][i].col_type;
3425             if (central_column_list[db + '_' + table][i].col_attribute !== '') {
3426                 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_attribute) + ') ';
3427             }
3428             if (central_column_list[db + '_' + table][i].col_length !== '') {
3429                 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_length) +') ';
3430             }
3431             fields += escapeHtml(central_column_list[db + '_' + table][i].col_extra) + '</span>' +
3432                 '</div></td>';
3433             if (pick) {
3434                 fields += '<td><input class="pick" style="width:100%" type="submit" value="' +
3435                     PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>';
3436             }
3437             fields += '</tr>';
3438         }
3439         var result_pointer = i;
3440         var search_in = '<input type="text" class="filter_rows" placeholder="' + PMA_messages.searchList + '">';
3441         if (fields === '') {
3442             fields = PMA_sprintf(PMA_messages.strEmptyCentralList, "'" + db + "'");
3443             search_in = '';
3444         }
3445         var seeMore = '';
3446         if (list_size > maxRows) {
3447             seeMore = "<fieldset class='tblFooters' style='text-align:center;font-weight:bold'>" +
3448                 "<a href='#' id='seeMore'>" + PMA_messages.seeMore + "</a></fieldset>";
3449         }
3450         var central_columns_dialog = "<div style='max-height:400px'>" +
3451             "<fieldset>" +
3452             search_in +
3453             "<table id='col_list' style='width:100%' class='values'>" + fields + "</table>" +
3454             "</fieldset>" +
3455             seeMore +
3456             "</div>";
3458         var width = parseInt(
3459             (parseInt($('html').css('font-size'), 10) / 13) * 500,
3460             10
3461         );
3462         if (! width) {
3463             width = 500;
3464         }
3465         var buttonOptions = {};
3466         var $central_columns_dialog = $(central_columns_dialog).dialog({
3467             minWidth: width,
3468             maxHeight: 450,
3469             modal: true,
3470             title: PMA_messages.pickColumnTitle,
3471             buttons: buttonOptions,
3472             open: function () {
3473                 $('#col_list').on("click", ".pick", function (){
3474                     $central_columns_dialog.remove();
3475                 });
3476                 $(".filter_rows").on("keyup", function () {
3477                     $.uiTableFilter($("#col_list"), $(this).val());
3478                 });
3479                 $("#seeMore").click(function() {
3480                     fields = '';
3481                     min = (list_size <= maxRows + result_pointer) ? list_size : maxRows + result_pointer;
3482                     for (i = result_pointer; i < min; i++) {
3484                         fields += '<tr><td><div><span style="font-weight:bold">' +
3485                             central_column_list[db + '_' + table][i].col_name +
3486                             '</span><br><span style="color:gray">' +
3487                             central_column_list[db + '_' + table][i].col_type;
3489                         if (central_column_list[db + '_' + table][i].col_attribute !== '') {
3490                             fields += '(' + central_column_list[db + '_' + table][i].col_attribute + ') ';
3491                         }
3492                         if (central_column_list[db + '_' + table][i].col_length !== '') {
3493                             fields += '(' + central_column_list[db + '_' + table][i].col_length + ') ';
3494                         }
3495                         fields += central_column_list[db + '_' + table][i].col_extra + '</span>' +
3496                             '</div></td>';
3497                         if (pick) {
3498                             fields += '<td><input class="pick" style="width:100%" type="submit" value="' +
3499                                 PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>';
3500                         }
3501                         fields += '</tr>';
3502                     }
3503                     $("#col_list").append(fields);
3504                     result_pointer = i;
3505                     if (result_pointer === list_size) {
3506                         $('.tblFooters').hide();
3507                     }
3508                     return false;
3509                 });
3510                 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus();
3511             },
3512             close: function () {
3513                 $('#col_list').off("click", ".pick");
3514                 $(".filter_rows").off("keyup");
3515                 $(this).remove();
3516             }
3517         });
3518         return false;
3519     });
3521    // $(document).on('click', 'a.show_central_list',function(e) {
3523    // });
3524     // When "add a new value" is clicked, append an empty text field
3525     $(document).on('click', "input.add_value", function (e) {
3526         e.preventDefault();
3527         var num_new_rows = $enum_editor_dialog.find("div.slider").slider('value');
3528         while (num_new_rows--) {
3529             $enum_editor_dialog.find('.values')
3530                 .append(
3531                     "<tr style='display: none;'><td>" +
3532                     "<input type='text' />" +
3533                     "</td><td class='drop'>" +
3534                     PMA_getImage('b_drop.png') +
3535                     "</td></tr>"
3536                 )
3537                 .find('tr:last')
3538                 .show('fast');
3539         }
3540     });
3542     // Removes the specified row from the enum editor
3543     $(document).on('click', "#enum_editor td.drop", function () {
3544         $(this).closest('tr').hide('fast', function () {
3545             $(this).remove();
3546         });
3547     });
3551  * Ensures indexes names are valid according to their type and, for a primary
3552  * key, lock index name to 'PRIMARY'
3553  * @param string   form_id  Variable which parses the form name as
3554  *                            the input
3555  * @return boolean  false    if there is no index form, true else
3556  */
3557 function checkIndexName(form_id)
3559     if ($("#" + form_id).length === 0) {
3560         return false;
3561     }
3563     // Gets the elements pointers
3564     var $the_idx_name = $("#input_index_name");
3565     var $the_idx_choice = $("#select_index_choice");
3567     // Index is a primary key
3568     if ($the_idx_choice.find("option:selected").val() == 'PRIMARY') {
3569         $the_idx_name.val('PRIMARY');
3570         $the_idx_name.prop("disabled", true);
3571     }
3573     // Other cases
3574     else {
3575         if ($the_idx_name.val() == 'PRIMARY') {
3576             $the_idx_name.val("");
3577         }
3578         $the_idx_name.prop("disabled", false);
3579     }
3581     return true;
3582 } // end of the 'checkIndexName()' function
3584 AJAX.registerTeardown('functions.js', function () {
3585     $(document).off('click', '#index_frm input[type=submit]');
3587 AJAX.registerOnload('functions.js', function () {
3588     /**
3589      * Handler for adding more columns to an index in the editor
3590      */
3591     $(document).on('click', '#index_frm input[type=submit]', function (event) {
3592         event.preventDefault();
3593         var rows_to_add = $(this)
3594             .closest('fieldset')
3595             .find('.slider')
3596             .slider('value');
3598         var tempEmptyVal = function () {
3599             $(this).val('');
3600         };
3602         var tempSetFocus = function () {
3603             if ($(this).find("option:selected").val() === '') {
3604                 return true;
3605             }
3606             $(this).closest("tr").find("input").focus();
3607         };
3609         while (rows_to_add--) {
3610             var $indexColumns = $('#index_columns');
3611             var $newrow = $indexColumns
3612                 .find('tbody > tr:first')
3613                 .clone()
3614                 .appendTo(
3615                     $indexColumns.find('tbody')
3616                 );
3617             $newrow.find(':input').each(tempEmptyVal);
3618             // focus index size input on column picked
3619             $newrow.find('select').change(tempSetFocus);
3620         }
3621     });
3624 function indexEditorDialog(url, title, callback_success, callback_failure)
3626     /*Remove the hidden dialogs if there are*/
3627     var $editIndexDialog = $('#edit_index_dialog');
3628     if ($editIndexDialog.length !== 0) {
3629         $editIndexDialog.remove();
3630     }
3631     var $div = $('<div id="edit_index_dialog"></div>');
3633     /**
3634      * @var button_options Object that stores the options
3635      *                     passed to jQueryUI dialog
3636      */
3637     var button_options = {};
3638     button_options[PMA_messages.strGo] = function () {
3639         /**
3640          * @var    the_form    object referring to the export form
3641          */
3642         var $form = $("#index_frm");
3643         var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
3644         PMA_prepareForAjaxRequest($form);
3645         //User wants to submit the form
3646         $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) {
3647             var $sqlqueryresults = $(".sqlqueryresults");
3648             if ($sqlqueryresults.length !== 0) {
3649                 $sqlqueryresults.remove();
3650             }
3651             if (typeof data !== 'undefined' && data.success === true) {
3652                 PMA_ajaxShowMessage(data.message);
3653                 var $resultQuery = $('.result_query');
3654                 if ($resultQuery.length) {
3655                     $resultQuery.remove();
3656                 }
3657                 if (data.sql_query) {
3658                     $('<div class="result_query"></div>')
3659                         .html(data.sql_query)
3660                         .prependTo('#page_content');
3661                     PMA_highlightSQL($('#page_content'));
3662                 }
3663                 $(".result_query .notice").remove();
3664                 $resultQuery.prepend(data.message);
3665                 /*Reload the field form*/
3666                 $("#table_index").remove();
3667                 $("<div id='temp_div'><div>")
3668                     .append(data.index_table)
3669                     .find("#table_index")
3670                     .insertAfter("#index_header");
3671                 var $editIndexDialog = $("#edit_index_dialog");
3672                 if ($editIndexDialog.length > 0) {
3673                     $editIndexDialog.dialog("close");
3674                 }
3675                 $('div.no_indexes_defined').hide();
3676                 if (callback_success) {
3677                     callback_success();
3678                 }
3679                 PMA_reloadNavigation();
3680             } else {
3681                 var $temp_div = $("<div id='temp_div'><div>").append(data.error);
3682                 var $error;
3683                 if ($temp_div.find(".error code").length !== 0) {
3684                     $error = $temp_div.find(".error code").addClass("error");
3685                 } else {
3686                     $error = $temp_div;
3687                 }
3688                 if (callback_failure) {
3689                     callback_failure();
3690                 }
3691                 PMA_ajaxShowMessage($error, false);
3692             }
3693         }); // end $.post()
3694     };
3695     button_options[PMA_messages.strPreviewSQL] = function () {
3696         // Function for Previewing SQL
3697         var $form = $('#index_frm');
3698         PMA_previewSQL($form);
3699     };
3700     button_options[PMA_messages.strCancel] = function () {
3701         $(this).dialog('close');
3702     };
3703     var $msgbox = PMA_ajaxShowMessage();
3704     $.get("tbl_indexes.php", url, function (data) {
3705         if (typeof data !== 'undefined' && data.success === false) {
3706             //in the case of an error, show the error message returned.
3707             PMA_ajaxShowMessage(data.error, false);
3708         } else {
3709             PMA_ajaxRemoveMessage($msgbox);
3710             // Show dialog if the request was successful
3711             $div
3712             .append(data.message)
3713             .dialog({
3714                 title: title,
3715                 width: 450,
3716                 height: 350,
3717                 open: PMA_verifyColumnsProperties,
3718                 modal: true,
3719                 buttons: button_options,
3720                 close: function () {
3721                     $(this).remove();
3722                 }
3723             });
3724             $div.find('.tblFooters').remove();
3725             showIndexEditDialog($div);
3726         }
3727     }); // end $.get()
3730 function showIndexEditDialog($outer)
3732     checkIndexType();
3733     checkIndexName("index_frm");
3734     var $indexColumns = $('#index_columns');
3735     $indexColumns.find('td').each(function () {
3736         $(this).css("width", $(this).width() + 'px');
3737     });
3738     $indexColumns.find('tbody').sortable({
3739         axis: 'y',
3740         containment: $indexColumns.find("tbody"),
3741         tolerance: 'pointer'
3742     });
3743     PMA_showHints($outer);
3744     PMA_init_slider();
3745     // Add a slider for selecting how many columns to add to the index
3746     $outer.find('.slider').slider({
3747         animate: true,
3748         value: 1,
3749         min: 1,
3750         max: 16,
3751         slide: function (event, ui) {
3752             $(this).closest('fieldset').find('input[type=submit]').val(
3753                 PMA_sprintf(PMA_messages.strAddToIndex, ui.value)
3754             );
3755         }
3756     });
3757     $('div.add_fields').removeClass('hide');
3758     // focus index size input on column picked
3759     $outer.find('table#index_columns select').change(function () {
3760         if ($(this).find("option:selected").val() === '') {
3761             return true;
3762         }
3763         $(this).closest("tr").find("input").focus();
3764     });
3765     // Focus the slider, otherwise it looks nearly transparent
3766     $('a.ui-slider-handle').addClass('ui-state-focus');
3767     // set focus on index name input, if empty
3768     var input = $outer.find('input#input_index_name');
3769     input.val() || input.focus();
3773  * Function to display tooltips that were
3774  * generated on the PHP side by PMA_Util::showHint()
3776  * @param object $div a div jquery object which specifies the
3777  *                    domain for searching for tooltips. If we
3778  *                    omit this parameter the function searches
3779  *                    in the whole body
3780  **/
3781 function PMA_showHints($div)
3783     if ($div === undefined || ! $div instanceof jQuery || $div.length === 0) {
3784         $div = $("body");
3785     }
3786     $div.find('.pma_hint').each(function () {
3787         PMA_tooltip(
3788             $(this).children('img'),
3789             'img',
3790             $(this).children('span').html()
3791         );
3792     });
3795 AJAX.registerOnload('functions.js', function () {
3796     PMA_showHints();
3799 function PMA_mainMenuResizerCallback() {
3800     // 5 px margin for jumping menu in Chrome
3801     return $(document.body).width() - 5;
3803 // This must be fired only once after the initial page load
3804 $(function () {
3805     // Initialise the menu resize plugin
3806     $('#topmenu').menuResizer(PMA_mainMenuResizerCallback);
3807     // register resize event
3808     $(window).resize(function () {
3809         $('#topmenu').menuResizer('resize');
3810     });
3814  * Get the row number from the classlist (for example, row_1)
3815  */
3816 function PMA_getRowNumber(classlist)
3818     return parseInt(classlist.split(/\s+row_/)[1], 10);
3822  * Changes status of slider
3823  */
3824 function PMA_set_status_label($element)
3826     var text;
3827     if ($element.css('display') == 'none') {
3828         text = '+ ';
3829     } else {
3830         text = '- ';
3831     }
3832     $element.closest('.slide-wrapper').prev().find('span').text(text);
3836  * var  toggleButton  This is a function that creates a toggle
3837  *                    sliding button given a jQuery reference
3838  *                    to the correct DOM element
3839  */
3840 var toggleButton = function ($obj) {
3841     // In rtl mode the toggle switch is flipped horizontally
3842     // so we need to take that into account
3843     var right;
3844     if ($('span.text_direction', $obj).text() == 'ltr') {
3845         right = 'right';
3846     } else {
3847         right = 'left';
3848     }
3849     /**
3850      *  var  h  Height of the button, used to scale the
3851      *          background image and position the layers
3852      */
3853     var h = $obj.height();
3854     $('img', $obj).height(h);
3855     $('table', $obj).css('bottom', h - 1);
3856     /**
3857      *  var  on   Width of the "ON" part of the toggle switch
3858      *  var  off  Width of the "OFF" part of the toggle switch
3859      */
3860     var on  = $('td.toggleOn', $obj).width();
3861     var off = $('td.toggleOff', $obj).width();
3862     // Make the "ON" and "OFF" parts of the switch the same size
3863     // + 2 pixels to avoid overflowed
3864     $('td.toggleOn > div', $obj).width(Math.max(on, off) + 2);
3865     $('td.toggleOff > div', $obj).width(Math.max(on, off) + 2);
3866     /**
3867      *  var  w  Width of the central part of the switch
3868      */
3869     var w = parseInt(($('img', $obj).height() / 16) * 22, 10);
3870     // Resize the central part of the switch on the top
3871     // layer to match the background
3872     $('table td:nth-child(2) > div', $obj).width(w);
3873     /**
3874      *  var  imgw    Width of the background image
3875      *  var  tblw    Width of the foreground layer
3876      *  var  offset  By how many pixels to move the background
3877      *               image, so that it matches the top layer
3878      */
3879     var imgw = $('img', $obj).width();
3880     var tblw = $('table', $obj).width();
3881     var offset = parseInt(((imgw - tblw) / 2), 10);
3882     // Move the background to match the layout of the top layer
3883     $obj.find('img').css(right, offset);
3884     /**
3885      *  var  offw    Outer width of the "ON" part of the toggle switch
3886      *  var  btnw    Outer width of the central part of the switch
3887      */
3888     var offw = $('td.toggleOff', $obj).outerWidth();
3889     var btnw = $('table td:nth-child(2)', $obj).outerWidth();
3890     // Resize the main div so that exactly one side of
3891     // the switch plus the central part fit into it.
3892     $obj.width(offw + btnw + 2);
3893     /**
3894      *  var  move  How many pixels to move the
3895      *             switch by when toggling
3896      */
3897     var move = $('td.toggleOff', $obj).outerWidth();
3898     // If the switch is initialized to the
3899     // OFF state we need to move it now.
3900     if ($('div.container', $obj).hasClass('off')) {
3901         if (right == 'right') {
3902             $('div.container', $obj).animate({'left': '-=' + move + 'px'}, 0);
3903         } else {
3904             $('div.container', $obj).animate({'left': '+=' + move + 'px'}, 0);
3905         }
3906     }
3907     // Attach an 'onclick' event to the switch
3908     $('div.container', $obj).click(function () {
3909         if ($(this).hasClass('isActive')) {
3910             return false;
3911         } else {
3912             $(this).addClass('isActive');
3913         }
3914         var $msg = PMA_ajaxShowMessage();
3915         var $container = $(this);
3916         var callback = $('span.callback', this).text();
3917         var operator, url, removeClass, addClass;
3918         // Perform the actual toggle
3919         if ($(this).hasClass('on')) {
3920             if (right == 'right') {
3921                 operator = '-=';
3922             } else {
3923                 operator = '+=';
3924             }
3925             url = $(this).find('td.toggleOff > span').text();
3926             removeClass = 'on';
3927             addClass = 'off';
3928         } else {
3929             if (right == 'right') {
3930                 operator = '+=';
3931             } else {
3932                 operator = '-=';
3933             }
3934             url = $(this).find('td.toggleOn > span').text();
3935             removeClass = 'off';
3936             addClass = 'on';
3937         }
3938         $.post(url, {'ajax_request': true}, function (data) {
3939             if (typeof data !== 'undefined' && data.success === true) {
3940                 PMA_ajaxRemoveMessage($msg);
3941                 $container
3942                 .removeClass(removeClass)
3943                 .addClass(addClass)
3944                 .animate({'left': operator + move + 'px'}, function () {
3945                     $container.removeClass('isActive');
3946                 });
3947                 eval(callback);
3948             } else {
3949                 PMA_ajaxShowMessage(data.error, false);
3950                 $container.removeClass('isActive');
3951             }
3952         });
3953     });
3957  * Unbind all event handlers before tearing down a page
3958  */
3959 AJAX.registerTeardown('functions.js', function () {
3960     $('div.container').unbind('click');
3963  * Initialise all toggle buttons
3964  */
3965 AJAX.registerOnload('functions.js', function () {
3966     $('div.toggleAjax').each(function () {
3967         var $button = $(this).show();
3968         $button.find('img').each(function () {
3969             if (this.complete) {
3970                 toggleButton($button);
3971             } else {
3972                 $(this).load(function () {
3973                     toggleButton($button);
3974                 });
3975             }
3976         });
3977     });
3981  * Unbind all event handlers before tearing down a page
3982  */
3983 AJAX.registerTeardown('functions.js', function () {
3984     $(document).off('change', 'select.pageselector');
3985     $(document).off('click', 'a.formLinkSubmit');
3986     $('#update_recent_tables').unbind('ready');
3987     $('#sync_favorite_tables').unbind('ready');
3990 AJAX.registerOnload('functions.js', function () {
3992     /**
3993      * Autosubmit page selector
3994      */
3995     $(document).on('change', 'select.pageselector', function (event) {
3996         event.stopPropagation();
3997         // Check where to load the new content
3998         if ($(this).closest("#pma_navigation").length === 0) {
3999             // For the main page we don't need to do anything,
4000             $(this).closest("form").submit();
4001         } else {
4002             // but for the navigation we need to manually replace the content
4003             PMA_navigationTreePagination($(this));
4004         }
4005     });
4007     /**
4008      * Load version information asynchronously.
4009      */
4010     if ($('li.jsversioncheck').length > 0) {
4011         $.getJSON('version_check.php', {'server' : PMA_commonParams.get('server')}, PMA_current_version);
4012     }
4014     if ($('#is_git_revision').length > 0) {
4015         setTimeout(PMA_display_git_revision, 10);
4016     }
4018     /**
4019      * Slider effect.
4020      */
4021     PMA_init_slider();
4023     /**
4024      * Enables the text generated by PMA_Util::linkOrButton() to be clickable
4025      */
4026     $(document).on('click', 'a.formLinkSubmit', function (e) {
4027         if (! $(this).hasClass('requireConfirm')) {
4028             submitFormLink($(this));
4029             return false;
4030         }
4031     });
4033     var $updateRecentTables = $('#update_recent_tables');
4034     if ($updateRecentTables.length) {
4035         $.get(
4036             $updateRecentTables.attr('href'),
4037             {no_debug: true},
4038             function (data) {
4039                 if (typeof data !== 'undefined' && data.success === true) {
4040                     $('#pma_recent_list').html(data.list);
4041                 }
4042             }
4043         );
4044     }
4046     // Sync favorite tables from localStorage to pmadb.
4047     if ($('#sync_favorite_tables').length) {
4048         $.ajax({
4049             url: $('#sync_favorite_tables').attr("href"),
4050             cache: false,
4051             type: 'POST',
4052             data: {
4053                 favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined')
4054                     ? window.localStorage.favorite_tables
4055                     : '',
4056                 no_debug: true
4057             },
4058             success: function (data) {
4059                 // Update localStorage.
4060                 if (isStorageSupported('localStorage')) {
4061                     window.localStorage.favorite_tables = data.favorite_tables;
4062                 }
4063                 $('#pma_favorite_list').html(data.list);
4064             }
4065         });
4066     }
4067 }); // end of $()
4070  * Submits the form placed in place of a link due to the excessive url length
4072  * @param $link anchor
4073  * @returns {Boolean}
4074  */
4075 function submitFormLink($link)
4077     if ($link.attr('href').indexOf('=') != -1) {
4078         var data = $link.attr('href').substr($link.attr('href').indexOf('#') + 1).split('=', 2);
4079         $link.parents('form').append('<input type="hidden" name="' + data[0] + '" value="' + data[1] + '"/>');
4080     }
4081     $link.parents('form').submit();
4085  * Initializes slider effect.
4086  */
4087 function PMA_init_slider()
4089     $('div.pma_auto_slider').each(function () {
4090         var $this = $(this);
4091         if ($this.data('slider_init_done')) {
4092             return;
4093         }
4094         var $wrapper = $('<div>', {'class': 'slide-wrapper'});
4095         $wrapper.toggle($this.is(':visible'));
4096         $('<a>', {href: '#' + this.id, "class": 'ajax'})
4097             .text($this.attr('title'))
4098             .prepend($('<span>'))
4099             .insertBefore($this)
4100             .click(function () {
4101                 var $wrapper = $this.closest('.slide-wrapper');
4102                 var visible = $this.is(':visible');
4103                 if (!visible) {
4104                     $wrapper.show();
4105                 }
4106                 $this[visible ? 'hide' : 'show']('blind', function () {
4107                     $wrapper.toggle(!visible);
4108                     $wrapper.parent().toggleClass("print_ignore", visible);
4109                     PMA_set_status_label($this);
4110                 });
4111                 return false;
4112             });
4113         $this.wrap($wrapper);
4114         $this.removeAttr('title');
4115         PMA_set_status_label($this);
4116         $this.data('slider_init_done', 1);
4117     });
4121  * Initializes slider effect.
4122  */
4123 AJAX.registerOnload('functions.js', function () {
4124     PMA_init_slider();
4128  * Restores sliders to the state they were in before initialisation.
4129  */
4130 AJAX.registerTeardown('functions.js', function () {
4131     $('div.pma_auto_slider').each(function () {
4132         var $this = $(this);
4133         $this.removeData();
4134         $this.parent().replaceWith($this);
4135         $this.parent().children('a').remove();
4136     });
4140  * Creates a message inside an object with a sliding effect
4142  * @param msg    A string containing the text to display
4143  * @param $obj   a jQuery object containing the reference
4144  *                 to the element where to put the message
4145  *                 This is optional, if no element is
4146  *                 provided, one will be created below the
4147  *                 navigation links at the top of the page
4149  * @return bool   True on success, false on failure
4150  */
4151 function PMA_slidingMessage(msg, $obj)
4153     if (msg === undefined || msg.length === 0) {
4154         // Don't show an empty message
4155         return false;
4156     }
4157     if ($obj === undefined || ! $obj instanceof jQuery || $obj.length === 0) {
4158         // If the second argument was not supplied,
4159         // we might have to create a new DOM node.
4160         if ($('#PMA_slidingMessage').length === 0) {
4161             $('#page_content').prepend(
4162                 '<span id="PMA_slidingMessage" ' +
4163                 'style="display: inline-block;"></span>'
4164             );
4165         }
4166         $obj = $('#PMA_slidingMessage');
4167     }
4168     if ($obj.has('div').length > 0) {
4169         // If there already is a message inside the
4170         // target object, we must get rid of it
4171         $obj
4172         .find('div')
4173         .first()
4174         .fadeOut(function () {
4175             $obj
4176             .children()
4177             .remove();
4178             $obj
4179             .append('<div>' + msg + '</div>');
4180             // highlight any sql before taking height;
4181             PMA_highlightSQL($obj);
4182             $obj.find('div')
4183                 .first()
4184                 .hide();
4185             $obj
4186             .animate({
4187                 height: $obj.find('div').first().height()
4188             })
4189             .find('div')
4190             .first()
4191             .fadeIn();
4192         });
4193     } else {
4194         // Object does not already have a message
4195         // inside it, so we simply slide it down
4196         $obj.width('100%')
4197             .html('<div>' + msg + '</div>');
4198         // highlight any sql before taking height;
4199         PMA_highlightSQL($obj);
4200         var h = $obj
4201             .find('div')
4202             .first()
4203             .hide()
4204             .height();
4205         $obj
4206         .find('div')
4207         .first()
4208         .css('height', 0)
4209         .show()
4210         .animate({
4211                 height: h
4212             }, function () {
4213             // Set the height of the parent
4214             // to the height of the child
4215                 $obj
4216                 .height(
4217                     $obj
4218                     .find('div')
4219                     .first()
4220                     .height()
4221                 );
4222             });
4223     }
4224     return true;
4225 } // end PMA_slidingMessage()
4228  * Attach CodeMirror2 editor to SQL edit area.
4229  */
4230 AJAX.registerOnload('functions.js', function () {
4231     var $elm = $('#sqlquery');
4232     if ($elm.length > 0) {
4233         if (typeof CodeMirror != 'undefined') {
4234             codemirror_editor = PMA_getSQLEditor($elm);
4235             codemirror_editor.focus();
4236             codemirror_editor.on("blur", updateQueryParameters);
4237         } else {
4238             // without codemirror
4239             $elm.focus()
4240                 .bind('blur', updateQueryParameters);
4241         }
4242     }
4243     PMA_highlightSQL($('body'));
4245 AJAX.registerTeardown('functions.js', function () {
4246     if (codemirror_editor) {
4247         $('#sqlquery').text(codemirror_editor.getValue());
4248         codemirror_editor.toTextArea();
4249         codemirror_editor = false;
4250     }
4252 AJAX.registerOnload('functions.js', function () {
4253     // initializes all lock-page elements lock-id and
4254     // val-hash data property
4255     $('#page_content form.lock-page textarea, ' +
4256             '#page_content form.lock-page input[type="text"], '+
4257             '#page_content form.lock-page input[type="number"], '+
4258             '#page_content form.lock-page select').each(function (i) {
4259         $(this).data('lock-id', i);
4260         // val-hash is the hash of default value of the field
4261         // so that it can be compared with new value hash
4262         // to check whether field was modified or not.
4263         $(this).data('val-hash', AJAX.hash($(this).val()));
4264     });
4266     // initializes lock-page elements (input types checkbox and radio buttons)
4267     // lock-id and val-hash data property
4268     $('#page_content form.lock-page input[type="checkbox"], ' +
4269             '#page_content form.lock-page input[type="radio"]').each(function (i) {
4270         $(this).data('lock-id', i);
4271         $(this).data('val-hash', AJAX.hash($(this).is(":checked")));
4272     });
4275  * jQuery plugin to cancel selection in HTML code.
4276  */
4277 (function ($) {
4278     $.fn.noSelect = function (p) { //no select plugin by Paulo P.Marinas
4279         var prevent = (p === null) ? true : p;
4280         var is_msie = navigator.userAgent.indexOf('MSIE') > -1 || !!window.navigator.userAgent.match(/Trident.*rv\:11\./);
4281         var is_firefox = navigator.userAgent.indexOf('Firefox') > -1;
4282         var is_safari = navigator.userAgent.indexOf("Safari") > -1;
4283         var is_opera = navigator.userAgent.indexOf("Presto") > -1;
4284         if (prevent) {
4285             return this.each(function () {
4286                 if (is_msie || is_safari) {
4287                     $(this).bind('selectstart', function () {
4288                         return false;
4289                     });
4290                 } else if (is_firefox) {
4291                     $(this).css('MozUserSelect', 'none');
4292                     $('body').trigger('focus');
4293                 } else if (is_opera) {
4294                     $(this).bind('mousedown', function () {
4295                         return false;
4296                     });
4297                 } else {
4298                     $(this).attr('unselectable', 'on');
4299                 }
4300             });
4301         } else {
4302             return this.each(function () {
4303                 if (is_msie || is_safari) {
4304                     $(this).unbind('selectstart');
4305                 } else if (is_firefox) {
4306                     $(this).css('MozUserSelect', 'inherit');
4307                 } else if (is_opera) {
4308                     $(this).unbind('mousedown');
4309                 } else {
4310                     $(this).removeAttr('unselectable');
4311                 }
4312             });
4313         }
4314     }; //end noSelect
4315 })(jQuery);
4318  * jQuery plugin to correctly filter input fields by value, needed
4319  * because some nasty values may break selector syntax
4320  */
4321 (function ($) {
4322     $.fn.filterByValue = function (value) {
4323         return this.filter(function () {
4324             return $(this).val() === value;
4325         });
4326     };
4327 })(jQuery);
4330  * Return value of a cell in a table.
4331  */
4332 function PMA_getCellValue(td) {
4333     var $td = $(td);
4334     if ($td.is('.null')) {
4335         return '';
4336     } else if ((! $td.is('.to_be_saved')
4337         || $td.is('.set'))
4338         && $td.data('original_data')
4339     ) {
4340         return $td.data('original_data');
4341     } else {
4342         return $td.text();
4343     }
4347  * Unbind all event handlers before tearing down a page
4348  */
4349 AJAX.registerTeardown('functions.js', function () {
4350     $(document).off('click', 'a.themeselect');
4351     $(document).off('change', '.autosubmit');
4352     $('a.take_theme').unbind('click');
4355 AJAX.registerOnload('functions.js', function () {
4356     /**
4357      * Theme selector.
4358      */
4359     $(document).on('click', 'a.themeselect', function (e) {
4360         window.open(
4361             e.target,
4362             'themes',
4363             'left=10,top=20,width=510,height=350,scrollbars=yes,status=yes,resizable=yes'
4364             );
4365         return false;
4366     });
4368     /**
4369      * Automatic form submission on change.
4370      */
4371     $(document).on('change', '.autosubmit', function (e) {
4372         $(this).closest('form').submit();
4373     });
4375     /**
4376      * Theme changer.
4377      */
4378     $('a.take_theme').click(function (e) {
4379         var what = this.name;
4380         if (window.opener && window.opener.document.forms.setTheme.elements.set_theme) {
4381             window.opener.document.forms.setTheme.elements.set_theme.value = what;
4382             window.opener.document.forms.setTheme.submit();
4383             window.close();
4384             return false;
4385         }
4386         return true;
4387     });
4391  * Print button
4392  */
4393 function printPage()
4395     // Do print the page
4396     if (typeof(window.print) != 'undefined') {
4397         window.print();
4398     }
4402  * Unbind all event handlers before tearing down a page
4403  */
4404 AJAX.registerTeardown('functions.js', function () {
4405     $('input#print').unbind('click');
4406     $(document).off('click', 'a.create_view.ajax');
4407     $(document).off('keydown', '#createViewDialog input, #createViewDialog select');
4408     $(document).off('change', '#fkc_checkbox');
4411 AJAX.registerOnload('functions.js', function () {
4412     $('input#print').click(printPage);
4413     /**
4414      * Ajaxification for the "Create View" action
4415      */
4416     $(document).on('click', 'a.create_view.ajax', function (e) {
4417         e.preventDefault();
4418         PMA_createViewDialog($(this));
4419     });
4420     /**
4421      * Attach Ajax event handlers for input fields in the editor
4422      * and used to submit the Ajax request when the ENTER key is pressed.
4423      */
4424     if ($('#createViewDialog').length !== 0) {
4425         $(document).on('keydown', '#createViewDialog input, #createViewDialog select', function (e) {
4426             if (e.which === 13) { // 13 is the ENTER key
4427                 e.preventDefault();
4429                 // with preventing default, selection by <select> tag
4430                 // was also prevented in IE
4431                 $(this).blur();
4433                 $(this).closest('.ui-dialog').find('.ui-button:first').click();
4434             }
4435         }); // end $(document).on()
4436     }
4438     syntaxHighlighter = PMA_getSQLEditor($('textarea[name="view[as]"]'));
4442 function PMA_createViewDialog($this)
4444     var $msg = PMA_ajaxShowMessage();
4445     var syntaxHighlighter = null;
4446     $.get($this.attr('href') + '&ajax_request=1&ajax_dialog=1', function (data) {
4447         if (typeof data !== 'undefined' && data.success === true) {
4448             PMA_ajaxRemoveMessage($msg);
4449             var buttonOptions = {};
4450             buttonOptions[PMA_messages.strGo] = function () {
4451                 if (typeof CodeMirror !== 'undefined') {
4452                     syntaxHighlighter.save();
4453                 }
4454                 $msg = PMA_ajaxShowMessage();
4455                 $.get('view_create.php', $('#createViewDialog').find('form').serialize(), function (data) {
4456                     PMA_ajaxRemoveMessage($msg);
4457                     if (typeof data !== 'undefined' && data.success === true) {
4458                         $('#createViewDialog').dialog("close");
4459                         $('.result_query').html(data.message);
4460                         PMA_reloadNavigation();
4461                     } else {
4462                         PMA_ajaxShowMessage(data.error, false);
4463                     }
4464                 });
4465             };
4466             buttonOptions[PMA_messages.strClose] = function () {
4467                 $(this).dialog("close");
4468             };
4469             var $dialog = $('<div/>').attr('id', 'createViewDialog').append(data.message).dialog({
4470                 width: 600,
4471                 minWidth: 400,
4472                 modal: true,
4473                 buttons: buttonOptions,
4474                 title: PMA_messages.strCreateView,
4475                 close: function () {
4476                     $(this).remove();
4477                 }
4478             });
4479             // Attach syntax highlighted editor
4480             syntaxHighlighter = PMA_getSQLEditor($dialog.find('textarea'));
4481             $('input:visible[type=text]', $dialog).first().focus();
4482         } else {
4483             PMA_ajaxShowMessage(data.error);
4484         }
4485     });
4489  * Makes the breadcrumbs and the menu bar float at the top of the viewport
4490  */
4491 $(function () {
4492     if ($("#floating_menubar").length && $('#PMA_disable_floating_menubar').length === 0) {
4493         var left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
4494         $("#floating_menubar")
4495             .css('margin-' + left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width())
4496             .css(left, 0)
4497             .css({
4498                 'position': 'fixed',
4499                 'top': 0,
4500                 'width': '100%',
4501                 'z-index': 99
4502             })
4503             .append($('#serverinfo'))
4504             .append($('#topmenucontainer'));
4505         // Allow the DOM to render, then adjust the padding on the body
4506         setTimeout(function () {
4507             $('body').css(
4508                 'padding-top',
4509                 $('#floating_menubar').outerHeight(true)
4510             );
4511             $('#topmenu').menuResizer('resize');
4512         }, 4);
4513     }
4517  * Scrolls the page to the top if clicking the serverinfo bar
4518  */
4519 $(function () {
4520     $(document).delegate("#serverinfo, #goto_pagetop", "click", function (event) {
4521         event.preventDefault();
4522         $('html, body').animate({scrollTop: 0}, 'fast');
4523     });
4526 var checkboxes_sel = "input.checkall:checkbox:enabled";
4528  * Watches checkboxes in a form to set the checkall box accordingly
4529  */
4530 var checkboxes_changed = function () {
4531     var $form = $(this.form);
4532     // total number of checkboxes in current form
4533     var total_boxes = $form.find(checkboxes_sel).length;
4534     // number of checkboxes checked in current form
4535     var checked_boxes = $form.find(checkboxes_sel + ":checked").length;
4536     var $checkall = $form.find("input.checkall_box");
4537     if (total_boxes == checked_boxes) {
4538         $checkall.prop({checked: true, indeterminate: false});
4539     }
4540     else if (checked_boxes > 0) {
4541         $checkall.prop({checked: true, indeterminate: true});
4542     }
4543     else {
4544         $checkall.prop({checked: false, indeterminate: false});
4545     }
4547 $(document).on("change", checkboxes_sel, checkboxes_changed);
4549 $(document).on("change", "input.checkall_box", function () {
4550     var is_checked = $(this).is(":checked");
4551     $(this.form).find(checkboxes_sel).prop("checked", is_checked)
4552     .parents("tr").toggleClass("marked", is_checked);
4556  * Watches checkboxes in a sub form to set the sub checkall box accordingly
4557  */
4558 var sub_checkboxes_changed = function () {
4559     var $form = $(this).parent().parent();
4560     // total number of checkboxes in current sub form
4561     var total_boxes = $form.find(checkboxes_sel).length;
4562     // number of checkboxes checked in current sub form
4563     var checked_boxes = $form.find(checkboxes_sel + ":checked").length;
4564     var $checkall = $form.find("input.sub_checkall_box");
4565     if (total_boxes == checked_boxes) {
4566         $checkall.prop({checked: true, indeterminate: false});
4567     }
4568     else if (checked_boxes > 0) {
4569         $checkall.prop({checked: true, indeterminate: true});
4570     }
4571     else {
4572         $checkall.prop({checked: false, indeterminate: false});
4573     }
4575 $(document).on("change", checkboxes_sel + ", input.checkall_box:checkbox:enabled", sub_checkboxes_changed);
4577 $(document).on("change", "input.sub_checkall_box", function () {
4578     var is_checked = $(this).is(":checked");
4579     var $form = $(this).parent().parent();
4580     $form.find(checkboxes_sel).prop("checked", is_checked)
4581     .parents("tr").toggleClass("marked", is_checked);
4585  * Toggles row colors of a set of 'tr' elements starting from a given element
4587  * @param $start Starting element
4588  */
4589 function toggleRowColors($start)
4591     for (var $curr_row = $start; $curr_row.length > 0; $curr_row = $curr_row.next()) {
4592         if ($curr_row.hasClass('odd')) {
4593             $curr_row.removeClass('odd').addClass('even');
4594         } else if ($curr_row.hasClass('even')) {
4595             $curr_row.removeClass('even').addClass('odd');
4596         }
4597     }
4601  * Formats a byte number to human-readable form
4603  * @param bytes the bytes to format
4604  * @param optional subdecimals the number of digits after the point
4605  * @param optional pointchar the char to use as decimal point
4606  */
4607 function formatBytes(bytes, subdecimals, pointchar) {
4608     if (!subdecimals) {
4609         subdecimals = 0;
4610     }
4611     if (!pointchar) {
4612         pointchar = '.';
4613     }
4614     var units = ['B', 'KiB', 'MiB', 'GiB'];
4615     for (var i = 0; bytes > 1024 && i < units.length; i++) {
4616         bytes /= 1024;
4617     }
4618     var factor = Math.pow(10, subdecimals);
4619     bytes = Math.round(bytes * factor) / factor;
4620     bytes = bytes.toString().split('.').join(pointchar);
4621     return bytes + ' ' + units[i];
4624 AJAX.registerOnload('functions.js', function () {
4625     /**
4626      * Opens pma more themes link in themes browser, in new window instead of popup
4627      * This way, we don't break HTML validity
4628      */
4629     $("a._blank").prop("target", "_blank");
4630     /**
4631      * Reveal the login form to users with JS enabled
4632      * and focus the appropriate input field
4633      */
4634     var $loginform = $('#loginform');
4635     if ($loginform.length) {
4636         $loginform.find('.js-show').show();
4637         if ($('#input_username').val()) {
4638             $('#input_password').focus();
4639         } else {
4640             $('#input_username').focus();
4641         }
4642     }
4646  * Dynamically adjust the width of the boxes
4647  * on the table and db operations pages
4648  */
4649 (function () {
4650     function DynamicBoxes() {
4651         var $boxContainer = $('#boxContainer');
4652         if ($boxContainer.length) {
4653             var minWidth = $boxContainer.data('box-width');
4654             var viewport = $(window).width() - $('#pma_navigation').width();
4655             var slots = Math.floor(viewport / minWidth);
4656             $boxContainer.children()
4657             .each(function () {
4658                 if (viewport < minWidth) {
4659                     $(this).width(minWidth);
4660                 } else {
4661                     $(this).css('width', ((1 /  slots) * 100) + "%");
4662                 }
4663             })
4664             .removeClass('clearfloat')
4665             .filter(':nth-child(' + slots + 'n+1)')
4666             .addClass('clearfloat');
4667         }
4668     }
4669     AJAX.registerOnload('functions.js', function () {
4670         DynamicBoxes();
4671     });
4672     $(function () {
4673         $(window).resize(DynamicBoxes);
4674     });
4675 })();
4678  * Formats timestamp for display
4679  */
4680 function PMA_formatDateTime(date, seconds) {
4681     var result = $.datepicker.formatDate('yy-mm-dd', date);
4682     var timefmt = 'HH:mm';
4683     if (seconds) {
4684         timefmt = 'HH:mm:ss';
4685     }
4686     return result + ' ' + $.datepicker.formatTime(
4687         timefmt, {
4688             hour: date.getHours(),
4689             minute: date.getMinutes(),
4690             second: date.getSeconds()
4691         }
4692     );
4696  * Check than forms have less fields than max allowed by PHP.
4697  */
4698 function checkNumberOfFields() {
4699     if (typeof maxInputVars === 'undefined') {
4700         return false;
4701     }
4702     if (false === maxInputVars) {
4703         return false;
4704     }
4705     $('form').each(function() {
4706         var nbInputs = $(this).find(':input').length;
4707         if (nbInputs > maxInputVars) {
4708             var warning = PMA_sprintf(PMA_messages.strTooManyInputs, maxInputVars);
4709             PMA_ajaxShowMessage(warning);
4710             return false;
4711         }
4712         return true;
4713     });
4715     return true;
4719  * Ignore the displayed php errors.
4720  * Simply removes the displayed errors.
4722  * @param  clearPrevErrors whether to clear errors stored
4723  *             in $_SESSION['prev_errors'] at server
4725  */
4726 function PMA_ignorePhpErrors(clearPrevErrors){
4727     if (typeof(clearPrevErrors) === "undefined" ||
4728         clearPrevErrors === null
4729     ) {
4730         str = false;
4731     }
4732     // send AJAX request to error_report.php with send_error_report=0, exception_type=php & token.
4733     // It clears the prev_errors stored in session.
4734     if(clearPrevErrors){
4735         var $pmaReportErrorsForm = $('#pma_report_errors_form');
4736         $pmaReportErrorsForm.find('input[name="send_error_report"]').val(0); // change send_error_report to '0'
4737         $pmaReportErrorsForm.submit();
4738     }
4740     // remove displayed errors
4741     var $pmaErrors = $('#pma_errors');
4742     $pmaErrors.fadeOut( "slow");
4743     $pmaErrors.remove();
4747  * checks whether browser supports web storage
4749  * @param type the type of storage i.e. localStorage or sessionStorage
4751  * @returns bool
4752  */
4753 function isStorageSupported(type)
4755     try {
4756         window[type].setItem('PMATest', 'test');
4757         // Check whether key-value pair was set successfully
4758         if (window[type].getItem('PMATest') === 'test') {
4759             // Supported, remove test variable from storage
4760             window[type].removeItem('PMATest');
4761             return true;
4762         }
4763     } catch(error) {
4764         // Not supported
4765         PMA_ajaxShowMessage(PMA_messages.strNoLocalStorage, false);
4766     }
4767     return false;
4771  * Unbind all event handlers before tearing down a page
4772  */
4773 AJAX.registerTeardown('functions.js', function(){
4774     $(document).off('keydown', 'form input, form textarea, form select');
4777 AJAX.registerOnload('functions.js', function () {
4778     /**
4779      * Handle 'Ctrl/Alt + Enter' form submits
4780      */
4781     $('form input, form textarea, form select').on('keydown', function(e){
4782         if((e.ctrlKey && e.which == 13) || (e.altKey && e.which == 13)) {
4783             $form = $(this).closest('form');
4784             if (! $form.find('input[type="submit"]') ||
4785                 ! $form.find('input[type="submit"]').click()
4786             ) {
4787                 $form.submit();
4788             }
4789         }
4790     });
4794  * Unbind all event handlers before tearing down a page
4795  */
4796 AJAX.registerTeardown('functions.js', function(){
4797     $(document).off('change', 'input[type=radio][name="pw_hash"]');
4800 AJAX.registerOnload('functions.js', function(){
4801     /*
4802      * Display warning regarding SSL when sha256_password
4803      * method is selected
4804      * Used in user_password.php (Change Password link on index.php)
4805      */
4806     $(document).on("change", 'select#select_authentication_plugin_cp', function() {
4807         if (this.value === 'sha256_password') {
4808             $('#ssl_reqd_warning_cp').show();
4809         } else {
4810             $('#ssl_reqd_warning_cp').hide();
4811         }
4812     });