1 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 * function used in or for navigation panel
5 * @package phpMyAdmin-Navigation
9 * Executed on page load
12 if (! $('#pma_navigation').length) {
13 // Don't bother running any code if the navigation is not even on the page
17 // Do not let the page reload on submitting the fast filter
18 $(document).on('submit', '.fast_filter', function (event) {
19 event.preventDefault();
22 // Fire up the resize handlers
26 * opens/closes (hides/shows) tree elements
29 $('#pma_navigation_tree a.expander').live('click', function (event) {
30 event.preventDefault();
31 event.stopImmediatePropagation();
32 var $icon = $(this).find('img');
33 if ($icon.is('.ic_b_plus')) {
34 expandTreeNode($(this));
36 collapseTreeNode($(this));
41 * Register event handler for click on the reload
42 * navigation icon at the top of the panel
44 $('#pma_navigation_reload').live('click', function (event) {
45 event.preventDefault();
46 $('#pma_navigation .throbber')
48 .css('visibility', 'visible');
49 PMA_reloadNavigation();
53 * Bind all "fast filter" events
55 $('#pma_navigation_tree li.fast_filter span')
56 .live('click', PMA_fastFilter.events.clear);
57 $('#pma_navigation_tree li.fast_filter input.searchClause')
58 .live('focus', PMA_fastFilter.events.focus)
59 .live('blur', PMA_fastFilter.events.blur)
60 .live('keyup', PMA_fastFilter.events.keyup);
63 * Ajax handler for pagination
65 $('#pma_navigation_tree div.pageselector a.ajax').live('click', function (event) {
66 event.preventDefault();
67 PMA_navigationTreePagination($(this));
73 $('#pma_navigation_tree.highlight li:not(.fast_filter)').live(
76 if ($('li:visible', this).length === 0) {
77 $(this).addClass('activePointer');
81 $('#pma_navigation_tree.highlight li:not(.fast_filter)').live(
84 $(this).removeClass('activePointer');
89 * Jump to recent table
91 $('#recentTable').live('change', function () {
92 if (this.value !== '') {
93 var arr = jQuery.parseJSON(this.value);
94 var $form = $(this).closest('form');
95 $form.find('input[name=db]').val(arr.db);
96 $form.find('input[name=table]').val(arr.table);
101 /** Create a Routine, Trigger or Event */
102 $('li.new_procedure a.ajax, li.new_function a.ajax').live('click', function (event) {
103 event.preventDefault();
104 var dialog = new RTE.object('routine');
105 dialog.editorDialog(1, $(this));
107 $('li.new_trigger a.ajax').live('click', function (event) {
108 event.preventDefault();
109 var dialog = new RTE.object('trigger');
110 dialog.editorDialog(1, $(this));
112 $('li.new_event a.ajax').live('click', function (event) {
113 event.preventDefault();
114 var dialog = new RTE.object('event');
115 dialog.editorDialog(1, $(this));
118 /** Edit Routines, Triggers and Events */
119 $('li.procedure > a.ajax, li.function > a.ajax').live('click', function (event) {
120 event.preventDefault();
121 var dialog = new RTE.object('routine');
122 dialog.editorDialog(0, $(this));
124 $('li.trigger > a.ajax').live('click', function (event) {
125 event.preventDefault();
126 var dialog = new RTE.object('trigger');
127 dialog.editorDialog(0, $(this));
129 $('li.event > a.ajax').live('click', function (event) {
130 event.preventDefault();
131 var dialog = new RTE.object('event');
132 dialog.editorDialog(0, $(this));
135 /** Export Routines, Triggers and Events */
136 $('li.procedure div:eq(1) a.ajax img,' +
137 ' li.function div:eq(1) a.ajax img,' +
138 ' li.trigger div:eq(1) a.ajax img,' +
139 ' li.event div:eq(1) a.ajax img'
140 ).live('click', function (event) {
141 event.preventDefault();
142 var dialog = new RTE.object();
143 dialog.exportDialog($(this).parent());
147 $('li.new_index a.ajax').live('click', function (event) {
148 event.preventDefault();
149 var url = $(this).attr('href').substr(
150 $(this).attr('href').indexOf('?') + 1
151 ) + '&ajax_request=true';
152 var title = PMA_messages.strAddIndex;
153 indexEditorDialog(url, title);
157 $('li.index a.ajax').live('click', function (event) {
158 event.preventDefault();
159 var url = $(this).attr('href').substr(
160 $(this).attr('href').indexOf('?') + 1
161 ) + '&ajax_request=true';
162 var title = PMA_messages.strEditIndex;
163 indexEditorDialog(url, title);
167 $('li.new_view a.ajax').live('click', function (event) {
168 event.preventDefault();
169 PMA_createViewDialog($(this));
172 /** Hide navigation tree item */
173 $('a.hideNavItem.ajax').live('click', function (event) {
174 event.preventDefault();
176 url: $(this).attr('href') + '&ajax_request=true',
177 success: function (data) {
178 if (data.success === true) {
179 PMA_reloadNavigation();
181 PMA_ajaxShowMessage(data.error);
187 /** Display a dialog to choose hidden navigation items to show */
188 $('a.showUnhide.ajax').live('click', function (event) {
189 event.preventDefault();
190 var $msg = PMA_ajaxShowMessage();
191 $.get($(this).attr('href') + '&ajax_request=1', function (data) {
192 if (data.success === true) {
193 PMA_ajaxRemoveMessage($msg);
194 var buttonOptions = {};
195 buttonOptions[PMA_messages.strClose] = function () {
196 $(this).dialog("close");
198 var $dialog = $('<div/>')
199 .attr('id', 'unhideNavItemDialog')
200 .append(data.message)
205 buttons: buttonOptions,
206 title: PMA_messages.strUnhideNavItem,
212 PMA_ajaxShowMessage(data.error);
217 /** Show a hidden navigation tree item */
218 $('a.unhideNavItem.ajax').live('click', function (event) {
219 event.preventDefault();
220 var $tr = $(this).parents('tr');
221 var $msg = PMA_ajaxShowMessage();
223 url: $(this).attr('href') + '&ajax_request=true',
224 success: function (data) {
225 PMA_ajaxRemoveMessage($msg);
226 if (data.success === true) {
228 PMA_reloadNavigation();
230 PMA_ajaxShowMessage(data.error);
236 PMA_showCurrentNavigation();
240 * Expands a node in navigation tree.
242 * @param $expandElem expander
243 * @param callback callback function
247 function expandTreeNode($expandElem, callback)
249 var $children = $expandElem.closest('li').children('div.list_container');
250 var $icon = $expandElem.find('img');
251 if ($expandElem.hasClass('loaded')) {
252 if ($icon.is('.ic_b_plus')) {
253 $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
254 $children.show('fast');
256 if (callback && typeof callback == 'function') {
260 var $throbber = $('#pma_navigation .throbber')
263 .css('visibility', 'visible')
266 $throbber.insertBefore($icon);
268 loadChildNodes($expandElem, function (data) {
269 if (data.success === true) {
270 var $destination = $expandElem.closest('li');
271 $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
273 .children('div.list_container')
275 if ($destination.find('ul > li').length == 1) {
276 $destination.find('ul > li')
277 .find('a.expander.container')
280 if (callback && typeof callback == 'function') {
284 PMA_ajaxShowMessage(data.error, false);
294 * Auto-scrolls the newly chosen database
296 * @param object $element The element to set to view
297 * @param object $container The container srollable element
300 function scrollToView($element, $container) {
301 var pushToOffset = $element.offset().top - $container.offset().top + $container.scrollTop();
302 $('#pma_navigation_tree_content').stop().animate({
303 scrollTop: pushToOffset
308 * Collapses a node in navigation tree.
310 * @param $expandElem expander
314 function collapseTreeNode($expandElem) {
315 var $children = $expandElem.closest('li').children('div.list_container');
316 var $icon = $expandElem.find('img');
317 if ($expandElem.hasClass('loaded')) {
318 if ($icon.is('.ic_b_minus')) {
319 $icon.removeClass('ic_b_minus').addClass('ic_b_plus');
320 $children.hide('fast');
327 * Loads child items of a node and executes a given callback
329 * @param $expandElem expander
330 * @param callback callback function
334 function loadChildNodes($expandElem, callback) {
335 var $destination = $expandElem.closest('li');
337 var searchClause = PMA_fastFilter.getSearchClause();
338 var searchClause2 = PMA_fastFilter.getSearchClause2($expandElem);
341 aPath: $expandElem.find('span.aPath').text(),
342 vPath: $expandElem.find('span.vPath').text(),
343 pos: $expandElem.find('span.pos').text(),
344 pos2_name: $expandElem.find('span.pos2_name').text(),
345 pos2_value: $expandElem.find('span.pos2_value').text(),
346 searchClause: searchClause,
347 searchClause2: searchClause2
350 var url = $('#pma_navigation').find('a.navigation_url').attr('href');
351 $.get(url, params, function (data) {
352 if (data.success === true) {
353 $expandElem.addClass('loaded');
354 $destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
355 $destination.append(data.message);
356 if (callback && typeof callback == 'function') {
364 * Expand the navigation and highlight the current database or table/view
368 function PMA_showCurrentNavigation()
370 var db = PMA_commonParams.get('db');
371 var table = PMA_commonParams.get('table');
372 $('#pma_navigation_tree')
374 .removeClass('selected');
376 var $dbItem = findLoadedItem(
377 $('#pma_navigation_tree > div'), db, 'database', !table
380 var $expander = $dbItem.children('div:first').children('a.expander');
381 // if not loaded or loaded but collapsed
382 if (! $expander.hasClass('loaded') ||
383 $expander.find('img').is('.ic_b_plus')
385 expandTreeNode($expander, function () {
386 handleTableOrDb(table, $dbItem);
389 handleTableOrDb(table, $dbItem);
394 function handleTableOrDb(table, $dbItem) {
396 loadAndHighlightTableOrView($dbItem, table);
398 var $container = $dbItem.children('div.list_container');
399 var $tableContainer = $container.children('ul').children('li.tableContainer');
400 if ($tableContainer.length > 0) {
401 var $expander = $tableContainer.children('div:first').children('a.expander');
402 expandTreeNode($expander, function () {
403 scrollToView($dbItem, $('#pma_navigation_tree_content'));
406 scrollToView($dbItem, $('#pma_navigation_tree_content'));
411 function findLoadedItem($container, name, clazz, doSelect) {
413 $container.children('ul').children('li').each(function () {
415 // this is a navigation group, recurse
416 if ($li.is('.navGroup')) {
417 var $container = $li.children('div.list_container');
418 var $childRet = findLoadedItem(
419 $container, name, clazz, doSelect
425 } else { // this is a real navigation item
426 // name and class matches
427 if (((clazz && $li.is('.' + clazz)) || ! clazz) &&
428 $li.children('a').text() == name) {
430 $li.addClass('selected');
432 // taverse up and expand and parent navigation groups
433 $li.parents('.navGroup').each(function () {
434 $cont = $(this).children('div.list_container');
435 if (! $cont.is(':visible')) {
437 .children('div:first')
438 .children('a.expander')
450 function loadAndHighlightTableOrView($dbItem, table) {
451 var $container = $dbItem.children('div.list_container');
452 var $tableContainer = $container
454 .children('li.tableContainer');
455 var $viewContainer = $container
457 .children('li.viewContainer');
459 if ($tableContainer.length > 0) {
460 var $expander = $tableContainer
461 .children('div:first')
462 .children('a.expander');
464 if (! $expander.hasClass('loaded')) {
465 loadChildNodes($expander, function (data) {
466 highlightTableOrView($tableContainer, $viewContainer, table);
469 highlightTableOrView($tableContainer, $viewContainer, table);
471 } else if ($viewContainer.length > 0) {
472 highlightView($viewContainer, table);
474 // no containers, highlight the item
475 var $tableOrView = findLoadedItem($container, table, null, true);
477 scrollToView($tableOrView, $('#pma_navigation_tree_content'));
482 function highlightTableOrView($tableContainer, $viewContainer, table)
484 if (isItemInContainer($tableContainer, table, 'table')) {
485 var $expander = $tableContainer
486 .children('div:first')
487 .children('a.expander');
488 if ($expander.find('img').is('.ic_b_plus')) {
489 expandTreeNode($expander);
491 var $table = findLoadedItem(
492 $tableContainer.children('div.list_container'),
496 scrollToView($table, $('#pma_navigation_tree_content'));
498 } else if ($viewContainer.length > 0) {
499 highlightView($viewContainer, table);
503 function isItemInContainer($container, name, clazz)
505 $items = $container.find('li.' + clazz);
507 $items.each(function () {
508 if ($(this).children('a').text() == name) {
516 function highlightView($viewContainer, view) {
517 var $expander = $viewContainer
518 .children('div:first')
519 .children('a.expander');
520 if (! $expander.hasClass('loaded') ||
521 $expander.find('img').is('.ic_b_plus')
523 expandTreeNode($expander, function () {
524 var $view = findLoadedItem(
525 $viewContainer.children('div.list_container'),
529 scrollToView($view, $('#pma_navigation_tree_content'));
533 var $view = findLoadedItem(
534 $viewContainer.children('div.list_container'),
538 scrollToView($view, $('#pma_navigation_tree_content'));
545 * Reloads the whole navigation tree while preserving its state
547 * @param function the callback function
550 function PMA_reloadNavigation(callback) {
551 var $throbber = $('#pma_navigation .throbber')
554 'visibility' : 'visible',
559 pos: $('#pma_navigation_tree').find('a.expander:first > span.pos').text()
561 // Traverse the navigation tree backwards to generate all the actual
562 // and virtual paths, as well as the positions in the pagination at
563 // various levels, if necessary.
565 $('#pma_navigation_tree').find('a.expander:visible').each(function () {
566 if ($(this).find('img').is('.ic_b_minus') &&
567 $(this).closest('li').find('div.list_container .ic_b_minus').length === 0
569 params['n' + count + '_aPath'] = $(this).find('span.aPath').text();
570 params['n' + count + '_vPath'] = $(this).find('span.vPath').text();
572 var pos2_name = $(this).find('span.pos2_name').text();
577 .find('span.pos2_name:last')
580 var pos2_value = $(this).find('span.pos2_value').text();
585 .find('span.pos2_value:last')
589 params['n' + count + '_pos2_name'] = pos2_name;
590 params['n' + count + '_pos2_value'] = pos2_value;
592 params['n' + count + '_pos3_name'] = $(this).find('span.pos3_name').text();
593 params['n' + count + '_pos3_value'] = $(this).find('span.pos3_value').text();
597 var url = $('#pma_navigation').find('a.navigation_url').attr('href');
598 $.post(url, params, function (data) {
599 // Hide throbber if it's visible
600 $('#pma_navigation .throbber')
602 .css('visibility', 'hidden');
604 $('#pma_navigation_tree').html(data.message).children('div').show();
605 PMA_showCurrentNavigation();
606 // Fire the callback, if any
607 if (typeof callback === 'function') {
611 PMA_ajaxShowMessage(data.error);
617 * Handles any requests to change the page in a branch of a tree
619 * This can be called from link click or select change event handlers
621 * @param object $this A jQuery object that points to the element that
622 * initiated the action of changing the page
626 function PMA_navigationTreePagination($this)
628 var $msgbox = PMA_ajaxShowMessage();
629 var isDbSelector = $this.closest('div.pageselector').is('.dbselector');
631 if ($this[0].tagName == 'A') {
632 url = $this.attr('href');
633 params = 'ajax_request=true';
634 } else { // tagName == 'SELECT'
635 url = 'navigation.php';
636 params = $this.closest("form").serialize() + '&ajax_request=true';
638 var searchClause = PMA_fastFilter.getSearchClause();
640 params += '&searchClause=' + encodeURIComponent(searchClause);
643 params += '&full=true';
645 var searchClause2 = PMA_fastFilter.getSearchClause2($this);
647 params += '&searchClause2=' + encodeURIComponent(searchClause2);
650 $.post(url, params, function (data) {
651 PMA_ajaxRemoveMessage($msgbox);
654 var val = PMA_fastFilter.getSearchClause();
655 $('#pma_navigation_tree')
660 $('#pma_navigation_tree')
661 .find('li.fast_filter input.searchClause')
665 var $parent = $this.closest('div.list_container').parent();
666 var val = PMA_fastFilter.getSearchClause2($this);
667 $this.closest('div.list_container').html(
668 $(data.message).children().show()
671 $parent.find('li.fast_filter input.searchClause').val(val);
673 $parent.find('span.pos2_value:first').text(
674 $parent.find('span.pos2_value:last').text()
676 $parent.find('span.pos3_value:first').text(
677 $parent.find('span.pos3_value:last').text()
681 PMA_ajaxShowMessage(data.error);
687 * @var ResizeHandler Custom object that manages the resizing of the navigation
689 * XXX: Must only be ever instanciated once
690 * XXX: Inside event handlers the 'this' object is accessed as 'event.data.resize_handler'
692 var ResizeHandler = function () {
694 * Whether the user has initiated a resize operation
698 * @var int panel_width Used by the collapser to know where to go
699 * back to when uncollapsing the panel
701 this.panel_width = 0;
703 * @var string left Used to provide support for RTL languages
705 this.left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
707 * Adjusts the width of the navigation panel to the specified value
709 * @param int pos Navigation width in pixels
713 this.setWidth = function (pos) {
714 var $resizer = $('#pma_navigation_resizer');
715 var resizer_width = $resizer.width();
716 var $collapser = $('#pma_navigation_collapser');
717 $('#pma_navigation').width(pos);
718 $('body').css('margin-' + this.left, pos + 'px');
719 $("#floating_menubar")
720 .css('margin-' + this.left, (pos + resizer_width) + 'px');
721 $resizer.css(this.left, pos + 'px');
724 .css(this.left, pos + resizer_width)
725 .html(this.getSymbol(pos))
726 .prop('title', PMA_messages.strShowPanel);
730 .html(this.getSymbol(pos))
731 .prop('title', PMA_messages.strHidePanel);
733 setTimeout(function () {
734 $(window).trigger('resize');
738 * Returns the horizontal position of the mouse,
739 * relative to the outer side of the navigation panel
741 * @param int pos Navigation width in pixels
745 this.getPos = function (event) {
746 var pos = event.pageX;
747 var windowWidth = $(window).width();
748 if (this.left != 'left') {
749 pos = windowWidth - event.pageX;
753 } else if (pos + 100 >= windowWidth) {
754 pos = windowWidth - 100;
756 this.panel_width = 0;
761 * Returns the HTML code for the arrow symbol used in the collapser
763 * @param int width The width of the panel
767 this.getSymbol = function (width) {
768 if (this.left == 'left') {
783 * Event handler for initiating a resize of the panel
785 * @param object e Event data (contains a reference to resizeHandler)
789 this.mousedown = function (event) {
790 event.preventDefault();
791 event.data.resize_handler.active = true;
792 $('body').css('cursor', 'col-resize');
795 * Event handler for terminating a resize of the panel
797 * @param object e Event data (contains a reference to resizeHandler)
801 this.mouseup = function (event) {
802 if (event.data.resize_handler.active) {
803 event.data.resize_handler.active = false;
804 $('body').css('cursor', '');
805 $.cookie('pma_navi_width', event.data.resize_handler.getPos(event));
806 $('#topmenu').menuResizer('resize');
810 * Event handler for updating the panel during a resize operation
812 * @param object e Event data (contains a reference to resizeHandler)
816 this.mousemove = function (event) {
817 if (event.data && event.data.resize_handler && event.data.resize_handler.active) {
818 event.preventDefault();
819 var pos = event.data.resize_handler.getPos(event);
820 event.data.resize_handler.setWidth(pos);
824 * Event handler for collapsing the panel
826 * @param object e Event data (contains a reference to resizeHandler)
830 this.collapse = function (event) {
831 event.preventDefault();
832 event.data.active = false;
833 var panel_width = event.data.resize_handler.panel_width;
834 var width = $('#pma_navigation').width();
835 if (width === 0 && panel_width === 0) {
838 event.data.resize_handler.setWidth(panel_width);
839 event.data.resize_handler.panel_width = width;
842 * Event handler for resizing the navigation tree height on window resize
846 this.treeResize = function (event) {
847 var $nav = $("#pma_navigation"),
848 $nav_tree = $("#pma_navigation_tree"),
849 $nav_header = $("#pma_navigation_header"),
850 $nav_tree_content = $("#pma_navigation_tree_content");
851 $nav_tree.height($nav.height() - $nav_header.height());
852 $nav_tree_content.height($nav_tree.height() - $nav_tree_content.position().top);
854 /* Initialisation section begins here */
855 if ($.cookie('pma_navi_width')) {
856 // If we have a cookie, set the width of the panel to its value
857 var pos = Math.abs(parseInt($.cookie('pma_navi_width'), 10) || 0);
859 $('#topmenu').menuResizer('resize');
861 // Register the events for the resizer and the collapser
862 $('#pma_navigation_resizer')
863 .live('mousedown', {'resize_handler': this}, this.mousedown);
865 .bind('mouseup', {'resize_handler': this}, this.mouseup)
866 .bind('mousemove', {'resize_handler': this}, $.throttle(this.mousemove, 4));
867 var $collapser = $('#pma_navigation_collapser');
868 $collapser.live('click', {'resize_handler': this}, this.collapse);
869 // Add the correct arrow symbol to the collapser
870 $collapser.html(this.getSymbol($('#pma_navigation').width()));
871 // Fix navigation tree height
872 $(window).on('resize', this.treeResize);
873 // need to call this now and then, browser might decide
874 // to show/hide horizontal scrollbars depending on page content width
875 setInterval(this.treeResize, 2000);
876 }; // End of ResizeHandler
879 * @var object PMA_fastFilter Handles the functionality that allows filtering
880 * of the items in a branch of the navigation tree
882 var PMA_fastFilter = {
884 * Construct for the asynchronous fast filter functionality
886 * @param object $this A jQuery object pointing to the list container
887 * which is the nearest parent of the fast filter
888 * @param string searchClause The query string for the filter
890 * @return new PMA_fastFilter.filter object
892 filter: function ($this, searchClause) {
894 * @var object $this A jQuery object pointing to the list container
895 * which is the nearest parent of the fast filter
899 * @var bool searchClause The query string for the filter
901 this.searchClause = searchClause;
903 * @var object $clone A clone of the original contents
904 * of the navigation branch before
905 * the fast filter was applied
907 this.$clone = $this.clone();
909 * @var bool swapped Whether the user clicked on the "N other results" link
911 this.swapped = false;
913 * @var object xhr A reference to the ajax request that is currently running
917 * @var int timeout Used to delay the request for asynchronous search
921 var $filterInput = $this.find('li.fast_filter input.searchClause');
922 if ($filterInput.length !== 0 &&
923 $filterInput.val() !== '' &&
924 $filterInput.val() != $filterInput[0].defaultValue
930 * Gets the query string from the database fast filter form
934 getSearchClause: function () {
936 var $input = $('#pma_navigation_tree')
937 .find('li.fast_filter.db_fast_filter input.searchClause');
938 if ($input.length && $input.val() != $input[0].defaultValue) {
939 retval = $input.val();
944 * Gets the query string from a second level item's fast filter form
945 * The retrieval is done by trasversing the navigation tree backwards
949 getSearchClause2: function ($this) {
950 var $filterContainer = $this.closest('div.list_container');
951 var $filterInput = $([]);
953 if ($filterContainer.find('li.fast_filter:not(.db_fast_filter) input.searchClause').length !== 0) {
954 $filterInput = $filterContainer.find('li.fast_filter:not(.db_fast_filter) input.searchClause');
956 } else if (! $filterContainer.is('div.list_container')) {
959 $filterContainer = $filterContainer
961 .closest('div.list_container');
963 var searchClause2 = '';
964 if ($filterInput.length !== 0 &&
965 $filterInput.first().val() != $filterInput[0].defaultValue
967 searchClause2 = $filterInput.val();
969 return searchClause2;
972 * @var hash events A list of functions that are bound to DOM events
973 * at the top of this file
976 focus: function (event) {
977 var $obj = $(this).closest('div.list_container');
978 if (! $obj.data('fastFilter')) {
981 new PMA_fastFilter.filter($obj, $(this).val())
984 if ($(this).val() == this.defaultValue) {
990 blur: function (event) {
991 if ($(this).val() === '') {
992 $(this).val(this.defaultValue);
994 var $obj = $(this).closest('div.list_container');
995 if ($(this).val() == this.defaultValue && $obj.data('fastFilter')) {
996 $obj.data('fastFilter').restore();
999 keyup: function (event) {
1000 var $obj = $(this).closest('div.list_container');
1002 if ($(this).val() != this.defaultValue && $(this).val() !== '') {
1003 $obj.find('div.pageselector').hide();
1004 str = $(this).val();
1008 * FIXME at the server level a value match is done while on
1009 * the client side it is a regex match. These two should be aligned
1012 // regex used for filtering.
1015 regex = new RegExp(str, 'i');
1020 // this is the div that houses the items to be filtered by this filter.
1022 if ($(this).closest('li.fast_filter').is('.db_fast_filter')) {
1023 outerContainer = $('#pma_navigation_tree_content');
1025 outerContainer = $obj;
1028 // filters items that are directly under the div as well as grouped in
1029 // groups. Does not filter child items (i.e. a database search does
1030 // not filter tables)
1031 var item_filter = function($curr) {
1032 $curr.children('ul').children('li.navGroup').each(function() {
1033 $(this).children('div.list_container').each(function() {
1034 item_filter($(this)); // recursive
1037 $curr.children('ul').children('li').children('a').not('.container').each(function() {
1038 if (regex.test($(this).text())) {
1039 $(this).parent().show().removeClass('hidden');
1041 $(this).parent().hide().addClass('hidden');
1045 item_filter(outerContainer);
1047 // hides containers that does not have any visible children
1048 var container_filter = function ($curr) {
1049 $curr.children('ul').children('li.navGroup').each(function() {
1050 var $group = $(this);
1051 $group.children('div.list_container').each(function() {
1052 container_filter($(this)); // recursive
1054 $group.show().removeClass('hidden');
1055 if ($group.children('div.list_container').children('ul')
1056 .children('li').not('.hidden').length === 0) {
1057 $group.hide().addClass('hidden');
1061 container_filter(outerContainer);
1063 if ($(this).val() != this.defaultValue && $(this).val() !== '') {
1064 if (! $obj.data('fastFilter')) {
1067 new PMA_fastFilter.filter($obj, $(this).val())
1070 $obj.data('fastFilter').update($(this).val());
1072 } else if ($obj.data('fastFilter')) {
1073 $obj.data('fastFilter').restore(true);
1076 clear: function (event) {
1077 event.stopPropagation();
1078 // Clear the input and apply the fast filter with empty input
1079 var filter = $(this).closest('div.list_container').data('fastFilter');
1083 var value = $(this).prev()[0].defaultValue;
1084 $(this).prev().val(value).trigger('keyup');
1089 * Handles a change in the search clause
1091 * @param string searchClause The query string for the filter
1095 PMA_fastFilter.filter.prototype.update = function (searchClause)
1097 if (this.searchClause != searchClause) {
1098 this.searchClause = searchClause;
1099 this.$this.find('.moreResults').remove();
1104 * After a delay of 250mS, initiates a request to retrieve search results
1105 * Multiple calls to this function will always abort the previous request
1109 PMA_fastFilter.filter.prototype.request = function ()
1112 clearTimeout(self.timeout);
1113 if (self.$this.find('li.fast_filter').find('img.throbber').length === 0) {
1114 self.$this.find('li.fast_filter').append(
1115 $('<div class="throbber"></div>').append(
1116 $('#pma_navigation_content')
1117 .find('img.throbber')
1119 .css('visibility', 'visible')
1123 self.timeout = setTimeout(function () {
1127 var url = $('#pma_navigation').find('a.navigation_url').attr('href');
1128 var results = self.$this.find('li:not(.hidden):not(.fast_filter):not(.navGroup)').not('[class^=new]').length;
1129 var params = self.$this.find('> ul > li > form.fast_filter').first().serialize() + "&results=" + results;
1130 if (self.$this.find('> ul > li > form.fast_filter:first input[name=searchClause]').length === 0) {
1131 var $input = $('#pma_navigation_tree').find('li.fast_filter.db_fast_filter input.searchClause');
1132 if ($input.length && $input.val() != $input[0].defaultValue) {
1133 params += '&searchClause=' + encodeURIComponent($input.val());
1141 complete: function (jqXHR) {
1142 var data = $.parseJSON(jqXHR.responseText);
1143 self.$this.find('li.fast_filter').find('div.throbber').remove();
1144 if (data && data.results) {
1145 var $listItem = $('<li />', {'class': 'moreResults'})
1146 .appendTo(self.$this.find('li.fast_filter'));
1147 var $link = $('<a />', {href: '#'})
1149 .appendTo($listItem)
1150 .click(function (event) {
1151 event.preventDefault();
1152 self.swap.apply(self, [data.message]);
1160 * Replaces the contents of the navigation branch with the search results
1162 * @param string list The search results
1166 PMA_fastFilter.filter.prototype.swap = function (list)
1168 this.swapped = true;
1170 .html($(list).html())
1174 .find('li.fast_filter input.searchClause')
1175 .val(this.searchClause);
1176 this.$this.data('fastFilter', this);
1179 * Restores the navigation to the original state after the fast filter is cleared
1181 * @param bool focus Whether to also focus the input box of the fast filter
1185 PMA_fastFilter.filter.prototype.restore = function (focus)
1188 this.swapped = false;
1189 this.$this.html(this.$clone.html()).children().show();
1190 this.$this.data('fastFilter', this);
1192 this.$this.find('li.fast_filter input.searchClause').focus();
1195 this.searchClause = '';
1196 this.$this.find('.moreResults').remove();
1197 this.$this.find('div.pageselector').show();
1198 this.$this.find('div.throbber').remove();