Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"version":3,"file":"menubar.min.js","sources":["../src/menubar.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Aria menubar functionality. Enhances a simple nested list structure into a full aria widget.\n * Based on the open ajax example: http://oaa-accessibility.org/example/26/\n *\n * @module     tool_lp/menubar\n * @copyright  2015 Damyon Wiese <damyon@moodle.com>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n    /** @property {boolean}  Flag to indicate if we have already registered a click event handler for the document. */\n    var documentClickHandlerRegistered = false;\n\n    /** @property {boolean} Flag to indicate whether there's an active, open menu. */\n    var menuActive = false;\n\n    /**\n     * Close all open submenus anywhere in the page (there should only ever be one open at a time).\n     *\n     * @method closeAllSubMenus\n     */\n    var closeAllSubMenus = function() {\n        $('.tool-lp-menu .tool-lp-sub-menu').attr('aria-hidden', 'true');\n        // Every menu's closed at this point, so set the menu active flag to false.\n        menuActive = false;\n    };\n\n    /**\n     * Constructor\n     *\n     * @param {jQuery} menuRoot Jquery collection matching the root of the menu.\n     * @param {Function[]} handlers called when a menu item is chosen.\n     */\n    var Menubar = function(menuRoot, handlers) {\n        // Setup private class variables.\n        this.menuRoot = menuRoot;\n        this.handlers = handlers;\n        this.rootMenus = this.menuRoot.children('li');\n        this.subMenus = this.rootMenus.children('ul');\n        this.subMenuItems = this.subMenus.children('li');\n        this.allItems = this.rootMenus.add(this.subMenuItems);\n        this.activeItem = null;\n        this.isChildOpen = false;\n\n        this.keys = {\n            tab:    9,\n            enter:  13,\n            esc:    27,\n            space:  32,\n            left:   37,\n            up:     38,\n            right:  39,\n            down:   40\n        };\n\n        this.addAriaAttributes();\n        // Add the event listeners.\n        this.addEventListeners();\n    };\n\n    /**\n     * Open a submenu, first it closes all other sub-menus and sets the open direction.\n     * @method openSubMenu\n     * @param {Node} menu\n     */\n    Menubar.prototype.openSubMenu = function(menu) {\n        this.setOpenDirection();\n        closeAllSubMenus();\n        menu.attr('aria-hidden', 'false');\n        // Set menu active flag to true when a menu is opened.\n        menuActive = true;\n    };\n\n\n    /**\n     * Bind the event listeners to the DOM\n     * @method addEventListeners\n     */\n    Menubar.prototype.addEventListeners = function() {\n        var currentThis = this;\n\n        // When clicking outside the menubar.\n        if (documentClickHandlerRegistered === false) {\n            $(document).click(function() {\n                // Check if a menu is opened.\n                if (menuActive) {\n                    // Close menu.\n                    closeAllSubMenus();\n                }\n            });\n            // Set this flag to true so that we won't need to add a document click handler for the other Menubar instances.\n            documentClickHandlerRegistered = true;\n        }\n\n        // Hovers.\n        this.subMenuItems.mouseenter(function() {\n            $(this).addClass('menu-hover');\n            return true;\n        });\n\n        this.subMenuItems.mouseout(function() {\n            $(this).removeClass('menu-hover');\n            return true;\n        });\n\n        // Mouse listeners.\n        this.allItems.click(function(e) {\n            return currentThis.handleClick($(this), e);\n        });\n\n        // Key listeners.\n        this.allItems.keydown(function(e) {\n            return currentThis.handleKeyDown($(this), e);\n        });\n\n        this.allItems.focus(function() {\n            return currentThis.handleFocus($(this));\n        });\n\n        this.allItems.blur(function() {\n            return currentThis.handleBlur($(this));\n        });\n    };\n\n    /**\n     * Process click events for the top menus.\n     *\n     * @method handleClick\n     * @param {Object} item is the jquery object of the item firing the event\n     * @param {Event} e is the associated event object\n     * @return {boolean} Returns false\n     */\n    Menubar.prototype.handleClick = function(item, e) {\n        e.stopPropagation();\n\n        var parentUL = item.parent();\n\n        if (parentUL.is('.tool-lp-menu')) {\n            // Toggle the child menu open/closed.\n            if (item.children('ul').first().attr('aria-hidden') == 'true') {\n                this.openSubMenu(item.children('ul').first());\n            } else {\n                item.children('ul').first().attr('aria-hidden', 'true');\n            }\n        } else {\n            // Remove hover and focus styling.\n            this.allItems.removeClass('menu-hover menu-focus');\n\n            // Clear the active item.\n            this.activeItem = null;\n\n            // Close the menu.\n            this.menuRoot.find('ul').not('.root-level').attr('aria-hidden', 'true');\n            // Follow any link, or call the click handlers.\n            var anchor = item.find('a').first();\n            var clickEvent = new $.Event('click');\n            clickEvent.target = anchor;\n            var eventHandled = false;\n            if (this.handlers) {\n                $.each(this.handlers, function(selector, handler) {\n                    if (eventHandled) {\n                        return;\n                    }\n                    if (item.find(selector).length > 0) {\n                        var callable = $.proxy(handler, anchor);\n                        // False means stop propogatting events.\n                        eventHandled = (callable(clickEvent) === false) || clickEvent.isDefaultPrevented();\n                    }\n                });\n            }\n            // If we didn't find a handler, and the HREF is # that probably means that\n            // we are handling it from somewhere else. Let's just do nothing in that case.\n            if (!eventHandled && anchor.attr('href') !== '#') {\n                window.location.href = anchor.attr('href');\n            }\n        }\n        return false;\n    };\n\n    /*\n     * Process focus events for the menu.\n     *\n     * @method handleFocus\n     * @param {Object} item is the jquery object of the item firing the event\n     * @return boolean Returns false\n     */\n    Menubar.prototype.handleFocus = function(item) {\n\n        // If activeItem is null, we are getting focus from outside the menu. Store\n        // the item that triggered the event.\n        if (this.activeItem === null) {\n            this.activeItem = item;\n        } else if (item[0] != this.activeItem[0]) {\n            return true;\n        }\n\n        // Get the set of jquery objects for all the parent items of the active item.\n        var parentItems = this.activeItem.parentsUntil('ul.tool-lp-menu').filter('li');\n\n        // Remove focus styling from all other menu items.\n        this.allItems.removeClass('menu-focus');\n\n        // Add focus styling to the active item.\n        this.activeItem.addClass('menu-focus');\n\n        // Add focus styling to all parent items.\n        parentItems.addClass('menu-focus');\n\n        // If the bChildOpen flag has been set, open the active item's child menu (if applicable).\n        if (this.isChildOpen === true) {\n\n            var itemUL = item.parent();\n\n            // If the itemUL is a root-level menu and item is a parent item,\n            // show the child menu.\n            if (itemUL.is('.tool-lp-menu') && (item.attr('aria-haspopup') == 'true')) {\n                this.openSubMenu(item.children('ul').first());\n            }\n        }\n\n        return true;\n    };\n\n    /*\n     * Process blur events for the menu.\n     *\n     * @method handleBlur\n     * @param {Object} item is the jquery object of the item firing the event\n     * @return boolean Returns false\n     */\n    Menubar.prototype.handleBlur = function(item) {\n        item.removeClass('menu-focus');\n\n        return true;\n    };\n\n    /*\n     * Determine if the menu should open to the left, or the right,\n     * based on the screen size and menu position.\n     * @method setOpenDirection\n     */\n    Menubar.prototype.setOpenDirection = function() {\n        var pos = this.menuRoot.offset();\n        var isRTL = $(document.body).hasClass('dir-rtl');\n        var openLeft = true;\n        var heightmenuRoot = this.rootMenus.outerHeight();\n        var widthmenuRoot = this.rootMenus.outerWidth();\n        // Sometimes the menuMinWidth is not enough to figure out if menu exceeds the window width.\n        // So we have to calculate the real menu width.\n        var subMenuContainer = this.rootMenus.find('ul.tool-lp-sub-menu');\n\n        // Reset margins.\n        subMenuContainer.css('margin-right', '');\n        subMenuContainer.css('margin-left', '');\n        subMenuContainer.css('margin-top', '');\n\n        subMenuContainer.attr('aria-hidden', false);\n        var menuRealWidth = subMenuContainer.outerWidth(),\n            menuRealHeight = subMenuContainer.outerHeight();\n\n        var margintop = null,\n            marginright = null,\n            marginleft = null;\n        var top = pos.top - $(window).scrollTop();\n        // Top is the same for RTL and LTR.\n        if (top + menuRealHeight > $(window).height()) {\n            margintop = menuRealHeight + heightmenuRoot;\n            subMenuContainer.css('margin-top', '-' + margintop + 'px');\n        }\n\n        if (isRTL) {\n            if (pos.left - menuRealWidth < 0) {\n                marginright = menuRealWidth - widthmenuRoot;\n                subMenuContainer.css('margin-right', '-' + marginright + 'px');\n            }\n        } else {\n            if (pos.left + menuRealWidth > $(window).width()) {\n                marginleft = menuRealWidth - widthmenuRoot;\n                subMenuContainer.css('margin-left', '-' + marginleft + 'px');\n            }\n        }\n\n        if (openLeft) {\n            this.menuRoot.addClass('tool-lp-menu-open-left');\n        } else {\n            this.menuRoot.removeClass('tool-lp-menu-open-left');\n        }\n\n    };\n\n    /*\n     * Process keyDown events for the menu.\n     *\n     * @method handleKeyDown\n     * @param {Object} item is the jquery object of the item firing the event\n     * @param {Event} e is the associated event object\n     * @return boolean Returns false if consuming the event\n     */\n    Menubar.prototype.handleKeyDown = function(item, e) {\n\n        if (e.altKey || e.ctrlKey) {\n            // Modifier key pressed: Do not process.\n            return true;\n        }\n\n        switch (e.keyCode) {\n            case this.keys.tab: {\n\n                // Hide all menu items and update their aria attributes.\n                this.menuRoot.find('ul').attr('aria-hidden', 'true');\n\n                // Remove focus styling from all menu items.\n                this.allItems.removeClass('menu-focus');\n\n                this.activeItem = null;\n\n                this.isChildOpen = false;\n\n                break;\n            }\n            case this.keys.esc: {\n                var itemUL = item.parent();\n\n                if (itemUL.is('.tool-lp-menu')) {\n                    // Hide the child menu and update the aria attributes.\n                    item.children('ul').first().attr('aria-hidden', 'true');\n                } else {\n\n                    // Move up one level.\n                    this.activeItem = itemUL.parent();\n\n                    // Reset the isChildOpen flag.\n                    this.isChildOpen = false;\n\n                    // Set focus on the new item.\n                    this.activeItem.focus();\n\n                    // Hide the active menu and update the aria attributes.\n                    itemUL.attr('aria-hidden', 'true');\n                }\n\n                e.stopPropagation();\n                return false;\n            }\n            case this.keys.enter:\n            case this.keys.space: {\n                // Trigger click handler.\n                return this.handleClick(item, e);\n            }\n\n            case this.keys.left: {\n\n                this.activeItem = this.moveToPrevious(item);\n\n                this.activeItem.focus();\n\n                e.stopPropagation();\n                return false;\n            }\n            case this.keys.right: {\n\n                this.activeItem = this.moveToNext(item);\n\n                this.activeItem.focus();\n\n                e.stopPropagation();\n                return false;\n            }\n            case this.keys.up: {\n\n                this.activeItem = this.moveUp(item);\n\n                this.activeItem.focus();\n\n                e.stopPropagation();\n                return false;\n            }\n            case this.keys.down: {\n\n                this.activeItem = this.moveDown(item);\n\n                this.activeItem.focus();\n\n                e.stopPropagation();\n                return false;\n            }\n        }\n\n        return true;\n\n    };\n\n\n    /**\n     * Move to the next menu level.\n     * This will be either the next root-level menu or the child of a menu parent. If\n     * at the root level and the active item is the last in the menu, this function will loop\n     * to the first menu item.\n     *\n     * If the menu is a horizontal menu, the first child element of the newly selected menu will\n     * be selected\n     *\n     * @method moveToNext\n     * @param {Object} item is the active menu item\n     * @return {Object} Returns the item to move to. Returns item is no move is possible\n     */\n    Menubar.prototype.moveToNext = function(item) {\n        // Item's containing menu.\n        var itemUL = item.parent();\n\n        // The items in the currently active menu.\n        var menuItems = itemUL.children('li');\n\n        // The number of items in the active menu.\n        var menuNum = menuItems.length;\n        // The items index in its menu.\n        var menuIndex = menuItems.index(item);\n        var newItem = null;\n        var childMenu = null;\n\n        if (itemUL.is('.tool-lp-menu')) {\n            // This is the root level move to next sibling. This will require closing\n            // the current child menu and opening the new one.\n\n            if (menuIndex < menuNum - 1) {\n                // Not the last root menu.\n                newItem = item.next();\n            } else { // Wrap to first item.\n                newItem = menuItems.first();\n            }\n\n            // Close the current child menu (if applicable).\n            if (item.attr('aria-haspopup') == 'true') {\n\n                childMenu = item.children('ul').first();\n\n                if (childMenu.attr('aria-hidden') == 'false') {\n                    // Update the child menu's aria-hidden attribute.\n                    childMenu.attr('aria-hidden', 'true');\n                    this.isChildOpen = true;\n                }\n            }\n\n            // Remove the focus styling from the current menu.\n            item.removeClass('menu-focus');\n\n            // Open the new child menu (if applicable).\n            if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n                childMenu = newItem.children('ul').first();\n\n                // Update the child's aria-hidden attribute.\n                this.openSubMenu(childMenu);\n            }\n        } else {\n            // This is not the root level. If there is a child menu to be moved into, do that;\n            // otherwise, move to the next root-level menu if there is one.\n            if (item.attr('aria-haspopup') == 'true') {\n\n                childMenu = item.children('ul').first();\n\n                newItem = childMenu.children('li').first();\n\n                // Show the child menu and update its aria attributes.\n                this.openSubMenu(childMenu);\n            } else {\n                // At deepest level, move to the next root-level menu.\n\n                var parentMenus = null;\n                var rootItem = null;\n\n                // Get list of all parent menus for item, up to the root level.\n                parentMenus = item.parentsUntil('ul.tool-lp-menu').filter('ul').not('.tool-lp-menu');\n\n                // Hide the current menu and update its aria attributes accordingly.\n                parentMenus.attr('aria-hidden', 'true');\n\n                // Remove the focus styling from the active menu.\n                parentMenus.find('li').removeClass('menu-focus');\n                parentMenus.last().parent().removeClass('menu-focus');\n\n                // The containing root for the menu.\n                rootItem = parentMenus.last().parent();\n\n                menuIndex = this.rootMenus.index(rootItem);\n\n                // If this is not the last root menu item, move to the next one.\n                if (menuIndex < this.rootMenus.length - 1) {\n                    newItem = rootItem.next();\n                } else {\n                    // Loop.\n                    newItem = this.rootMenus.first();\n                }\n\n                // Add the focus styling to the new menu.\n                newItem.addClass('menu-focus');\n\n                if (newItem.attr('aria-haspopup') == 'true') {\n                    childMenu = newItem.children('ul').first();\n\n                    newItem = childMenu.children('li').first();\n\n                    // Show the child menu and update it's aria attributes.\n                    this.openSubMenu(childMenu);\n                    this.isChildOpen = true;\n                }\n            }\n        }\n\n        return newItem;\n    };\n\n    /**\n     * Member function to move to the previous menu level.\n     * This will be either the previous root-level menu or the child of a menu parent. If\n     * at the root level and the active item is the first in the menu, this function will loop\n     * to the last menu item.\n     *\n     * If the menu is a horizontal menu, the first child element of the newly selected menu will\n     * be selected\n     *\n     * @method moveToPrevious\n     * @param {Object} item is the active menu item\n     * @return {Object} Returns the item to move to. Returns item is no move is possible\n     */\n    Menubar.prototype.moveToPrevious = function(item) {\n        // Item's containing menu.\n        var itemUL = item.parent();\n        // The items in the currently active menu.\n        var menuItems = itemUL.children('li');\n        // The items index in its menu.\n        var menuIndex = menuItems.index(item);\n        var newItem = null;\n        var childMenu = null;\n\n        if (itemUL.is('.tool-lp-menu')) {\n            // This is the root level move to previous sibling. This will require closing\n            // the current child menu and opening the new one.\n\n            if (menuIndex > 0) {\n                // Not the first root menu.\n                newItem = item.prev();\n            } else {\n                // Wrap to last item.\n                newItem = menuItems.last();\n            }\n\n            // Close the current child menu (if applicable).\n            if (item.attr('aria-haspopup') == 'true') {\n                childMenu = item.children('ul').first();\n\n                if (childMenu.attr('aria-hidden') == 'false') {\n                    // Update the child menu's aria-hidden attribute.\n                    childMenu.attr('aria-hidden', 'true');\n                    this.isChildOpen = true;\n                }\n            }\n\n            // Remove the focus styling from the current menu.\n            item.removeClass('menu-focus');\n\n            // Open the new child menu (if applicable).\n            if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n                childMenu = newItem.children('ul').first();\n\n                // Update the child's aria-hidden attribute.\n                this.openSubMenu(childMenu);\n\n            }\n        } else {\n            // This is not the root level. If there is a parent menu that is not the\n            // root menu, move up one level; otherwise, move to first item of the previous\n            // root menu.\n\n            var parentLI = itemUL.parent();\n            var parentUL = parentLI.parent();\n\n            // If this is a vertical menu or is not the first child menu\n            // of the root-level menu, move up one level.\n            if (!parentUL.is('.tool-lp-menu')) {\n\n                newItem = itemUL.parent();\n\n                // Hide the active menu and update aria-hidden.\n                itemUL.attr('aria-hidden', 'true');\n\n                // Remove the focus highlight from the item.\n                item.removeClass('menu-focus');\n\n            } else {\n                // Move to previous root-level menu.\n\n                // Hide the current menu and update the aria attributes accordingly.\n                itemUL.attr('aria-hidden', 'true');\n\n                // Remove the focus styling from the active menu.\n                item.removeClass('menu-focus');\n                parentLI.removeClass('menu-focus');\n\n                menuIndex = this.rootMenus.index(parentLI);\n\n                if (menuIndex > 0) {\n                    // Move to the previous root-level menu.\n                    newItem = parentLI.prev();\n                } else {\n                    // Loop to last root-level menu.\n                    newItem = this.rootMenus.last();\n                }\n\n                // Add the focus styling to the new menu.\n                newItem.addClass('menu-focus');\n\n                if (newItem.attr('aria-haspopup') == 'true') {\n                    childMenu = newItem.children('ul').first();\n\n                    // Show the child menu and update it's aria attributes.\n                    this.openSubMenu(childMenu);\n                    this.isChildOpen = true;\n\n                    newItem = childMenu.children('li').first();\n                }\n            }\n        }\n\n        return newItem;\n    };\n\n    /**\n     * Member function to select the next item in a menu.\n     * If the active item is the last in the menu, this function will loop to the\n     * first menu item.\n     *\n     * @method moveDown\n     * @param {Object} item is the active menu item\n     * @param {String} startChr is the character to attempt to match against the beginning of the\n     *                          menu item titles. If found, focus moves to the next menu item beginning with that character.\n     * @return {Object} Returns the item to move to. Returns item is no move is possible\n     */\n    Menubar.prototype.moveDown = function(item, startChr) {\n        // Item's containing menu.\n        var itemUL = item.parent();\n        // The items in the currently active menu.\n        var menuItems = itemUL.children('li').not('.separator');\n        // The number of items in the active menu.\n        var menuNum = menuItems.length;\n        // The items index in its menu.\n        var menuIndex = menuItems.index(item);\n        var newItem = null;\n        var newItemUL = null;\n\n        if (itemUL.is('.tool-lp-menu')) {\n            // This is the root level menu.\n\n            if (item.attr('aria-haspopup') != 'true') {\n                // No child menu to move to.\n                return item;\n            }\n\n            // Move to the first item in the child menu.\n            newItemUL = item.children('ul').first();\n            newItem = newItemUL.children('li').first();\n\n            // Make sure the child menu is visible.\n            this.openSubMenu(newItemUL);\n\n            return newItem;\n        }\n\n        // If $item is not the last item in its menu, move to the next item. If startChr is specified, move\n        // to the next item with a title that begins with that character.\n        if (startChr) {\n            var match = false;\n            var curNdx = menuIndex + 1;\n\n            // Check if the active item was the last one on the list.\n            if (curNdx == menuNum) {\n                curNdx = 0;\n            }\n\n            // Iterate through the menu items (starting from the current item and wrapping) until a match is found\n            // or the loop returns to the current menu item.\n            while (curNdx != menuIndex) {\n\n                var titleChr = menuItems.eq(curNdx).html().charAt(0);\n\n                if (titleChr.toLowerCase() == startChr) {\n                    match = true;\n                    break;\n                }\n\n                curNdx = curNdx + 1;\n\n                if (curNdx == menuNum) {\n                    // Reached the end of the list, start again at the beginning.\n                    curNdx = 0;\n                }\n            }\n\n            if (match === true) {\n                newItem = menuItems.eq(curNdx);\n\n                // Remove the focus styling from the current item.\n                item.removeClass('menu-focus');\n\n                return newItem;\n            } else {\n                return item;\n            }\n        } else {\n            if (menuIndex < menuNum - 1) {\n                newItem = menuItems.eq(menuIndex + 1);\n            } else {\n                newItem = menuItems.first();\n            }\n        }\n\n        // Remove the focus styling from the current item.\n        item.removeClass('menu-focus');\n\n        return newItem;\n    };\n\n    /**\n     * Function moveUp() is a member function to select the previous item in a menu.\n     * If the active item is the first in the menu, this function will loop to the\n     * last menu item.\n     *\n     * @method moveUp\n     * @param {Object} item is the active menu item\n     * @return {Object} Returns the item to move to. Returns item is no move is possible\n     */\n    Menubar.prototype.moveUp = function(item) {\n        // Item's containing menu.\n        var itemUL = item.parent();\n        // The items in the currently active menu.\n        var menuItems = itemUL.children('li').not('.separator');\n        // The items index in its menu.\n        var menuIndex = menuItems.index(item);\n        var newItem = null;\n\n        if (itemUL.is('.tool-lp-menu')) {\n            // This is the root level menu.\n            // Nothing to do.\n            return item;\n        }\n\n        // If item is not the first item in its menu, move to the previous item.\n        if (menuIndex > 0) {\n            newItem = menuItems.eq(menuIndex - 1);\n        } else {\n            // Loop to top of menu.\n            newItem = menuItems.last();\n        }\n\n        // Remove the focus styling from the current item.\n        item.removeClass('menu-focus');\n\n        return newItem;\n    };\n\n    /**\n     * Enhance the dom with aria attributes.\n     * @method addAriaAttributes\n     */\n    Menubar.prototype.addAriaAttributes = function() {\n        this.menuRoot.attr('role', 'menubar');\n        this.rootMenus.attr('role', 'menuitem');\n        this.rootMenus.attr('tabindex', '0');\n        this.rootMenus.attr('aria-haspopup', 'true');\n        this.subMenus.attr('role', 'menu');\n        this.subMenus.attr('aria-hidden', 'true');\n        this.subMenuItems.attr('role', 'menuitem');\n        this.subMenuItems.attr('tabindex', '-1');\n\n        // For CSS styling and effects.\n        this.menuRoot.addClass('tool-lp-menu');\n        this.allItems.addClass('tool-lp-menu-item');\n        this.rootMenus.addClass('tool-lp-root-menu');\n        this.subMenus.addClass('tool-lp-sub-menu');\n        this.subMenuItems.addClass('dropdown-item');\n    };\n\n    return /** @alias module:tool_lp/menubar */ {\n        /**\n         * Create a menu bar object for every node matching the selector.\n         *\n         * The expected DOM structure is shown below.\n         * <ul> <- This is the target of the selector parameter.\n         *   <li> <- This is repeated for each top level menu.\n         *      Text <- This is the text for the top level menu.\n         *      <ul> <- This is a list of the entries in this top level menu.\n         *         <li> <- This is repeated for each menu entry.\n         *            <a href=\"someurl\">Choice 1</a> <- The anchor for the menu.\n         *         </li>\n         *      </ul>\n         *   </li>\n         * </ul>\n         *\n         * @method enhance\n         * @param {String} selector - The selector for the outer most menu node.\n         * @param {Function} handler - Javascript handler for when a menu item was chosen. If the\n         *                             handler returns true (or does not exist), the\n         *                             menu will look for an anchor with a link to follow.\n         *                             For example, if the menu entry has a \"data-action\" attribute\n         *                             and we want to call a javascript function when that entry is chosen,\n         *                             we could pass a list of handlers like this:\n         *                             { \"[data-action='add']\" : callAddFunction }\n         */\n        enhance: function(selector, handler) {\n            $(selector).each(function(index, element) {\n                var menuRoot = $(element);\n                // Don't enhance the same menu twice.\n                if (menuRoot.data(\"menubarEnhanced\") !== true) {\n                    (new Menubar(menuRoot, handler));\n                    menuRoot.data(\"menubarEnhanced\", true);\n                }\n            });\n        },\n\n        /**\n         * Handy function to close all open menus anywhere on the page.\n         * @method closeAll\n         */\n        closeAll: closeAllSubMenus\n    };\n});\n"],"names":["define","$","documentClickHandlerRegistered","menuActive","closeAllSubMenus","attr","Menubar","menuRoot","handlers","rootMenus","this","children","subMenus","subMenuItems","allItems","add","activeItem","isChildOpen","keys","tab","enter","esc","space","left","up","right","down","addAriaAttributes","addEventListeners","prototype","openSubMenu","menu","setOpenDirection","currentThis","document","click","mouseenter","addClass","mouseout","removeClass","e","handleClick","keydown","handleKeyDown","focus","handleFocus","blur","handleBlur","item","stopPropagation","parent","is","first","find","not","anchor","clickEvent","Event","target","eventHandled","each","selector","handler","length","callable","proxy","isDefaultPrevented","window","location","href","parentItems","parentsUntil","filter","pos","offset","isRTL","body","hasClass","heightmenuRoot","outerHeight","widthmenuRoot","outerWidth","subMenuContainer","css","menuRealWidth","menuRealHeight","margintop","marginright","marginleft","top","scrollTop","height","width","altKey","ctrlKey","keyCode","itemUL","moveToPrevious","moveToNext","moveUp","moveDown","menuItems","menuNum","menuIndex","index","newItem","childMenu","next","parentMenus","rootItem","last","prev","parentLI","startChr","newItemUL","match","curNdx","eq","html","charAt","toLowerCase","enhance","element","data","closeAll"],"mappings":"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"}