# Yahoo! UI Library

### Menu 2.6.0

Yahoo! UI Library > menu > menumanager.js (source view)

Show Private Show Protected Show Deprecated

    /**
    * @module menu
    * @description <p>The Menu family of components features a collection of
    * controls that make it easy to add menus to your website or web application.
    * With the Menu Controls you can create website fly-out menus, customized
    * context menus, or application-style menu bars with just a small amount of
    * scripting.</p><p>The Menu family of controls features:</p>
    * <ul>
    *    <li>Keyboard and mouse navigation.</li>
    *    <li>A rich event model that provides access to all of a menu's
    *    interesting moments.</li>
    *    <li>Support for
    *    <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive
    *    Enhancement</a>; Menus can be created from simple,
    *    semantic markup on the page or purely through JavaScript.</li>
    * </ul>
    * @title Menu
    * @namespace YAHOO.widget
    * @requires Event, Dom, Container
    */
    (function () {

        var _DIV = "DIV",
        	_HD = "hd",
        	_BD = "bd",
        	_FT = "ft",
        	_LI = "LI",
        	_DISABLED = "disabled",
    		_MOUSEOVER = "mouseover",
    		_MOUSEOUT = "mouseout",
    		_MOUSEDOWN = "mousedown",
    		_MOUSEUP = "mouseup",
    		_FOCUS = YAHOO.env.ua.ie ? "focusin" : "focus",
    		_CLICK = "click",
    		_KEYDOWN = "keydown",
    		_KEYUP = "keyup",
    		_KEYPRESS = "keypress",
    		_CLICK_TO_HIDE = "clicktohide",
    		_POSITION = "position",
    		_DYNAMIC = "dynamic",
    		_SHOW_DELAY = "showdelay",
    		_SELECTED = "selected",
    		_VISIBLE = "visible",
    		_UL = "UL",
    		_MENUMANAGER = "MenuManager",

        	Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event,
            Lang = YAHOO.lang;

        /**
        * Singleton that manages a collection of all menus and menu items.  Listens
        * for DOM events at the document level and dispatches the events to the
        * corresponding menu or menu item.
        *
        * @namespace YAHOO.widget
        * @class MenuManager
        * @static
        */
        YAHOO.widget.MenuManager = function () {

            // Private member variables

            // Flag indicating if the DOM event handlers have been attached

            var m_bInitializedEventHandlers = false,

            // Collection of menus

            m_oMenus = {},

            // Collection of visible menus

            m_oVisibleMenus = {},

            //  Collection of menu items

            m_oItems = {},

            // Map of DOM event types to their equivalent CustomEvent types

            m_oEventTypes = {
                "click": "clickEvent",
                "mousedown": "mouseDownEvent",
                "mouseup": "mouseUpEvent",
                "mouseover": "mouseOverEvent",
                "mouseout": "mouseOutEvent",
                "keydown": "keyDownEvent",
                "keyup": "keyUpEvent",
                "keypress": "keyPressEvent",
                "focus": "focusEvent",
                "focusin": "focusEvent",
                "blur": "blurEvent",
                "focusout": "blurEvent"
            },

        	// The element in the DOM that currently has focus

    		m_oFocusedElement = null,

            m_oFocusedMenuItem = null;

            // Private methods

            /**
            * @method getMenuRootElement
            * @description Finds the root DIV node of a menu or the root LI node of
            * a menu item.
            * @private
            * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
            * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object
            * specifying an HTML element.
            */
            function getMenuRootElement(p_oElement) {

                var oParentNode,
                	returnVal;

                if (p_oElement && p_oElement.tagName) {

                    switch (p_oElement.tagName.toUpperCase()) {

                    case _DIV:

                        oParentNode = p_oElement.parentNode;

                        // Check if the DIV is the inner "body" node of a menu

                        if ((
                                Dom.hasClass(p_oElement, _HD) ||
                                Dom.hasClass(p_oElement, _BD) ||
                                Dom.hasClass(p_oElement, _FT)
                            ) &&
                            oParentNode &&
                            oParentNode.tagName &&
                            oParentNode.tagName.toUpperCase() == _DIV) {

                            returnVal = oParentNode;

                        }
                        else {

                            returnVal = p_oElement;

                        }

                        break;

                    case _LI:

                        returnVal = p_oElement;

                        break;

                    default:

                        oParentNode = p_oElement.parentNode;

                        if (oParentNode) {

                            returnVal = getMenuRootElement(oParentNode);

                        }

                        break;

                    }

                }

                return returnVal;

            }

            // Private event handlers

            /**
            * @method onDOMEvent
            * @description Generic, global event handler for all of a menu's
            * DOM-based events.  This listens for events against the document
            * object.  If the target of a given event is a member of a menu or
            * menu item's DOM, the instance's corresponding Custom Event is fired.
            * @private
            * @param {Event} p_oEvent Object representing the DOM event object
            * passed back by the event utility (YAHOO.util.Event).
            */
            function onDOMEvent(p_oEvent) {

                // Get the target node of the DOM event

                var oTarget = Event.getTarget(p_oEvent),

                // See if the target of the event was a menu, or a menu item

                oElement = getMenuRootElement(oTarget),
                sCustomEventType,
                sTagName,
                sId,
                oMenuItem,
                oMenu;

                if (oElement) {

                    sTagName = oElement.tagName.toUpperCase();

                    if (sTagName == _LI) {

                        sId = oElement.id;

                        if (sId && m_oItems[sId]) {

                            oMenuItem = m_oItems[sId];
                            oMenu = oMenuItem.parent;

                        }

                    }
                    else if (sTagName == _DIV) {

                        if (oElement.id) {

                            oMenu = m_oMenus[oElement.id];

                        }

                    }

                }

                if (oMenu) {

                    sCustomEventType = m_oEventTypes[p_oEvent.type];

                    // Fire the Custom Event that corresponds the current DOM event

                    if (oMenuItem && !oMenuItem.cfg.getProperty(_DISABLED)) {

                        oMenuItem[sCustomEventType].fire(p_oEvent);

                    }

                    oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);

                }
                else if (p_oEvent.type == _MOUSEDOWN) {

                    /*
                        If the target of the event wasn't a menu, hide all
                        dynamically positioned menus
                    */

                    for (var i in m_oVisibleMenus) {

                        if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {

                            oMenu = m_oVisibleMenus[i];

                            if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) &&
                                !(oMenu instanceof YAHOO.widget.MenuBar) &&
                                oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {

                                oMenu.hide();

                            }
                            else {

    							if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) {

    								oMenu._cancelShowDelay();

    							}

    							if (oMenu.activeItem) {

    								oMenu.activeItem.blur();
    								oMenu.activeItem.cfg.setProperty(_SELECTED, false);

    								oMenu.activeItem = null;

    							}

                            }

                        }

                    }

                }
                else if (p_oEvent.type == _FOCUS) {

                	m_oFocusedElement = oTarget;

                }

            }

            /**
            * @method onMenuDestroy
            * @description "destroy" event handler for a menu.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            * @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event.
            */
            function onMenuDestroy(p_sType, p_aArgs, p_oMenu) {

                if (m_oMenus[p_oMenu.id]) {

                    this.removeMenu(p_oMenu);

                }

            }

            /**
            * @method onMenuFocus
            * @description "focus" event handler for a MenuItem instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
            function onMenuFocus(p_sType, p_aArgs) {

                var oItem = p_aArgs[1];

                if (oItem) {

                    m_oFocusedMenuItem = oItem;

                }

            }

            /**
            * @method onMenuBlur
            * @description "blur" event handler for a MenuItem instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
            function onMenuBlur(p_sType, p_aArgs) {

                m_oFocusedMenuItem = null;

            }

            /**
            * @method onMenuHide
            * @description "hide" event handler for a Menu instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
    		* @param <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
    		* level-one-html.html#ID-58190037">p_oFocusedElement</a> The HTML element that had focus
    		* prior to the Menu being made visible
            */
        	function onMenuHide(p_sType, p_aArgs, p_oFocusedElement) {

    			/*
    				Restore focus to the element in the DOM that had focus prior to the Menu
    				being made visible
    			*/

    			if (p_oFocusedElement && p_oFocusedElement.focus) {

    				try {
    					p_oFocusedElement.focus();
    				}
    				catch(ex) {
    				}

    			}

        		this.hideEvent.unsubscribe(onMenuHide, p_oFocusedElement);

        	}

            /**
            * @method onMenuShow
            * @description "show" event handler for a MenuItem instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
        	function onMenuShow(p_sType, p_aArgs) {

    			/*
    				Dynamically positioned, root Menus focus themselves when visible, and will then,
    				when hidden, restore focus to the UI control that had focus before the Menu was
    				made visible
    			*/

    			if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) {

    				this.hideEvent.subscribe(onMenuHide, m_oFocusedElement);
    				this.focus();

    			}

        	}

            /**
            * @method onMenuVisibleConfigChange
            * @description Event handler for when the "visible" configuration
            * property of a Menu instance changes.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
            function onMenuVisibleConfigChange(p_sType, p_aArgs) {

                var bVisible = p_aArgs[0],
                    sId = this.id;

                if (bVisible) {

                    m_oVisibleMenus[sId] = this;

                    YAHOO.log(this + " added to the collection of visible menus.",
                    	"info", _MENUMANAGER);

                }
                else if (m_oVisibleMenus[sId]) {

                    delete m_oVisibleMenus[sId];

                    YAHOO.log(this + " removed from the collection of visible menus.",
                    	"info", _MENUMANAGER);

                }

            }

            /**
            * @method onItemDestroy
            * @description "destroy" event handler for a MenuItem instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
            function onItemDestroy(p_sType, p_aArgs) {

                removeItem(this);

            }

            /**
            * @method removeItem
            * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems.
            * @private
            * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed.
            */
            function removeItem(p_oMenuItem) {

                var sId = p_oMenuItem.id;

                if (sId && m_oItems[sId]) {

                    if (m_oFocusedMenuItem == p_oMenuItem) {

                        m_oFocusedMenuItem = null;

                    }

                    delete m_oItems[sId];

                    p_oMenuItem.destroyEvent.unsubscribe(onItemDestroy);

                    YAHOO.log(p_oMenuItem + " successfully unregistered.", "info", _MENUMANAGER);

                }

            }

            /**
            * @method onItemAdded
            * @description "itemadded" event handler for a Menu instance.
            * @private
            * @param {String} p_sType String representing the name of the event
            * that was fired.
            * @param {Array} p_aArgs Array of arguments sent when the event
            * was fired.
            */
            function onItemAdded(p_sType, p_aArgs) {

                var oItem = p_aArgs[0],
                    sId;

                if (oItem instanceof YAHOO.widget.MenuItem) {

                    sId = oItem.id;

                    if (!m_oItems[sId]) {

                        m_oItems[sId] = oItem;

                        oItem.destroyEvent.subscribe(onItemDestroy);

                        YAHOO.log(oItem + " successfully registered.", "info", _MENUMANAGER);

                    }

                }

            }

            return {

                // Privileged methods

                /**
                * @method addMenu
                * @description Adds a menu to the collection of known menus.
                * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu
                * instance to be added.
                */
                addMenu: function (p_oMenu) {

                    var oDoc;

                    if (p_oMenu instanceof YAHOO.widget.Menu && p_oMenu.id &&
                        !m_oMenus[p_oMenu.id]) {

                        m_oMenus[p_oMenu.id] = p_oMenu;

                        if (!m_bInitializedEventHandlers) {

                            oDoc = document;

                            Event.on(oDoc, _MOUSEOVER, onDOMEvent, this, true);
                            Event.on(oDoc, _MOUSEOUT, onDOMEvent, this, true);
                            Event.on(oDoc, _MOUSEDOWN, onDOMEvent, this, true);
                            Event.on(oDoc, _MOUSEUP, onDOMEvent, this, true);
                            Event.on(oDoc, _CLICK, onDOMEvent, this, true);
                            Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true);
                            Event.on(oDoc, _KEYUP, onDOMEvent, this, true);
                            Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true);

    						Event.onFocus(oDoc, onDOMEvent, this, true);
    						Event.onBlur(oDoc, onDOMEvent, this, true);

                            m_bInitializedEventHandlers = true;

                            YAHOO.log("DOM event handlers initialized.", "info", _MENUMANAGER);

                        }

                        p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange);
                        p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
                        p_oMenu.itemAddedEvent.subscribe(onItemAdded);
                        p_oMenu.focusEvent.subscribe(onMenuFocus);
                        p_oMenu.blurEvent.subscribe(onMenuBlur);
                        p_oMenu.showEvent.subscribe(onMenuShow);

                        YAHOO.log(p_oMenu + " successfully registered.", "info", _MENUMANAGER);

                    }

                },

                /**
                * @method removeMenu
                * @description Removes a menu from the collection of known menus.
                * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu
                * instance to be removed.
                */
                removeMenu: function (p_oMenu) {

                    var sId,
                        aItems,
                        i;

                    if (p_oMenu) {

                        sId = p_oMenu.id;

                        if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) {

                            // Unregister each menu item

                            aItems = p_oMenu.getItems();

                            if (aItems && aItems.length > 0) {

                                i = aItems.length - 1;

                                do {

                                    removeItem(aItems[i]);

                                }
                                while (i--);

                            }

                            // Unregister the menu

                            delete m_oMenus[sId];

                            YAHOO.log(p_oMenu + " successfully unregistered.", "info", _MENUMANAGER);

                            /*
                                 Unregister the menu from the collection of
                                 visible menus
                            */

                            if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) {

                                delete m_oVisibleMenus[sId];

                                YAHOO.log(p_oMenu + " unregistered from the" +
                                            " collection of visible menus.", "info", _MENUMANAGER);

                            }

                            // Unsubscribe event listeners

                            if (p_oMenu.cfg) {

                                p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE,
                                    onMenuVisibleConfigChange);

                            }

                            p_oMenu.destroyEvent.unsubscribe(onMenuDestroy,
                                p_oMenu);

                            p_oMenu.itemAddedEvent.unsubscribe(onItemAdded);
                            p_oMenu.focusEvent.unsubscribe(onMenuFocus);
                            p_oMenu.blurEvent.unsubscribe(onMenuBlur);

                        }

                    }

                },

                /**
                * @method hideVisible
                * @description Hides all visible, dynamically positioned menus
                * (excluding instances of YAHOO.widget.MenuBar).
                */
                hideVisible: function () {

                    var oMenu;

                    for (var i in m_oVisibleMenus) {

                        if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {

                            oMenu = m_oVisibleMenus[i];

                            if (!(oMenu instanceof YAHOO.widget.MenuBar) &&
                                oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {

                                oMenu.hide();

                            }

                        }

                    }

                },

                /**
                * @method getVisible
                * @description Returns a collection of all visible menus registered
                * with the menu manger.
                * @return {Object}
                */
                getVisible: function () {

                    return m_oVisibleMenus;

                },

                /**
                * @method getMenus
                * @description Returns a collection of all menus registered with the
                * menu manger.
                * @return {Object}
                */
                getMenus: function () {

                    return m_oMenus;

                },

                /**
                * @method getMenu
                * @description Returns a menu with the specified id.
                * @param {String} p_sId String specifying the id of the
                * <code>&#60;div&#62;</code> element representing the menu to
                * be retrieved.
                * @return {YAHOO.widget.Menu}
                */
                getMenu: function (p_sId) {

                    var returnVal;

                    if (p_sId in m_oMenus) {

    					returnVal = m_oMenus[p_sId];

    				}

                	return returnVal;

                },

                /**
                * @method getMenuItem
                * @description Returns a menu item with the specified id.
                * @param {String} p_sId String specifying the id of the
                * <code>&#60;li&#62;</code> element representing the menu item to
                * be retrieved.
                * @return {YAHOO.widget.MenuItem}
                */
                getMenuItem: function (p_sId) {

        			var returnVal;

        			if (p_sId in m_oItems) {

    					returnVal = m_oItems[p_sId];

    				}

    				return returnVal;

                },

                /**
                * @method getMenuItemGroup
                * @description Returns an array of menu item instances whose
                * corresponding <code>&#60;li&#62;</code> elements are child
                * nodes of the <code>&#60;ul&#62;</code> element with the
                * specified id.
                * @param {String} p_sId String specifying the id of the
                * <code>&#60;ul&#62;</code> element representing the group of
                * menu items to be retrieved.
                * @return {Array}
                */
                getMenuItemGroup: function (p_sId) {

                    var oUL = Dom.get(p_sId),
                        aItems,
                        oNode,
                        oItem,
                        sId,
                        returnVal;

                    if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) {

                        oNode = oUL.firstChild;

                        if (oNode) {

                            aItems = [];

                            do {

                                sId = oNode.id;

                                if (sId) {

                                    oItem = this.getMenuItem(sId);

                                    if (oItem) {

                                        aItems[aItems.length] = oItem;

                                    }

                                }

                            }
                            while ((oNode = oNode.nextSibling));

                            if (aItems.length > 0) {

                                returnVal = aItems;

                            }

                        }

                    }

    				return returnVal;

                },

                /**
                * @method getFocusedMenuItem
                * @description Returns a reference to the menu item that currently
                * has focus.
                * @return {YAHOO.widget.MenuItem}
                */
                getFocusedMenuItem: function () {

                    return m_oFocusedMenuItem;

                },

                /**
                * @method getFocusedMenu
                * @description Returns a reference to the menu that currently
                * has focus.
                * @return {YAHOO.widget.Menu}
                */
                getFocusedMenu: function () {

    				var returnVal;

                    if (m_oFocusedMenuItem) {

                        returnVal = m_oFocusedMenuItem.parent.getRoot();

                    }

        			return returnVal;

                },

                /**
                * @method toString
                * @description Returns a string representing the menu manager.
                * @return {String}
                */
                toString: function () {

                    return _MENUMANAGER;

                }

            };

        }();

    })();

Copyright © 2008 Yahoo! Inc. All rights reserved.