Merge branch 'QA_4_3'
[phpmyadmin.git] / js / console.js
blob4002308fab9eca54b7497bb58575287d89639cee
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * Used in or for console
4  *
5  * @package phpMyAdmin-Console
6  */
8 /**
9  * Console object
10  */
11 var PMA_console = {
12     /**
13      * @var object, jQuery object, selector is '#pma_console>.content'
14      * @access private
15      */
16     $consoleContent: null,
17     /**
18      * @var object, jQuery object, selector is '#pma_console .content',
19      *  used for resizer
20      * @access private
21      */
22     $consoleAllContents: null,
23     /**
24      * @var object, jQuery object, selector is '#pma_console .toolbar'
25      * @access private
26      */
27     $consoleToolbar: null,
28     /**
29      * @var object, jQuery object, selector is '#pma_console .template'
30      * @access private
31      */
32     $consoleTemplates: null,
33     /**
34      * @var object, jQuery object, form for submit
35      * @access private
36      */
37     $requestForm: null,
38     /**
39      * @var object, contain console config
40      * @access private
41      */
42     config: null,
43     /**
44      * @var bool, if console element exist, it'll be true
45      * @access public
46      */
47     isEnabled: false,
48     /**
49      * @var bool, make sure console events bind only once
50      * @access private
51      */
52     isInitialized: false,
53     /**
54      * Used for console initialize, reinit is ok, just some variable assignment
55      *
56      * @return void
57      */
58     initialize: function() {
60         if($('#pma_console').length === 0) {
61             return;
62         }
64         PMA_console.isEnabled = true;
66         // Cookie var checks and init
67         if(! $.cookie('pma_console_height')) {
68             $.cookie('pma_console_height', 92);
69         }
70         if(! $.cookie('pma_console_mode')) {
71             $.cookie('pma_console_mode', 'info');
72         }
74         // Vars init
75         PMA_console.$consoleToolbar = $('#pma_console>.toolbar');
76         PMA_console.$consoleContent = $('#pma_console>.content');
77         PMA_console.$consoleAllContents = $('#pma_console .content');
78         PMA_console.$consoleTemplates = $('#pma_console>.templates');
80         // Generate a from for post
81         PMA_console.$requestForm = $('<form method="post" action="import.php">' +
82             '<input name="is_js_confirmed" value="0">' +
83             '<textarea name="sql_query"></textarea>' +
84             '<input name="console_message_id" value="0">' +
85             '<input name="server" value="">' +
86             '<input name="db" value="">' +
87             '<input name="table" value="">' +
88             '<input name="token" value="' +
89             PMA_commonParams.get('token') +
90             '">' +
91             '</form>'
92         );
93         PMA_console.$requestForm.bind('submit', AJAX.requestHandler);
95         // Event binds shouldn't run again
96         if(PMA_console.isInitialized === false) {
98             // Load config first
99             var tempConfig = JSON.parse($.cookie('pma_console_config'));
100             if(tempConfig) {
101                 if(tempConfig.alwaysExpand === true) {
102                     $('#pma_console_options input[name=always_expand]').prop('checked', true);
103                 }
104                 if(tempConfig.startHistory === true) {
105                     $('#pma_console_options input[name=start_history]').prop('checked', true);
106                 }
107                 if(tempConfig.currentQuery === true) {
108                     $('#pma_console_options input[name=current_query]').prop('checked', true);
109                 }
110             } else {
111                 $('#pma_console_options input[name=current_query]').prop('checked', true);
112             }
114             PMA_console.updateConfig();
116             PMA_consoleResizer.initialize();
117             PMA_consoleInput.initialize();
118             PMA_consoleMessages.initialize();
119             PMA_consoleBookmarks.initialize();
121             PMA_console.$consoleToolbar.children('.console_switch').click(PMA_console.toggle);
122             $(document).keydown(function(event) {
123                 // Ctrl + Alt + C
124                 if(event.ctrlKey && event.altKey && event.keyCode === 67) {
125                     PMA_console.toggle();
126                 }
127             });
129             $('#pma_console .toolbar').children().mousedown(function(event) {
130                 event.preventDefault();
131                 event.stopImmediatePropagation();
132             });
134             $('#pma_console .button.clear').click(function() {
135                 PMA_consoleMessages.clear();
136             });
138             $('#pma_console .button.history').click(function() {
139                 PMA_consoleMessages.showHistory();
140             });
142             $('#pma_console .button.options').click(function() {
143                 PMA_console.showCard('#pma_console_options');
144             });
146             PMA_console.$consoleContent.click(function(event) {
147                 if (event.target == this) {
148                     PMA_consoleInput.focus();
149                 }
150             });
152             $('#pma_console .mid_layer').click(function() {
153                 PMA_console.hideCard($(this).parent().children('.card'));
154             });
155             $('#pma_bookmarks .switch_button').click(function() {
156                 PMA_console.hideCard($(this).closest('.card'));
157             });
159             $('#pma_console_options input[type=checkbox]').change(function() {
160                 PMA_console.updateConfig();
161             });
163             $('#pma_console_options .button.default').click(function() {
164                 $('#pma_console_options input[name=always_expand]').prop('checked', false);
165                 $('#pma_console_options input[name=start_history]').prop('checked', false);
166                 $('#pma_console_options input[name=current_query]').prop('checked', true);
167                 PMA_console.updateConfig();
168             });
170             $(document).ajaxComplete(function (event, xhr) {
171                 try {
172                     var data = $.parseJSON(xhr.responseText);
173                     PMA_console.ajaxCallback(data);
174                 } catch (e) {
175                     console.log("Invalid JSON!" + e.message);
176                 }
177             });
179             PMA_console.isInitialized = true;
180         }
182         // Change console mode from cookie
183         switch($.cookie('pma_console_mode')) {
184             case 'collapse':
185                 PMA_console.collapse();
186                 break;
187             /* jshint -W086 */// no break needed in default section
188             default:
189                 $.cookie('pma_console_mode', 'info');
190             case 'info':
191             /* jshint +W086 */
192                 PMA_console.info();
193                 break;
194             case 'show':
195                 var pmaWindowScrollHead = $(window).scrollTop();
196                 PMA_console.show(true);
197                 $(window).scrollTop(pmaWindowScrollHead);
198                 PMA_console.scrollBottom();
199                 break;
200         }
201     },
202     /**
203      * Execute query and show results in console
204      *
205      * @return void
206      */
207     execute: function(queryString, options) {
208         if(typeof(queryString) != 'string' || ! /[a-z]|[A-Z]/.test(queryString)){
209             return;
210         }
211         PMA_console.$requestForm.children('textarea').val(queryString);
212         PMA_console.$requestForm.children('[name=server]').attr('value', PMA_commonParams.get('server'));
213         if(options && options.db) {
214             PMA_console.$requestForm.children('[name=db]').val(options.db);
215             if(options.table) {
216                 PMA_console.$requestForm.children('[name=table]').val(options.table);
217             } else {
218                 PMA_console.$requestForm.children('[name=table]').val('');
219             }
220         } else {
221             PMA_console.$requestForm.children('[name=db]').val(
222                 (PMA_commonParams.get('db').length > 0 ? PMA_commonParams.get('db') : ''));
223         }
224         PMA_console.$requestForm.find('[name=profiling]').remove();
225         if(options && options.profiling === true) {
226             PMA_console.$requestForm.append('<input name="profiling" value="on">');
227         }
228         if (! confirmQuery(PMA_console.$requestForm[0], PMA_console.$requestForm.children('textarea')[0])) {
229             return;
230         }
231         PMA_console.$requestForm.children('[name=console_message_id]')
232             .val(PMA_consoleMessages.appendQuery({sql_query: queryString}).message_id);
233         PMA_console.$requestForm.trigger('submit');
234         PMA_consoleInput.clear();
235     },
236     ajaxCallback: function(data) {
237         if(data && data.console_message_id) {
238             PMA_consoleMessages.updateQuery(data.console_message_id, data.success,
239                 (data._reloadQuerywindow ? data._reloadQuerywindow : false));
240         } else if( data && data._reloadQuerywindow) {
241             if(data._reloadQuerywindow.sql_query.length > 0) {
242                 PMA_consoleMessages.appendQuery(data._reloadQuerywindow, 'successed')
243                     .$message.addClass(PMA_console.config.currentQuery ? '' : 'hide');
244             }
245         }
246     },
247     /**
248      * Change console to collapse mode
249      *
250      * @return void
251      */
252     collapse: function() {
253         $.cookie('pma_console_mode', 'collapse');
254         var pmaConsoleHeight = $.cookie('pma_console_height');
256         if(pmaConsoleHeight < 32) {
257             $.cookie('pma_console_height', 92);
258         }
259         PMA_console.$consoleToolbar.addClass('collapsed');
260         PMA_console.$consoleAllContents.height(pmaConsoleHeight);
261         PMA_console.$consoleContent.stop();
262         PMA_console.$consoleContent.animate({'margin-bottom': -1 * PMA_console.$consoleContent.outerHeight() + 'px'},
263             'fast', 'easeOutQuart', function() {
264                 PMA_console.$consoleContent.css({display:'none'});
265                 $(window).trigger('resize');
266             });
267         PMA_console.hideCard();
268     },
269     /**
270      * Show console
271      *
272      * @param bool inputFocus If true, focus the input line after show()
273      * @return void
274      */
275     show: function(inputFocus) {
276         $.cookie('pma_console_mode', 'show');
278         var pmaConsoleHeight = $.cookie('pma_console_height');
280         if(pmaConsoleHeight < 32) {
281             $.cookie('pma_console_height', 32);
282             PMA_console.collapse();
283             return;
284         }
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             });
295         if(inputFocus) {
296             PMA_consoleInput.focus();
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($.cookie('pma_console_mode')) {
318             case 'collapse':
319             case 'info':
320                 var pmaWindowScrollHead = $(window).scrollTop();
321                 PMA_console.show(true);
322                 $(window).scrollTop(pmaWindowScrollHead);
323                 break;
324             case 'show':
325                 PMA_console.collapse();
326                 break;
327             default:
328                 PMA_consoleInitialize();
329         }
330     },
331     /**
332      * Scroll console to bottom
333      *
334      * @return void
335      */
336     scrollBottom: function() {
337         PMA_console.$consoleContent.scrollTop(PMA_console.$consoleContent.prop("scrollHeight"));
338     },
339     /**
340      * Show card
341      *
342      * @param string cardSelector Selector, select string will be "#pma_console " + cardSelector
343      * this param also can be JQuery object, if you need.
344      *
345      * @return void
346      */
347     showCard: function(cardSelector) {
348         var $card = null;
349         if(typeof(cardSelector) !== 'string') {
350             if (cardSelector.length > 0) {
351                 $card = cardSelector;
352             } else {
353                 return;
354             }
355         } else {
356             $card = $("#pma_console " + cardSelector);
357         }
358         if($card.length === 0) {
359             return;
360         }
361         $card.parent().children('.mid_layer').show().fadeTo(0, 0.15);
362         $card.addClass('show');
363         PMA_consoleInput.blur();
364         if($card.parents('.card').length > 0) {
365             PMA_console.showCard($card.parents('.card'));
366         }
367     },
368     /**
369      * Scroll console to bottom
370      *
371      * @param object $targetCard Target card JQuery object, if it's empty, function will hide all cards
372      * @return void
373      */
374     hideCard: function($targetCard) {
375         if(! $targetCard) {
376             $('#pma_console .mid_layer').fadeOut(140);
377             $('#pma_console .card').removeClass('show');
378         } else if($targetCard.length > 0) {
379             $targetCard.parent().find('.mid_layer').fadeOut(140);
380             $targetCard.find('.card').removeClass('show');
381             $targetCard.removeClass('show');
382         }
383     },
384     /**
385      * Used for update console config
386      *
387      * @return void
388      */
389     updateConfig: function() {
390         PMA_console.config = {
391             alwaysExpand: $('#pma_console_options input[name=always_expand]').prop('checked'),
392             startHistory: $('#pma_console_options input[name=start_history]').prop('checked'),
393             currentQuery: $('#pma_console_options input[name=current_query]').prop('checked')
394         };
395         $.cookie('pma_console_config', JSON.stringify(PMA_console.config));
396     },
397     isSelect: function (queryString) {
398         var reg_exp = /^SELECT\s+/i;
399         return reg_exp.test(queryString);
400     }
404  * Resizer object
405  * Careful: this object UI logics highly related with functions under PMA_console
406  * Resizing min-height is 32, if small than it, console will collapse
407  */
408 var PMA_consoleResizer = {
409     _posY: 0,
410     _height: 0,
411     _resultHeight: 0,
412     /**
413      * Mousedown event handler for bind to resizer
414      *
415      * @return void
416      */
417     _mousedown: function(event) {
418         if($.cookie('pma_console_mode') !== 'show') {
419             return;
420         }
421         PMA_consoleResizer._posY = event.pageY;
422         PMA_consoleResizer._height = PMA_console.$consoleContent.height();
423         $(document).mousemove(PMA_consoleResizer._mousemove);
424         $(document).mouseup(PMA_consoleResizer._mouseup);
425         // Disable text selection while resizing
426         $(document).bind('selectstart', function(){ return false; });
427     },
428     /**
429      * Mousemove event handler for bind to resizer
430      *
431      * @return void
432      */
433     _mousemove: function(event) {
434         PMA_consoleResizer._resultHeight = PMA_consoleResizer._height + (PMA_consoleResizer._posY -event.pageY);
435         // Content min-height is 32, if adjusting height small than it we'll move it out of the page
436         if(PMA_consoleResizer._resultHeight <= 32) {
437             PMA_console.$consoleAllContents.height(32);
438             PMA_console.$consoleContent.css('margin-bottom', PMA_consoleResizer._resultHeight - 32);
439         }
440         else {
441             // Logic below makes viewable area always at bottom when adjusting height and content already at bottom
442             if(PMA_console.$consoleContent.scrollTop() + PMA_console.$consoleContent.innerHeight() + 16
443                 >= PMA_console.$consoleContent.prop('scrollHeight')) {
444                 PMA_console.$consoleAllContents.height(PMA_consoleResizer._resultHeight);
445                 PMA_console.scrollBottom();
446             } else {
447                 PMA_console.$consoleAllContents.height(PMA_consoleResizer._resultHeight);
448             }
449         }
450     },
451     /**
452      * Mouseup event handler for bind to resizer
453      *
454      * @return void
455      */
456     _mouseup: function() {
457         $.cookie('pma_console_height', PMA_consoleResizer._resultHeight);
458         PMA_console.show();
459         $(document).unbind('mousemove');
460         $(document).unbind('mouseup');
461         $(document).unbind('selectstart');
462     },
463     /**
464      * Used for console resizer initialize
465      *
466      * @return void
467      */
468     initialize: function() {
469         $('#pma_console .toolbar').unbind('mousedown');
470         $('#pma_console .toolbar').mousedown(PMA_consoleResizer._mousedown);
471     }
476  * Console input object
477  */
478 var PMA_consoleInput = {
479     /**
480      * @var array, contains Codemirror objects or input jQuery objects
481      * @access private
482      */
483     _inputs: null,
484     /**
485      * @var bool, if codemirror enabled
486      * @access private
487      */
488     _codemirror: false,
489     /**
490      * @var int, count for history navigation, 0 for current input
491      * @access private
492      */
493     _historyCount: 0,
494     /**
495      * @var string, current input when navigating through history
496      * @access private
497      */
498     _historyPreserveCurrent: null,
499     /**
500      * Used for console input initialize
501      *
502      * @return void
503      */
504     initialize: function() {
505         // _cm object can't be reinitialize
506         if(PMA_consoleInput._inputs !== null) {
507             return;
508         }
509         if(typeof CodeMirror !== 'undefined') {
510             PMA_consoleInput._codemirror = true;
511         }
512         PMA_consoleInput._inputs = [];
513         if (PMA_consoleInput._codemirror) {
514             PMA_consoleInput._inputs.console = CodeMirror($('#pma_console .console_query_input')[0], {
515                 theme: 'pma',
516                 mode: 'text/x-sql',
517                 lineWrapping: true,
518                 extraKeys: {"Ctrl-Space": "autocomplete"},
519                 hintOptions: {"completeSingle": false, "completeOnSingleClick": true}
520             });
521             PMA_consoleInput._inputs.console.on("inputRead", codemirrorAutocompleteOnInputRead);
522             PMA_consoleInput._inputs.console.on("keydown", function(instance, event) {
523                 PMA_consoleInput._historyNavigate(event);
524             });
525             if ($('#pma_bookmarks').length !== 0) {
526                 PMA_consoleInput._inputs.bookmark = CodeMirror($('#pma_console .bookmark_add_input')[0], {
527                     theme: 'pma',
528                     mode: 'text/x-sql',
529                     lineWrapping: true,
530                     extraKeys: {"Ctrl-Space": "autocomplete"},
531                     hintOptions: {"completeSingle": false, "completeOnSingleClick": true}
532                 });
533                 PMA_consoleInput._inputs.bookmark.on("inputRead", codemirrorAutocompleteOnInputRead);
534             }
535         } else {
536             PMA_consoleInput._inputs.console =
537                 $('<textarea>').appendTo('#pma_console .console_query_input')
538                     .on('keydown', PMA_consoleInput._historyNavigate);
539             if ($('#pma_bookmarks').length !== 0) {
540                 PMA_consoleInput._inputs.bookmark =
541                     $('<textarea>').appendTo('#pma_console .bookmark_add_input');
542             }
543         }
544         $('#pma_console .console_query_input').keydown(PMA_consoleInput._keydown);
545     },
546     _historyNavigate: function(event) {
547         if (event.keyCode == 38 || event.keyCode == 40) {
548             var upPermitted = false;
549             var downPermitted = false;
550             var editor = PMA_consoleInput._inputs.console;
551             var cursorLine;
552             var totalLine;
553             if (PMA_consoleInput._codemirror) {
554                 cursorLine = editor.getCursor().line;
555                 totalLine = editor.lineCount();
556             } else {
557                 // Get cursor position from textarea
558                 var text = PMA_consoleInput.getText();
559                 cursorLine = text.substr(0, editor.prop("selectionStart")).split("\n").length - 1;
560                 totalLine = text.split(/\r*\n/).length;
561             }
562             if (cursorLine === 0) {
563                 upPermitted = true;
564             }
565             if (cursorLine == totalLine - 1) {
566                 downPermitted = true;
567             }
568             var nextCount;
569             var queryString = false;
570             if (upPermitted && event.keyCode == 38) {
571                 // Navigate up in history
572                 if (PMA_consoleInput._historyCount === 0) {
573                     PMA_consoleInput._historyPreserveCurrent = PMA_consoleInput.getText();
574                 }
575                 nextCount = PMA_consoleInput._historyCount + 1;
576                 queryString = PMA_consoleMessages.getHistory(nextCount);
577             } else if (downPermitted && event.keyCode == 40) {
578                 // Navigate down in history
579                 if (PMA_consoleInput._historyCount === 0) {
580                     return;
581                 }
582                 nextCount = PMA_consoleInput._historyCount - 1;
583                 if (nextCount === 0) {
584                     queryString = PMA_consoleInput._historyPreserveCurrent;
585                 } else {
586                     queryString = PMA_consoleMessages.getHistory(nextCount);
587                 }
588             }
589             if (queryString !== false) {
590                 PMA_consoleInput._historyCount = nextCount;
591                 PMA_consoleInput.setText(queryString, 'console');
592                 if (PMA_consoleInput._codemirror) {
593                     editor.setCursor(editor.lineCount(), 0);
594                 }
595                 event.preventDefault();
596             }
597         }
598     },
599     /**
600      * Mousedown event handler for bind to input
601      * Shortcut is Ctrl+Enter key
602      *
603      * @return void
604      */
605     _keydown: function(event) {
606         if(event.ctrlKey && event.keyCode === 13) {
607             PMA_consoleInput.execute();
608         }
609     },
610     /**
611      * Used for send text to PMA_console.execute()
612      *
613      * @return void
614      */
615     execute: function() {
616         if (PMA_consoleInput._codemirror) {
617             PMA_console.execute(PMA_consoleInput._inputs.console.getValue());
618         } else {
619             PMA_console.execute(PMA_consoleInput._inputs.console.val());
620         }
621     },
622     /**
623      * Used for clear the input
624      *
625      * @param string target, default target is console input
626      * @return void
627      */
628     clear: function(target) {
629         PMA_consoleInput.setText('', target);
630     },
631     /**
632      * Used for set focus to input
633      *
634      * @return void
635      */
636     focus: function() {
637         PMA_consoleInput._inputs.console.focus();
638     },
639     /**
640      * Used for blur input
641      *
642      * @return void
643      */
644     blur: function() {
645         if (PMA_consoleInput._codemirror) {
646             PMA_consoleInput._inputs.console.getInputField().blur();
647         } else {
648             PMA_consoleInput._inputs.console.blur();
649         }
650     },
651     /**
652      * Used for set text in input
653      *
654      * @param string text
655      * @param string target
656      * @return void
657      */
658     setText: function(text, target) {
659         if (PMA_consoleInput._codemirror) {
660             switch(target) {
661                 case 'bookmark':
662                     PMA_console.execute(PMA_consoleInput._inputs.bookmark.setValue(text));
663                     break;
664                 default:
665                 case 'console':
666                     PMA_console.execute(PMA_consoleInput._inputs.console.setValue(text));
667             }
668         } else {
669             switch(target) {
670                 case 'bookmark':
671                     PMA_console.execute(PMA_consoleInput._inputs.bookmark.val(text));
672                     break;
673                 default:
674                 case 'console':
675                     PMA_console.execute(PMA_consoleInput._inputs.console.val(text));
676             }
677         }
678     },
679     getText: function(target) {
680         if (PMA_consoleInput._codemirror) {
681             switch(target) {
682                 case 'bookmark':
683                     return PMA_consoleInput._inputs.bookmark.getValue();
684                 default:
685                 case 'console':
686                     return PMA_consoleInput._inputs.console.getValue();
687             }
688         } else {
689             switch(target) {
690                 case 'bookmark':
691                     return PMA_consoleInput._inputs.bookmark.val();
692                 default:
693                 case 'console':
694                     return PMA_consoleInput._inputs.console.val();
695             }
696         }
697     }
703  * Console messages, and message items management object
704  */
705 var PMA_consoleMessages = {
706     /**
707      * Used for clear the messages
708      *
709      * @return void
710      */
711     clear: function() {
712         $('#pma_console .content .console_message_container .message:not(.welcome)').addClass('hide');
713         $('#pma_console .content .console_message_container .message.failed').remove();
714         $('#pma_console .content .console_message_container .message.expanded').find('.action.collapse').click();
715     },
716     /**
717      * Used for show history messages
718      *
719      * @return void
720      */
721     showHistory: function() {
722         $('#pma_console .content .console_message_container .message.hide').removeClass('hide');
723     },
724     /**
725      * Used for getting a perticular history query
726      *
727      * @param int nthLast get nth query message from latest, i.e 1st is last
728      * @return string message
729      */
730     getHistory: function(nthLast) {
731         var $queries = $('#pma_console .content .console_message_container .query');
732         var length = $queries.length;
733         var $query = $queries.eq(length - nthLast);
734         if (!$query || (length - nthLast) < 0) {
735             return false;
736         } else {
737             return $query.text();
738         }
739     },
740     /**
741      * Used for log new message
742      *
743      * @param string msgString Message to show
744      * @param string msgType Message type
745      * @return object, {message_id, $message}
746      */
747     append: function(msgString, msgType) {
748         if(typeof(msgString) !== 'string') {
749             return false;
750         }
751         // Generate an ID for each message, we can find them later
752         var msgId = Math.round(Math.random()*(899999999999)+100000000000);
753         var now = new Date();
754         var $newMessage =
755             $('<div class="message '
756                 + (PMA_console.config.alwaysExpand ? 'expanded' : 'collapsed')
757                 +'" msgid="' + msgId + '"><div class="action_content"></div></div>');
758         switch(msgType) {
759             case 'query':
760                 $newMessage.append('<div class="query highlighted"></div>');
761                 if(PMA_consoleInput._codemirror) {
762                     CodeMirror.runMode(msgString,
763                         'text/x-sql', $newMessage.children('.query')[0]);
764                 } else {
765                     $newMessage.children('.query').text(msgString);
766                 }
767                 $newMessage.children('.action_content')
768                     .append(PMA_console.$consoleTemplates.children('.query_actions').html());
769                 break;
770             default:
771             case 'normal':
772                 $newMessage.append('<div>' + msgString + '</div>');
773         }
774         PMA_consoleMessages._msgEventBinds($newMessage);
775         $newMessage.find('span.text.query_time span')
776             .text(now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds())
777             .parent().attr('title', now);
778         return {message_id: msgId,
779                 $message: $newMessage.appendTo('#pma_console .content .console_message_container')};
780     },
781     /**
782      * Used for log new query
783      *
784      * @param string queryData Struct should be
785      * {sql_query: "Query string", db: "Target DB", table: "Target Table"}
786      * @param string state Message state
787      * @return object, {message_id: string message id, $message: JQuery object}
788      */
789     appendQuery: function(queryData, state) {
790         var targetMessage = PMA_consoleMessages.append(queryData.sql_query, 'query');
791         if(! targetMessage) {
792             return false;
793         }
794         if(queryData.db && queryData.table) {
795             targetMessage.$message.attr('targetdb', queryData.db);
796             targetMessage.$message.attr('targettable', queryData.table);
797             targetMessage.$message.find('.text.targetdb span').text(queryData.db);
798         }
799         if(PMA_console.isSelect(queryData.sql_query)) {
800             targetMessage.$message.addClass('select');
801         }
802         switch(state) {
803             case 'failed':
804                 targetMessage.$message.addClass('failed');
805                 break;
806             case 'successed':
807                 targetMessage.$message.addClass('successed');
808                 break;
809             default:
810             case 'pending':
811                 targetMessage.$message.addClass('pending');
812         }
813         return targetMessage;
814     },
815     _msgEventBinds: function($targetMessage) {
816         // Leave unbinded elements, remove binded.
817         $targetMessage = $targetMessage.filter(':not(.binded)');
818         if($targetMessage.length === 0) {
819             return;
820         }
821         $targetMessage.addClass('binded');
823         $targetMessage.find('.action.expand').click(function () {
824             $(this).closest('.message').removeClass('collapsed');
825             $(this).closest('.message').addClass('expanded');
826         });
827         $targetMessage.find('.action.collapse').click(function () {
828             $(this).closest('.message').addClass('collapsed');
829             $(this).closest('.message').removeClass('expanded');
830         });
831         $targetMessage.find('.action.edit').click(function () {
832             PMA_consoleInput.setText($(this).parent().siblings('.query').text());
833             PMA_consoleInput.focus();
834         });
835         $targetMessage.find('.action.requery').click(function () {
836             var query = $(this).parent().siblings('.query').text();
837             var $message = $(this).closest('.message');
838             if(confirm(PMA_messages.strConsoleRequeryConfirm + '\n'
839                 + (query.length<100 ? query : query.slice(0, 100) + '...'))) {
840                 PMA_console.execute(query, {db: $message.attr('targetdb'), table: $message.attr('targettable')});
841             }
842         });
843         $targetMessage.find('.action.bookmark').click(function () {
844             var query = $(this).parent().siblings('.query').text();
845             var $message = $(this).closest('.message');
846             PMA_consoleBookmarks.addBookmark(query, $message.attr('targetdb'));
847             PMA_console.showCard('#pma_bookmarks .card.add');
848         });
849         $targetMessage.find('.action.edit_bookmark').click(function () {
850             var query = $(this).parent().siblings('.query').text();
851             var $message = $(this).closest('.message');
852             var isShared = $message.find('span.bookmark_label').hasClass('shared');
853             var label = $message.find('span.bookmark_label').text();
854             PMA_consoleBookmarks.addBookmark(query, $message.attr('targetdb'), label, isShared);
855             PMA_console.showCard('#pma_bookmarks .card.add');
856         });
857         $targetMessage.find('.action.delete_bookmark').click(function () {
858             var $message = $(this).closest('.message');
859             if(confirm(PMA_messages.strConsoleDeleteBookmarkConfirm + '\n' + $message.find('.bookmark_label').text())) {
860                 $.post('import.php',
861                     {token: PMA_commonParams.get('token'),
862                     action_bookmark: 2,
863                     ajax_request: true,
864                     id_bookmark: $message.attr('bookmarkid')},
865                     function () {
866                         PMA_consoleBookmarks.refresh();
867                     });
868             }
869         });
870         $targetMessage.find('.action.profiling').click(function () {
871             var $message = $(this).closest('.message');
872             PMA_console.execute($(this).parent().siblings('.query').text(),
873                 {db: $message.attr('targetdb'),
874                 table: $message.attr('targettable'),
875                 profiling: true});
876         });
877         $targetMessage.find('.action.explain').click(function () {
878             var $message = $(this).closest('.message');
879             PMA_console.execute('EXPLAIN ' + $(this).parent().siblings('.query').text(),
880                 {db: $message.attr('targetdb'),
881                 table: $message.attr('targettable')});
882         });
883         if(PMA_consoleInput._codemirror) {
884             $targetMessage.find('.query:not(.highlighted)').each(function(index, elem) {
885                     CodeMirror.runMode($(elem).text(),
886                         'text/x-sql', elem);
887                     $(this).addClass('highlighted');
888                 });
889         }
890     },
891     msgAppend: function(msgId, msgString, msgType) {
892         var $targetMessage = $('#pma_console .content .console_message_container .message[msgid=' + msgId +']');
893         if($targetMessage.length === 0 || isNaN(parseInt(msgId)) || typeof(msgString) !== 'string') {
894             return false;
895         }
896         $targetMessage.append('<div>' + msgString + '</div>');
897     },
898     updateQuery: function(msgId, isSuccessed, queryData) {
899         var $targetMessage = $('#pma_console .console_message_container .message[msgid=' + parseInt(msgId) +']');
900         if($targetMessage.length === 0 || isNaN(parseInt(msgId))) {
901             return false;
902         }
903         $targetMessage.removeClass('pending failed successed');
904         if(isSuccessed) {
905             $targetMessage.addClass('successed');
906             if(queryData) {
907                 $targetMessage.children('.query').text('');
908                 $targetMessage.removeClass('select');
909                 if(PMA_console.isSelect(queryData.sql_query)) {
910                     $targetMessage.addClass('select');
911                 }
912                 if(PMA_consoleInput._codemirror) {
913                     CodeMirror.runMode(queryData.sql_query, 'text/x-sql', $targetMessage.children('.query')[0]);
914                 } else {
915                     $targetMessage.children('.query').text(queryData.sql_query);
916                 }
917                 $targetMessage.attr('targetdb', queryData.db);
918                 $targetMessage.attr('targettable', queryData.table);
919                 $targetMessage.find('.text.targetdb span').text(queryData.db);
920             }
921         } else {
922             $targetMessage.addClass('failed');
923         }
924     },
925     /**
926      * Used for console messages initialize
927      *
928      * @return void
929      */
930     initialize: function() {
931         PMA_consoleMessages._msgEventBinds($('#pma_console .message:not(.binded)'));
932         if(PMA_console.config.startHistory) {
933             PMA_consoleMessages.showHistory();
934         }
935     }
940  * Console bookmarks card, and bookmarks items management object
941  */
942 var PMA_consoleBookmarks = {
943     _bookmarks: [],
944     addBookmark: function (queryString, targetDb, label, isShared, id) {
945         $('#pma_bookmarks .add [name=shared]').prop('checked', false);
946         $('#pma_bookmarks .add [name=label]').val('');
947         $('#pma_bookmarks .add [name=targetdb]').val('');
948         $('#pma_bookmarks .add [name=id_bookmark]').val('');
949         PMA_consoleInput.setText('', 'bookmark');
951         switch(arguments.length) {
952             case 4:
953                 $('#pma_bookmarks .add [name=shared]').prop('checked', isShared);
954             case 3:
955                 $('#pma_bookmarks .add [name=label]').val(label);
956             case 2:
957                 $('#pma_bookmarks .add [name=targetdb]').val(targetDb);
958             case 1:
959                 PMA_consoleInput.setText(queryString, 'bookmark');
960             default:
961                 break;
962         }
963     },
964     refresh: function () {
965         $.get('import.php?console_bookmark_refresh=refresh&token=' + PMA_commonParams.get('token'),
966             {'ajax_request': true},
967             function(data) {
968                 if(data.console_message_bookmark) {
969                     $('#pma_bookmarks .content.bookmark').html(data.console_message_bookmark);
970                     PMA_consoleMessages._msgEventBinds($('#pma_bookmarks .message:not(.binded)'));
971                 }
972             });
973     },
974     /**
975      * Used for console bookmarks initialize
976      * message events are already binded by PMA_consoleMsg._msgEventBinds
977      *
978      * @return void
979      */
980     initialize: function() {
981         if($('#pma_bookmarks').length === 0) {
982             return;
983         }
984         $('#pma_console .button.bookmarks').click(function() {
985             PMA_console.showCard('#pma_bookmarks');
986         });
987         $('#pma_bookmarks .button.add').click(function() {
988             PMA_console.showCard('#pma_bookmarks .card.add');
989         });
990         $('#pma_bookmarks .card.add [name=submit]').click(function () {
991             if ($('#pma_bookmarks .card.add [name=label]').val().length === 0
992                 || PMA_consoleInput.getText('bookmark').length === 0)
993             {
994                 alert(PMA_messages.strFormEmpty);
995                 return;
996             }
997             $(this).prop('disabled', true);
998             $.post('import.php',
999                 {token: PMA_commonParams.get('token'),
1000                 ajax_request: true,
1001                 console_bookmark_add: 'true',
1002                 label: $('#pma_bookmarks .card.add [name=label]').val(),
1003                 db: $('#pma_bookmarks .card.add [name=targetdb]').val(),
1004                 bookmark_query: PMA_consoleInput.getText('bookmark'),
1005                 shared: $('#pma_bookmarks .card.add [name=shared]').prop('checked')},
1006                 function () {
1007                     PMA_consoleBookmarks.refresh();
1008                     $('#pma_bookmarks .card.add [name=submit]').prop('disabled', false);
1009                     PMA_console.hideCard($('#pma_bookmarks .card.add'));
1010                 });
1011         });
1012         $('#pma_console .button.refresh').click(function() {
1013             PMA_consoleBookmarks.refresh();
1014         });
1015     }
1019  * Executed on page load
1020  */
1021 $(function () {
1022     PMA_console.initialize();