Test
[phpmyadmin/ammaryasirr.git] / js / functions.js
blobe25ec982ea918af60e8ef804ebe2d6aec57b1eac
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * general function, usally for data manipulation pages
4  *
5  */
7 /**
8  * @var sql_box_locked lock for the sqlbox textarea in the querybox/querywindow
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 = new Array();
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
24  */
25 var codemirror_editor = false;
27 /**
28  * @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
29  */
30 var chart_activeTimeouts = new Object();
31     
33 /**
34  * Add a hidden field to the form to indicate that this will be an
35  * Ajax request (only if this hidden field does not exist)
36  *
37  * @param   object   the form
38  */
39 function PMA_prepareForAjaxRequest($form) {
40     if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
41         $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />');
42     }
45 /**
46  * Generate a new password and copy it to the password input areas
47  *
48  * @param   object   the form that holds the password fields
49  *
50  * @return  boolean  always true
51  */
52 function suggestPassword(passwd_form) {
53     // restrict the password to just letters and numbers to avoid problems:
54     // "editors and viewers regard the password as multiple words and
55     // things like double click no longer work"
56     var pwchars = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
57     var passwordlength = 16;    // do we want that to be dynamic?  no, keep it simple :)
58     var passwd = passwd_form.generated_pw;
59     passwd.value = '';
61     for ( i = 0; i < passwordlength; i++ ) {
62         passwd.value += pwchars.charAt( Math.floor( Math.random() * pwchars.length ) )
63     }
64     passwd_form.text_pma_pw.value = passwd.value;
65     passwd_form.text_pma_pw2.value = passwd.value;
66     return true;
69 /**
70  * Version string to integer conversion.
71  */
72 function parseVersionString (str) {
73     if (typeof(str) != 'string') { return false; }
74     var add = 0;
75     // Parse possible alpha/beta/rc/
76     var state = str.split('-');
77     if (state.length >= 2) {
78         if (state[1].substr(0, 2) == 'rc') {
79             add = - 20 - parseInt(state[1].substr(2));
80         } else if (state[1].substr(0, 4) == 'beta') {
81             add =  - 40 - parseInt(state[1].substr(4));
82         } else if (state[1].substr(0, 5) == 'alpha') {
83             add =  - 60 - parseInt(state[1].substr(5));
84         } else if (state[1].substr(0, 3) == 'dev') {
85             /* We don't handle dev, it's git snapshot */
86             add = 0;
87         }
88     }
89     // Parse version
90     var x = str.split('.');
91     // Use 0 for non existing parts
92     var maj = parseInt(x[0]) || 0;
93     var min = parseInt(x[1]) || 0;
94     var pat = parseInt(x[2]) || 0;
95     var hotfix = parseInt(x[3]) || 0;
96     return  maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
99 /**
100  * Indicates current available version on main page.
101  */
102 function PMA_current_version() {
103     var current = parseVersionString(pmaversion);
104     var latest = parseVersionString(PMA_latest_version);
105     var version_information_message = PMA_messages['strLatestAvailable'] + ' ' + PMA_latest_version;
106     if (latest > current) {
107         var message = $.sprintf(PMA_messages['strNewerVersion'], PMA_latest_version, PMA_latest_date);
108         if (Math.floor(latest / 10000) == Math.floor(current / 10000)) {
109             /* Security update */
110             klass = 'error';
111         } else {
112             klass = 'notice';
113         }
114         $('#maincontainer').after('<div class="' + klass + '">' + message + '</div>');
115     }
116     if (latest == current) {
117         version_information_message = ' (' + PMA_messages['strUpToDate'] + ')';
118     }
119     $('#li_pma_version').append(version_information_message);
123  * for libraries/display_change_password.lib.php
124  *     libraries/user_password.php
126  */
128 function displayPasswordGenerateButton() {
129     $('#tr_element_before_generate_password').parent().append('<tr><td>' + PMA_messages['strGeneratePassword'] + '</td><td><input type="button" id="button_generate_password" value="' + PMA_messages['strGenerate'] + '" onclick="suggestPassword(this.form)" /><input type="text" name="generated_pw" id="generated_pw" /></td></tr>');
130     $('#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" id="button_generate_password" value="' + PMA_messages['strGenerate'] + '" onclick="suggestPassword(this.form)" /></span><input type="text" name="generated_pw" id="generated_pw" /></div>');
134  * Adds a date/time picker to an element
136  * @param   object  $this_element   a jQuery object pointing to the element
137  */
138 function PMA_addDatepicker($this_element) {
139     var showTimeOption = false;
140     if ($this_element.is('.datetimefield')) {
141         showTimeOption = true;
142     }
144     $this_element
145         .datepicker({
146         showOn: 'button',
147         buttonImage: themeCalendarImage, // defined in js/messages.php
148         buttonImageOnly: true,
149         duration: '',
150         time24h: true,
151         stepMinutes: 1,
152         stepHours: 1,
153         showTime: showTimeOption,
154         dateFormat: 'yy-mm-dd', // yy means year with four digits
155         altTimeField: '',
156         beforeShow: function(input, inst) {
157             // Remember that we came from the datepicker; this is used
158             // in tbl_change.js by verificationsAfterFieldChange()
159             $this_element.data('comes_from', 'datepicker');
160         },
161         constrainInput: false
162      });
166  * selects the content of a given object, f.e. a textarea
168  * @param   object  element     element of which the content will be selected
169  * @param   var     lock        variable which holds the lock for this element
170  *                              or true, if no lock exists
171  * @param   boolean only_once   if true this is only done once
172  *                              f.e. only on first focus
173  */
174 function selectContent( element, lock, only_once ) {
175     if ( only_once && only_once_elements[element.name] ) {
176         return;
177     }
179     only_once_elements[element.name] = true;
181     if ( lock  ) {
182         return;
183     }
185     element.select();
189  * Displays a confirmation box before to submit a "DROP/DELETE/ALTER" query.
190  * This function is called while clicking links
192  * @param   object   the link
193  * @param   object   the sql query to submit
195  * @return  boolean  whether to run the query or not
196  */
197 function confirmLink(theLink, theSqlQuery)
199     // Confirmation is not required in the configuration file
200     // or browser is Opera (crappy js implementation)
201     if (PMA_messages['strDoYouReally'] == '' || typeof(window.opera) != 'undefined') {
202         return true;
203     }
205     var is_confirmed = confirm(PMA_messages['strDoYouReally'] + ' :\n' + theSqlQuery);
206     if (is_confirmed) {
207         if ( typeof(theLink.href) != 'undefined' ) {
208             theLink.href += '&is_js_confirmed=1';
209         } else if ( typeof(theLink.form) != 'undefined' ) {
210             theLink.form.action += '?is_js_confirmed=1';
211         }
212     }
214     return is_confirmed;
215 } // end of the 'confirmLink()' function
219  * Displays a confirmation box before doing some action
221  * @param   object   the message to display
223  * @return  boolean  whether to run the query or not
225  * @todo used only by libraries/display_tbl.lib.php. figure out how it is used
226  *       and replace with a jQuery equivalent
227  */
228 function confirmAction(theMessage)
230     // TODO: Confirmation is not required in the configuration file
231     // or browser is Opera (crappy js implementation)
232     if (typeof(window.opera) != 'undefined') {
233         return true;
234     }
236     var is_confirmed = confirm(theMessage);
238     return is_confirmed;
239 } // end of the 'confirmAction()' function
243  * Displays an error message if a "DROP DATABASE" statement is submitted
244  * while it isn't allowed, else confirms a "DROP/DELETE/ALTER" query before
245  * sumitting it if required.
246  * This function is called by the 'checkSqlQuery()' js function.
248  * @param   object   the form
249  * @param   object   the sql query textarea
251  * @return  boolean  whether to run the query or not
253  * @see     checkSqlQuery()
254  */
255 function confirmQuery(theForm1, sqlQuery1)
257     // Confirmation is not required in the configuration file
258     if (PMA_messages['strDoYouReally'] == '') {
259         return true;
260     }
262     // The replace function (js1.2) isn't supported
263     else if (typeof(sqlQuery1.value.replace) == 'undefined') {
264         return true;
265     }
267     // js1.2+ -> validation with regular expressions
268     else {
269         // "DROP DATABASE" statement isn't allowed
270         if (PMA_messages['strNoDropDatabases'] != '') {
271             var drop_re = new RegExp('(^|;)\\s*DROP\\s+(IF EXISTS\\s+)?DATABASE\\s', 'i');
272             if (drop_re.test(sqlQuery1.value)) {
273                 alert(PMA_messages['strNoDropDatabases']);
274                 theForm1.reset();
275                 sqlQuery1.focus();
276                 return false;
277             } // end if
278         } // end if
280         // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
281         //
282         // TODO: find a way (if possible) to use the parser-analyser
283         // for this kind of verification
284         // For now, I just added a ^ to check for the statement at
285         // beginning of expression
287         var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|DATABASE|PROCEDURE)\\s', 'i');
288         var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
289         var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
290         var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
292         if (do_confirm_re_0.test(sqlQuery1.value)
293             || do_confirm_re_1.test(sqlQuery1.value)
294             || do_confirm_re_2.test(sqlQuery1.value)
295             || do_confirm_re_3.test(sqlQuery1.value)) {
296             var message      = (sqlQuery1.value.length > 100)
297                              ? sqlQuery1.value.substr(0, 100) + '\n    ...'
298                              : sqlQuery1.value;
299             var is_confirmed = confirm(PMA_messages['strDoYouReally'] + ' :\n' + message);
300             // statement is confirmed -> update the
301             // "is_js_confirmed" form field so the confirm test won't be
302             // run on the server side and allows to submit the form
303             if (is_confirmed) {
304                 theForm1.elements['is_js_confirmed'].value = 1;
305                 return true;
306             }
307             // statement is rejected -> do not submit the form
308             else {
309                 window.focus();
310                 sqlQuery1.focus();
311                 return false;
312             } // end if (handle confirm box result)
313         } // end if (display confirm box)
314     } // end confirmation stuff
316     return true;
317 } // end of the 'confirmQuery()' function
321  * Displays a confirmation box before disabling the BLOB repository for a given database.
322  * This function is called while clicking links
324  * @param   object   the database
326  * @return  boolean  whether to disable the repository or not
327  */
328 function confirmDisableRepository(theDB)
330     // Confirmation is not required in the configuration file
331     // or browser is Opera (crappy js implementation)
332     if (PMA_messages['strDoYouReally'] == '' || typeof(window.opera) != 'undefined') {
333         return true;
334     }
336     var is_confirmed = confirm(PMA_messages['strBLOBRepositoryDisableStrongWarning'] + '\n' + PMA_messages['strBLOBRepositoryDisableAreYouSure']);
338     return is_confirmed;
339 } // end of the 'confirmDisableBLOBRepository()' function
343  * Displays an error message if the user submitted the sql query form with no
344  * sql query, else checks for "DROP/DELETE/ALTER" statements
346  * @param   object   the form
348  * @return  boolean  always false
350  * @see     confirmQuery()
351  */
352 function checkSqlQuery(theForm)
354     var sqlQuery = theForm.elements['sql_query'];
355     var isEmpty  = 1;
357     // The replace function (js1.2) isn't supported -> basic tests
358     if (typeof(sqlQuery.value.replace) == 'undefined') {
359         isEmpty      = (sqlQuery.value == '') ? 1 : 0;
360         if (isEmpty && typeof(theForm.elements['sql_file']) != 'undefined') {
361             isEmpty  = (theForm.elements['sql_file'].value == '') ? 1 : 0;
362         }
363         if (isEmpty && typeof(theForm.elements['sql_localfile']) != 'undefined') {
364             isEmpty  = (theForm.elements['sql_localfile'].value == '') ? 1 : 0;
365         }
366         if (isEmpty && typeof(theForm.elements['id_bookmark']) != 'undefined') {
367             isEmpty  = (theForm.elements['id_bookmark'].value == null || theForm.elements['id_bookmark'].value == '');
368         }
369     }
370     // js1.2+ -> validation with regular expressions
371     else {
372         var space_re = new RegExp('\\s+');
373         if (typeof(theForm.elements['sql_file']) != 'undefined' &&
374                 theForm.elements['sql_file'].value.replace(space_re, '') != '') {
375             return true;
376         }
377         if (typeof(theForm.elements['sql_localfile']) != 'undefined' &&
378                 theForm.elements['sql_localfile'].value.replace(space_re, '') != '') {
379             return true;
380         }
381         if (isEmpty && typeof(theForm.elements['id_bookmark']) != 'undefined' &&
382                 (theForm.elements['id_bookmark'].value != null || theForm.elements['id_bookmark'].value != '') &&
383                 theForm.elements['id_bookmark'].selectedIndex != 0
384                 ) {
385             return true;
386         }
387         // Checks for "DROP/DELETE/ALTER" statements
388         if (sqlQuery.value.replace(space_re, '') != '') {
389             if (confirmQuery(theForm, sqlQuery)) {
390                 return true;
391             } else {
392                 return false;
393             }
394         }
395         theForm.reset();
396         isEmpty = 1;
397     }
399     if (isEmpty) {
400         sqlQuery.select();
401         alert(PMA_messages['strFormEmpty']);
402         sqlQuery.focus();
403         return false;
404     }
406     return true;
407 } // end of the 'checkSqlQuery()' function
410  * Check if a form's element is empty.
411  * An element containing only spaces is also considered empty
413  * @param   object   the form
414  * @param   string   the name of the form field to put the focus on
416  * @return  boolean  whether the form field is empty or not
417  */
418 function emptyCheckTheField(theForm, theFieldName)
420     var isEmpty  = 1;
421     var theField = theForm.elements[theFieldName];
422     // Whether the replace function (js1.2) is supported or not
423     var isRegExp = (typeof(theField.value.replace) != 'undefined');
425     if (!isRegExp) {
426         isEmpty      = (theField.value == '') ? 1 : 0;
427     } else {
428         var space_re = new RegExp('\\s+');
429         isEmpty      = (theField.value.replace(space_re, '') == '') ? 1 : 0;
430     }
432     return isEmpty;
433 } // end of the 'emptyCheckTheField()' function
437  * Check whether a form field is empty or not
439  * @param   object   the form
440  * @param   string   the name of the form field to put the focus on
442  * @return  boolean  whether the form field is empty or not
443  */
444 function emptyFormElements(theForm, theFieldName)
446     var theField = theForm.elements[theFieldName];
447     var isEmpty = emptyCheckTheField(theForm, theFieldName);
450     return isEmpty;
451 } // end of the 'emptyFormElements()' function
455  * Ensures a value submitted in a form is numeric and is in a range
457  * @param   object   the form
458  * @param   string   the name of the form field to check
459  * @param   integer  the minimum authorized value
460  * @param   integer  the maximum authorized value
462  * @return  boolean  whether a valid number has been submitted or not
463  */
464 function checkFormElementInRange(theForm, theFieldName, message, min, max)
466     var theField         = theForm.elements[theFieldName];
467     var val              = parseInt(theField.value);
469     if (typeof(min) == 'undefined') {
470         min = 0;
471     }
472     if (typeof(max) == 'undefined') {
473         max = Number.MAX_VALUE;
474     }
476     // It's not a number
477     if (isNaN(val)) {
478         theField.select();
479         alert(PMA_messages['strNotNumber']);
480         theField.focus();
481         return false;
482     }
483     // It's a number but it is not between min and max
484     else if (val < min || val > max) {
485         theField.select();
486         alert(message.replace('%d', val));
487         theField.focus();
488         return false;
489     }
490     // It's a valid number
491     else {
492         theField.value = val;
493     }
494     return true;
496 } // end of the 'checkFormElementInRange()' function
499 function checkTableEditForm(theForm, fieldsCnt)
501     // TODO: avoid sending a message if user just wants to add a line
502     // on the form but has not completed at least one field name
504     var atLeastOneField = 0;
505     var i, elm, elm2, elm3, val, id;
507     for (i=0; i<fieldsCnt; i++)
508     {
509         id = "#field_" + i + "_2";
510         elm = $(id);
511         val = elm.val()
512         if (val == 'VARCHAR' || val == 'CHAR' || val == 'BIT' || val == 'VARBINARY' || val == 'BINARY') {
513             elm2 = $("#field_" + i + "_3");
514             val = parseInt(elm2.val());
515             elm3 = $("#field_" + i + "_1");
516             if (isNaN(val) && elm3.val() != "") {
517                 elm2.select();
518                 alert(PMA_messages['strNotNumber']);
519                 elm2.focus();
520                 return false;
521             }
522         }
524         if (atLeastOneField == 0) {
525             id = "field_" + i + "_1";
526             if (!emptyCheckTheField(theForm, id)) {
527                 atLeastOneField = 1;
528             }
529         }
530     }
531     if (atLeastOneField == 0) {
532         var theField = theForm.elements["field_0_1"];
533         alert(PMA_messages['strFormEmpty']);
534         theField.focus();
535         return false;
536     }
538     // at least this section is under jQuery
539     if ($("input.textfield[name='table']").val() == "") {
540         alert(PMA_messages['strFormEmpty']);
541         $("input.textfield[name='table']").focus();
542         return false;
543     }
546     return true;
547 } // enf of the 'checkTableEditForm()' function
551  * Ensures the choice between 'transmit', 'zipped', 'gzipped' and 'bzipped'
552  * checkboxes is consistant
554  * @param   object   the form
555  * @param   string   a code for the action that causes this function to be run
557  * @return  boolean  always true
558  */
559 function checkTransmitDump(theForm, theAction)
561     var formElts = theForm.elements;
563     // 'zipped' option has been checked
564     if (theAction == 'zip' && formElts['zip'].checked) {
565         if (!formElts['asfile'].checked) {
566             theForm.elements['asfile'].checked = true;
567         }
568         if (typeof(formElts['gzip']) != 'undefined' && formElts['gzip'].checked) {
569             theForm.elements['gzip'].checked = false;
570         }
571         if (typeof(formElts['bzip']) != 'undefined' && formElts['bzip'].checked) {
572             theForm.elements['bzip'].checked = false;
573         }
574     }
575     // 'gzipped' option has been checked
576     else if (theAction == 'gzip' && formElts['gzip'].checked) {
577         if (!formElts['asfile'].checked) {
578             theForm.elements['asfile'].checked = true;
579         }
580         if (typeof(formElts['zip']) != 'undefined' && formElts['zip'].checked) {
581             theForm.elements['zip'].checked = false;
582         }
583         if (typeof(formElts['bzip']) != 'undefined' && formElts['bzip'].checked) {
584             theForm.elements['bzip'].checked = false;
585         }
586     }
587     // 'bzipped' option has been checked
588     else if (theAction == 'bzip' && formElts['bzip'].checked) {
589         if (!formElts['asfile'].checked) {
590             theForm.elements['asfile'].checked = true;
591         }
592         if (typeof(formElts['zip']) != 'undefined' && formElts['zip'].checked) {
593             theForm.elements['zip'].checked = false;
594         }
595         if (typeof(formElts['gzip']) != 'undefined' && formElts['gzip'].checked) {
596             theForm.elements['gzip'].checked = false;
597         }
598     }
599     // 'transmit' option has been unchecked
600     else if (theAction == 'transmit' && !formElts['asfile'].checked) {
601         if (typeof(formElts['zip']) != 'undefined' && formElts['zip'].checked) {
602             theForm.elements['zip'].checked = false;
603         }
604         if ((typeof(formElts['gzip']) != 'undefined' && formElts['gzip'].checked)) {
605             theForm.elements['gzip'].checked = false;
606         }
607         if ((typeof(formElts['bzip']) != 'undefined' && formElts['bzip'].checked)) {
608             theForm.elements['bzip'].checked = false;
609         }
610     }
612     return true;
613 } // end of the 'checkTransmitDump()' function
615 $(document).ready(function() {
616     /**
617      * Row marking in horizontal mode (use "live" so that it works also for
618      * next pages reached via AJAX); a tr may have the class noclick to remove
619      * this behavior.
620      */
621     $('tr.odd:not(.noclick), tr.even:not(.noclick)').live('click',function(e) {
622         // do not trigger when clicked on anchor
623         if ($(e.target).is('a, img, a *')) {
624             return;
625         }
626         var $tr = $(this);
627         
628         // make the table unselectable (to prevent default highlighting when shift+click)
629         $tr.parents('table').noSelect();
630         
631         if (!e.shiftKey || last_clicked_row == -1) {
632             // usual click
633             
634             // XXX: FF fires two click events for <label> (label and checkbox), so we need to handle this differently
635             var $checkbox = $tr.find(':checkbox');
636             if ($checkbox.length) {
637                 // checkbox in a row, add or remove class depending on checkbox state
638                 var checked = $checkbox.attr('checked');
639                 if (!$(e.target).is(':checkbox, label')) {
640                     checked = !checked;
641                     $checkbox.attr('checked', checked);
642                 }
643                 if (checked) {
644                     $tr.addClass('marked');
645                 } else {
646                     $tr.removeClass('marked');
647                 }
648                 last_click_checked = checked;
649             } else {
650                 // normaln data table, just toggle class
651                 $tr.toggleClass('marked');
652                 last_click_checked = false;
653             }
654             
655             // remember the last clicked row
656             last_clicked_row = last_click_checked ? $('tr.odd:not(.noclick), tr.even:not(.noclick)').index(this) : -1;
657             last_shift_clicked_row = -1;
658         } else {
659             // handle the shift click
660             var start, end;
661             
662             // clear last shift click result
663             if (last_shift_clicked_row >= 0) {
664                 if (last_shift_clicked_row >= last_clicked_row) {
665                     start = last_clicked_row;
666                     end = last_shift_clicked_row;
667                 } else {
668                     start = last_shift_clicked_row;
669                     end = last_clicked_row;
670                 }
671                 $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
672                     .slice(start, end + 1)
673                     .removeClass('marked')
674                     .find(':checkbox')
675                     .attr('checked', false);
676             }
677             
678             // handle new shift click
679             var curr_row = $('tr.odd:not(.noclick), tr.even:not(.noclick)').index(this);
680             if (curr_row >= last_clicked_row) {
681                 start = last_clicked_row;
682                 end = curr_row;
683             } else {
684                 start = curr_row;
685                 end = last_clicked_row;
686             }
687             $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)')
688                 .slice(start, end + 1)
689                 .addClass('marked')
690                 .find(':checkbox')
691                 .attr('checked', true);
692             
693             // remember the last shift clicked row
694             last_shift_clicked_row = curr_row;
695         }
696     });
698     /**
699      * Add a date/time picker to each element that needs it
700      */
701     $('.datefield, .datetimefield').each(function() {
702         PMA_addDatepicker($(this));
703         });
707  * True if last click is to check a row.
708  */
709 var last_click_checked = false;
712  * Zero-based index of last clicked row.
713  * Used to handle the shift + click event in the code above.
714  */
715 var last_clicked_row = -1;
718  * Zero-based index of last shift clicked row.
719  */
720 var last_shift_clicked_row = -1;
723  * Row highlighting in horizontal mode (use "live"
724  * so that it works also for pages reached via AJAX)
725  */
726 $(document).ready(function() {
727     $('tr.odd, tr.even').live('hover',function(event) {
728         var $tr = $(this);
729         $tr.toggleClass('hover',event.type=='mouseover');
730         $tr.children().toggleClass('hover',event.type=='mouseover');
731     });
735  * This array is used to remember mark status of rows in browse mode
736  */
737 var marked_row = new Array;
740  * marks all rows and selects its first checkbox inside the given element
741  * the given element is usaly a table or a div containing the table or tables
743  * @param    container    DOM element
744  */
745 function markAllRows( container_id ) {
747     $("#"+container_id).find("input:checkbox:enabled").attr('checked', 'checked')
748     .parents("tr").addClass("marked");
749     return true;
753  * marks all rows and selects its first checkbox inside the given element
754  * the given element is usaly a table or a div containing the table or tables
756  * @param    container    DOM element
757  */
758 function unMarkAllRows( container_id ) {
760     $("#"+container_id).find("input:checkbox:enabled").removeAttr('checked')
761     .parents("tr").removeClass("marked");
762     return true;
766  * Checks/unchecks all checkbox in given conainer (f.e. a form, fieldset or div)
768  * @param   string   container_id  the container id
769  * @param   boolean  state         new value for checkbox (true or false)
770  * @return  boolean  always true
771  */
772 function setCheckboxes( container_id, state ) {
774     if(state) {
775         $("#"+container_id).find("input:checkbox").attr('checked', 'checked');
776     }
777     else {
778         $("#"+container_id).find("input:checkbox").removeAttr('checked');
779     }
781     return true;
782 } // end of the 'setCheckboxes()' function
785   * Checks/unchecks all options of a <select> element
786   *
787   * @param   string   the form name
788   * @param   string   the element name
789   * @param   boolean  whether to check or to uncheck options
790   *
791   * @return  boolean  always true
792   */
793 function setSelectOptions(the_form, the_select, do_check)
795     $("form[name='"+ the_form +"'] select[name='"+the_select+"']").find("option").attr('selected', do_check);
796     return true;
797 } // end of the 'setSelectOptions()' function
800  * Sets current value for query box.
801  */
802 function setQuery(query) {
803     if (codemirror_editor) {
804         codemirror_editor.setValue(query);
805     } else {
806         document.sqlform.sql_query.value = query;
807     }
812   * Create quick sql statements.
813   *
814   */
815 function insertQuery(queryType) {
816     if (queryType == "clear") {
817         setQuery('');
818         return;
819     }
821     var myQuery = document.sqlform.sql_query;
822     var query = "";
823     var myListBox = document.sqlform.dummy;
824     var table = document.sqlform.table.value;
826     if (myListBox.options.length > 0) {
827         sql_box_locked = true;
828         var chaineAj = "";
829         var valDis = "";
830         var editDis = "";
831         var NbSelect = 0;
832         for (var i=0; i < myListBox.options.length; i++) {
833             NbSelect++;
834             if (NbSelect > 1) {
835                 chaineAj += ", ";
836                 valDis += ",";
837                 editDis += ",";
838             }
839             chaineAj += myListBox.options[i].value;
840             valDis += "[value-" + NbSelect + "]";
841             editDis += myListBox.options[i].value + "=[value-" + NbSelect + "]";
842         }
843         if (queryType == "selectall") {
844             query = "SELECT * FROM `" + table + "` WHERE 1";
845         } else if (queryType == "select") {
846             query = "SELECT " + chaineAj + " FROM `" + table + "` WHERE 1";
847         } else if (queryType == "insert") {
848                query = "INSERT INTO `" + table + "`(" + chaineAj + ") VALUES (" + valDis + ")";
849         } else if (queryType == "update") {
850             query = "UPDATE `" + table + "` SET " + editDis + " WHERE 1";
851         } else if(queryType == "delete") {
852             query = "DELETE FROM `" + table + "` WHERE 1";
853         }
854         setQuery(query);
855         sql_box_locked = false;
856     }
861   * Inserts multiple fields.
862   *
863   */
864 function insertValueQuery() {
865     var myQuery = document.sqlform.sql_query;
866     var myListBox = document.sqlform.dummy;
868     if(myListBox.options.length > 0) {
869         sql_box_locked = true;
870         var chaineAj = "";
871         var NbSelect = 0;
872         for(var i=0; i<myListBox.options.length; i++) {
873             if (myListBox.options[i].selected){
874                 NbSelect++;
875                 if (NbSelect > 1)
876                     chaineAj += ", ";
877                 chaineAj += myListBox.options[i].value;
878             }
879         }
881         /* CodeMirror support */
882         if (codemirror_editor) {
883             codemirror_editor.replaceSelection(chaineAj);
884         //IE support
885         } else if (document.selection) {
886             myQuery.focus();
887             sel = document.selection.createRange();
888             sel.text = chaineAj;
889             document.sqlform.insert.focus();
890         }
891         //MOZILLA/NETSCAPE support
892         else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart == "0") {
893             var startPos = document.sqlform.sql_query.selectionStart;
894             var endPos = document.sqlform.sql_query.selectionEnd;
895             var chaineSql = document.sqlform.sql_query.value;
897             myQuery.value = chaineSql.substring(0, startPos) + chaineAj + chaineSql.substring(endPos, chaineSql.length);
898         } else {
899             myQuery.value += chaineAj;
900         }
901         sql_box_locked = false;
902     }
906   * listbox redirection
907   */
908 function goToUrl(selObj, goToLocation) {
909     eval("document.location.href = '" + goToLocation + "pos=" + selObj.options[selObj.selectedIndex].value + "'");
913  * getElement
914  */
915 function getElement(e,f){
916     if(document.layers){
917         f=(f)?f:self;
918         if(f.document.layers[e]) {
919             return f.document.layers[e];
920         }
921         for(W=0;W<f.document.layers.length;W++) {
922             return(getElement(e,f.document.layers[W]));
923         }
924     }
925     if(document.all) {
926         return document.all[e];
927     }
928     return document.getElementById(e);
932   * Refresh the WYSIWYG scratchboard after changes have been made
933   */
934 function refreshDragOption(e) {
935     var elm = $('#' + e);
936     if (elm.css('visibility') == 'visible') {
937         refreshLayout();
938         TableDragInit();
939     }
943   * Refresh/resize the WYSIWYG scratchboard
944   */
945 function refreshLayout() {
946     var elm = $('#pdflayout')
947     var orientation = $('#orientation_opt').val();
948     if($('#paper_opt').length==1){
949         var paper = $('#paper_opt').val();
950     }else{
951         var paper = 'A4';
952     }
953     if (orientation == 'P') {
954         posa = 'x';
955         posb = 'y';
956     } else {
957         posa = 'y';
958         posb = 'x';
959     }
960     elm.css('width', pdfPaperSize(paper, posa) + 'px');
961     elm.css('height', pdfPaperSize(paper, posb) + 'px');
965   * Show/hide the WYSIWYG scratchboard
966   */
967 function ToggleDragDrop(e) {
968     var elm = $('#' + e);
969     if (elm.css('visibility') == 'hidden') {
970         PDFinit(); /* Defined in pdf_pages.php */
971         elm.css('visibility', 'visible');
972         elm.css('display', 'block');
973         $('#showwysiwyg').val('1')
974     } else {
975         elm.css('visibility', 'hidden');
976         elm.css('display', 'none');
977         $('#showwysiwyg').val('0')
978     }
982   * PDF scratchboard: When a position is entered manually, update
983   * the fields inside the scratchboard.
984   */
985 function dragPlace(no, axis, value) {
986     var elm = $('#table_' + no);
987     if (axis == 'x') {
988         elm.css('left', value + 'px');
989     } else {
990         elm.css('top', value + 'px');
991     }
995  * Returns paper sizes for a given format
996  */
997 function pdfPaperSize(format, axis) {
998     switch (format.toUpperCase()) {
999         case '4A0':
1000             if (axis == 'x') return 4767.87; else return 6740.79;
1001             break;
1002         case '2A0':
1003             if (axis == 'x') return 3370.39; else return 4767.87;
1004             break;
1005         case 'A0':
1006             if (axis == 'x') return 2383.94; else return 3370.39;
1007             break;
1008         case 'A1':
1009             if (axis == 'x') return 1683.78; else return 2383.94;
1010             break;
1011         case 'A2':
1012             if (axis == 'x') return 1190.55; else return 1683.78;
1013             break;
1014         case 'A3':
1015             if (axis == 'x') return 841.89; else return 1190.55;
1016             break;
1017         case 'A4':
1018             if (axis == 'x') return 595.28; else return 841.89;
1019             break;
1020         case 'A5':
1021             if (axis == 'x') return 419.53; else return 595.28;
1022             break;
1023         case 'A6':
1024             if (axis == 'x') return 297.64; else return 419.53;
1025             break;
1026         case 'A7':
1027             if (axis == 'x') return 209.76; else return 297.64;
1028             break;
1029         case 'A8':
1030             if (axis == 'x') return 147.40; else return 209.76;
1031             break;
1032         case 'A9':
1033             if (axis == 'x') return 104.88; else return 147.40;
1034             break;
1035         case 'A10':
1036             if (axis == 'x') return 73.70; else return 104.88;
1037             break;
1038         case 'B0':
1039             if (axis == 'x') return 2834.65; else return 4008.19;
1040             break;
1041         case 'B1':
1042             if (axis == 'x') return 2004.09; else return 2834.65;
1043             break;
1044         case 'B2':
1045             if (axis == 'x') return 1417.32; else return 2004.09;
1046             break;
1047         case 'B3':
1048             if (axis == 'x') return 1000.63; else return 1417.32;
1049             break;
1050         case 'B4':
1051             if (axis == 'x') return 708.66; else return 1000.63;
1052             break;
1053         case 'B5':
1054             if (axis == 'x') return 498.90; else return 708.66;
1055             break;
1056         case 'B6':
1057             if (axis == 'x') return 354.33; else return 498.90;
1058             break;
1059         case 'B7':
1060             if (axis == 'x') return 249.45; else return 354.33;
1061             break;
1062         case 'B8':
1063             if (axis == 'x') return 175.75; else return 249.45;
1064             break;
1065         case 'B9':
1066             if (axis == 'x') return 124.72; else return 175.75;
1067             break;
1068         case 'B10':
1069             if (axis == 'x') return 87.87; else return 124.72;
1070             break;
1071         case 'C0':
1072             if (axis == 'x') return 2599.37; else return 3676.54;
1073             break;
1074         case 'C1':
1075             if (axis == 'x') return 1836.85; else return 2599.37;
1076             break;
1077         case 'C2':
1078             if (axis == 'x') return 1298.27; else return 1836.85;
1079             break;
1080         case 'C3':
1081             if (axis == 'x') return 918.43; else return 1298.27;
1082             break;
1083         case 'C4':
1084             if (axis == 'x') return 649.13; else return 918.43;
1085             break;
1086         case 'C5':
1087             if (axis == 'x') return 459.21; else return 649.13;
1088             break;
1089         case 'C6':
1090             if (axis == 'x') return 323.15; else return 459.21;
1091             break;
1092         case 'C7':
1093             if (axis == 'x') return 229.61; else return 323.15;
1094             break;
1095         case 'C8':
1096             if (axis == 'x') return 161.57; else return 229.61;
1097             break;
1098         case 'C9':
1099             if (axis == 'x') return 113.39; else return 161.57;
1100             break;
1101         case 'C10':
1102             if (axis == 'x') return 79.37; else return 113.39;
1103             break;
1104         case 'RA0':
1105             if (axis == 'x') return 2437.80; else return 3458.27;
1106             break;
1107         case 'RA1':
1108             if (axis == 'x') return 1729.13; else return 2437.80;
1109             break;
1110         case 'RA2':
1111             if (axis == 'x') return 1218.90; else return 1729.13;
1112             break;
1113         case 'RA3':
1114             if (axis == 'x') return 864.57; else return 1218.90;
1115             break;
1116         case 'RA4':
1117             if (axis == 'x') return 609.45; else return 864.57;
1118             break;
1119         case 'SRA0':
1120             if (axis == 'x') return 2551.18; else return 3628.35;
1121             break;
1122         case 'SRA1':
1123             if (axis == 'x') return 1814.17; else return 2551.18;
1124             break;
1125         case 'SRA2':
1126             if (axis == 'x') return 1275.59; else return 1814.17;
1127             break;
1128         case 'SRA3':
1129             if (axis == 'x') return 907.09; else return 1275.59;
1130             break;
1131         case 'SRA4':
1132             if (axis == 'x') return 637.80; else return 907.09;
1133             break;
1134         case 'LETTER':
1135             if (axis == 'x') return 612.00; else return 792.00;
1136             break;
1137         case 'LEGAL':
1138             if (axis == 'x') return 612.00; else return 1008.00;
1139             break;
1140         case 'EXECUTIVE':
1141             if (axis == 'x') return 521.86; else return 756.00;
1142             break;
1143         case 'FOLIO':
1144             if (axis == 'x') return 612.00; else return 936.00;
1145             break;
1146     } // end switch
1148     return 0;
1152  * for playing media from the BLOB repository
1154  * @param   var
1155  * @param   var     url_params  main purpose is to pass the token
1156  * @param   var     bs_ref      BLOB repository reference
1157  * @param   var     m_type      type of BLOB repository media
1158  * @param   var     w_width     width of popup window
1159  * @param   var     w_height    height of popup window
1160  */
1161 function popupBSMedia(url_params, bs_ref, m_type, is_cust_type, w_width, w_height)
1163     // if width not specified, use default
1164     if (w_width == undefined)
1165         w_width = 640;
1167     // if height not specified, use default
1168     if (w_height == undefined)
1169         w_height = 480;
1171     // open popup window (for displaying video/playing audio)
1172     var mediaWin = window.open('bs_play_media.php?' + url_params + '&bs_reference=' + bs_ref + '&media_type=' + m_type + '&custom_type=' + is_cust_type, 'viewBSMedia', 'width=' + w_width + ', height=' + w_height + ', resizable=1, scrollbars=1, status=0');
1176  * popups a request for changing MIME types for files in the BLOB repository
1178  * @param   var     db                      database name
1179  * @param   var     table                   table name
1180  * @param   var     reference               BLOB repository reference
1181  * @param   var     current_mime_type       current MIME type associated with BLOB repository reference
1182  */
1183 function requestMIMETypeChange(db, table, reference, current_mime_type)
1185     // no mime type specified, set to default (nothing)
1186     if (undefined == current_mime_type)
1187         current_mime_type = "";
1189     // prompt user for new mime type
1190     var new_mime_type = prompt("Enter custom MIME type", current_mime_type);
1192     // if new mime_type is specified and is not the same as the previous type, request for mime type change
1193     if (new_mime_type && new_mime_type != current_mime_type)
1194         changeMIMEType(db, table, reference, new_mime_type);
1198  * changes MIME types for files in the BLOB repository
1200  * @param   var     db              database name
1201  * @param   var     table           table name
1202  * @param   var     reference       BLOB repository reference
1203  * @param   var     mime_type       new MIME type to be associated with BLOB repository reference
1204  */
1205 function changeMIMEType(db, table, reference, mime_type)
1207     // specify url and parameters for jQuery POST
1208     var mime_chg_url = 'bs_change_mime_type.php';
1209     var params = {bs_db: db, bs_table: table, bs_reference: reference, bs_new_mime_type: mime_type};
1211     // jQuery POST
1212     jQuery.post(mime_chg_url, params);
1216  * Jquery Coding for inline editing SQL_QUERY
1217  */
1218 $(document).ready(function(){
1219     $(".inline_edit_sql").live('click', function(){
1220         var db         = $(this).prev().find("input[name='db']").val();
1221         var table      = $(this).prev().find("input[name='table']").val();
1222         var token      = $(this).prev().find("input[name='token']").val();
1223         var sql_query  = $(this).prev().find("input[name='sql_query']").val();
1224         var $inner_sql = $(this).parent().prev().find('.inner_sql');
1225         var old_text   = $inner_sql.html();
1227         var new_content = "<textarea name=\"sql_query_edit\" id=\"sql_query_edit\">" + sql_query + "</textarea>\n";
1228         new_content    += "<input type=\"button\" class=\"btnSave\" value=\"" + PMA_messages['strGo'] + "\">\n";
1229         new_content    += "<input type=\"button\" class=\"btnDiscard\" value=\"" + PMA_messages['strCancel'] + "\">\n";
1230         $inner_sql.replaceWith(new_content);
1231         $(".btnSave").each(function(){
1232             $(this).click(function(){
1233                 sql_query = $(this).prev().val();
1234                 window.location.replace("import.php"
1235                                       + "?db=" + encodeURIComponent(db)
1236                                       + "&table=" + encodeURIComponent(table)
1237                                       + "&sql_query=" + encodeURIComponent(sql_query)
1238                                       + "&show_query=1"
1239                                       + "&token=" + token);
1240             });
1241         });
1242         $(".btnDiscard").each(function(){
1243             $(this).click(function(){
1244                 $(this).closest(".sql").html("<span class=\"syntax\"><span class=\"inner_sql\">" + old_text + "</span></span>");
1245             });
1246         });
1247         return false;
1248     });
1250     $('.sqlbutton').click(function(evt){
1251         insertQuery(evt.target.id);
1252         return false;
1253     });
1255     $("#export_type").change(function(){
1256         if($("#export_type").val()=='svg'){
1257             $("#show_grid_opt").attr("disabled","disabled");
1258             $("#orientation_opt").attr("disabled","disabled");
1259             $("#with_doc").attr("disabled","disabled");
1260             $("#show_table_dim_opt").removeAttr("disabled");
1261             $("#all_table_same_wide").removeAttr("disabled");
1262             $("#paper_opt").removeAttr("disabled","disabled");
1263             $("#show_color_opt").removeAttr("disabled","disabled");
1264             //$(this).css("background-color","yellow");
1265         }else if($("#export_type").val()=='dia'){
1266             $("#show_grid_opt").attr("disabled","disabled");
1267             $("#with_doc").attr("disabled","disabled");
1268             $("#show_table_dim_opt").attr("disabled","disabled");
1269             $("#all_table_same_wide").attr("disabled","disabled");
1270             $("#paper_opt").removeAttr("disabled","disabled");
1271             $("#show_color_opt").removeAttr("disabled","disabled");
1272             $("#orientation_opt").removeAttr("disabled","disabled");
1273         }else if($("#export_type").val()=='eps'){
1274             $("#show_grid_opt").attr("disabled","disabled");
1275             $("#orientation_opt").removeAttr("disabled");
1276             $("#with_doc").attr("disabled","disabled");
1277             $("#show_table_dim_opt").attr("disabled","disabled");
1278             $("#all_table_same_wide").attr("disabled","disabled");
1279             $("#paper_opt").attr("disabled","disabled");
1280             $("#show_color_opt").attr("disabled","disabled");
1282         }else if($("#export_type").val()=='pdf'){
1283             $("#show_grid_opt").removeAttr("disabled");
1284             $("#orientation_opt").removeAttr("disabled");
1285             $("#with_doc").removeAttr("disabled","disabled");
1286             $("#show_table_dim_opt").removeAttr("disabled","disabled");
1287             $("#all_table_same_wide").removeAttr("disabled","disabled");
1288             $("#paper_opt").removeAttr("disabled","disabled");
1289             $("#show_color_opt").removeAttr("disabled","disabled");
1290         }else{
1291             // nothing
1292         }
1293     });
1295     $('#sqlquery').focus().keydown(function (e) {
1296         if (e.ctrlKey && e.keyCode == 13) {
1297             $("#sqlqueryform").submit();
1298         }
1299     });
1301     if ($('#input_username')) {
1302         if ($('#input_username').val() == '') {
1303             $('#input_username').focus();
1304         } else {
1305             $('#input_password').focus();
1306         }
1307     }
1311  * Show a message on the top of the page for an Ajax request
1313  * @param   var     message     string containing the message to be shown.
1314  *                              optional, defaults to 'Loading...'
1315  * @param   var     timeout     number of milliseconds for the message to be visible
1316  *                              optional, defaults to 5000
1317  * @return  jQuery object       jQuery Element that holds the message div
1318  */
1319 function PMA_ajaxShowMessage(message, timeout) {
1321     //Handle the case when a empty data.message is passed. We don't want the empty message
1322     if (message == '') {
1323         return true;
1324     } else if (! message) {
1325         // If the message is undefined, show the default
1326         message = PMA_messages['strLoading'];
1327     }
1329     /**
1330      * @var timeout Number of milliseconds for which the message will be visible
1331      * @default 5000 ms
1332      */
1333     if (! timeout) {
1334         timeout = 5000;
1335     }
1337     // Create a parent element for the AJAX messages, if necessary
1338     if ($('#loading_parent').length == 0) {
1339         $('<div id="loading_parent"></div>')
1340         .insertBefore("#serverinfo");
1341     }
1343     // Update message count to create distinct message elements every time
1344     ajax_message_count++;
1346     // Remove all old messages, if any
1347     $(".ajax_notification[id^=ajax_message_num]").remove();
1349     /**
1350      * @var    $retval    a jQuery object containing the reference
1351      *                    to the created AJAX message
1352      */
1353     var $retval = $('<span class="ajax_notification" id="ajax_message_num_' + ajax_message_count + '"></span>')
1354         .hide()
1355         .appendTo("#loading_parent")
1356         .html(message)
1357         .fadeIn('medium')
1358         .delay(timeout)
1359         .fadeOut('medium', function() {
1360             $(this).remove();
1361         });
1363     return $retval;
1367  * Removes the message shown for an Ajax operation when it's completed
1368  */
1369 function PMA_ajaxRemoveMessage($this_msgbox) {
1370     if ($this_msgbox != undefined && $this_msgbox instanceof jQuery) {
1371         $this_msgbox
1372         .stop(true, true)
1373         .fadeOut('medium');
1374     }
1378  * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected
1379  */
1380 function PMA_showNoticeForEnum(selectElement) {
1381     var enum_notice_id = selectElement.attr("id").split("_")[1];
1382     enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2]) + 1);
1383     var selectedType = selectElement.attr("value");
1384     if (selectedType == "ENUM" || selectedType == "SET") {
1385         $("p[id='enum_notice_" + enum_notice_id + "']").show();
1386     } else {
1387         $("p[id='enum_notice_" + enum_notice_id + "']").hide();
1388     }
1392  * Generates a dialog box to pop up the create_table form
1393  */
1394 function PMA_createTableDialog( div, url , target) {
1395      /**
1396      *  @var    button_options  Object that stores the options passed to jQueryUI
1397      *                          dialog
1398      */
1399      var button_options = {};
1400      // in the following function we need to use $(this)
1401      button_options[PMA_messages['strCancel']] = function() {$(this).parent().dialog('close').remove();}
1403      var button_options_error = {};
1404      button_options_error[PMA_messages['strOK']] = function() {$(this).parent().dialog('close').remove();}
1406      var $msgbox = PMA_ajaxShowMessage();
1408      $.get( target , url ,  function(data) {
1409          //in the case of an error, show the error message returned.
1410          if (data.success != undefined && data.success == false) {
1411              div
1412              .append(data.error)
1413              .dialog({
1414                  title: PMA_messages['strCreateTable'],
1415                  height: 230,
1416                  width: 900,
1417                  open: PMA_verifyTypeOfAllColumns,
1418                  buttons : button_options_error
1419              })// end dialog options
1420              //remove the redundant [Back] link in the error message.
1421              .find('fieldset').remove();
1422          } else {
1423              div
1424              .append(data)
1425              .dialog({
1426                  title: PMA_messages['strCreateTable'],
1427                  height: 600,
1428                  width: 900,
1429                  open: PMA_verifyTypeOfAllColumns,
1430                  buttons : button_options
1431              }); // end dialog options
1432          }
1433          PMA_ajaxRemoveMessage($msgbox);
1434      }) // end $.get()
1439  * Creates a highcharts chart in the given container
1441  * @param   var     settings    object with highcharts properties that should be applied. (See also http://www.highcharts.com/ref/)
1442  *                              requires at least settings.chart.renderTo and settings.series to be set.
1443  *                              In addition there may be an additional property object 'realtime' that allows for realtime charting:
1444  *                              realtime: {
1445  *                                  url: adress to get the data from (will always add token, ajax_request=1 and chart_data=1 to the GET request)
1446  *                                  type: the GET request will also add type=[value of the type property] to the request
1447  *                                  callback: Callback function that should draw the point, it's called with 4 parameters in this order: 
1448  *                                      - the chart object
1449  *                                      - the current response value of the GET request, JSON parsed
1450  *                                      - the previous response value of the GET request, JSON parsed
1451  *                                      - the number of added points
1452  * 
1453  * @return  object   The created highcharts instance
1454  */
1455 function PMA_createChart(passedSettings) {
1456     var container = passedSettings.chart.renderTo;
1457     
1458     var settings = {
1459         chart: {
1460             type: 'spline',
1461             marginRight: 10,
1462             events: {
1463                 load: function() {
1464                     var thisChart = this;
1465                     var lastValue = null, curValue = null;
1466                     var numLoadedPoints = 0, otherSum = 0;
1467                     var diff;
1468                     // No realtime updates for graphs that are being exported, and disabled when no callback is set
1469                     if(thisChart.options.chart.forExport == true || 
1470                         ! passedSettings.realtime || 
1471                         ! passedSettings.realtime.callback) return;
1472                             
1473                     thisChart.options.realtime.timeoutCallBack = function() {
1474                         $.post(passedSettings.realtime.url,
1475                             { ajax_request: true, chart_data: 1, type: passedSettings.realtime.type },
1476                             function(data) {
1477                                 curValue = jQuery.parseJSON(data);
1478                                 
1479                                 if(lastValue==null) diff = curValue.x - thisChart.xAxis[0].getExtremes().max;
1480                                 else diff = parseInt(curValue.x - lastValue.x);
1481                                 
1482                                 thisChart.xAxis[0].setExtremes(
1483                                     thisChart.xAxis[0].getExtremes().min+diff, 
1484                                     thisChart.xAxis[0].getExtremes().max+diff, 
1485                                     false
1486                                 );
1487                                 
1488                                 passedSettings.realtime.callback(thisChart,curValue,lastValue,numLoadedPoints);
1489                                 
1490                                 lastValue = curValue;
1491                                 numLoadedPoints++;
1492                                 
1493                                 // Timeout has been cleared => don't start a new timeout
1494                                 if(chart_activeTimeouts[container]==null) return;
1495                                 
1496                                 chart_activeTimeouts[container] = setTimeout(
1497                                     thisChart.options.realtime.timeoutCallBack, 
1498                                     thisChart.options.realtime.refreshRate
1499                                 ); 
1500                         });
1501                     }
1502                     
1503                     chart_activeTimeouts[container] = setTimeout(thisChart.options.realtime.timeoutCallBack, 0);
1504                 }
1505             }
1506         },
1507         plotOptions: {
1508             series: {
1509                 marker: {
1510                     radius: 3
1511                 }
1512             }
1513         },
1514         credits: {
1515             enabled:false
1516         },
1517         xAxis: {
1518             type: 'datetime'
1519         },
1520         yAxis: {
1521             min: 0,
1522             title: {
1523                 text: PMA_messages['strTotalCount']
1524             },
1525             plotLines: [{
1526                 value: 0,
1527                 width: 1,
1528                 color: '#808080'
1529             }]
1530         },
1531         tooltip: {
1532             formatter: function() {
1533                     return '<b>' + this.series.name +'</b><br/>' +
1534                     Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + 
1535                     Highcharts.numberFormat(this.y, 2);
1536             }
1537         },
1538         exporting: {
1539             enabled: true
1540         },
1541         series: []
1542     }
1543     
1544     /* Set/Get realtime chart default values */
1545     if(passedSettings.realtime) {
1546         if(!passedSettings.realtime.refreshRate) 
1547             passedSettings.realtime.refreshRate = 5000;
1548         
1549         if(!passedSettings.realtime.numMaxPoints) 
1550             passedSettings.realtime.numMaxPoints = 30;
1551         
1552         settings.xAxis.min = new Date().getTime() - passedSettings.realtime.numMaxPoints * passedSettings.realtime.refreshRate;
1553         settings.xAxis.max = new Date().getTime() + passedSettings.realtime.refreshRate / 4;
1554     }
1556     // Overwrite/Merge default settings with passedsettings
1557     $.extend(true,settings,passedSettings);
1559     return new Highcharts.Chart(settings);
1563  * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not
1564  *  return a jQuery object yet and hence cannot be chained
1566  * @param   string      question
1567  * @param   string      url         URL to be passed to the callbackFn to make
1568  *                                  an Ajax call to
1569  * @param   function    callbackFn  callback to execute after user clicks on OK
1570  */
1572 jQuery.fn.PMA_confirm = function(question, url, callbackFn) {
1573     if (PMA_messages['strDoYouReally'] == '') {
1574         return true;
1575     }
1577     /**
1578      *  @var    button_options  Object that stores the options passed to jQueryUI
1579      *                          dialog
1580      */
1581     var button_options = {};
1582     button_options[PMA_messages['strOK']] = function(){
1583                                                 $(this).dialog("close").remove();
1585                                                 if($.isFunction(callbackFn)) {
1586                                                     callbackFn.call(this, url);
1587                                                 }
1588                                             };
1589     button_options[PMA_messages['strCancel']] = function() {$(this).dialog("close").remove();}
1591     $('<div id="confirm_dialog"></div>')
1592     .prepend(question)
1593     .dialog({buttons: button_options});
1597  * jQuery function to sort a table's body after a new row has been appended to it.
1598  * Also fixes the even/odd classes of the table rows at the end.
1600  * @param   string      text_selector   string to select the sortKey's text
1602  * @return  jQuery Object for chaining purposes
1603  */
1604 jQuery.fn.PMA_sort_table = function(text_selector) {
1605     return this.each(function() {
1607         /**
1608          * @var table_body  Object referring to the table's <tbody> element
1609          */
1610         var table_body = $(this);
1611         /**
1612          * @var rows    Object referring to the collection of rows in {@link table_body}
1613          */
1614         var rows = $(this).find('tr').get();
1616         //get the text of the field that we will sort by
1617         $.each(rows, function(index, row) {
1618             row.sortKey = $.trim($(row).find(text_selector).text().toLowerCase());
1619         })
1621         //get the sorted order
1622         rows.sort(function(a,b) {
1623             if(a.sortKey < b.sortKey) {
1624                 return -1;
1625             }
1626             if(a.sortKey > b.sortKey) {
1627                 return 1;
1628             }
1629             return 0;
1630         })
1632         //pull out each row from the table and then append it according to it's order
1633         $.each(rows, function(index, row) {
1634             $(table_body).append(row);
1635             row.sortKey = null;
1636         })
1638         //Re-check the classes of each row
1639         $(this).find('tr:odd')
1640         .removeClass('even').addClass('odd')
1641         .end()
1642         .find('tr:even')
1643         .removeClass('odd').addClass('even');
1644     })
1648  * jQuery coding for 'Create Table'.  Used on db_operations.php,
1649  * db_structure.php and db_tracking.php (i.e., wherever
1650  * libraries/display_create_table.lib.php is used)
1652  * Attach Ajax Event handlers for Create Table
1653  */
1654 $(document).ready(function() {
1656      /**
1657      * Attach event handler to the submit action of the create table minimal form
1658      * and retrieve the full table form and display it in a dialog
1659      *
1660      * @uses    PMA_ajaxShowMessage()
1661      */
1662     $("#create_table_form_minimal.ajax").live('submit', function(event) {
1663         event.preventDefault();
1664         $form = $(this);
1665         PMA_prepareForAjaxRequest($form);
1667         /*variables which stores the common attributes*/
1668         var url = $form.serialize();
1669         var action = $form.attr('action');
1670         var div =  $('<div id="create_table_dialog"></div>');
1672         /*Calling to the createTableDialog function*/
1673         PMA_createTableDialog(div, url, action);
1675         // empty table name and number of columns from the minimal form
1676         $form.find('input[name=table],input[name=num_fields]').val('');
1677     });
1679     /**
1680      * Attach event handler for submission of create table form (save)
1681      *
1682      * @uses    PMA_ajaxShowMessage()
1683      * @uses    $.PMA_sort_table()
1684      *
1685      */
1686     // .live() must be called after a selector, see http://api.jquery.com/live
1687     $("#create_table_form input[name=do_save_data]").live('click', function(event) {
1688         event.preventDefault();
1690         /**
1691          *  @var    the_form    object referring to the create table form
1692          */
1693         var $form = $("#create_table_form");
1695         /*
1696          * First validate the form; if there is a problem, avoid submitting it
1697          *
1698          * checkTableEditForm() needs a pure element and not a jQuery object,
1699          * this is why we pass $form[0] as a parameter (the jQuery object
1700          * is actually an array of DOM elements)
1701          */
1703         if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) {
1704             // OK, form passed validation step
1705             if ($form.hasClass('ajax')) {
1706                 PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
1707                 PMA_prepareForAjaxRequest($form);
1708                 //User wants to submit the form
1709                 $.post($form.attr('action'), $form.serialize() + "&do_save_data=" + $(this).val(), function(data) {
1710                     if(data.success == true) {
1711                         $('#properties_message')
1712                          .removeClass('error')
1713                          .html('');
1714                         PMA_ajaxShowMessage(data.message);
1715                         // Only if the create table dialog (distinct panel) exists
1716                         if ($("#create_table_dialog").length > 0) {
1717                             $("#create_table_dialog").dialog("close").remove();
1718                         }
1720                         /**
1721                          * @var tables_table    Object referring to the <tbody> element that holds the list of tables
1722                          */
1723                         var tables_table = $("#tablesForm").find("tbody").not("#tbl_summary_row");
1724                         // this is the first table created in this db
1725                         if (tables_table.length == 0) {
1726                             if (window.parent && window.parent.frame_content) {
1727                                 window.parent.frame_content.location.reload();
1728                             }
1729                         } else {
1730                             /**
1731                              * @var curr_last_row   Object referring to the last <tr> element in {@link tables_table}
1732                              */
1733                             var curr_last_row = $(tables_table).find('tr:last');
1734                             /**
1735                              * @var curr_last_row_index_string   String containing the index of {@link curr_last_row}
1736                              */
1737                             var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0];
1738                             /**
1739                              * @var curr_last_row_index Index of {@link curr_last_row}
1740                              */
1741                             var curr_last_row_index = parseFloat(curr_last_row_index_string);
1742                             /**
1743                              * @var new_last_row_index   Index of the new row to be appended to {@link tables_table}
1744                              */
1745                             var new_last_row_index = curr_last_row_index + 1;
1746                             /**
1747                              * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table}
1748                              */
1749                             var new_last_row_id = 'checkbox_tbl_' + new_last_row_index;
1751                             data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id);
1752                             //append to table
1753                             $(data.new_table_string)
1754                              .appendTo(tables_table);
1756                             //Sort the table
1757                             $(tables_table).PMA_sort_table('th');
1758                         }
1760                         //Refresh navigation frame as a new table has been added
1761                         if (window.parent && window.parent.frame_navigation) {
1762                             window.parent.frame_navigation.location.reload();
1763                         }
1764                     } else {
1765                         $('#properties_message')
1766                          .addClass('error')
1767                          .html(data.error);
1768                         // scroll to the div containing the error message
1769                         $('#properties_message')[0].scrollIntoView();
1770                     }
1771                 }) // end $.post()
1772             } // end if ($form.hasClass('ajax')
1773             else {
1774                 // non-Ajax submit
1775                 $form.append('<input type="hidden" name="do_save_data" value="save" />');
1776                 $form.submit();
1777             }
1778         } // end if (checkTableEditForm() )
1779     }) // end create table form (save)
1781     /**
1782      * Attach event handler for create table form (add fields)
1783      *
1784      * @uses    PMA_ajaxShowMessage()
1785      * @uses    $.PMA_sort_table()
1786      * @uses    window.parent.refreshNavigation()
1787      *
1788      */
1789     // .live() must be called after a selector, see http://api.jquery.com/live
1790     $("#create_table_form.ajax input[name=submit_num_fields]").live('click', function(event) {
1791         event.preventDefault();
1793         /**
1794          *  @var    the_form    object referring to the create table form
1795          */
1796         var $form = $("#create_table_form");
1798         var $msgbox = PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
1799         PMA_prepareForAjaxRequest($form);
1801         //User wants to add more fields to the table
1802         $.post($form.attr('action'), $form.serialize() + "&submit_num_fields=" + $(this).val(), function(data) {
1803             // if 'create_table_dialog' exists
1804             if ($("#create_table_dialog").length > 0) {
1805                 $("#create_table_dialog").html(data);
1806             }
1807             // if 'create_table_div' exists
1808             if ($("#create_table_div").length > 0) {
1809                 $("#create_table_div").html(data);
1810             }
1811             PMA_verifyTypeOfAllColumns();
1812             PMA_ajaxRemoveMessage($msgbox);
1813         }) //end $.post()
1815     }) // end create table form (add fields)
1817 }, 'top.frame_content'); //end $(document).ready for 'Create Table'
1820  * Attach Ajax event handlers for Drop Database. Moved here from db_structure.js
1821  * as it was also required on db_create.php
1823  * @uses    $.PMA_confirm()
1824  * @uses    PMA_ajaxShowMessage()
1825  * @uses    window.parent.refreshNavigation()
1826  * @uses    window.parent.refreshMain()
1827  * @see $cfg['AjaxEnable']
1828  */
1829 $(document).ready(function() {
1830     $("#drop_db_anchor").live('click', function(event) {
1831         event.preventDefault();
1833         //context is top.frame_content, so we need to use window.parent.db to access the db var
1834         /**
1835          * @var question    String containing the question to be asked for confirmation
1836          */
1837         var question = PMA_messages['strDropDatabaseStrongWarning'] + '\n' + PMA_messages['strDoYouReally'] + ' :\n' + 'DROP DATABASE ' + window.parent.db;
1839         $(this).PMA_confirm(question, $(this).attr('href') ,function(url) {
1841             PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
1842             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function(data) {
1843                 //Database deleted successfully, refresh both the frames
1844                 window.parent.refreshNavigation();
1845                 window.parent.refreshMain();
1846             }) // end $.get()
1847         }); // end $.PMA_confirm()
1848     }); //end of Drop Database Ajax action
1849 }) // end of $(document).ready() for Drop Database
1852  * Attach Ajax event handlers for 'Create Database'.  Used wherever libraries/
1853  * display_create_database.lib.php is used, ie main.php and server_databases.php
1855  * @uses    PMA_ajaxShowMessage()
1856  * @see $cfg['AjaxEnable']
1857  */
1858 $(document).ready(function() {
1860     $('#create_database_form.ajax').live('submit', function(event) {
1861         event.preventDefault();
1863         $form = $(this);
1865         PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
1866         PMA_prepareForAjaxRequest($form);
1868         $.post($form.attr('action'), $form.serialize(), function(data) {
1869             if(data.success == true) {
1870                 PMA_ajaxShowMessage(data.message);
1872                 //Append database's row to table
1873                 $("#tabledatabases")
1874                 .find('tbody')
1875                 .append(data.new_db_string)
1876                 .PMA_sort_table('.name')
1877                 .find('#db_summary_row')
1878                 .appendTo('#tabledatabases tbody')
1879                 .removeClass('odd even');
1881                 var $databases_count_object = $('#databases_count');
1882                 var databases_count = parseInt($databases_count_object.text());
1883                 $databases_count_object.text(++databases_count);
1884                 //Refresh navigation frame as a new database has been added
1885                 if (window.parent && window.parent.frame_navigation) {
1886                     window.parent.frame_navigation.location.reload();
1887                 }
1888             }
1889             else {
1890                 PMA_ajaxShowMessage(data.error);
1891             }
1892         }) // end $.post()
1893     }) // end $().live()
1894 })  // end $(document).ready() for Create Database
1897  * Attach Ajax event handlers for 'Change Password' on main.php
1898  */
1899 $(document).ready(function() {
1901     /**
1902      * Attach Ajax event handler on the change password anchor
1903      * @see $cfg['AjaxEnable']
1904      */
1905     $('#change_password_anchor.dialog_active').live('click',function(event) {
1906         event.preventDefault();
1907         return false;
1908         });
1909     $('#change_password_anchor.ajax').live('click', function(event) {
1910         event.preventDefault();
1911         $(this).removeClass('ajax').addClass('dialog_active');
1912         /**
1913          * @var button_options  Object containing options to be passed to jQueryUI's dialog
1914          */
1915         var button_options = {};
1916         button_options[PMA_messages['strCancel']] = function() {$(this).dialog('close').remove();}
1917         $.get($(this).attr('href'), {'ajax_request': true}, function(data) {
1918             $('<div id="change_password_dialog"></div>')
1919             .dialog({
1920                 title: PMA_messages['strChangePassword'],
1921                 width: 600,
1922                 close: function(ev,ui) {$(this).remove();},
1923                 buttons : button_options,
1924                 beforeClose: function(ev,ui){ $('#change_password_anchor.dialog_active').removeClass('dialog_active').addClass('ajax')}
1925             })
1926             .append(data);
1927             displayPasswordGenerateButton();
1928         }) // end $.get()
1929     }) // end handler for change password anchor
1931     /**
1932      * Attach Ajax event handler for Change Password form submission
1933      *
1934      * @uses    PMA_ajaxShowMessage()
1935      * @see $cfg['AjaxEnable']
1936      */
1937     $("#change_password_form.ajax").find('input[name=change_pw]').live('click', function(event) {
1938         event.preventDefault();
1940         /**
1941          * @var the_form    Object referring to the change password form
1942          */
1943         var the_form = $("#change_password_form");
1945         /**
1946          * @var this_value  String containing the value of the submit button.
1947          * Need to append this for the change password form on Server Privileges
1948          * page to work
1949          */
1950         var this_value = $(this).val();
1952         var $msgbox = PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
1953         $(the_form).append('<input type="hidden" name="ajax_request" value="true" />');
1955         $.post($(the_form).attr('action'), $(the_form).serialize() + '&change_pw='+ this_value, function(data) {
1956             if(data.success == true) {
1957                 $("#topmenucontainer").after(data.sql_query);
1958                 $("#change_password_dialog").hide().remove();
1959                 $("#edit_user_dialog").dialog("close").remove();
1960                 $('#change_password_anchor.dialog_active').removeClass('dialog_active').addClass('ajax');
1961                 PMA_ajaxRemoveMessage($msgbox);
1962             }
1963             else {
1964                 PMA_ajaxShowMessage(data.error);
1965             }
1966         }) // end $.post()
1967     }) // end handler for Change Password form submission
1968 }) // end $(document).ready() for Change Password
1971  * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when
1972  * the page loads and when the selected data type changes
1973  */
1974 $(document).ready(function() {
1975     // is called here for normal page loads and also when opening
1976     // the Create table dialog
1977     PMA_verifyTypeOfAllColumns();
1978     //
1979     // needs live() to work also in the Create Table dialog
1980     $("select[class='column_type']").live('change', function() {
1981         PMA_showNoticeForEnum($(this));
1982     });
1985 function PMA_verifyTypeOfAllColumns() {
1986     $("select[class='column_type']").each(function() {
1987         PMA_showNoticeForEnum($(this));
1988     });
1992  * Closes the ENUM/SET editor and removes the data in it
1993  */
1994 function disable_popup() {
1995     $("#popup_background").fadeOut("fast");
1996     $("#enum_editor").fadeOut("fast");
1997     // clear the data from the text boxes
1998     $("#enum_editor #values input").remove();
1999     $("#enum_editor input[type='hidden']").remove();
2003  * Opens the ENUM/SET editor and controls its functions
2004  */
2005 $(document).ready(function() {
2006     // Needs live() to work also in the Create table dialog
2007     $("a[class='open_enum_editor']").live('click', function() {
2008         // Center the popup
2009         var windowWidth = document.documentElement.clientWidth;
2010         var windowHeight = document.documentElement.clientHeight;
2011         var popupWidth = windowWidth/2;
2012         var popupHeight = windowHeight*0.8;
2013         var popupOffsetTop = windowHeight/2 - popupHeight/2;
2014         var popupOffsetLeft = windowWidth/2 - popupWidth/2;
2015         $("#enum_editor").css({"position":"absolute", "top": popupOffsetTop, "left": popupOffsetLeft, "width": popupWidth, "height": popupHeight});
2017         // Make it appear
2018         $("#popup_background").css({"opacity":"0.7"});
2019         $("#popup_background").fadeIn("fast");
2020         $("#enum_editor").fadeIn("fast");
2022         // Get the values
2023         var values = $(this).parent().prev("input").attr("value").split(",");
2024         $.each(values, function(index, val) {
2025             if(jQuery.trim(val) != "") {
2026                  // enclose the string in single quotes if it's not already
2027                  if(val.substr(0, 1) != "'") {
2028                       val = "'" + val;
2029                  }
2030                  if(val.substr(val.length-1, val.length) != "'") {
2031                       val = val + "'";
2032                  }
2033                 // escape the single quotes, except the mandatory ones enclosing the entire string
2034                 val = val.substr(1, val.length-2).replace(/''/g, "'").replace(/\\\\/g, '\\').replace(/\\'/g, "'").replace(/'/g, "&#039;");
2035                 // escape the greater-than symbol
2036                 val = val.replace(/>/g, "&gt;");
2037                 $("#enum_editor #values").append("<input type='text' value=" + val + " />");
2038             }
2039         });
2040         // So we know which column's data is being edited
2041         $("#enum_editor").append("<input type='hidden' value='" + $(this).parent().prev("input").attr("id") + "' />");
2042         return false;
2043     });
2045     // If the "close" link is clicked, close the enum editor
2046     // Needs live() to work also in the Create table dialog
2047     $("a[class='close_enum_editor']").live('click', function() {
2048         disable_popup();
2049     });
2051     // If the "cancel" link is clicked, close the enum editor
2052     // Needs live() to work also in the Create table dialog
2053     $("a[class='cancel_enum_editor']").live('click', function() {
2054         disable_popup();
2055     });
2057     // When "add a new value" is clicked, append an empty text field
2058     // Needs live() to work also in the Create table dialog
2059     $("a[class='add_value']").live('click', function() {
2060         $("#enum_editor #values").append("<input type='text' />");
2061     });
2063     // When the submit button is clicked, put the data back into the original form
2064     // Needs live() to work also in the Create table dialog
2065     $("#enum_editor input[type='submit']").live('click', function() {
2066         var value_array = new Array();
2067         $.each($("#enum_editor #values input"), function(index, input_element) {
2068             val = jQuery.trim(input_element.value);
2069             if(val != "") {
2070                 value_array.push("'" + val.replace(/\\/g, '\\\\').replace(/'/g, "''") + "'");
2071             }
2072         });
2073         // get the Length/Values text field where this value belongs
2074         var values_id = $("#enum_editor input[type='hidden']").attr("value");
2075         $("input[id='" + values_id + "']").attr("value", value_array.join(","));
2076         disable_popup();
2077      });
2079     /**
2080      * Hides certain table structure actions, replacing them with the word "More". They are displayed
2081      * in a dropdown menu when the user hovers over the word "More."
2082      */
2083     displayMoreTableOpts();
2086 function displayMoreTableOpts() {
2087     // Remove the actions from the table cells (they are available by default for JavaScript-disabled browsers)
2088     // if the table is not a view or information_schema (otherwise there is only one action to hide and there's no point)
2089     if($("input[type='hidden'][name='table_type']").val() == "table") {
2090         var $table = $("table[id='tablestructure']");
2091         $table.find("td[class='browse']").remove();
2092         $table.find("td[class='primary']").remove();
2093         $table.find("td[class='unique']").remove();
2094         $table.find("td[class='index']").remove();
2095         $table.find("td[class='fulltext']").remove();
2096         $table.find("td[class='spatial']").remove();
2097         $table.find("th[class='action']").attr("colspan", 3);
2099         // Display the "more" text
2100         $table.find("td[class='more_opts']").show();
2102         // Position the dropdown
2103         $(".structure_actions_dropdown").each(function() {
2104             // Optimize DOM querying
2105             var $this_dropdown = $(this);
2106              // The top offset must be set for IE even if it didn't change
2107             var cell_right_edge_offset = $this_dropdown.parent().position().left + $this_dropdown.parent().innerWidth();
2108             var left_offset = cell_right_edge_offset - $this_dropdown.innerWidth();
2109             var top_offset = $this_dropdown.parent().position().top + $this_dropdown.parent().innerHeight();
2110             $this_dropdown.offset({ top: top_offset, left: left_offset });
2111         });
2113         // A hack for IE6 to prevent the after_field select element from being displayed on top of the dropdown by
2114         // positioning an iframe directly on top of it
2115         var $after_field = $("select[name='after_field']");
2116         $("iframe[class='IE_hack']")
2117             .width($after_field.width())
2118             .height($after_field.height())
2119             .offset({
2120                 top: $after_field.offset().top,
2121                 left: $after_field.offset().left
2122             });
2124         // When "more" is hovered over, show the hidden actions
2125         $table.find("td[class='more_opts']")
2126             .mouseenter(function() {
2127                 if($.browser.msie && $.browser.version == "6.0") {
2128                     $("iframe[class='IE_hack']")
2129                         .show()
2130                         .width($after_field.width()+4)
2131                         .height($after_field.height()+4)
2132                         .offset({
2133                             top: $after_field.offset().top,
2134                             left: $after_field.offset().left
2135                         });
2136                 }
2137                 $(".structure_actions_dropdown").hide(); // Hide all the other ones that may be open
2138                 $(this).children(".structure_actions_dropdown").show();
2139                 // Need to do this again for IE otherwise the offset is wrong
2140                 if($.browser.msie) {
2141                     var left_offset_IE = $(this).offset().left + $(this).innerWidth() - $(this).children(".structure_actions_dropdown").innerWidth();
2142                     var top_offset_IE = $(this).offset().top + $(this).innerHeight();
2143                     $(this).children(".structure_actions_dropdown").offset({
2144                         top: top_offset_IE,
2145                         left: left_offset_IE });
2146                 }
2147             })
2148             .mouseleave(function() {
2149                 $(this).children(".structure_actions_dropdown").hide();
2150                 if($.browser.msie && $.browser.version == "6.0") {
2151                     $("iframe[class='IE_hack']").hide();
2152                 }
2153             });
2154     }
2156 $(document).ready(initTooltips);
2158 /* Displays tooltips */
2159 function initTooltips() {
2160     // Hide the footnotes from the footer (which are displayed for
2161     // JavaScript-disabled browsers) since the tooltip is sufficient
2162     $(".footnotes").hide();
2163     $(".footnotes span").each(function() {
2164         $(this).children("sup").remove();
2165     });
2166     // The border and padding must be removed otherwise a thin yellow box remains visible
2167     $(".footnotes").css("border", "none");
2168     $(".footnotes").css("padding", "0px");
2170     // Replace the superscripts with the help icon
2171     $("sup[class='footnotemarker']").hide();
2172     $("img[class='footnotemarker']").show();
2174     $("img[class='footnotemarker']").each(function() {
2175         var span_id = $(this).attr("id");
2176         span_id = span_id.split("_")[1];
2177         var tooltip_text = $(".footnotes span[id='footnote_" + span_id + "']").html();
2178         $(this).qtip({
2179             content: tooltip_text,
2180             show: { delay: 0 },
2181             hide: { delay: 1000 },
2182             style: { background: '#ffffcc' }
2183         });
2184     });
2187 function menuResize()
2189     var cnt = $('#topmenu');
2190     var wmax = cnt.innerWidth() - 5; // 5 px margin for jumping menu in Chrome
2191     var submenu = cnt.find('.submenu');
2192     var submenu_w = submenu.outerWidth(true);
2193     var submenu_ul = submenu.find('ul');
2194     var li = cnt.find('> li');
2195     var li2 = submenu_ul.find('li');
2196     var more_shown = li2.length > 0;
2197     var w = more_shown ? submenu_w : 0;
2199     // hide menu items
2200     var hide_start = 0;
2201     for (var i = 0; i < li.length-1; i++) { // li.length-1: skip .submenu element
2202         var el = $(li[i]);
2203         var el_width = el.outerWidth(true);
2204         el.data('width', el_width);
2205         w += el_width;
2206         if (w > wmax) {
2207             w -= el_width;
2208             if (w + submenu_w < wmax) {
2209                 hide_start = i;
2210             } else {
2211                 hide_start = i-1;
2212                 w -= $(li[i-1]).data('width');
2213             }
2214             break;
2215         }
2216     }
2218     if (hide_start > 0) {
2219         for (var i = hide_start; i < li.length-1; i++) {
2220             $(li[i])[more_shown ? 'prependTo' : 'appendTo'](submenu_ul);
2221         }
2222         submenu.addClass('shown');
2223     } else if (more_shown) {
2224         w -= submenu_w;
2225         // nothing hidden, maybe something can be restored
2226         for (var i = 0; i < li2.length; i++) {
2227             //console.log(li2[i], submenu_w);
2228             w += $(li2[i]).data('width');
2229             // item fits or (it is the last item and it would fit if More got removed)
2230             if (w+submenu_w < wmax || (i == li2.length-1 && w < wmax)) {
2231                 $(li2[i]).insertBefore(submenu);
2232                 if (i == li2.length-1) {
2233                     submenu.removeClass('shown');
2234                 }
2235                 continue;
2236             }
2237             break;
2238         }
2239     }
2240     if (submenu.find('.tabactive').length) {
2241         submenu.addClass('active').find('> a').removeClass('tab').addClass('tabactive');
2242     } else {
2243         submenu.removeClass('active').find('> a').addClass('tab').removeClass('tabactive');
2244     }
2247 $(function() {
2248     var topmenu = $('#topmenu');
2249     if (topmenu.length == 0) {
2250         return;
2251     }
2252     // create submenu container
2253     var link = $('<a />', {href: '#', 'class': 'tab'})
2254         .text(PMA_messages['strMore'])
2255         .click(function(e) {
2256             e.preventDefault();
2257         });
2258     var img = topmenu.find('li:first-child img');
2259     if (img.length) {
2260         img.clone().attr('src', img.attr('src').replace(/\/[^\/]+$/, '/b_more.png')).prependTo(link);
2261     }
2262     var submenu = $('<li />', {'class': 'submenu'})
2263         .append(link)
2264         .append($('<ul />'))
2265         .mouseenter(function() {
2266             if ($(this).find('ul .tabactive').length == 0) {
2267                 $(this).addClass('submenuhover').find('> a').addClass('tabactive');
2268             }
2269         })
2270         .mouseleave(function() {
2271             if ($(this).find('ul .tabactive').length == 0) {
2272                 $(this).removeClass('submenuhover').find('> a').removeClass('tabactive');
2273             }
2274         });
2275     topmenu.append(submenu);
2277     // populate submenu and register resize event
2278     $(window).resize(menuResize);
2279     menuResize();
2283  * Get the row number from the classlist (for example, row_1)
2284  */
2285 function PMA_getRowNumber(classlist) {
2286     return parseInt(classlist.split(/row_/)[1]);
2290  * Changes status of slider
2291  */
2292 function PMA_set_status_label(id) {
2293     if ($('#' + id).css('display') == 'none') {
2294         $('#anchor_status_' + id).text('+ ');
2295     } else {
2296         $('#anchor_status_' + id).text('- ');
2297     }
2301  * Initializes slider effect.
2302  */
2303 function PMA_init_slider() {
2304     $('.pma_auto_slider').each(function(idx, e) {
2305         if ($(e).hasClass('slider_init_done')) return;
2306         $(e).addClass('slider_init_done');
2307         $('<span id="anchor_status_' + e.id + '"></span>')
2308             .insertBefore(e);
2309         PMA_set_status_label(e.id);
2311         $('<a href="#' + e.id + '" id="anchor_' + e.id + '">' + e.title + '</a>')
2312             .insertBefore(e)
2313             .click(function() {
2314                 $('#' + e.id).toggle('clip', function() {
2315                     PMA_set_status_label(e.id);
2316                 });
2317                 return false;
2318             });
2319     });
2323  * Vertical pointer
2324  */
2325 $(document).ready(function() {
2326     $('.vpointer').live('hover',
2327         //handlerInOut
2328         function(e) {
2329         var $this_td = $(this);
2330         var row_num = PMA_getRowNumber($this_td.attr('class'));
2331         // for all td of the same vertical row, toggle hover
2332         $('.vpointer').filter('.row_' + row_num).toggleClass('hover');
2333         }
2334         );
2335 }) // end of $(document).ready() for vertical pointer
2337 $(document).ready(function() {
2338     /**
2339      * Vertical marker
2340      */
2341     $('.vmarker').live('click', function(e) {
2342         var $this_td = $(this);
2343         var row_num = PMA_getRowNumber($this_td.attr('class'));
2344         // for all td of the same vertical row, toggle the marked class
2345         $('.vmarker').filter('.row_' + row_num).toggleClass('marked');
2346         });
2348     /**
2349      * Reveal visual builder anchor
2350      */
2352     $('#visual_builder_anchor').show();
2354     /**
2355      * Page selector in db Structure (non-AJAX)
2356      */
2357     $('#tableslistcontainer').find('#pageselector').live('change', function() {
2358         $(this).parent("form").submit();
2359     });
2361     /**
2362      * Page selector in navi panel (non-AJAX)
2363      */
2364     $('#navidbpageselector').find('#pageselector').live('change', function() {
2365         $(this).parent("form").submit();
2366     });
2368     /**
2369      * Page selector in browse_foreigners windows (non-AJAX)
2370      */
2371     $('#body_browse_foreigners').find('#pageselector').live('change', function() {
2372         $(this).closest("form").submit();
2373     });
2375     /**
2376      * Load version information asynchronously.
2377      */
2378     if ($('.jsversioncheck').length > 0) {
2379         (function() {
2380             var s = document.createElement('script');
2381             s.type = 'text/javascript';
2382             s.async = true;
2383             s.src = 'http://www.phpmyadmin.net/home_page/version.js';
2384             s.onload = PMA_current_version;
2385             var x = document.getElementsByTagName('script')[0];
2386             x.parentNode.insertBefore(s, x);
2387         })();
2388     }
2390     /**
2391      * Slider effect.
2392      */
2393     PMA_init_slider();
2395     /**
2396      * Enables the text generated by PMA_linkOrButton() to be clickable
2397      */
2398     $('.clickprevimage')
2399         .css('color', function(index) {
2400             return $('a').css('color');
2401         })
2402         .css('cursor', function(index) {
2403             return $('a').css('cursor');
2404         }) //todo: hover effect
2405         .live('click',function(e) {
2406             $this_span = $(this);
2407             if ($this_span.closest('td').is('.inline_edit_anchor')) {
2408             // this would bind a second click event to the inline edit
2409             // anchor and would disturb its behavior
2410             } else {
2411                 $this_span.parent().find('input:image').click();
2412             }
2413         });
2415     $('#update_recent_tables').ready(function() {
2416         if (window.parent.frame_navigation != undefined
2417             && window.parent.frame_navigation.PMA_reloadRecentTable != undefined)
2418         {
2419             window.parent.frame_navigation.PMA_reloadRecentTable();
2420         }
2421     });
2423 }) // end of $(document).ready()
2426  * Attach Ajax event handlers for Export of Routines, Triggers and Events.
2428  * @uses    PMA_ajaxShowMessage()
2429  * @uses    PMA_ajaxRemoveMessage()
2431  * @see $cfg['AjaxEnable']
2432  */
2433 $(document).ready(function() {
2434     $('.export_routine_anchor, .export_trigger_anchor, .export_event_anchor').live('click', function(event) {
2435         event.preventDefault();
2436         var $msg = PMA_ajaxShowMessage(PMA_messages['strLoading']);
2437         $.get($(this).attr('href'), {'ajax_request': true}, function(data) {
2438             if(data.success == true) {
2439                 PMA_ajaxRemoveMessage($msg);
2440                 /**
2441                  * @var button_options  Object containing options for jQueryUI dialog buttons
2442                  */
2443                 var button_options = {};
2444                 button_options[PMA_messages['strClose']] = function() {$(this).dialog("close").remove();}
2445                 /**
2446                  * Display the dialog to the user
2447                  */
2448                 var $ajaxDialog = $('<div style="font-size: 0.9em;">'+data.message+'</div>').dialog({
2449                                       width: 500,
2450                                       buttons: button_options,
2451                                       title: data.title
2452                                   });
2453                 // Attach syntax highlited editor to export dialog
2454                 var elm = $ajaxDialog.find('textarea');
2455                 CodeMirror.fromTextArea(elm[0], {lineNumbers: true, matchBrackets: true, indentUnit: 4, mode: "text/x-mysql"});
2456             } else {
2457                 PMA_ajaxShowMessage(data.error);
2458             }
2459         }) // end $.get()
2460     }); // end $.live()
2461 }); // end of $(document).ready() for Export of Routines, Triggers and Events.
2464  * Creates a message inside an object with a sliding effect
2466  * @param   msg    A string containing the text to display
2467  * @param   $obj   a jQuery object containing the reference
2468  *                 to the element where to put the message
2469  *                 This is optional, if no element is
2470  *                 provided, one will be created below the
2471  *                 navigation links at the top of the page
2473  * @return  bool   True on success, false on failure
2474  */
2475 function PMA_slidingMessage(msg, $obj) {
2476     if (msg == undefined || msg.length == 0) {
2477         // Don't show an empty message
2478         return false;
2479     }
2480     if ($obj == undefined || ! $obj instanceof jQuery || $obj.length == 0) {
2481         // If the second argument was not supplied,
2482         // we might have to create a new DOM node.
2483         if ($('#PMA_slidingMessage').length == 0) {
2484             $('#topmenucontainer')
2485             .after('<span id="PMA_slidingMessage" '
2486                  + 'style="display: inline-block;"></span>');
2487         }
2488         $obj = $('#PMA_slidingMessage');
2489     }
2490     if ($obj.has('div').length > 0) {
2491         // If there already is a message inside the
2492         // target object, we must get rid of it
2493         $obj
2494         .find('div')
2495         .first()
2496         .fadeOut(function () {
2497             $obj
2498             .children()
2499             .remove();
2500             $obj
2501             .append('<div style="display: none;">' + msg + '</div>')
2502             .animate({
2503                 height: $obj.find('div').first().height()
2504             })
2505             .find('div')
2506             .first()
2507             .fadeIn();
2508         });
2509     } else {
2510         // Object does not already have a message
2511         // inside it, so we simply slide it down
2512         var h = $obj
2513                 .width('100%')
2514                 .html('<div style="display: none;">' + msg + '</div>')
2515                 .find('div')
2516                 .first()
2517                 .height();
2518         $obj
2519         .find('div')
2520         .first()
2521         .css('height', 0)
2522         .show()
2523         .animate({
2524                 height: h
2525             }, function() {
2526             // Set the height of the parent
2527             // to the height of the child
2528             $obj
2529             .height(
2530                 $obj
2531                 .find('div')
2532                 .first()
2533                 .height()
2534             );
2535         });
2536     }
2537     return true;
2538 } // end PMA_slidingMessage()
2541  * Attach Ajax event handlers for Drop functionality of Routines, Triggers and Events.
2543  * @uses    $.PMA_confirm()
2544  * @uses    PMA_ajaxShowMessage()
2545  * @see     $cfg['AjaxEnable']
2546  */
2547 $(document).ready(function() {
2548     $('.drop_routine_anchor, .drop_trigger_anchor, .drop_event_anchor').live('click', function(event) {
2549         event.preventDefault();
2550         /**
2551          * @var $curr_row    Object containing reference to the current row
2552          */
2553         var $curr_row = $(this).parents('tr');
2554         /**
2555          * @var question    String containing the question to be asked for confirmation
2556          */
2557         var question = $('<div></div>').text($curr_row.children('td').children('.drop_sql').html());
2558         $(this).PMA_confirm(question, $(this).attr('href'), function(url) {
2559             /**
2560              * @var    $msg    jQuery object containing the reference to
2561              *                 the AJAX message shown to the user.
2562              */
2563             var $msg = PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
2564             $.get(url, {'is_js_confirmed': 1, 'ajax_request': true}, function(data) {
2565                 if(data.success == true) {
2566                     /**
2567                      * @var $table    Object containing reference to the main list of elements.
2568                      */
2569                     var $table = $curr_row.parent();
2570                     if ($table.find('tr').length == 2) {
2571                         $table.hide("slow", function () {
2572                             $(this).find('tr.even, tr.odd').remove();
2573                             $('#nothing2display').show("slow");
2574                         });
2575                     } else {
2576                         $curr_row.hide("slow", function () {
2577                             $(this).remove();
2578                             // Now we have removed the row from the list, but maybe
2579                             // some row classes are wrong now. So we will itirate
2580                             // throught all rows and assign correct classes to them.
2581                             /**
2582                              * @var    ct    Count of processed rows.
2583                              */
2584                             var ct = 0;
2585                             $table.find('tr').has('td').each(function() {
2586                                 rowclass = (ct % 2 == 0) ? 'even' : 'odd';
2587                                 $(this).removeClass().addClass(rowclass);
2588                                 ct++;
2589                             });
2590                         });
2591                     }
2592                     // Show the query that we just executed
2593                     PMA_ajaxRemoveMessage($msg);
2594                     PMA_slidingMessage(data.sql_query);
2595                 } else {
2596                     PMA_ajaxShowMessage(PMA_messages['strErrorProcessingRequest'] + " : " + data.error);
2597                 }
2598             }) // end $.get()
2599         }) // end $.PMA_confirm()
2600     }); // end $.live()
2601 }); //end $(document).ready() for Drop functionality of Routines, Triggers and Events.
2604  * Attach Ajax event handlers for Drop Table.
2606  * @uses    $.PMA_confirm()
2607  * @uses    PMA_ajaxShowMessage()
2608  * @uses    window.parent.refreshNavigation()
2609  * @uses    window.parent.refreshMain()
2610  * @see $cfg['AjaxEnable']
2611  */
2612 $(document).ready(function() {
2613     $("#drop_tbl_anchor").live('click', function(event) {
2614         event.preventDefault();
2616         //context is top.frame_content, so we need to use window.parent.db to access the db var
2617         /**
2618          * @var question    String containing the question to be asked for confirmation
2619          */
2620         var question = PMA_messages['strDropTableStrongWarning'] + '\n' + PMA_messages['strDoYouReally'] + ' :\n' + 'DROP TABLE ' + window.parent.table;
2622         $(this).PMA_confirm(question, $(this).attr('href') ,function(url) {
2624             PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
2625             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function(data) {
2626                 //Database deleted successfully, refresh both the frames
2627                 window.parent.refreshNavigation();
2628                 window.parent.refreshMain();
2629             }) // end $.get()
2630         }); // end $.PMA_confirm()
2631     }); //end of Drop Table Ajax action
2632 }) // end of $(document).ready() for Drop Table
2635  * Attach Ajax event handlers for Truncate Table.
2637  * @uses    $.PMA_confirm()
2638  * @uses    PMA_ajaxShowMessage()
2639  * @uses    window.parent.refreshNavigation()
2640  * @uses    window.parent.refreshMain()
2641  * @see $cfg['AjaxEnable']
2642  */
2643 $(document).ready(function() {
2644     $("#truncate_tbl_anchor").live('click', function(event) {
2645         event.preventDefault();
2647         //context is top.frame_content, so we need to use window.parent.db to access the db var
2648         /**
2649          * @var question    String containing the question to be asked for confirmation
2650          */
2651         var question = PMA_messages['strTruncateTableStrongWarning'] + '\n' + PMA_messages['strDoYouReally'] + ' :\n' + 'TRUNCATE TABLE ' + window.parent.table;
2653         $(this).PMA_confirm(question, $(this).attr('href') ,function(url) {
2655             PMA_ajaxShowMessage(PMA_messages['strProcessingRequest']);
2656             $.get(url, {'is_js_confirmed': '1', 'ajax_request': true}, function(data) {
2657                 //Database deleted successfully, refresh both the frames
2658                 window.parent.refreshNavigation();
2659                 window.parent.refreshMain();
2660             }) // end $.get()
2661         }); // end $.PMA_confirm()
2662     }); //end of Drop Table Ajax action
2663 }) // end of $(document).ready() for Drop Table
2666  * Attach CodeMirror2 editor to SQL edit area.
2667  */
2668 $(document).ready(function() {
2669     var elm = $('#sqlquery');
2670     if (elm.length > 0) {
2671         codemirror_editor = CodeMirror.fromTextArea(elm[0], {lineNumbers: true, matchBrackets: true, indentUnit: 4, mode: "text/x-mysql"});
2672     }
2676  * jQuery plugin to cancel selection in HTML code.
2677  */
2678 (function ($) {
2679     $.fn.noSelect = function (p) { //no select plugin by Paulo P.Marinas
2680         var prevent = (p == null) ? true : p;
2681         if (prevent) {
2682             return this.each(function () {
2683                 if ($.browser.msie || $.browser.safari) $(this).bind('selectstart', function () {
2684                     return false;
2685                 });
2686                 else if ($.browser.mozilla) {
2687                     $(this).css('MozUserSelect', 'none');
2688                     $('body').trigger('focus');
2689                 } else if ($.browser.opera) $(this).bind('mousedown', function () {
2690                     return false;
2691                 });
2692                 else $(this).attr('unselectable', 'on');
2693             });
2694         } else {
2695             return this.each(function () {
2696                 if ($.browser.msie || $.browser.safari) $(this).unbind('selectstart');
2697                 else if ($.browser.mozilla) $(this).css('MozUserSelect', 'inherit');
2698                 else if ($.browser.opera) $(this).unbind('mousedown');
2699                 else $(this).removeAttr('unselectable', 'on');
2700             });
2701         }
2702     }; //end noSelect    
2703 })(jQuery);