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