Translated using Weblate (Belarusian)
[phpmyadmin.git] / js / navigation.js
bloba16605d4f04e9da27ae5b6fb8c81d4b6b1ae13e2
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * function used in or for navigation panel
4  *
5  * @package phpMyAdmin-Navigation
6  */
8 /**
9  * updates the tree state in sessionStorage
10  *
11  * @returns void
12  */
13 function navTreeStateUpdate() {
14     // update if session storage is supported
15     if (isStorageSupported('sessionStorage')) {
16         var storage = window.sessionStorage;
17         // try catch necessary here to detect whether
18         // content to be stored exceeds storage capacity
19         try {
20             storage.setItem('navTreePaths', JSON.stringify(traverseNavigationForPaths()));
21             storage.setItem('server', PMA_commonParams.get('server'));
22             storage.setItem('token', PMA_commonParams.get('token'));
23         } catch(error) {
24             // storage capacity exceeded & old navigation tree
25             // state is no more valid, so remove it
26             storage.removeItem('navTreePaths');
27             storage.removeItem('server');
28             storage.removeItem('token');
29         }
30     }
34 /**
35  * updates the filter state in sessionStorage
36  *
37  * @returns void
38  */
39 function navFilterStateUpdate(filterName, filterValue) {
40     if (isStorageSupported('sessionStorage')) {
41         var storage = window.sessionStorage;
42         try {
43             var currentFilter = $.extend({}, JSON.parse(storage.getItem('navTreeSearchFilters')));
44             var filter = {};
45             filter[filterName] = filterValue;
46             currentFilter = $.extend(currentFilter, filter);
47             storage.setItem('navTreeSearchFilters', JSON.stringify(currentFilter));
48         } catch (error) {
49             storage.removeItem('navTreeSearchFilters');
50         }
51     }
55 /**
56  * restores the filter state on navigation reload
57  *
58  * @returns void
59  */
60 function navFilterStateRestore() {
61     if (isStorageSupported('sessionStorage')
62         && typeof window.sessionStorage.navTreeSearchFilters !== 'undefined'
63     ) {
64         var searchClauses = JSON.parse(window.sessionStorage.navTreeSearchFilters);
65         if (Object.keys(searchClauses).length < 1) {
66             return;
67         }
68         // restore database filter if present and not empty
69         if (searchClauses.hasOwnProperty("dbFilter")
70             && searchClauses.dbFilter.length
71         ) {
72             $obj = $('#pma_navigation_tree');
73             if (! $obj.data('fastFilter')) {
74                 $obj.data(
75                     'fastFilter',
76                     new PMA_fastFilter.filter($obj, "")
77                 );
78             }
79             $obj.find('li.fast_filter.db_fast_filter input.searchClause')
80                 .val(searchClauses.dbFilter)
81                 .trigger('keyup');
82         }
83         // find all table filters present in the tree
84         $tableFilters = $('#pma_navigation_tree li.database')
85             .children('div.list_container')
86             .find('li.fast_filter input.searchClause');
87         // restore table filters
88         $tableFilters.each(function () {
89             $obj = $(this).closest('div.list_container');
90             // aPath associated with this filter
91             var filterName = $(this).siblings('input[name=aPath]').val();
92             // if this table's filter has a state stored in storage
93             if (searchClauses.hasOwnProperty(filterName)
94                 && searchClauses[filterName].length
95             ) {
96                 // clear state if item is not visible,
97                 // happens when table filter becomes invisible
98                 // as db filter has already been applied
99                 if (! $obj.is(":visible")) {
100                     navFilterStateUpdate(filterName, "");
101                     return true;
102                 }
103                 if (! $obj.data('fastFilter')) {
104                     $obj.data(
105                         'fastFilter',
106                         new PMA_fastFilter.filter($obj, "")
107                     );
108                 }
109                 $(this).val(searchClauses[filterName])
110                     .trigger('keyup');
111             }
112         });
113     }
117  * Loads child items of a node and executes a given callback
119  * @param isNode
120  * @param $expandElem expander
121  * @param callback    callback function
123  * @returns void
124  */
125 function loadChildNodes(isNode, $expandElem, callback) {
127     var $destination = null;
128     var params = null;
130     if (isNode) {
131         if (!$expandElem.hasClass('expander')) {
132             return;
133         }
134         $destination = $expandElem.closest('li');
135         params = {
136             aPath: $expandElem.find('span.aPath').text(),
137             vPath: $expandElem.find('span.vPath').text(),
138             pos: $expandElem.find('span.pos').text(),
139             pos2_name: $expandElem.find('span.pos2_name').text(),
140             pos2_value: $expandElem.find('span.pos2_value').text(),
141             searchClause: '',
142             searchClause2: ''
143         };
144         if ($expandElem.closest('ul').hasClass('search_results')) {
145             params.searchClause = PMA_fastFilter.getSearchClause();
146             params.searchClause2 = PMA_fastFilter.getSearchClause2($expandElem);
147         }
148     } else {
149         $destination = $('#pma_navigation_tree_content');
150         params = {
151             aPath: $expandElem.attr('aPath'),
152             vPath: $expandElem.attr('vPath'),
153             pos: $expandElem.attr('pos'),
154             pos2_name: '',
155             pos2_value: '',
156             searchClause: '',
157             searchClause2: ''
158         };
159     }
161     var url = $('#pma_navigation').find('a.navigation_url').attr('href');
162     $.get(url, params, function (data) {
163         if (typeof data !== 'undefined' && data.success === true) {
164             $destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
165             if (isNode) {
166                 $destination.append(data.message);
167                 $expandElem.addClass('loaded');
168             } else {
169                 $destination.html(data.message);
170                 $destination.children()
171                     .first()
172                     .css({
173                         border: '0px',
174                         margin: '0em',
175                         padding : '0em'
176                     })
177                     .slideDown('slow');
178             }
179             if (data._errors) {
180                 var $errors = $(data._errors);
181                 if ($errors.children().length > 0) {
182                     $('#pma_errors').replaceWith(data._errors);
183                 }
184             }
185             if (callback && typeof callback == 'function') {
186                 callback(data);
187             }
188         } else if(data.redirect_flag == "1") {
189             if (window.location.href.indexOf('?') === -1) {
190                 window.location.href += '?session_expired=1';
191             } else {
192                 window.location.href += '&session_expired=1';
193             }
194             window.location.reload();
195         } else {
196             var $throbber = $expandElem.find('img.throbber');
197             $throbber.hide();
198             var $icon = $expandElem.find('img.ic_b_plus');
199             $icon.show();
200             PMA_ajaxShowMessage(data.error, false);
201         }
202     });
206  * Collapses a node in navigation tree.
208  * @param $expandElem expander
210  * @returns void
211  */
212 function collapseTreeNode($expandElem) {
213     var $children = $expandElem.closest('li').children('div.list_container');
214     var $icon = $expandElem.find('img');
215     if ($expandElem.hasClass('loaded')) {
216         if ($icon.is('.ic_b_minus')) {
217             $icon.removeClass('ic_b_minus').addClass('ic_b_plus');
218             $children.slideUp('fast');
219         }
220     }
221     $expandElem.blur();
222     $children.promise().done(navTreeStateUpdate);
226  * Traverse the navigation tree backwards to generate all the actual
227  * and virtual paths, as well as the positions in the pagination at
228  * various levels, if necessary.
230  * @return Object
231  */
232 function traverseNavigationForPaths() {
233     var params = {
234         pos: $('#pma_navigation_tree').find('div.dbselector select').val()
235     };
236     if ($('#navi_db_select').length) {
237         return params;
238     }
239     var count = 0;
240     $('#pma_navigation_tree').find('a.expander:visible').each(function () {
241         if ($(this).find('img').is('.ic_b_minus') &&
242             $(this).closest('li').find('div.list_container .ic_b_minus').length === 0
243         ) {
244             params['n' + count + '_aPath'] = $(this).find('span.aPath').text();
245             params['n' + count + '_vPath'] = $(this).find('span.vPath').text();
247             var pos2_name = $(this).find('span.pos2_name').text();
248             if (! pos2_name) {
249                 pos2_name = $(this)
250                     .parent()
251                     .parent()
252                     .find('span.pos2_name:last')
253                     .text();
254             }
255             var pos2_value = $(this).find('span.pos2_value').text();
256             if (! pos2_value) {
257                 pos2_value = $(this)
258                     .parent()
259                     .parent()
260                     .find('span.pos2_value:last')
261                     .text();
262             }
264             params['n' + count + '_pos2_name'] = pos2_name;
265             params['n' + count + '_pos2_value'] = pos2_value;
267             params['n' + count + '_pos3_name'] = $(this).find('span.pos3_name').text();
268             params['n' + count + '_pos3_value'] = $(this).find('span.pos3_value').text();
269             count++;
270         }
271     });
272     return params;
276  * Executed on page load
277  */
278 $(function () {
279     if (! $('#pma_navigation').length) {
280         // Don't bother running any code if the navigation is not even on the page
281         return;
282     }
284     // Do not let the page reload on submitting the fast filter
285     $(document).on('submit', '.fast_filter', function (event) {
286         event.preventDefault();
287     });
289     // Fire up the resize handlers
290     new ResizeHandler();
292     /**
293      * opens/closes (hides/shows) tree elements
294      * loads data via ajax
295      */
296     $(document).on('click', '#pma_navigation_tree a.expander', function (event) {
297         event.preventDefault();
298         event.stopImmediatePropagation();
299         var $icon = $(this).find('img');
300         if ($icon.is('.ic_b_plus')) {
301             expandTreeNode($(this));
302         } else {
303             collapseTreeNode($(this));
304         }
305     });
307     /**
308      * Register event handler for click on the reload
309      * navigation icon at the top of the panel
310      */
311     $(document).on('click', '#pma_navigation_reload', function (event) {
312         event.preventDefault();
313         // reload icon object
314         var $icon = $(this).find('img');
315         // source of the hidden throbber icon
316         var icon_throbber_src = $('#pma_navigation').find('.throbber').attr('src');
317         // source of the reload icon
318         var icon_reload_src = $icon.attr('src');
319         // replace the source of the reload icon with the one for throbber
320         $icon.attr('src', icon_throbber_src);
321         PMA_reloadNavigation();
322         // after one second, put back the reload icon
323         setTimeout(function () {
324             $icon.attr('src', icon_reload_src);
325         }, 1000);
326     });
328     $(document).on("change", '#navi_db_select',  function (event) {
329         if (! $(this).val()) {
330             PMA_commonParams.set('db', '');
331             PMA_reloadNavigation();
332         }
333         $(this).closest('form').trigger('submit');
334     });
336     /**
337      * Register event handler for click on the collapse all
338      * navigation icon at the top of the navigation tree
339      */
340     $(document).on('click', '#pma_navigation_collapse', function (event) {
341         event.preventDefault();
342         $('#pma_navigation_tree').find('a.expander').each(function() {
343             var $icon = $(this).find('img');
344             if ($icon.is('.ic_b_minus')) {
345                 $(this).click();
346             }
347         });
348     });
350     /**
351      * Register event handler to toggle
352      * the 'link with main panel' icon on mouseenter.
353      */
354     $(document).on('mouseenter', '#pma_navigation_sync', function (event) {
355         event.preventDefault();
356         var synced = $('#pma_navigation_tree').hasClass('synced');
357         var $img = $('#pma_navigation_sync').children('img');
358         if (synced) {
359             $img.removeClass('ic_s_link').addClass('ic_s_unlink');
360         } else {
361             $img.removeClass('ic_s_unlink').addClass('ic_s_link');
362         }
363     });
365     /**
366      * Register event handler to toggle
367      * the 'link with main panel' icon on mouseout.
368      */
369     $(document).on('mouseout', '#pma_navigation_sync', function (event) {
370         event.preventDefault();
371         var synced = $('#pma_navigation_tree').hasClass('synced');
372         var $img = $('#pma_navigation_sync').children('img');
373         if (synced) {
374             $img.removeClass('ic_s_unlink').addClass('ic_s_link');
375         } else {
376             $img.removeClass('ic_s_link').addClass('ic_s_unlink');
377         }
378     });
380     /**
381      * Register event handler to toggle
382      * the linking with main panel behavior
383      */
384     $(document).on('click', '#pma_navigation_sync', function (event) {
385         event.preventDefault();
386         var synced = $('#pma_navigation_tree').hasClass('synced');
387         var $img = $('#pma_navigation_sync').children('img');
388         if (synced) {
389             $img
390                 .removeClass('ic_s_unlink')
391                 .addClass('ic_s_link')
392                 .attr('alt', PMA_messages.linkWithMain)
393                 .attr('title', PMA_messages.linkWithMain);
394             $('#pma_navigation_tree')
395                 .removeClass('synced')
396                 .find('li.selected')
397                 .removeClass('selected');
398         } else {
399             $img
400                 .removeClass('ic_s_link')
401                 .addClass('ic_s_unlink')
402                 .attr('alt', PMA_messages.unlinkWithMain)
403                 .attr('title', PMA_messages.unlinkWithMain);
404             $('#pma_navigation_tree').addClass('synced');
405             PMA_showCurrentNavigation();
406         }
407     });
409     /**
410      * Bind all "fast filter" events
411      */
412     $(document).on('click', '#pma_navigation_tree li.fast_filter span', PMA_fastFilter.events.clear);
413     $(document).on('focus', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.focus);
414     $(document).on('blur', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.blur);
415     $(document).on('keyup', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.keyup);
417     /**
418      * Ajax handler for pagination
419      */
420     $(document).on('click', '#pma_navigation_tree div.pageselector a.ajax', function (event) {
421         event.preventDefault();
422         PMA_navigationTreePagination($(this));
423     });
425     /**
426      * Node highlighting
427      */
428     $(document).on(
429         'mouseover',
430         '#pma_navigation_tree.highlight li:not(.fast_filter)',
431         function () {
432             if ($('li:visible', this).length === 0) {
433                 $(this).addClass('activePointer');
434             }
435         }
436     );
437     $(document).on(
438         'mouseout',
439         '#pma_navigation_tree.highlight li:not(.fast_filter)',
440         function () {
441             $(this).removeClass('activePointer');
442         }
443     );
445     /** Create a Routine, Trigger or Event */
446     $(document).on('click', 'li.new_procedure a.ajax, li.new_function a.ajax', function (event) {
447         event.preventDefault();
448         var dialog = new RTE.object('routine');
449         dialog.editorDialog(1, $(this));
450     });
451     $(document).on('click', 'li.new_trigger a.ajax', function (event) {
452         event.preventDefault();
453         var dialog = new RTE.object('trigger');
454         dialog.editorDialog(1, $(this));
455     });
456     $(document).on('click', 'li.new_event a.ajax', function (event) {
457         event.preventDefault();
458         var dialog = new RTE.object('event');
459         dialog.editorDialog(1, $(this));
460     });
462     /** Edit Routines, Triggers or Events */
463     $(document).on('click', 'li.procedure > a.ajax, li.function > a.ajax', function (event) {
464         event.preventDefault();
465         var dialog = new RTE.object('routine');
466         dialog.editorDialog(0, $(this));
467     });
468     $(document).on('click', 'li.trigger > a.ajax', function (event) {
469         event.preventDefault();
470         var dialog = new RTE.object('trigger');
471         dialog.editorDialog(0, $(this));
472     });
473     $(document).on('click', 'li.event > a.ajax', function (event) {
474         event.preventDefault();
475         var dialog = new RTE.object('event');
476         dialog.editorDialog(0, $(this));
477     });
479     /** Execute Routines */
480     $(document).on('click', 'li.procedure div a.ajax img,' +
481         ' li.function div a.ajax img', function (event) {
482         event.preventDefault();
483         var dialog = new RTE.object('routine');
484         dialog.executeDialog($(this).parent());
485     });
486     /** Export Triggers and Events */
487     $(document).on('click', 'li.trigger div:eq(1) a.ajax img,' +
488         ' li.event div:eq(1) a.ajax img', function (event) {
489         event.preventDefault();
490         var dialog = new RTE.object();
491         dialog.exportDialog($(this).parent());
492     });
494     /** New index */
495     $(document).on('click', '#pma_navigation_tree li.new_index a.ajax', function (event) {
496         event.preventDefault();
497         var url = $(this).attr('href').substr(
498             $(this).attr('href').indexOf('?') + 1
499         ) + '&ajax_request=true';
500         var title = PMA_messages.strAddIndex;
501         indexEditorDialog(url, title);
502     });
504     /** Edit index */
505     $(document).on('click', 'li.index a.ajax', function (event) {
506         event.preventDefault();
507         var url = $(this).attr('href').substr(
508             $(this).attr('href').indexOf('?') + 1
509         ) + '&ajax_request=true';
510         var title = PMA_messages.strEditIndex;
511         indexEditorDialog(url, title);
512     });
514     /** New view */
515     $(document).on('click', 'li.new_view a.ajax', function (event) {
516         event.preventDefault();
517         PMA_createViewDialog($(this));
518     });
520     /** Hide navigation tree item */
521     $(document).on('click', 'a.hideNavItem.ajax', function (event) {
522         event.preventDefault();
523         $.ajax({
524             type: 'POST',
525             data: {
526                 server: PMA_commonParams.get('server'),
527                 token: PMA_commonParams.get('token')
528             },
529             url: $(this).attr('href') + '&ajax_request=true',
530             success: function (data) {
531                 if (typeof data !== 'undefined' && data.success === true) {
532                     PMA_reloadNavigation();
533                 } else {
534                     PMA_ajaxShowMessage(data.error);
535                 }
536             }
537         });
538     });
540     /** Display a dialog to choose hidden navigation items to show */
541     $(document).on('click', 'a.showUnhide.ajax', function (event) {
542         event.preventDefault();
543         var $msg = PMA_ajaxShowMessage();
544         $.get($(this).attr('href') + '&ajax_request=1', function (data) {
545             if (typeof data !== 'undefined' && data.success === true) {
546                 PMA_ajaxRemoveMessage($msg);
547                 var buttonOptions = {};
548                 buttonOptions[PMA_messages.strClose] = function () {
549                     $(this).dialog("close");
550                 };
551                 $('<div/>')
552                     .attr('id', 'unhideNavItemDialog')
553                     .append(data.message)
554                     .dialog({
555                         width: 400,
556                         minWidth: 200,
557                         modal: true,
558                         buttons: buttonOptions,
559                         title: PMA_messages.strUnhideNavItem,
560                         close: function () {
561                             $(this).remove();
562                         }
563                     });
564             } else {
565                 PMA_ajaxShowMessage(data.error);
566             }
567         });
568     });
570     /** Show a hidden navigation tree item */
571     $(document).on('click', 'a.unhideNavItem.ajax', function (event) {
572         event.preventDefault();
573         var $tr = $(this).parents('tr');
574         var $msg = PMA_ajaxShowMessage();
575         $.ajax({
576             type: 'POST',
577             data: {
578                 server: PMA_commonParams.get('server'),
579                 token: PMA_commonParams.get('token')
580             },
581             url: $(this).attr('href') + '&ajax_request=true',
582             success: function (data) {
583                 PMA_ajaxRemoveMessage($msg);
584                 if (typeof data !== 'undefined' && data.success === true) {
585                     $tr.remove();
586                     PMA_reloadNavigation();
587                 } else {
588                     PMA_ajaxShowMessage(data.error);
589                 }
590             }
591         });
592     });
594     // Add/Remove favorite table using Ajax.
595     $(document).on("click", ".favorite_table_anchor", function (event) {
596         event.preventDefault();
597         $self = $(this);
598         var anchor_id = $self.attr("id");
599         if($self.data("favtargetn") !== null) {
600             if($('a[data-favtargets="' + $self.data("favtargetn") + '"]').length > 0)
601             {
602                 $('a[data-favtargets="' + $self.data("favtargetn") + '"]').trigger('click');
603                 return;
604             }
605         }
607         $.ajax({
608             url: $self.attr('href'),
609             cache: false,
610             type: 'POST',
611             data: {
612                 favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined')
613                     ? window.localStorage.favorite_tables
614                     : '',
615                 server: PMA_commonParams.get('server'),
616                 token: PMA_commonParams.get('token')
617             },
618             success: function (data) {
619                 if (data.changes) {
620                     $('#pma_favorite_list').html(data.list);
621                     $('#' + anchor_id).parent().html(data.anchor);
622                     PMA_tooltip(
623                         $('#' + anchor_id),
624                         'a',
625                         $('#' + anchor_id).attr("title")
626                     );
627                     // Update localStorage.
628                     if (isStorageSupported('localStorage')) {
629                         window.localStorage.favorite_tables = data.favorite_tables;
630                     }
631                 } else {
632                     PMA_ajaxShowMessage(data.message);
633                 }
634             }
635         });
636     });
637     // Check if session storage is supported
638     if (isStorageSupported('sessionStorage')) {
639         var storage = window.sessionStorage;
640         // remove tree from storage if Navi_panel config form is submitted
641         $(document).on('submit', 'form.config-form', function(event) {
642             storage.removeItem('navTreePaths');
643         });
644         // Initialize if no previous state is defined
645         if ($('#pma_navigation_tree_content').length &&
646             typeof storage.navTreePaths === 'undefined'
647         ) {
648             PMA_reloadNavigation();
649         } else if (PMA_commonParams.get('server') === storage.server &&
650             PMA_commonParams.get('token') === storage.token
651         ) {
652             // Reload the tree to the state before page refresh
653             PMA_reloadNavigation(navFilterStateRestore, JSON.parse(storage.navTreePaths));
654         } else {
655             // If the user is different
656             navTreeStateUpdate();
657         }
658     }
662  * Expands a node in navigation tree.
664  * @param $expandElem expander
665  * @param callback    callback function
667  * @returns void
668  */
669 function expandTreeNode($expandElem, callback) {
670     var $children = $expandElem.closest('li').children('div.list_container');
671     var $icon = $expandElem.find('img');
672     if ($expandElem.hasClass('loaded')) {
673         if ($icon.is('.ic_b_plus')) {
674             $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
675             $children.slideDown('fast');
676         }
677         if (callback && typeof callback == 'function') {
678             callback.call();
679         }
680         $children.promise().done(navTreeStateUpdate);
681     } else {
682         var $throbber = $('#pma_navigation').find('.throbber')
683             .first()
684             .clone()
685             .css({visibility: 'visible', display: 'block'})
686             .click(false);
687         $icon.hide();
688         $throbber.insertBefore($icon);
690         loadChildNodes(true, $expandElem, function (data) {
691             if (typeof data !== 'undefined' && data.success === true) {
692                 var $destination = $expandElem.closest('li');
693                 $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
694                 $children = $destination.children('div.list_container');
695                 $children.slideDown('fast');
696                 if ($destination.find('ul > li').length == 1) {
697                     $destination.find('ul > li')
698                         .find('a.expander.container')
699                         .click();
700                 }
701                 if (callback && typeof callback == 'function') {
702                     callback.call();
703                 }
704                 PMA_showFullName($destination);
705             } else {
706                 PMA_ajaxShowMessage(data.error, false);
707             }
708             $icon.show();
709             $throbber.remove();
710             $children.promise().done(navTreeStateUpdate);
711         });
712     }
713     $expandElem.blur();
717  * Auto-scrolls the newly chosen database
719  * @param  object   $element    The element to set to view
720  * @param  boolean  $forceToTop Whether to force scroll to top
722  */
723 function scrollToView($element, $forceToTop) {
724     navFilterStateRestore();
725     var $container = $('#pma_navigation_tree_content');
726     var elemTop = $element.offset().top - $container.offset().top;
727     var textHeight = 20;
728     var scrollPadding = 20; // extra padding from top of bottom when scrolling to view
729     if (elemTop < 0 || $forceToTop) {
730         $container.stop().animate({
731             scrollTop: elemTop + $container.scrollTop() - scrollPadding
732         });
733     } else if (elemTop + textHeight > $container.height()) {
734         $container.stop().animate({
735             scrollTop: elemTop + textHeight - $container.height() + $container.scrollTop() + scrollPadding
736         });
737     }
741  * Expand the navigation and highlight the current database or table/view
743  * @returns void
744  */
745 function PMA_showCurrentNavigation() {
746     var db = PMA_commonParams.get('db');
747     var table = PMA_commonParams.get('table');
748     $('#pma_navigation_tree')
749         .find('li.selected')
750         .removeClass('selected');
751     if (db) {
752         var $dbItem = findLoadedItem(
753             $('#pma_navigation_tree').find('> div'), db, 'database', !table
754         );
755         if ($('#navi_db_select').length &&
756             $('option:selected', $('#navi_db_select')).length
757         ) {
758             if (! PMA_selectCurrentDb()) {
759                 return;
760             }
761             // If loaded database in navigation is not same as current one
762             if ($('#pma_navigation_tree_content').find('span.loaded_db:first').text()
763                 !== $('#navi_db_select').val()
764             ) {
765                 loadChildNodes(false, $('option:selected', $('#navi_db_select')), function (data) {
766                     handleTableOrDb(table, $('#pma_navigation_tree_content'));
767                     var $children = $('#pma_navigation_tree_content').children('div.list_container');
768                     $children.promise().done(navTreeStateUpdate);
769                 });
770             } else {
771                 handleTableOrDb(table, $('#pma_navigation_tree_content'));
772             }
773         } else if ($dbItem) {
774             var $expander = $dbItem.children('div:first').children('a.expander');
775             // if not loaded or loaded but collapsed
776             if (! $expander.hasClass('loaded') ||
777                 $expander.find('img').is('.ic_b_plus')
778             ) {
779                 expandTreeNode($expander, function () {
780                     handleTableOrDb(table, $dbItem);
781                 });
782             } else {
783                 handleTableOrDb(table, $dbItem);
784             }
785         }
786     } else if ($('#navi_db_select').length && $('#navi_db_select').val()) {
787         $('#navi_db_select').val('').hide().trigger('change');
788     }
789     PMA_showFullName($('#pma_navigation_tree'));
791     function handleTableOrDb(table, $dbItem) {
792         if (table) {
793             loadAndHighlightTableOrView($dbItem, table);
794         } else {
795             var $container = $dbItem.children('div.list_container');
796             var $tableContainer = $container.children('ul').children('li.tableContainer');
797             if ($tableContainer.length > 0) {
798                 var $expander = $tableContainer.children('div:first').children('a.expander');
799                 $tableContainer.addClass('selected');
800                 expandTreeNode($expander, function () {
801                     scrollToView($dbItem, true);
802                 });
803             } else {
804                 scrollToView($dbItem, true);
805             }
806         }
807     }
809     function findLoadedItem($container, name, clazz, doSelect) {
810         var ret = false;
811         $container.children('ul').children('li').each(function () {
812             var $li = $(this);
813             // this is a navigation group, recurse
814             if ($li.is('.navGroup')) {
815                 var $container = $li.children('div.list_container');
816                 var $childRet = findLoadedItem(
817                     $container, name, clazz, doSelect
818                 );
819                 if ($childRet) {
820                     ret = $childRet;
821                     return false;
822                 }
823             } else { // this is a real navigation item
824                 // name and class matches
825                 if (((clazz && $li.is('.' + clazz)) || ! clazz) &&
826                         $li.children('a').text() == name) {
827                     if (doSelect) {
828                         $li.addClass('selected');
829                     }
830                     // taverse up and expand and parent navigation groups
831                     $li.parents('.navGroup').each(function () {
832                         var $cont = $(this).children('div.list_container');
833                         if (! $cont.is(':visible')) {
834                             $(this)
835                                 .children('div:first')
836                                 .children('a.expander')
837                                 .click();
838                         }
839                     });
840                     ret = $li;
841                     return false;
842                 }
843             }
844         });
845         return ret;
846     }
848     function loadAndHighlightTableOrView($dbItem, itemName) {
849         var $container = $dbItem.children('div.list_container');
850         var $expander;
851         var $whichItem = isItemInContainer($container, itemName, 'li.table, li.view');
852         //If item already there in some container
853         if ($whichItem) {
854             //get the relevant container while may also be a subcontainer
855             var $relatedContainer = $whichItem.closest('li.subContainer').length
856                 ? $whichItem.closest('li.subContainer')
857                 : $dbItem;
858             $whichItem = findLoadedItem(
859                 $relatedContainer.children('div.list_container'),
860                 itemName, null, true
861             );
862             //Show directly
863             showTableOrView($whichItem, $relatedContainer.children('div:first').children('a.expander'));
864         //else if item not there, try loading once
865         } else {
866             var $sub_containers = $dbItem.find('.subContainer');
867             //If there are subContainers i.e. tableContainer or viewContainer
868             if($sub_containers.length > 0) {
869                 var $containers = [];
870                 $sub_containers.each(function (index) {
871                     $containers[index] = $(this);
872                     $expander = $containers[index]
873                         .children('div:first')
874                         .children('a.expander');
875                     if (! $expander.hasClass('loaded')) {
876                         loadAndShowTableOrView($expander, $containers[index], itemName);
877                     }
878                 });
879             // else if no subContainers
880             } else {
881                 $expander = $dbItem
882                     .children('div:first')
883                     .children('a.expander');
884                 if (! $expander.hasClass('loaded')) {
885                     loadAndShowTableOrView($expander, $dbItem, itemName);
886                 }
887             }
888         }
889     }
891     function loadAndShowTableOrView($expander, $relatedContainer, itemName) {
892         loadChildNodes(true, $expander, function (data) {
893             var $whichItem = findLoadedItem(
894                 $relatedContainer.children('div.list_container'),
895                 itemName, null, true
896             );
897             if ($whichItem) {
898                 showTableOrView($whichItem, $expander);
899             }
900         });
901     }
903     function showTableOrView($whichItem, $expander) {
904         expandTreeNode($expander, function (data) {
905             if ($whichItem) {
906                 scrollToView($whichItem, false);
907             }
908         });
909     }
911     function isItemInContainer($container, name, clazz)
912     {
913         var $whichItem = null;
914         $items = $container.find(clazz);
915         var found = false;
916         $items.each(function () {
917             if ($(this).children('a').text() == name) {
918                 $whichItem = $(this);
919                 return false;
920             }
921         });
922         return $whichItem;
923     }
927  * Disable navigation panel settings
929  * @return void
930  */
931 function PMA_disableNaviSettings() {
932     $('#pma_navigation_settings_icon').addClass('hide');
933     $('#pma_navigation_settings').remove();
937  * Ensure that navigation panel settings is properly setup.
938  * If not, set it up
940  * @return void
941  */
942 function PMA_ensureNaviSettings(selflink) {
943     $('#pma_navigation_settings_icon').removeClass('hide');
945     if (!$('#pma_navigation_settings').length) {
946         var params = {
947             getNaviSettings: true,
948             server: PMA_commonParams.get('server'),
949             token: PMA_commonParams.get('token')
950         };
951         var url = $('#pma_navigation').find('a.navigation_url').attr('href');
952         $.post(url, params, function (data) {
953             if (typeof data !== 'undefined' && data.success) {
954                 $('#pma_navi_settings_container').html(data.message);
955                 setupRestoreField();
956                 setupValidation();
957                 setupConfigTabs();
958                 $('#pma_navigation_settings').find('form').attr('action', selflink);
959             } else {
960                 PMA_ajaxShowMessage(data.error);
961             }
962         });
963     } else {
964         $('#pma_navigation_settings').find('form').attr('action', selflink);
965     }
969  * Reloads the whole navigation tree while preserving its state
971  * @param  function     the callback function
972  * @param  Object       stored navigation paths
974  * @return void
975  */
976 function PMA_reloadNavigation(callback, paths) {
977     var params = {
978         reload: true,
979         no_debug: true,
980         server: PMA_commonParams.get('server'),
981         token: PMA_commonParams.get('token')
982     };
983     paths = paths || traverseNavigationForPaths();
984     $.extend(params, paths);
985     if ($('#navi_db_select').length) {
986         params.db = PMA_commonParams.get('db');
987         requestNaviReload(params);
988         return;
989     }
990     requestNaviReload(params);
992     function requestNaviReload(params) {
993         var url = $('#pma_navigation').find('a.navigation_url').attr('href');
994         $.post(url, params, function (data) {
995             if (typeof data !== 'undefined' && data.success) {
996                 $('#pma_navigation_tree').html(data.message).children('div').show();
997                 if ($('#pma_navigation_tree').hasClass('synced')) {
998                     PMA_selectCurrentDb();
999                     PMA_showCurrentNavigation();
1000                 }
1001                 // Fire the callback, if any
1002                 if (typeof callback === 'function') {
1003                     callback.call();
1004                 }
1005                 navTreeStateUpdate();
1006             } else {
1007                 PMA_ajaxShowMessage(data.error);
1008             }
1009         });
1010     }
1013 function PMA_selectCurrentDb() {
1014     var $naviDbSelect = $('#navi_db_select');
1016     if (!$naviDbSelect.length) {
1017         return false;
1018     }
1020     if (PMA_commonParams.get('db')) { // db selected
1021         $naviDbSelect.show();
1022     }
1024     $naviDbSelect.val(PMA_commonParams.get('db'));
1025     return $naviDbSelect.val() === PMA_commonParams.get('db');
1030  * Handles any requests to change the page in a branch of a tree
1032  * This can be called from link click or select change event handlers
1034  * @param object $this A jQuery object that points to the element that
1035  * initiated the action of changing the page
1037  * @return void
1038  */
1039 function PMA_navigationTreePagination($this) {
1040     var $msgbox = PMA_ajaxShowMessage();
1041     var isDbSelector = $this.closest('div.pageselector').is('.dbselector');
1042     var url, params;
1043     if ($this[0].tagName == 'A') {
1044         url = $this.attr('href');
1045         params = 'ajax_request=true&token=' + PMA_commonParams.get('token');
1046     } else { // tagName == 'SELECT'
1047         url = 'navigation.php';
1048         params = $this.closest("form").serialize() + '&ajax_request=true';
1049     }
1050     var searchClause = PMA_fastFilter.getSearchClause();
1051     if (searchClause) {
1052         params += '&searchClause=' + encodeURIComponent(searchClause);
1053     }
1054     if (isDbSelector) {
1055         params += '&full=true';
1056     } else {
1057         var searchClause2 = PMA_fastFilter.getSearchClause2($this);
1058         if (searchClause2) {
1059             params += '&searchClause2=' + encodeURIComponent(searchClause2);
1060         }
1061     }
1062     $.post(url, params, function (data) {
1063         if (typeof data !== 'undefined' && data.success) {
1064             PMA_ajaxRemoveMessage($msgbox);
1065             if (isDbSelector) {
1066                 var val = PMA_fastFilter.getSearchClause();
1067                 $('#pma_navigation_tree')
1068                     .html(data.message)
1069                     .children('div')
1070                     .show();
1071                 if (val) {
1072                     $('#pma_navigation_tree')
1073                         .find('li.fast_filter input.searchClause')
1074                         .val(val);
1075                 }
1076             } else {
1077                 var $parent = $this.closest('div.list_container').parent();
1078                 var val = PMA_fastFilter.getSearchClause2($this);
1079                 $this.closest('div.list_container').html(
1080                     $(data.message).children().show()
1081                 );
1082                 if (val) {
1083                     $parent.find('li.fast_filter input.searchClause').val(val);
1084                 }
1085                 $parent.find('span.pos2_value:first').text(
1086                     $parent.find('span.pos2_value:last').text()
1087                 );
1088                 $parent.find('span.pos3_value:first').text(
1089                     $parent.find('span.pos3_value:last').text()
1090                 );
1091             }
1092         } else {
1093             PMA_ajaxShowMessage(data.error);
1094             PMA_handleRedirectAndReload(data);
1095         }
1096         navTreeStateUpdate();
1097     });
1101  * @var ResizeHandler Custom object that manages the resizing of the navigation
1103  * XXX: Must only be ever instanciated once
1104  * XXX: Inside event handlers the 'this' object is accessed as 'event.data.resize_handler'
1105  */
1106 var ResizeHandler = function () {
1107     /**
1108      * @var int panel_width Used by the collapser to know where to go
1109      *                      back to when uncollapsing the panel
1110      */
1111     this.panel_width = 0;
1112     /**
1113      * @var string left Used to provide support for RTL languages
1114      */
1115     this.left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
1116     /**
1117      * Adjusts the width of the navigation panel to the specified value
1118      *
1119      * @param int pos Navigation width in pixels
1120      *
1121      * @return void
1122      */
1123     this.setWidth = function (pos) {
1124         var $resizer = $('#pma_navigation_resizer');
1125         var resizer_width = $resizer.width();
1126         var $collapser = $('#pma_navigation_collapser');
1127         $('#pma_navigation').width(pos);
1128         $('body').css('margin-' + this.left, pos + 'px');
1129         $("#floating_menubar, #pma_console")
1130             .css('margin-' + this.left, (pos + resizer_width) + 'px');
1131         $resizer.css(this.left, pos + 'px');
1132         if (pos === 0) {
1133             $collapser
1134                 .css(this.left, pos + resizer_width)
1135                 .html(this.getSymbol(pos))
1136                 .prop('title', PMA_messages.strShowPanel);
1137         } else {
1138             $collapser
1139                 .css(this.left, pos)
1140                 .html(this.getSymbol(pos))
1141                 .prop('title', PMA_messages.strHidePanel);
1142         }
1143         setTimeout(function () {
1144             $(window).trigger('resize');
1145         }, 4);
1146     };
1147     /**
1148      * Returns the horizontal position of the mouse,
1149      * relative to the outer side of the navigation panel
1150      *
1151      * @param int pos Navigation width in pixels
1152      *
1153      * @return void
1154      */
1155     this.getPos = function (event) {
1156         var pos = event.pageX;
1157         var windowWidth = $(window).width();
1158         var windowScroll = $(window).scrollLeft();
1159         pos = pos - windowScroll;
1160         if (this.left != 'left') {
1161             pos = windowWidth - event.pageX;
1162         }
1163         if (pos < 0) {
1164             pos = 0;
1165         } else if (pos + 100 >= windowWidth) {
1166             pos = windowWidth - 100;
1167         } else {
1168             this.panel_width = 0;
1169         }
1170         return pos;
1171     };
1172     /**
1173      * Returns the HTML code for the arrow symbol used in the collapser
1174      *
1175      * @param int width The width of the panel
1176      *
1177      * @return string
1178      */
1179     this.getSymbol = function (width) {
1180         if (this.left == 'left') {
1181             if (width === 0) {
1182                 return '&rarr;';
1183             } else {
1184                 return '&larr;';
1185             }
1186         } else {
1187             if (width === 0) {
1188                 return '&larr;';
1189             } else {
1190                 return '&rarr;';
1191             }
1192         }
1193     };
1194     /**
1195      * Event handler for initiating a resize of the panel
1196      *
1197      * @param object e Event data (contains a reference to resizeHandler)
1198      *
1199      * @return void
1200      */
1201     this.mousedown = function (event) {
1202         event.preventDefault();
1203         $(document)
1204             .bind('mousemove', {'resize_handler': event.data.resize_handler},
1205                 $.throttle(event.data.resize_handler.mousemove, 4))
1206             .bind('mouseup', {'resize_handler': event.data.resize_handler},
1207                 event.data.resize_handler.mouseup);
1208         $('body').css('cursor', 'col-resize');
1209     };
1210     /**
1211      * Event handler for terminating a resize of the panel
1212      *
1213      * @param object e Event data (contains a reference to resizeHandler)
1214      *
1215      * @return void
1216      */
1217     this.mouseup = function (event) {
1218         $('body').css('cursor', '');
1219         $.cookie('pma_navi_width', event.data.resize_handler.getPos(event));
1220         $('#topmenu').menuResizer('resize');
1221         $(document)
1222             .unbind('mousemove')
1223             .unbind('mouseup');
1224     };
1225     /**
1226      * Event handler for updating the panel during a resize operation
1227      *
1228      * @param object e Event data (contains a reference to resizeHandler)
1229      *
1230      * @return void
1231      */
1232     this.mousemove = function (event) {
1233         event.preventDefault();
1234         var pos = event.data.resize_handler.getPos(event);
1235         event.data.resize_handler.setWidth(pos);
1236         if ($('.sticky_columns').length !== 0) {
1237             handleAllStickyColumns();
1238         }
1239     };
1240     /**
1241      * Event handler for collapsing the panel
1242      *
1243      * @param object e Event data (contains a reference to resizeHandler)
1244      *
1245      * @return void
1246      */
1247     this.collapse = function (event) {
1248         event.preventDefault();
1249         var panel_width = event.data.resize_handler.panel_width;
1250         var width = $('#pma_navigation').width();
1251         if (width === 0 && panel_width === 0) {
1252             panel_width = 240;
1253         }
1254         event.data.resize_handler.setWidth(panel_width);
1255         event.data.resize_handler.panel_width = width;
1256     };
1257     /**
1258      * Event handler for resizing the navigation tree height on window resize
1259      *
1260      * @return void
1261      */
1262     this.treeResize = function (event) {
1263         var $nav        = $("#pma_navigation"),
1264             $nav_tree   = $("#pma_navigation_tree"),
1265             $nav_header = $("#pma_navigation_header"),
1266             $nav_tree_content = $("#pma_navigation_tree_content");
1267         $nav_tree.height($nav.height() - $nav_header.height());
1268         if ($nav_tree_content.length > 0) {
1269             $nav_tree_content.height($nav_tree.height() - $nav_tree_content.position().top);
1270         } else {
1271             //TODO: in fast filter search response there is no #pma_navigation_tree_content, needs to be added in php
1272             $nav_tree.css({
1273                 'overflow-y': 'auto'
1274             });
1275         }
1276         // Set content bottom space beacuse of console
1277         $('body').css('margin-bottom', $('#pma_console').height() + 'px');
1278     };
1279     /* Initialisation section begins here */
1280     if ($.cookie('pma_navi_width')) {
1281         // If we have a cookie, set the width of the panel to its value
1282         var pos = Math.abs(parseInt($.cookie('pma_navi_width'), 10) || 0);
1283         this.setWidth(pos);
1284         $('#topmenu').menuResizer('resize');
1285     }
1286     // Register the events for the resizer and the collapser
1287     $(document).on('mousedown', '#pma_navigation_resizer', {'resize_handler': this}, this.mousedown);
1288     $(document).on('click', '#pma_navigation_collapser', {'resize_handler': this}, this.collapse);
1290     // Add the correct arrow symbol to the collapser
1291     $('#pma_navigation_collapser').html(this.getSymbol($('#pma_navigation').width()));
1292     // Fix navigation tree height
1293     $(window).on('resize', this.treeResize);
1294     // need to call this now and then, browser might decide
1295     // to show/hide horizontal scrollbars depending on page content width
1296     setInterval(this.treeResize, 2000);
1297     this.treeResize();
1298 }; // End of ResizeHandler
1301  * @var object PMA_fastFilter Handles the functionality that allows filtering
1302  *                            of the items in a branch of the navigation tree
1303  */
1304 var PMA_fastFilter = {
1305     /**
1306      * Construct for the asynchronous fast filter functionality
1307      *
1308      * @param object $this        A jQuery object pointing to the list container
1309      *                            which is the nearest parent of the fast filter
1310      * @param string searchClause The query string for the filter
1311      *
1312      * @return new PMA_fastFilter.filter object
1313      */
1314     filter: function ($this, searchClause) {
1315         /**
1316          * @var object $this A jQuery object pointing to the list container
1317          *                   which is the nearest parent of the fast filter
1318          */
1319         this.$this = $this;
1320         /**
1321          * @var bool searchClause The query string for the filter
1322          */
1323         this.searchClause = searchClause;
1324         /**
1325          * @var object $clone A clone of the original contents
1326          *                    of the navigation branch before
1327          *                    the fast filter was applied
1328          */
1329         this.$clone = $this.clone();
1330         /**
1331          * @var object xhr A reference to the ajax request that is currently running
1332          */
1333         this.xhr = null;
1334         /**
1335          * @var int timeout Used to delay the request for asynchronous search
1336          */
1337         this.timeout = null;
1339         var $filterInput = $this.find('li.fast_filter input.searchClause');
1340         if ($filterInput.length !== 0 &&
1341             $filterInput.val() !== '' &&
1342             $filterInput.val() != $filterInput[0].defaultValue
1343         ) {
1344             this.request();
1345         }
1346     },
1347     /**
1348      * Gets the query string from the database fast filter form
1349      *
1350      * @return string
1351      */
1352     getSearchClause: function () {
1353         var retval = '';
1354         var $input = $('#pma_navigation_tree')
1355             .find('li.fast_filter.db_fast_filter input.searchClause');
1356         if ($input.length && $input.val() != $input[0].defaultValue) {
1357             retval = $input.val();
1358         }
1359         return retval;
1360     },
1361     /**
1362      * Gets the query string from a second level item's fast filter form
1363      * The retrieval is done by trasversing the navigation tree backwards
1364      *
1365      * @return string
1366      */
1367     getSearchClause2: function ($this) {
1368         var $filterContainer = $this.closest('div.list_container');
1369         var $filterInput = $([]);
1370         if ($filterContainer
1371             .find('li.fast_filter:not(.db_fast_filter) input.searchClause')
1372             .length !== 0) {
1373             $filterInput = $filterContainer
1374                 .find('li.fast_filter:not(.db_fast_filter) input.searchClause');
1375         }
1376         var searchClause2 = '';
1377         if ($filterInput.length !== 0 &&
1378             $filterInput.first().val() != $filterInput[0].defaultValue
1379         ) {
1380             searchClause2 = $filterInput.val();
1381         }
1382         return searchClause2;
1383     },
1384     /**
1385      * @var hash events A list of functions that are bound to DOM events
1386      *                  at the top of this file
1387      */
1388     events: {
1389         focus: function (event) {
1390             var $obj = $(this).closest('div.list_container');
1391             if (! $obj.data('fastFilter')) {
1392                 $obj.data(
1393                     'fastFilter',
1394                     new PMA_fastFilter.filter($obj, $(this).val())
1395                 );
1396             }
1397             if ($(this).val() == this.defaultValue) {
1398                 $(this).val('');
1399             } else {
1400                 $(this).select();
1401             }
1402         },
1403         blur: function (event) {
1404             if ($(this).val() === '') {
1405                 $(this).val(this.defaultValue);
1406             }
1407             var $obj = $(this).closest('div.list_container');
1408             if ($(this).val() == this.defaultValue && $obj.data('fastFilter')) {
1409                 $obj.data('fastFilter').restore();
1410             }
1411         },
1412         keyup: function (event) {
1413             var $obj = $(this).closest('div.list_container');
1414             var str = '';
1415             if ($(this).val() != this.defaultValue && $(this).val() !== '') {
1416                 $obj.find('div.pageselector').hide();
1417                 str = $(this).val();
1418             }
1420             /**
1421              * FIXME at the server level a value match is done while on
1422              * the client side it is a regex match. These two should be aligned
1423              */
1425             // regex used for filtering.
1426             var regex;
1427             try {
1428                 regex = new RegExp(str, 'i');
1429             } catch (err) {
1430                 return;
1431             }
1433             // this is the div that houses the items to be filtered by this filter.
1434             var outerContainer;
1435             if ($(this).closest('li.fast_filter').is('.db_fast_filter')) {
1436                 outerContainer = $('#pma_navigation_tree_content');
1437             } else {
1438                 outerContainer = $obj;
1439             }
1441             // filters items that are directly under the div as well as grouped in
1442             // groups. Does not filter child items (i.e. a database search does
1443             // not filter tables)
1444             var item_filter = function($curr) {
1445                 $curr.children('ul').children('li.navGroup').each(function() {
1446                     $(this).children('div.list_container').each(function() {
1447                         item_filter($(this)); // recursive
1448                     });
1449                 });
1450                 $curr.children('ul').children('li').children('a').not('.container').each(function() {
1451                     if (regex.test($(this).text())) {
1452                         $(this).parent().show().removeClass('hidden');
1453                     } else {
1454                         $(this).parent().hide().addClass('hidden');
1455                     }
1456                 });
1457             };
1458             item_filter(outerContainer);
1460             // hides containers that does not have any visible children
1461             var container_filter = function ($curr) {
1462                 $curr.children('ul').children('li.navGroup').each(function() {
1463                     var $group = $(this);
1464                     $group.children('div.list_container').each(function() {
1465                         container_filter($(this)); // recursive
1466                     });
1467                     $group.show().removeClass('hidden');
1468                     if ($group.children('div.list_container').children('ul')
1469                             .children('li').not('.hidden').length === 0) {
1470                         $group.hide().addClass('hidden');
1471                     }
1472                 });
1473             };
1474             container_filter(outerContainer);
1476             if ($(this).val() != this.defaultValue && $(this).val() !== '') {
1477                 if (! $obj.data('fastFilter')) {
1478                     $obj.data(
1479                         'fastFilter',
1480                         new PMA_fastFilter.filter($obj, $(this).val())
1481                     );
1482                 } else {
1483                     if (event.keyCode == 13) {
1484                         $obj.data('fastFilter').update($(this).val());
1485                     }
1486                 }
1487             } else if ($obj.data('fastFilter')) {
1488                 $obj.data('fastFilter').restore(true);
1489             }
1490             // update filter state
1491             var filterName;
1492             if ($(this).attr('name') == 'searchClause2') {
1493                 filterName = $(this).siblings('input[name=aPath]').val();
1494             } else {
1495                 filterName = 'dbFilter';
1496             }
1497             navFilterStateUpdate(filterName, $(this).val());
1498         },
1499         clear: function (event) {
1500             event.stopPropagation();
1501             // Clear the input and apply the fast filter with empty input
1502             var filter = $(this).closest('div.list_container').data('fastFilter');
1503             if (filter) {
1504                 filter.restore();
1505             }
1506             var value = $(this).prev()[0].defaultValue;
1507             $(this).prev().val(value).trigger('keyup');
1508         }
1509     }
1512  * Handles a change in the search clause
1514  * @param string searchClause The query string for the filter
1516  * @return void
1517  */
1518 PMA_fastFilter.filter.prototype.update = function (searchClause) {
1519     if (this.searchClause != searchClause) {
1520         this.searchClause = searchClause;
1521         this.request();
1522     }
1525  * After a delay of 250mS, initiates a request to retrieve search results
1526  * Multiple calls to this function will always abort the previous request
1528  * @return void
1529  */
1530 PMA_fastFilter.filter.prototype.request = function () {
1531     var self = this;
1532     if (self.$this.find('li.fast_filter').find('img.throbber').length === 0) {
1533         self.$this.find('li.fast_filter').append(
1534             $('<div class="throbber"></div>').append(
1535                 $('#pma_navigation_content')
1536                     .find('img.throbber')
1537                     .clone()
1538                     .css({visibility: 'visible', display: 'block'})
1539             )
1540         );
1541     }
1542     if (self.xhr) {
1543         self.xhr.abort();
1544     }
1545     var url = $('#pma_navigation').find('a.navigation_url').attr('href');
1546     var params = self.$this.find('> ul > li > form.fast_filter').first().serialize();
1548     if (self.$this.find('> ul > li > form.fast_filter:first input[name=searchClause]').length === 0) {
1549         var $input = $('#pma_navigation_tree').find('li.fast_filter.db_fast_filter input.searchClause');
1550         if ($input.length && $input.val() != $input[0].defaultValue) {
1551             params += '&searchClause=' + encodeURIComponent($input.val());
1552         }
1553     }
1554     self.xhr = $.ajax({
1555         url: url,
1556         type: 'post',
1557         dataType: 'json',
1558         data: params,
1559         complete: function (jqXHR, status) {
1560             if (status != 'abort') {
1561                 var data = JSON.parse(jqXHR.responseText);
1562                 self.$this.find('li.fast_filter').find('div.throbber').remove();
1563                 if (data && data.results) {
1564                     self.swap.apply(self, [data.message]);
1565                 }
1566             }
1567         }
1568     });
1571  * Replaces the contents of the navigation branch with the search results
1573  * @param string list The search results
1575  * @return void
1576  */
1577 PMA_fastFilter.filter.prototype.swap = function (list) {
1578     this.$this
1579         .html($(list).html())
1580         .children()
1581         .show()
1582         .end()
1583         .find('li.fast_filter input.searchClause')
1584         .val(this.searchClause);
1585     this.$this.data('fastFilter', this);
1588  * Restores the navigation to the original state after the fast filter is cleared
1590  * @param bool focus Whether to also focus the input box of the fast filter
1592  * @return void
1593  */
1594 PMA_fastFilter.filter.prototype.restore = function (focus) {
1595     if(this.$this.children('ul').first().hasClass('search_results')) {
1596         this.$this.html(this.$clone.html()).children().show();
1597         this.$this.data('fastFilter', this);
1598         if (focus) {
1599             this.$this.find('li.fast_filter input.searchClause').focus();
1600         }
1601     }
1602     this.searchClause = '';
1603     this.$this.find('div.pageselector').show();
1604     this.$this.find('div.throbber').remove();
1608  * Show full name when cursor hover and name not shown completely
1610  * @param object $containerELem Container element
1612  * @return void
1613  */
1614 function PMA_showFullName($containerELem) {
1616     $containerELem.find('.hover_show_full').mouseenter(function() {
1617         /** mouseenter */
1618         var $this = $(this);
1619         var thisOffset = $this.offset();
1620         if($this.text() === '') {
1621             return;
1622         }
1623         var $parent = $this.parent();
1624         if(  ($parent.offset().left + $parent.outerWidth())
1625            < (thisOffset.left + $this.outerWidth()))
1626         {
1627             var $fullNameLayer = $('#full_name_layer');
1628             if($fullNameLayer.length === 0)
1629             {
1630                 $('body').append('<div id="full_name_layer" class="hide"></div>');
1631                 $('#full_name_layer').mouseleave(function() {
1632                     /** mouseleave */
1633                     $(this).addClass('hide')
1634                            .removeClass('hovering');
1635                 }).mouseenter(function() {
1636                     /** mouseenter */
1637                     $(this).addClass('hovering');
1638                 });
1639                 $fullNameLayer = $('#full_name_layer');
1640             }
1641             $fullNameLayer.removeClass('hide');
1642             $fullNameLayer.css({left: thisOffset.left, top: thisOffset.top});
1643             $fullNameLayer.html($this.clone());
1644             setTimeout(function() {
1645                 if(! $fullNameLayer.hasClass('hovering'))
1646                 {
1647                     $fullNameLayer.trigger('mouseleave');
1648                 }
1649             }, 200);
1650         }
1651     });