Translated using Weblate (Korean)
[phpmyadmin.git] / js / console.js
blobc66f4590b35bea8f629479a1374e69b4172a7505
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * Used in or for console
4  *
5  * @package phpMyAdmin-Console
6  */
8 /**
9  * Console object
10  */
11 var PMA_console = {
12     /**
13      * @var object, jQuery object, selector is '#pma_console>.content'
14      * @access private
15      */
16     $consoleContent: null,
17     /**
18      * @var object, jQuery object, selector is '#pma_console .content',
19      *  used for resizer
20      * @access private
21      */
22     $consoleAllContents: null,
23     /**
24      * @var object, jQuery object, selector is '#pma_console .toolbar'
25      * @access private
26      */
27     $consoleToolbar: null,
28     /**
29      * @var object, jQuery object, selector is '#pma_console .template'
30      * @access private
31      */
32     $consoleTemplates: null,
33     /**
34      * @var object, jQuery object, form for submit
35      * @access private
36      */
37     $requestForm: null,
38     /**
39      * @var object, contain console config
40      * @access private
41      */
42     config: null,
43     /**
44      * @var bool, if console element exist, it'll be true
45      * @access public
46      */
47     isEnabled: false,
48     /**
49      * @var bool, make sure console events bind only once
50      * @access private
51      */
52     isInitialized: false,
53     /**
54      * Used for console initialize, reinit is ok, just some variable assignment
55      *
56      * @return void
57      */
58     initialize: function() {
60         if ($('#pma_console').length === 0) {
61             return;
62         }
64         PMA_console.isEnabled = true;
66         // Cookie var checks and init
67         if (! $.cookie('pma_console_height')) {
68             $.cookie('pma_console_height', 92);
69         }
70         if (! $.cookie('pma_console_mode')) {
71             $.cookie('pma_console_mode', 'info');
72         }
74         // Vars init
75         PMA_console.$consoleToolbar = $("#pma_console").find(">.toolbar");
76         PMA_console.$consoleContent = $("#pma_console").find(">.content");
77         PMA_console.$consoleAllContents = $('#pma_console').find('.content');
78         PMA_console.$consoleTemplates = $('#pma_console').find('>.templates');
80         // Generate a from for post
81         PMA_console.$requestForm = $('<form method="post" action="import.php">' +
82             '<input name="is_js_confirmed" value="0">' +
83             '<textarea name="sql_query"></textarea>' +
84             '<input name="console_message_id" value="0">' +
85             '<input name="server" value="">' +
86             '<input name="db" value="">' +
87             '<input name="table" value="">' +
88             '<input name="token" value="' +
89             PMA_commonParams.get('token') +
90             '">' +
91             '</form>'
92         );
93         PMA_console.$requestForm.bind('submit', AJAX.requestHandler);
95         // Event binds shouldn't run again
96         if (PMA_console.isInitialized === false) {
98             // Load config first
99             var tempConfig = JSON.parse($.cookie('pma_console_config'));
100             if (tempConfig) {
101                 if (tempConfig.alwaysExpand === true) {
102                     $('#pma_console_options input[name=always_expand]').prop('checked', true);
103                 }
104                 if (tempConfig.startHistory === true) {
105                     $('#pma_console_options').find('input[name=start_history]').prop('checked', true);
106                 }
107                 if (tempConfig.currentQuery === true) {
108                     $('#pma_console_options').find('input[name=current_query]').prop('checked', true);
109                 }
110                 if (ConsoleEnterExecutes === true) {
111                     $('#pma_console_options').find('input[name=enter_executes]').prop('checked', true);
112                 }
113                 if (tempConfig.darkTheme === true) {
114                     $('#pma_console_options').find('input[name=dark_theme]').prop('checked', true);
115                     $('#pma_console').find('>.content').addClass('console_dark_theme');
116                 }
117             } else {
118                 $('#pma_console_options').find('input[name=current_query]').prop('checked', true);
119             }
121             PMA_console.updateConfig();
123             PMA_consoleResizer.initialize();
124             PMA_consoleInput.initialize();
125             PMA_consoleMessages.initialize();
126             PMA_consoleBookmarks.initialize();
127             PMA_consoleDebug.initialize();
129             PMA_console.$consoleToolbar.children('.console_switch').click(PMA_console.toggle);
130             $(document).keydown(function(event) {
131                 // Ctrl + Alt + C
132                 if (event.ctrlKey && event.altKey && event.keyCode === 67) {
133                     PMA_console.toggle();
134                 }
135             });
137             $('#pma_console').find('.toolbar').children().mousedown(function(event) {
138                 event.preventDefault();
139                 event.stopImmediatePropagation();
140             });
142             $('#pma_console').find('.button.clear').click(function() {
143                 PMA_consoleMessages.clear();
144             });
146             $('#pma_console').find('.button.history').click(function() {
147                 PMA_consoleMessages.showHistory();
148             });
150             $('#pma_console').find('.button.options').click(function() {
151                 PMA_console.showCard('#pma_console_options');
152             });
154             $('#pma_console').find('.button.debug').click(function() {
155                 PMA_console.showCard('#debug_console');
156             });
158             PMA_console.$consoleContent.click(function(event) {
159                 if (event.target == this) {
160                     PMA_consoleInput.focus();
161                 }
162             });
164             $('#pma_console').find('.mid_layer').click(function() {
165                 PMA_console.hideCard($(this).parent().children('.card'));
166             });
167             $('#debug_console').find('.switch_button').click(function() {
168                 PMA_console.hideCard($(this).closest('.card'));
169             });
170             $('#pma_bookmarks').find('.switch_button').click(function() {
171                 PMA_console.hideCard($(this).closest('.card'));
172             });
173             $('#pma_console_options').find('.switch_button').click(function() {
174                 PMA_console.hideCard($(this).closest('.card'));
175             });
177             $('#pma_console_options').find('input[type=checkbox]').change(function() {
178                 PMA_console.updateConfig();
179             });
181             $('#pma_console_options').find('.button.default').click(function() {
182                 $('#pma_console_options input[name=always_expand]').prop('checked', false);
183                 $('#pma_console_options').find('input[name=start_history]').prop('checked', false);
184                 $('#pma_console_options').find('input[name=current_query]').prop('checked', true);
185                 $('#pma_console_options').find('input[name=enter_executes]').prop('checked', false);
186                 $('#pma_console_options').find('input[name=dark_theme]').prop('checked', false);
187                 PMA_console.updateConfig();
188             });
190             $('#pma_console_options').find('input[name=enter_executes]').change(function() {
191                 PMA_consoleMessages.showInstructions(PMA_console.config.enterExecutes);
192             });
194             $(document).ajaxComplete(function (event, xhr) {
195                 try {
196                     var data = $.parseJSON(xhr.responseText);
197                     PMA_console.ajaxCallback(data);
198                 } catch (e) {
199                     console.log("Invalid JSON!" + e.message);
200                     if (AJAX.xhr && AJAX.xhr.status === 0 && AJAX.xhr.statusText !== 'abort') {
201                         PMA_ajaxShowMessage($('<div />',{class:'error',html:PMA_messages.strRequestFailed+' ( '+AJAX.xhr.statusText+' )'}));
202                         AJAX.active = false;
203                         AJAX.xhr = null;
204                     }
205                 }
206             });
208             PMA_console.isInitialized = true;
209         }
211         // Change console mode from cookie
212         switch($.cookie('pma_console_mode')) {
213             case 'collapse':
214                 PMA_console.collapse();
215                 break;
216             /* jshint -W086 */// no break needed in default section
217             default:
218                 $.cookie('pma_console_mode', 'info');
219             case 'info':
220             /* jshint +W086 */
221                 PMA_console.info();
222                 break;
223             case 'show':
224                 PMA_console.show(true);
225                 PMA_console.scrollBottom();
226                 break;
227         }
228     },
229     /**
230      * Execute query and show results in console
231      *
232      * @return void
233      */
234     execute: function(queryString, options) {
235         if (typeof(queryString) != 'string' || ! /[a-z]|[A-Z]/.test(queryString)) {
236             return;
237         }
238         PMA_console.$requestForm.children('textarea').val(queryString);
239         PMA_console.$requestForm.children('[name=server]').attr('value', PMA_commonParams.get('server'));
240         if (options && options.db) {
241             PMA_console.$requestForm.children('[name=db]').val(options.db);
242             if (options.table) {
243                 PMA_console.$requestForm.children('[name=table]').val(options.table);
244             } else {
245                 PMA_console.$requestForm.children('[name=table]').val('');
246             }
247         } else {
248             PMA_console.$requestForm.children('[name=db]').val(
249                 (PMA_commonParams.get('db').length > 0 ? PMA_commonParams.get('db') : ''));
250         }
251         PMA_console.$requestForm.find('[name=profiling]').remove();
252         if (options && options.profiling === true) {
253             PMA_console.$requestForm.append('<input name="profiling" value="on">');
254         }
255         if (! confirmQuery(PMA_console.$requestForm[0], PMA_console.$requestForm.children('textarea')[0])) {
256             return;
257         }
258         PMA_console.$requestForm.children('[name=console_message_id]')
259             .val(PMA_consoleMessages.appendQuery({sql_query: queryString}).message_id);
260         PMA_console.$requestForm.trigger('submit');
261         PMA_consoleInput.clear();
262         PMA_reloadNavigation();
263     },
264     ajaxCallback: function(data) {
265         if (data && data.console_message_id) {
266             PMA_consoleMessages.updateQuery(data.console_message_id, data.success,
267                 (data._reloadQuerywindow ? data._reloadQuerywindow : false));
268         } else if ( data && data._reloadQuerywindow) {
269             if (data._reloadQuerywindow.sql_query.length > 0) {
270                 PMA_consoleMessages.appendQuery(data._reloadQuerywindow, 'successed')
271                     .$message.addClass(PMA_console.config.currentQuery ? '' : 'hide');
272             }
273         }
274     },
275     /**
276      * Change console to collapse mode
277      *
278      * @return void
279      */
280     collapse: function() {
281         $.cookie('pma_console_mode', 'collapse');
282         var pmaConsoleHeight = $.cookie('pma_console_height');
284         if (pmaConsoleHeight < 32) {
285             $.cookie('pma_console_height', 92);
286         }
287         PMA_console.$consoleToolbar.addClass('collapsed');
288         PMA_console.$consoleAllContents.height(pmaConsoleHeight);
289         PMA_console.$consoleContent.stop();
290         PMA_console.$consoleContent.animate({'margin-bottom': -1 * PMA_console.$consoleContent.outerHeight() + 'px'},
291             'fast', 'easeOutQuart', function() {
292                 PMA_console.$consoleContent.css({display:'none'});
293                 $(window).trigger('resize');
294             });
295         PMA_console.hideCard();
296     },
297     /**
298      * Show console
299      *
300      * @param bool inputFocus If true, focus the input line after show()
301      * @return void
302      */
303     show: function(inputFocus) {
304         $.cookie('pma_console_mode', 'show');
306         var pmaConsoleHeight = $.cookie('pma_console_height');
308         if (pmaConsoleHeight < 32) {
309             $.cookie('pma_console_height', 32);
310             PMA_console.collapse();
311             return;
312         }
313         PMA_console.$consoleContent.css({display:'block'});
314         if (PMA_console.$consoleToolbar.hasClass('collapsed')) {
315             PMA_console.$consoleToolbar.removeClass('collapsed');
316         }
317         PMA_console.$consoleAllContents.height(pmaConsoleHeight);
318         PMA_console.$consoleContent.stop();
319         PMA_console.$consoleContent.animate({'margin-bottom': 0},
320             'fast', 'easeOutQuart', function() {
321                 $(window).trigger('resize');
322                 if (inputFocus) {
323                     PMA_consoleInput.focus();
324                 }
325             });
326     },
327     /**
328      * Change console to SQL information mode
329      * this mode shows current SQL query
330      * This mode is the default mode
331      *
332      * @return void
333      */
334     info: function() {
335         // Under construction
336         PMA_console.collapse();
337     },
338     /**
339      * Toggle console mode between collapse/show
340      * Used for toggle buttons and shortcuts
341      *
342      * @return void
343      */
344     toggle: function() {
345         switch($.cookie('pma_console_mode')) {
346             case 'collapse':
347             case 'info':
348                 PMA_console.show(true);
349                 break;
350             case 'show':
351                 PMA_console.collapse();
352                 break;
353             default:
354                 PMA_consoleInitialize();
355         }
356     },
357     /**
358      * Scroll console to bottom
359      *
360      * @return void
361      */
362     scrollBottom: function() {
363         PMA_console.$consoleContent.scrollTop(PMA_console.$consoleContent.prop("scrollHeight"));
364     },
365     /**
366      * Show card
367      *
368      * @param string cardSelector Selector, select string will be "#pma_console " + cardSelector
369      * this param also can be JQuery object, if you need.
370      *
371      * @return void
372      */
373     showCard: function(cardSelector) {
374         var $card = null;
375         if (typeof(cardSelector) !== 'string') {
376             if (cardSelector.length > 0) {
377                 $card = cardSelector;
378             } else {
379                 return;
380             }
381         } else {
382             $card = $("#pma_console " + cardSelector);
383         }
384         if ($card.length === 0) {
385             return;
386         }
387         $card.parent().children('.mid_layer').show().fadeTo(0, 0.15);
388         $card.addClass('show');
389         PMA_consoleInput.blur();
390         if ($card.parents('.card').length > 0) {
391             PMA_console.showCard($card.parents('.card'));
392         }
393     },
394     /**
395      * Scroll console to bottom
396      *
397      * @param object $targetCard Target card JQuery object, if it's empty, function will hide all cards
398      * @return void
399      */
400     hideCard: function($targetCard) {
401         if (! $targetCard) {
402             $('#pma_console').find('.mid_layer').fadeOut(140);
403             $('#pma_console').find('.card').removeClass('show');
404         } else if ($targetCard.length > 0) {
405             $targetCard.parent().find('.mid_layer').fadeOut(140);
406             $targetCard.find('.card').removeClass('show');
407             $targetCard.removeClass('show');
408         }
409     },
410     /**
411      * Used for update console config
412      *
413      * @return void
414      */
415     updateConfig: function() {
416         PMA_console.config = {
417             alwaysExpand: $('#pma_console_options input[name=always_expand]').prop('checked'),
418             startHistory: $('#pma_console_options').find('input[name=start_history]').prop('checked'),
419             currentQuery: $('#pma_console_options').find('input[name=current_query]').prop('checked'),
420             enterExecutes: $('#pma_console_options').find('input[name=enter_executes]').prop('checked'),
421             darkTheme: $('#pma_console_options').find('input[name=dark_theme]').prop('checked')
422         };
423         $.cookie('pma_console_config', JSON.stringify(PMA_console.config));
424         /*Setting the dark theme of the console*/
425         if (PMA_console.config.darkTheme) {
426             $('#pma_console').find('>.content').addClass('console_dark_theme');
427         } else {
428             $('#pma_console').find('>.content').removeClass('console_dark_theme');
429         }
430     },
431     isSelect: function (queryString) {
432         var reg_exp = /^SELECT\s+/i;
433         return reg_exp.test(queryString);
434     }
438  * Resizer object
439  * Careful: this object UI logics highly related with functions under PMA_console
440  * Resizing min-height is 32, if small than it, console will collapse
441  */
442 var PMA_consoleResizer = {
443     _posY: 0,
444     _height: 0,
445     _resultHeight: 0,
446     /**
447      * Mousedown event handler for bind to resizer
448      *
449      * @return void
450      */
451     _mousedown: function(event) {
452         if ($.cookie('pma_console_mode') !== 'show') {
453             return;
454         }
455         PMA_consoleResizer._posY = event.pageY;
456         PMA_consoleResizer._height = PMA_console.$consoleContent.height();
457         $(document).mousemove(PMA_consoleResizer._mousemove);
458         $(document).mouseup(PMA_consoleResizer._mouseup);
459         // Disable text selection while resizing
460         $(document).bind('selectstart', function() { return false; });
461     },
462     /**
463      * Mousemove event handler for bind to resizer
464      *
465      * @return void
466      */
467     _mousemove: function(event) {
468         if (event.pageY < 35) {
469             event.pageY = 35
470         }
471         PMA_consoleResizer._resultHeight = PMA_consoleResizer._height + (PMA_consoleResizer._posY -event.pageY);
472         // Content min-height is 32, if adjusting height small than it we'll move it out of the page
473         if (PMA_consoleResizer._resultHeight <= 32) {
474             PMA_console.$consoleAllContents.height(32);
475             PMA_console.$consoleContent.css('margin-bottom', PMA_consoleResizer._resultHeight - 32);
476         }
477         else {
478             // Logic below makes viewable area always at bottom when adjusting height and content already at bottom
479             if (PMA_console.$consoleContent.scrollTop() + PMA_console.$consoleContent.innerHeight() + 16
480                 >= PMA_console.$consoleContent.prop('scrollHeight')) {
481                 PMA_console.$consoleAllContents.height(PMA_consoleResizer._resultHeight);
482                 PMA_console.scrollBottom();
483             } else {
484                 PMA_console.$consoleAllContents.height(PMA_consoleResizer._resultHeight);
485             }
486         }
487     },
488     /**
489      * Mouseup event handler for bind to resizer
490      *
491      * @return void
492      */
493     _mouseup: function() {
494         $.cookie('pma_console_height', PMA_consoleResizer._resultHeight);
495         PMA_console.show();
496         $(document).unbind('mousemove');
497         $(document).unbind('mouseup');
498         $(document).unbind('selectstart');
499     },
500     /**
501      * Used for console resizer initialize
502      *
503      * @return void
504      */
505     initialize: function() {
506         $('#pma_console').find('.toolbar').unbind('mousedown');
507         $('#pma_console').find('.toolbar').mousedown(PMA_consoleResizer._mousedown);
508     }
513  * Console input object
514  */
515 var PMA_consoleInput = {
516     /**
517      * @var array, contains Codemirror objects or input jQuery objects
518      * @access private
519      */
520     _inputs: null,
521     /**
522      * @var bool, if codemirror enabled
523      * @access private
524      */
525     _codemirror: false,
526     /**
527      * @var int, count for history navigation, 0 for current input
528      * @access private
529      */
530     _historyCount: 0,
531     /**
532      * @var string, current input when navigating through history
533      * @access private
534      */
535     _historyPreserveCurrent: null,
536     /**
537      * Used for console input initialize
538      *
539      * @return void
540      */
541     initialize: function() {
542         // _cm object can't be reinitialize
543         if (PMA_consoleInput._inputs !== null) {
544             return;
545         }
546         if (typeof CodeMirror !== 'undefined') {
547             PMA_consoleInput._codemirror = true;
548         }
549         PMA_consoleInput._inputs = [];
550         if (PMA_consoleInput._codemirror) {
551             PMA_consoleInput._inputs.console = CodeMirror($('#pma_console').find('.console_query_input')[0], {
552                 theme: 'pma',
553                 mode: 'text/x-sql',
554                 lineWrapping: true,
555                 extraKeys: {"Ctrl-Space": "autocomplete"},
556                 hintOptions: {"completeSingle": false, "completeOnSingleClick": true},
557                 gutters: ["CodeMirror-lint-markers"],
558                 lint: {
559                     "getAnnotations": CodeMirror.sqlLint,
560                     "async": true,
561                 }
562             });
563             PMA_consoleInput._inputs.console.on("inputRead", codemirrorAutocompleteOnInputRead);
564             PMA_consoleInput._inputs.console.on("keydown", function(instance, event) {
565                 PMA_consoleInput._historyNavigate(event);
566             });
567             if ($('#pma_bookmarks').length !== 0) {
568                 PMA_consoleInput._inputs.bookmark = CodeMirror($('#pma_console').find('.bookmark_add_input')[0], {
569                     theme: 'pma',
570                     mode: 'text/x-sql',
571                     lineWrapping: true,
572                     extraKeys: {"Ctrl-Space": "autocomplete"},
573                     hintOptions: {"completeSingle": false, "completeOnSingleClick": true},
574                     gutters: ["CodeMirror-lint-markers"],
575                     lint: {
576                         "getAnnotations": CodeMirror.sqlLint,
577                         "async": true,
578                     }
579                 });
580                 PMA_consoleInput._inputs.bookmark.on("inputRead", codemirrorAutocompleteOnInputRead);
581             }
582         } else {
583             PMA_consoleInput._inputs.console =
584                 $('<textarea>').appendTo('#pma_console .console_query_input')
585                     .on('keydown', PMA_consoleInput._historyNavigate);
586             if ($('#pma_bookmarks').length !== 0) {
587                 PMA_consoleInput._inputs.bookmark =
588                     $('<textarea>').appendTo('#pma_console .bookmark_add_input');
589             }
590         }
591         $('#pma_console').find('.console_query_input').keydown(PMA_consoleInput._keydown);
592     },
593     _historyNavigate: function(event) {
594         if (event.keyCode == 38 || event.keyCode == 40) {
595             var upPermitted = false;
596             var downPermitted = false;
597             var editor = PMA_consoleInput._inputs.console;
598             var cursorLine;
599             var totalLine;
600             if (PMA_consoleInput._codemirror) {
601                 cursorLine = editor.getCursor().line;
602                 totalLine = editor.lineCount();
603             } else {
604                 // Get cursor position from textarea
605                 var text = PMA_consoleInput.getText();
606                 cursorLine = text.substr(0, editor.prop("selectionStart")).split("\n").length - 1;
607                 totalLine = text.split(/\r*\n/).length;
608             }
609             if (cursorLine === 0) {
610                 upPermitted = true;
611             }
612             if (cursorLine == totalLine - 1) {
613                 downPermitted = true;
614             }
615             var nextCount;
616             var queryString = false;
617             if (upPermitted && event.keyCode == 38) {
618                 // Navigate up in history
619                 if (PMA_consoleInput._historyCount === 0) {
620                     PMA_consoleInput._historyPreserveCurrent = PMA_consoleInput.getText();
621                 }
622                 nextCount = PMA_consoleInput._historyCount + 1;
623                 queryString = PMA_consoleMessages.getHistory(nextCount);
624             } else if (downPermitted && event.keyCode == 40) {
625                 // Navigate down in history
626                 if (PMA_consoleInput._historyCount === 0) {
627                     return;
628                 }
629                 nextCount = PMA_consoleInput._historyCount - 1;
630                 if (nextCount === 0) {
631                     queryString = PMA_consoleInput._historyPreserveCurrent;
632                 } else {
633                     queryString = PMA_consoleMessages.getHistory(nextCount);
634                 }
635             }
636             if (queryString !== false) {
637                 PMA_consoleInput._historyCount = nextCount;
638                 PMA_consoleInput.setText(queryString, 'console');
639                 if (PMA_consoleInput._codemirror) {
640                     editor.setCursor(editor.lineCount(), 0);
641                 }
642                 event.preventDefault();
643             }
644         }
645     },
646     /**
647      * Mousedown event handler for bind to input
648      * Shortcut is Ctrl+Enter key or just ENTER, depending on console's
649      * configuration.
650      *
651      * @return void
652      */
653     _keydown: function(event) {
654         if (PMA_console.config.enterExecutes) {
655             // Enter, but not in combination with Shift (which writes a new line).
656             if (!event.shiftKey && event.keyCode === 13) {
657                 PMA_consoleInput.execute();
658             }
659         } else {
660             // Ctrl+Enter
661             if (event.ctrlKey && event.keyCode === 13) {
662                 PMA_consoleInput.execute();
663             }
664         }
665     },
666     /**
667      * Used for send text to PMA_console.execute()
668      *
669      * @return void
670      */
671     execute: function() {
672         if (PMA_consoleInput._codemirror) {
673             PMA_console.execute(PMA_consoleInput._inputs.console.getValue());
674         } else {
675             PMA_console.execute(PMA_consoleInput._inputs.console.val());
676         }
677     },
678     /**
679      * Used for clear the input
680      *
681      * @param string target, default target is console input
682      * @return void
683      */
684     clear: function(target) {
685         PMA_consoleInput.setText('', target);
686     },
687     /**
688      * Used for set focus to input
689      *
690      * @return void
691      */
692     focus: function() {
693         PMA_consoleInput._inputs.console.focus();
694     },
695     /**
696      * Used for blur input
697      *
698      * @return void
699      */
700     blur: function() {
701         if (PMA_consoleInput._codemirror) {
702             PMA_consoleInput._inputs.console.getInputField().blur();
703         } else {
704             PMA_consoleInput._inputs.console.blur();
705         }
706     },
707     /**
708      * Used for set text in input
709      *
710      * @param string text
711      * @param string target
712      * @return void
713      */
714     setText: function(text, target) {
715         if (PMA_consoleInput._codemirror) {
716             switch(target) {
717                 case 'bookmark':
718                     PMA_console.execute(PMA_consoleInput._inputs.bookmark.setValue(text));
719                     break;
720                 default:
721                 case 'console':
722                     PMA_console.execute(PMA_consoleInput._inputs.console.setValue(text));
723             }
724         } else {
725             switch(target) {
726                 case 'bookmark':
727                     PMA_console.execute(PMA_consoleInput._inputs.bookmark.val(text));
728                     break;
729                 default:
730                 case 'console':
731                     PMA_console.execute(PMA_consoleInput._inputs.console.val(text));
732             }
733         }
734     },
735     getText: function(target) {
736         if (PMA_consoleInput._codemirror) {
737             switch(target) {
738                 case 'bookmark':
739                     return PMA_consoleInput._inputs.bookmark.getValue();
740                 default:
741                 case 'console':
742                     return PMA_consoleInput._inputs.console.getValue();
743             }
744         } else {
745             switch(target) {
746                 case 'bookmark':
747                     return PMA_consoleInput._inputs.bookmark.val();
748                 default:
749                 case 'console':
750                     return PMA_consoleInput._inputs.console.val();
751             }
752         }
753     }
759  * Console messages, and message items management object
760  */
761 var PMA_consoleMessages = {
762     /**
763      * Used for clear the messages
764      *
765      * @return void
766      */
767     clear: function() {
768         $('#pma_console').find('.content .console_message_container .message:not(.welcome)').addClass('hide');
769         $('#pma_console').find('.content .console_message_container .message.failed').remove();
770         $('#pma_console').find('.content .console_message_container .message.expanded').find('.action.collapse').click();
771     },
772     /**
773      * Used for show history messages
774      *
775      * @return void
776      */
777     showHistory: function() {
778         $('#pma_console').find('.content .console_message_container .message.hide').removeClass('hide');
779     },
780     /**
781      * Used for getting a perticular history query
782      *
783      * @param int nthLast get nth query message from latest, i.e 1st is last
784      * @return string message
785      */
786     getHistory: function(nthLast) {
787         var $queries = $('#pma_console').find('.content .console_message_container .query');
788         var length = $queries.length;
789         var $query = $queries.eq(length - nthLast);
790         if (!$query || (length - nthLast) < 0) {
791             return false;
792         } else {
793             return $query.text();
794         }
795     },
796     /**
797      * Used to show the correct message depending on which key
798      * combination executes the query (Ctrl+Enter or Enter).
799      *
800      * @param bool enterExecutes Only Enter has to be pressed to execute query.
801      * @return void
802      */
803     showInstructions: function(enterExecutes) {
804         enterExecutes = +enterExecutes || 0; // conversion to int
805         var $welcomeMsg = $('#pma_console').find('.content .console_message_container .message.welcome span');
806         $welcomeMsg.children('[id^=instructions]').hide();
807         $welcomeMsg.children('#instructions-' + enterExecutes).show();
808     },
809     /**
810      * Used for log new message
811      *
812      * @param string msgString Message to show
813      * @param string msgType Message type
814      * @return object, {message_id, $message}
815      */
816     append: function(msgString, msgType) {
817         if (typeof(msgString) !== 'string') {
818             return false;
819         }
820         // Generate an ID for each message, we can find them later
821         var msgId = Math.round(Math.random()*(899999999999)+100000000000);
822         var now = new Date();
823         var $newMessage =
824             $('<div class="message ' +
825                 (PMA_console.config.alwaysExpand ? 'expanded' : 'collapsed') +
826                 '" msgid="' + msgId + '"><div class="action_content"></div></div>');
827         switch(msgType) {
828             case 'query':
829                 $newMessage.append('<div class="query highlighted"></div>');
830                 if (PMA_consoleInput._codemirror) {
831                     CodeMirror.runMode(msgString,
832                         'text/x-sql', $newMessage.children('.query')[0]);
833                 } else {
834                     $newMessage.children('.query').text(msgString);
835                 }
836                 $newMessage.children('.action_content')
837                     .append(PMA_console.$consoleTemplates.children('.query_actions').html());
838                 break;
839             default:
840             case 'normal':
841                 $newMessage.append('<div>' + msgString + '</div>');
842         }
843         PMA_consoleMessages._msgEventBinds($newMessage);
844         $newMessage.find('span.text.query_time span')
845             .text(now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds())
846             .parent().attr('title', now);
847         return {message_id: msgId,
848                 $message: $newMessage.appendTo('#pma_console .content .console_message_container')};
849     },
850     /**
851      * Used for log new query
852      *
853      * @param string queryData Struct should be
854      * {sql_query: "Query string", db: "Target DB", table: "Target Table"}
855      * @param string state Message state
856      * @return object, {message_id: string message id, $message: JQuery object}
857      */
858     appendQuery: function(queryData, state) {
859         var targetMessage = PMA_consoleMessages.append(queryData.sql_query, 'query');
860         if (! targetMessage) {
861             return false;
862         }
863         if (queryData.db && queryData.table) {
864             targetMessage.$message.attr('targetdb', queryData.db);
865             targetMessage.$message.attr('targettable', queryData.table);
866             targetMessage.$message.find('.text.targetdb span').text(queryData.db);
867         }
868         if (PMA_console.isSelect(queryData.sql_query)) {
869             targetMessage.$message.addClass('select');
870         }
871         switch(state) {
872             case 'failed':
873                 targetMessage.$message.addClass('failed');
874                 break;
875             case 'successed':
876                 targetMessage.$message.addClass('successed');
877                 break;
878             default:
879             case 'pending':
880                 targetMessage.$message.addClass('pending');
881         }
882         return targetMessage;
883     },
884     _msgEventBinds: function($targetMessage) {
885         // Leave unbinded elements, remove binded.
886         $targetMessage = $targetMessage.filter(':not(.binded)');
887         if ($targetMessage.length === 0) {
888             return;
889         }
890         $targetMessage.addClass('binded');
892         $targetMessage.find('.action.expand').click(function () {
893             $(this).closest('.message').removeClass('collapsed');
894             $(this).closest('.message').addClass('expanded');
895         });
896         $targetMessage.find('.action.collapse').click(function () {
897             $(this).closest('.message').addClass('collapsed');
898             $(this).closest('.message').removeClass('expanded');
899         });
900         $targetMessage.find('.action.edit').click(function () {
901             PMA_consoleInput.setText($(this).parent().siblings('.query').text());
902             PMA_consoleInput.focus();
903         });
904         $targetMessage.find('.action.requery').click(function () {
905             var query = $(this).parent().siblings('.query').text();
906             var $message = $(this).closest('.message');
907             if (confirm(PMA_messages.strConsoleRequeryConfirm + '\n' +
908                 (query.length<100 ? query : query.slice(0, 100) + '...'))
909             ) {
910                 PMA_console.execute(query, {db: $message.attr('targetdb'), table: $message.attr('targettable')});
911             }
912         });
913         $targetMessage.find('.action.bookmark').click(function () {
914             var query = $(this).parent().siblings('.query').text();
915             var $message = $(this).closest('.message');
916             PMA_consoleBookmarks.addBookmark(query, $message.attr('targetdb'));
917             PMA_console.showCard('#pma_bookmarks .card.add');
918         });
919         $targetMessage.find('.action.edit_bookmark').click(function () {
920             var query = $(this).parent().siblings('.query').text();
921             var $message = $(this).closest('.message');
922             var isShared = $message.find('span.bookmark_label').hasClass('shared');
923             var label = $message.find('span.bookmark_label').text();
924             PMA_consoleBookmarks.addBookmark(query, $message.attr('targetdb'), label, isShared);
925             PMA_console.showCard('#pma_bookmarks .card.add');
926         });
927         $targetMessage.find('.action.delete_bookmark').click(function () {
928             var $message = $(this).closest('.message');
929             if (confirm(PMA_messages.strConsoleDeleteBookmarkConfirm + '\n' + $message.find('.bookmark_label').text())) {
930                 $.post('import.php',
931                     {token: PMA_commonParams.get('token'),
932                     server: PMA_commonParams.get('server'),
933                     action_bookmark: 2,
934                     ajax_request: true,
935                     id_bookmark: $message.attr('bookmarkid')},
936                     function () {
937                         PMA_consoleBookmarks.refresh();
938                     });
939             }
940         });
941         $targetMessage.find('.action.profiling').click(function () {
942             var $message = $(this).closest('.message');
943             PMA_console.execute($(this).parent().siblings('.query').text(),
944                 {db: $message.attr('targetdb'),
945                 table: $message.attr('targettable'),
946                 profiling: true});
947         });
948         $targetMessage.find('.action.explain').click(function () {
949             var $message = $(this).closest('.message');
950             PMA_console.execute('EXPLAIN ' + $(this).parent().siblings('.query').text(),
951                 {db: $message.attr('targetdb'),
952                 table: $message.attr('targettable')});
953         });
954         $targetMessage.find('.action.dbg_show_trace').click(function () {
955             var $message = $(this).closest('.message');
956             if (!$message.find('.trace').length) {
957                 PMA_consoleDebug.getQueryDetails(
958                     $message.data('queryInfo'),
959                     $message.data('totalTime'),
960                     $message
961                 );
962                 PMA_consoleMessages._msgEventBinds($message.find('.message:not(.binded)'));
963             }
964             $message.addClass('show_trace');
965             $message.removeClass('hide_trace');
966         });
967         $targetMessage.find('.action.dbg_hide_trace').click(function () {
968             var $message = $(this).closest('.message');
969             $message.addClass('hide_trace');
970             $message.removeClass('show_trace');
971         });
972         $targetMessage.find('.action.dbg_show_args').click(function () {
973             var $message = $(this).closest('.message');
974             $message.addClass('show_args expanded');
975             $message.removeClass('hide_args collapsed');
976         });
977         $targetMessage.find('.action.dbg_hide_args').click(function () {
978             var $message = $(this).closest('.message');
979             $message.addClass('hide_args collapsed');
980             $message.removeClass('show_args expanded');
981         });
982         if (PMA_consoleInput._codemirror) {
983             $targetMessage.find('.query:not(.highlighted)').each(function(index, elem) {
984                     CodeMirror.runMode($(elem).text(),
985                         'text/x-sql', elem);
986                     $(this).addClass('highlighted');
987                 });
988         }
989     },
990     msgAppend: function(msgId, msgString, msgType) {
991         var $targetMessage = $('#pma_console').find('.content .console_message_container .message[msgid=' + msgId +']');
992         if ($targetMessage.length === 0 || isNaN(parseInt(msgId)) || typeof(msgString) !== 'string') {
993             return false;
994         }
995         $targetMessage.append('<div>' + msgString + '</div>');
996     },
997     updateQuery: function(msgId, isSuccessed, queryData) {
998         var $targetMessage = $('#pma_console').find('.console_message_container .message[msgid=' + parseInt(msgId) +']');
999         if ($targetMessage.length === 0 || isNaN(parseInt(msgId))) {
1000             return false;
1001         }
1002         $targetMessage.removeClass('pending failed successed');
1003         if (isSuccessed) {
1004             $targetMessage.addClass('successed');
1005             if (queryData) {
1006                 $targetMessage.children('.query').text('');
1007                 $targetMessage.removeClass('select');
1008                 if (PMA_console.isSelect(queryData.sql_query)) {
1009                     $targetMessage.addClass('select');
1010                 }
1011                 if (PMA_consoleInput._codemirror) {
1012                     CodeMirror.runMode(queryData.sql_query, 'text/x-sql', $targetMessage.children('.query')[0]);
1013                 } else {
1014                     $targetMessage.children('.query').text(queryData.sql_query);
1015                 }
1016                 $targetMessage.attr('targetdb', queryData.db);
1017                 $targetMessage.attr('targettable', queryData.table);
1018                 $targetMessage.find('.text.targetdb span').text(queryData.db);
1019             }
1020         } else {
1021             $targetMessage.addClass('failed');
1022         }
1023     },
1024     /**
1025      * Used for console messages initialize
1026      *
1027      * @return void
1028      */
1029     initialize: function() {
1030         PMA_consoleMessages._msgEventBinds($('#pma_console').find('.message:not(.binded)'));
1031         if (PMA_console.config.startHistory) {
1032             PMA_consoleMessages.showHistory();
1033         }
1034         PMA_consoleMessages.showInstructions(PMA_console.config.enterExecutes);
1035     }
1040  * Console bookmarks card, and bookmarks items management object
1041  */
1042 var PMA_consoleBookmarks = {
1043     _bookmarks: [],
1044     addBookmark: function (queryString, targetDb, label, isShared, id) {
1045         $('#pma_bookmarks').find('.add [name=shared]').prop('checked', false);
1046         $('#pma_bookmarks').find('.add [name=label]').val('');
1047         $('#pma_bookmarks').find('.add [name=targetdb]').val('');
1048         $('#pma_bookmarks').find('.add [name=id_bookmark]').val('');
1049         PMA_consoleInput.setText('', 'bookmark');
1051         switch(arguments.length) {
1052             case 4:
1053                 $('#pma_bookmarks').find('.add [name=shared]').prop('checked', isShared);
1054             case 3:
1055                 $('#pma_bookmarks').find('.add [name=label]').val(label);
1056             case 2:
1057                 $('#pma_bookmarks').find('.add [name=targetdb]').val(targetDb);
1058             case 1:
1059                 PMA_consoleInput.setText(queryString, 'bookmark');
1060             default:
1061                 break;
1062         }
1063     },
1064     refresh: function () {
1065         $.get('import.php',
1066             {ajax_request: true,
1067             token: PMA_commonParams.get('token'),
1068             server: PMA_commonParams.get('server'),
1069             console_bookmark_refresh: 'refresh'},
1070             function(data) {
1071                 if (data.console_message_bookmark) {
1072                     $('#pma_bookmarks').find('.content.bookmark').html(data.console_message_bookmark);
1073                     PMA_consoleMessages._msgEventBinds($('#pma_bookmarks').find('.message:not(.binded)'));
1074                 }
1075             });
1076     },
1077     /**
1078      * Used for console bookmarks initialize
1079      * message events are already binded by PMA_consoleMsg._msgEventBinds
1080      *
1081      * @return void
1082      */
1083     initialize: function() {
1084         if ($('#pma_bookmarks').length === 0) {
1085             return;
1086         }
1087         $('#pma_console').find('.button.bookmarks').click(function() {
1088             PMA_console.showCard('#pma_bookmarks');
1089         });
1090         $('#pma_bookmarks').find('.button.add').click(function() {
1091             PMA_console.showCard('#pma_bookmarks .card.add');
1092         });
1093         $('#pma_bookmarks').find('.card.add [name=submit]').click(function () {
1094             if ($('#pma_bookmarks').find('.card.add [name=label]').val().length === 0
1095                 || PMA_consoleInput.getText('bookmark').length === 0)
1096             {
1097                 alert(PMA_messages.strFormEmpty);
1098                 return;
1099             }
1100             $(this).prop('disabled', true);
1101             $.post('import.php',
1102                 {token: PMA_commonParams.get('token'),
1103                 ajax_request: true,
1104                 console_bookmark_add: 'true',
1105                 label: $('#pma_bookmarks').find('.card.add [name=label]').val(),
1106                 server: PMA_commonParams.get('server'),
1107                 db: $('#pma_bookmarks').find('.card.add [name=targetdb]').val(),
1108                 bookmark_query: PMA_consoleInput.getText('bookmark'),
1109                 shared: $('#pma_bookmarks').find('.card.add [name=shared]').prop('checked')},
1110                 function () {
1111                     PMA_consoleBookmarks.refresh();
1112                     $('#pma_bookmarks').find('.card.add [name=submit]').prop('disabled', false);
1113                     PMA_console.hideCard($('#pma_bookmarks').find('.card.add'));
1114                 });
1115         });
1116         $('#pma_console').find('.button.refresh').click(function() {
1117             PMA_consoleBookmarks.refresh();
1118         });
1119     }
1122 var PMA_consoleDebug;
1123 PMA_consoleDebug = {
1124     _config: {
1125         groupQueries: false,
1126         orderBy: 'exec', // Possible 'exec' => Execution order, 'time' => Time taken, 'count'
1127         order: 'asc' // Possible 'asc', 'desc'
1128     },
1129     _lastDebugInfo: {
1130         debugInfo: null,
1131         url: null
1132     },
1133     initialize: function () {
1134         // Try to get debug info after every AJAX request
1135         $(document).ajaxSuccess(function (event, xhr, settings, data) {
1136             if (data._debug) {
1137                 PMA_consoleDebug.showLog(data._debug, settings.url);
1138             }
1139         });
1141         // Initialize config
1142         this._initConfig();
1144         if (this.configParam('groupQueries')) {
1145             $('#debug_console').addClass('grouped');
1146         } else {
1147             $('#debug_console').addClass('ungrouped');
1148             if (PMA_consoleDebug.configParam('orderBy') == 'count') {
1149                 $('#debug_console').find('.button.order_by.sort_exec').addClass('active');
1150             }
1151         }
1152         var orderBy = this.configParam('orderBy');
1153         var order = this.configParam('order');
1154         $('#debug_console').find('.button.order_by.sort_' + orderBy).addClass('active');
1155         $('#debug_console').find('.button.order.order_' + order).addClass('active');
1157         // Initialize actions in toolbar
1158         $('#debug_console').find('.button.group_queries').click(function () {
1159             $('#debug_console').addClass('grouped');
1160             $('#debug_console').removeClass('ungrouped');
1161             PMA_consoleDebug.configParam('groupQueries', true);
1162             PMA_consoleDebug.refresh();
1163             if (PMA_consoleDebug.configParam('orderBy') == 'count') {
1164                 $('#debug_console').find('.button.order_by.sort_exec').removeClass('active');
1165             }
1166         });
1167         $('#debug_console').find('.button.ungroup_queries').click(function () {
1168             $('#debug_console').addClass('ungrouped');
1169             $('#debug_console').removeClass('grouped');
1170             PMA_consoleDebug.configParam('groupQueries', false);
1171             PMA_consoleDebug.refresh();
1172             if (PMA_consoleDebug.configParam('orderBy') == 'count') {
1173                 $('#debug_console').find('.button.order_by.sort_exec').addClass('active');
1174             }
1175         });
1176         $('#debug_console').find('.button.order_by').click(function () {
1177             var $this = $(this);
1178             $('#debug_console').find('.button.order_by').removeClass('active');
1179             $this.addClass('active');
1180             if ($this.hasClass('sort_time')) {
1181                 PMA_consoleDebug.configParam('orderBy', 'time');
1182             } else if ($this.hasClass('sort_exec')) {
1183                 PMA_consoleDebug.configParam('orderBy', 'exec');
1184             } else if ($this.hasClass('sort_count')) {
1185                 PMA_consoleDebug.configParam('orderBy', 'count');
1186             }
1187             PMA_consoleDebug.refresh();
1188         });
1189         $('#debug_console').find('.button.order').click(function () {
1190             var $this = $(this);
1191             $('#debug_console').find('.button.order').removeClass('active');
1192             $this.addClass('active');
1193             if ($this.hasClass('order_asc')) {
1194                 PMA_consoleDebug.configParam('order', 'asc');
1195             } else if ($this.hasClass('order_desc')) {
1196                 PMA_consoleDebug.configParam('order', 'desc');
1197             }
1198             PMA_consoleDebug.refresh();
1199         });
1201         // Show SQL debug info for first page load
1202         if (typeof debugSQLInfo !== 'undefined' && debugSQLInfo !== 'null') {
1203             $('#pma_console').find('.button.debug').removeClass('hide');
1204         }
1205         else {
1206             return;
1207         }
1208         PMA_consoleDebug.showLog(debugSQLInfo);
1209     },
1210     _initConfig: function () {
1211         var config = JSON.parse($.cookie('pma_console_dbg_config'));
1212         if (config) {
1213             for (var name in config) {
1214                 if (config.hasOwnProperty(name)) {
1215                     this._config[name] = config[name];
1216                 }
1217             }
1218         }
1219     },
1220     configParam: function (name, value) {
1221         if (typeof value === 'undefined') {
1222             return this._config[name];
1223         }
1224         this._config[name] = value;
1225         $.cookie('pma_console_dbg_config', JSON.stringify(this._config));
1226         return value;
1227     },
1228     _formatFunctionCall: function (dbgStep) {
1229         var functionName = '';
1230         if ('class' in dbgStep) {
1231             functionName += dbgStep.class;
1232             functionName += dbgStep.type;
1233         }
1234         functionName += dbgStep.function;
1235         if (dbgStep.args.length) {
1236             functionName += '(...)';
1237         } else {
1238             functionName += '()';
1239         }
1240         return functionName;
1241     },
1242     _formatFunctionArgs: function (dbgStep) {
1243         var $args = $('<div>');
1244         if (dbgStep.args.length) {
1245             $args.append('<div class="message welcome">')
1246                 .append(
1247                 $('<div class="message welcome">')
1248                     .text(
1249                     PMA_sprintf(
1250                         PMA_messages.strConsoleDebugArgsSummary,
1251                         dbgStep.args.length
1252                     )
1253                 )
1254             );
1255             for (var i = 0; i < dbgStep.args.length; i++) {
1256                 $args.append(
1257                     $('<div class="message">')
1258                         .html(
1259                         '<pre>' +
1260                         escapeHtml(JSON.stringify(dbgStep.args[i], null, "  ")) +
1261                         '</pre>'
1262                     )
1263                 );
1264             }
1265         }
1266         return $args;
1267     },
1268     _formatFileName: function (dbgStep) {
1269         var fileName = '';
1270         if ('file' in dbgStep) {
1271             fileName += dbgStep.file;
1272             fileName += '#' + dbgStep.line;
1273         }
1274         return fileName;
1275     },
1276     _formatBackTrace: function (dbgTrace) {
1277         var $traceElem = $('<div class="trace">');
1278         $traceElem.append(
1279             $('<div class="message welcome">')
1280         );
1281         var step, $stepElem;
1282         for (var stepId in dbgTrace) {
1283             if (dbgTrace.hasOwnProperty(stepId)) {
1284                 step = dbgTrace[stepId];
1285                 if (!Array.isArray(step) && typeof step !== 'object') {
1286                     $stepElem =
1287                         $('<div class="message traceStep collapsed hide_args">')
1288                             .append(
1289                             $('<span>').text(step)
1290                         );
1291                 } else {
1292                     if (typeof step.args === 'string' && step.args) {
1293                         step.args = [step.args];
1294                     }
1295                     $stepElem =
1296                         $('<div class="message traceStep collapsed hide_args">')
1297                             .append(
1298                             $('<span class="function">').text(this._formatFunctionCall(step))
1299                         )
1300                             .append(
1301                             $('<span class="file">').text(this._formatFileName(step))
1302                         );
1303                     if (step.args.length) {
1304                         $stepElem
1305                             .append(
1306                             $('<span class="args">').html(this._formatFunctionArgs(step))
1307                         )
1308                             .prepend(
1309                             $('<div class="action_content">')
1310                                 .append(
1311                                 '<span class="action dbg_show_args">' +
1312                                 PMA_messages.strConsoleDebugShowArgs +
1313                                 '</span> '
1314                             )
1315                                 .append(
1316                                 '<span class="action dbg_hide_args">' +
1317                                 PMA_messages.strConsoleDebugHideArgs +
1318                                 '</span> '
1319                             )
1320                         );
1321                     }
1322                 }
1323                 $traceElem.append($stepElem);
1324             }
1325         }
1326         return $traceElem;
1327     },
1328     _formatQueryOrGroup: function (queryInfo, totalTime) {
1329         var grouped, queryText, queryTime, count, i;
1330         if (Array.isArray(queryInfo)) {
1331             // It is grouped
1332             grouped = true;
1334             queryText = queryInfo[0].query;
1336             queryTime = 0;
1337             for (i in queryInfo) {
1338                 queryTime += queryInfo[i].time;
1339             }
1341             count = queryInfo.length;
1342         } else {
1343             queryText = queryInfo.query;
1344             queryTime = queryInfo.time;
1345         }
1347         var $query = $('<div class="message collapsed hide_trace">')
1348             .append(
1349             $('#debug_console').find('.templates .debug_query').clone()
1350         )
1351             .append(
1352             $('<div class="query">')
1353                 .text(queryText)
1354         )
1355             .data('queryInfo', queryInfo)
1356             .data('totalTime', totalTime);
1357         if (grouped) {
1358             $query.find('.text.count').removeClass('hide');
1359             $query.find('.text.count span').text(count);
1360         }
1361         $query.find('.text.time span').text(queryTime + 's (' + ((queryTime * 100) / totalTime).toFixed(3) + '%)');
1363         return $query;
1364     },
1365     _appendQueryExtraInfo: function (query, $elem) {
1366         if ('error' in query) {
1367             $elem.append(
1368                 $('<div>').html(query.error)
1369             );
1370         }
1371         $elem.append(this._formatBackTrace(query.trace));
1372     },
1373     getQueryDetails: function (queryInfo, totalTime, $query) {
1374         if (Array.isArray(queryInfo)) {
1375             var $singleQuery;
1376             for (var i in queryInfo) {
1377                 $singleQuery = $('<div class="message welcome trace">')
1378                     .text((parseInt(i) + 1) + '.')
1379                     .append(
1380                     $('<span class="time">').text(
1381                         PMA_messages.strConsoleDebugTimeTaken +
1382                         ' ' + queryInfo[i].time + 's' +
1383                         ' (' + ((queryInfo[i].time * 100) / totalTime).toFixed(3) + '%)'
1384                     )
1385                 );
1386                 this._appendQueryExtraInfo(queryInfo[i], $singleQuery);
1387                 $query
1388                     .append('<div class="message welcome trace">')
1389                     .append($singleQuery);
1390             }
1391         } else {
1392             this._appendQueryExtraInfo(queryInfo, $query);
1393         }
1394     },
1395     showLog: function (debugInfo, url) {
1396         this._lastDebugInfo.debugInfo = debugInfo;
1397         this._lastDebugInfo.url = url;
1399         $('#debug_console').find('.debugLog').empty();
1400         $("#debug_console").find(".debug>.welcome").empty();
1402         var debugJson = false, i;
1403         if (typeof debugInfo === "object" && 'queries' in debugInfo) {
1404             // Copy it to debugJson, so that it doesn't get changed
1405             if (!('queries' in debugInfo)) {
1406                 debugJson = false;
1407             } else {
1408                 debugJson = {queries: []};
1409                 for (i in debugInfo.queries) {
1410                     debugJson.queries[i] = debugInfo.queries[i];
1411                 }
1412             }
1413         } else if (typeof debugInfo === "string") {
1414             try {
1415                 debugJson = JSON.parse(debugInfo);
1416             } catch (e) {
1417                 debugJson = false;
1418             }
1419             if (debugJson && !('queries' in debugJson)) {
1420                 debugJson = false;
1421             }
1422         }
1423         if (debugJson === false) {
1424             $("#debug_console").find(".debug>.welcome").text(
1425                 PMA_messages.strConsoleDebugError
1426             );
1427             return;
1428         }
1429         var allQueries = debugJson.queries;
1430         var uniqueQueries = {};
1432         var totalExec = allQueries.length;
1434         // Calculate total time and make unique query array
1435         var totalTime = 0;
1436         for (i = 0; i < totalExec; ++i) {
1437             totalTime += allQueries[i].time;
1438             if (!(allQueries[i].hash in uniqueQueries)) {
1439                 uniqueQueries[allQueries[i].hash] = [];
1440             }
1441             uniqueQueries[allQueries[i].hash].push(allQueries[i]);
1442         }
1443         // Count total unique queries, convert uniqueQueries to Array
1444         var totalUnique = 0, uniqueArray = [];
1445         for (var hash in uniqueQueries) {
1446             if (uniqueQueries.hasOwnProperty(hash)) {
1447                 ++totalUnique;
1448                 uniqueArray.push(uniqueQueries[hash]);
1449             }
1450         }
1451         uniqueQueries = uniqueArray;
1452         // Show summary
1453         $("#debug_console").find(".debug>.welcome").append(
1454             $('<span class="debug_summary">').text(
1455                 PMA_sprintf(
1456                     PMA_messages.strConsoleDebugSummary,
1457                     totalUnique,
1458                     totalExec,
1459                     totalTime
1460                 )
1461             )
1462         );
1463         if (url) {
1464             $("#debug_console").find(".debug>.welcome").append(
1465                 $('<span class="script_name">').text(url.split('?')[0])
1466             );
1467         }
1469         // For sorting queries
1470         function sortByTime(a, b) {
1471             var order = ((PMA_consoleDebug.configParam('order') == 'asc') ? 1 : -1);
1472             if (Array.isArray(a) && Array.isArray(b)) {
1473                 // It is grouped
1474                 var timeA = 0, timeB = 0, i;
1475                 for (i in a) {
1476                     timeA += a[i].time;
1477                 }
1478                 for (i in b) {
1479                     timeB += b[i].time;
1480                 }
1481                 return (timeA - timeB) * order;
1482             } else {
1483                 return (a.time - b.time) * order;
1484             }
1485         }
1487         function sortByCount(a, b) {
1488             var order = ((PMA_consoleDebug.configParam('order') == 'asc') ? 1 : -1);
1489             return (a.length - b.length) * order;
1490         }
1492         var orderBy = this.configParam('orderBy');
1493         var order = PMA_consoleDebug.configParam('order');
1495         if (this.configParam('groupQueries')) {
1496             // Sort queries
1497             if (orderBy == 'time') {
1498                 uniqueQueries.sort(sortByTime);
1499             } else if (orderBy == 'count') {
1500                 uniqueQueries.sort(sortByCount);
1501             } else if (orderBy == 'exec' && order == 'desc') {
1502                 uniqueQueries.reverse();
1503             }
1504             for (i in uniqueQueries) {
1505                 if (orderBy == 'time') {
1506                     uniqueQueries[i].sort(sortByTime);
1507                 } else if (orderBy == 'exec' && order == 'desc') {
1508                     uniqueQueries[i].reverse();
1509                 }
1510                 $('#debug_console').find('.debugLog').append(this._formatQueryOrGroup(uniqueQueries[i], totalTime));
1511             }
1512         } else {
1513             if (orderBy == 'time') {
1514                 allQueries.sort(sortByTime);
1515             } else if (order == 'desc') {
1516                 allQueries.reverse();
1517             }
1518             for (i = 0; i < totalExec; ++i) {
1519                 $('#debug_console').find('.debugLog').append(this._formatQueryOrGroup(allQueries[i], totalTime));
1520             }
1521         }
1523         PMA_consoleMessages._msgEventBinds($('#debug_console').find('.message:not(.binded)'));
1524     },
1525     refresh: function () {
1526         var last = this._lastDebugInfo;
1527         PMA_consoleDebug.showLog(last.debugInfo, last.url);
1528     }
1531 /**s
1532  * Executed on page load
1533  */
1534 $(function () {
1535     PMA_console.initialize();