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