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