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