YUI recommends YUI 3.

YUI 2 has been deprecated since 2011. This site acts as an archive for files and documentation.

Yahoo! UI Library

Menu  2.2.2

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

/**
* @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>Screen-reader accessibility.</li>
*    <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 Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event;


/**
* 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 menu items 

        m_oItems = {},


        // Collection of visible menus
    
        m_oVisibleMenus = {},


        // 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"
        },


        m_oFocusedMenuItem = null;


    var m_oLogger = new YAHOO.widget.LogWriter("MenuManager");


    // Private methods


    /**
    * @method addItem
    * @description Adds an item to the collection of known menu items.
    * @private
    * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem 
    * instance to be added.
    */
    function addItem(p_oItem) {

        var sId = p_oItem.id;

        if(p_oItem && m_oItems[sId] != p_oItem) {
    
            m_oItems[sId] = p_oItem;

            p_oItem.destroyEvent.subscribe(onItemDestroy);

            m_oLogger.log("Item: " + 
                p_oItem.toString() + " successfully registered.");

        }
    
    }


    /**
    * @method removeItem
    * @description Removes an item from the collection of known menu items.
    * @private
    * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem 
    * instance to be removed.
    */
    function removeItem(p_oItem) {
    
        var sId = p_oItem.id;

        if(sId && m_oItems[sId]) {

            delete m_oItems[sId];

            m_oLogger.log("Item: " + 
                p_oItem.toString() + " successfully unregistered.");

        }
    
    }


    /**
    * @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;

        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"
                    ) {
                    
                        return oParentNode;
                    
                    }
                    else {
                    
                        return p_oElement;
                    
                    }
                
                break;

                case "LI":
    
                    return p_oElement;

                default:
    
                    oParentNode = p_oElement.parentNode;
    
                    if(oParentNode) {
                    
                        return getMenuRootElement(oParentNode);
                    
                    }
                
                break;
            
            }

        }
        
    }



    // 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),
            oMenuItem,
            oMenu; 


        if(oElement) {

            var sTagName = oElement.tagName.toUpperCase();
    
            if(sTagName == "LI") {
        
                var 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) {

            var 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);                   


                if (p_oEvent.type == "keyup" || p_oEvent.type == "mousedown") {

                    if (m_oFocusedMenuItem != oMenuItem) {
                    
                        if(m_oFocusedMenuItem) {

                            m_oFocusedMenuItem.blurEvent.fire();
                        
                        }

                        oMenuItem.focusEvent.fire();
                    
                    }
                
                }

            }
    
            oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);
        
        }
        else if(p_oEvent.type == "mousedown") {

            if(m_oFocusedMenuItem) {

                m_oFocusedMenuItem.blurEvent.fire();

                m_oFocusedMenuItem = null;

            }


            /*
                If the target of the event wasn't a menu, hide all 
                dynamically positioned menus
            */
            
            for(var i in m_oMenus) {
    
                if(YAHOO.lang.hasOwnProperty(m_oMenus,i)) {
    
                    oMenu = m_oMenus[i];
    
                    if(
                        oMenu.cfg.getProperty("clicktohide") && 
                        oMenu.cfg.getProperty("position") == "dynamic"
                    ) {
    
                        oMenu.hide();
    
                    }
                    else {

                        oMenu.clearActiveItem(true);
    
                    }
    
                }
    
            } 

        }
        else if(p_oEvent.type == "keyup") { 

            if(m_oFocusedMenuItem) {

                m_oFocusedMenuItem.blurEvent.fire();

                m_oFocusedMenuItem = null;

            }

        }

    }


    /**
    * @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.
    */
    function onMenuDestroy(p_sType, p_aArgs) {

        if(m_oMenus[this.id]) {

            delete m_oMenus[this.id];

            m_oLogger.log("Menu: " + 
                this.toString() + " successfully unregistered.");

        }

    }


    /**
    * @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[0];
        
        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 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) {

        var sId = this.id;

        if(sId && m_oItems[sId]) {

            delete m_oItems[sId];

        }

    }


    /**
    * @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];
        
        if(bVisible) {

            m_oVisibleMenus[this.id] = this;
            
            m_oLogger.log("Menu: " + 
                this.toString() + 
                " registered with the collection of visible menus.");
        
        }
        else if(m_oVisibleMenus[this.id]) {
        
            delete m_oVisibleMenus[this.id];
            
            m_oLogger.log("Menu: " + 
                this.toString() + 
                " unregistered from the collection of visible menus.");
        
        }
    
    }


    /**
    * @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) {
    
        addItem(p_aArgs[0]);
    
    }
    

    /**
    * @method onItemRemoved
    * @description "itemremoved" 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 onItemRemoved(p_sType, p_aArgs) {

        removeItem(p_aArgs[0]);
    
    }



    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) {
    
            if(p_oMenu && p_oMenu.id && !m_oMenus[p_oMenu.id]) {
    
                m_oMenus[p_oMenu.id] = p_oMenu;
            
        
                if(!m_bInitializedEventHandlers) {
        
                    var 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);


                    m_bInitializedEventHandlers = true;
                    
                    m_oLogger.log("DOM event handlers initialized.");
        
                }
        
                p_oMenu.destroyEvent.subscribe(onMenuDestroy);
                
                p_oMenu.cfg.subscribeToConfigEvent(
                    "visible", 
                    onMenuVisibleConfigChange
                );
        
                p_oMenu.itemAddedEvent.subscribe(onItemAdded);
                p_oMenu.itemRemovedEvent.subscribe(onItemRemoved);
                p_oMenu.focusEvent.subscribe(onMenuFocus);
                p_oMenu.blurEvent.subscribe(onMenuBlur);
    
                m_oLogger.log("Menu: " + 
                    p_oMenu.toString() + " successfully registered.");
    
            }
    
        },

    
        /**
        * @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) {
    
            if(p_oMenu && m_oMenus[p_oMenu.id]) {
    
                delete m_oMenus[p_oMenu.id];
    
                m_oLogger.log("Menu: " + 
                    p_oMenu.toString() + " successfully unregistered.");
    
            }
    
        },
    
    
        /**
        * @method hideVisible
        * @description Hides all visible, dynamically positioned menus.
        */
        hideVisible: function() {
    
            var oMenu;
    
            for(var i in m_oVisibleMenus) {
    
                if(YAHOO.lang.hasOwnProperty(m_oVisibleMenus,i)) {
    
                    oMenu = m_oVisibleMenus[i];
    
                    if(oMenu.cfg.getProperty("position") == "dynamic") {
    
                        oMenu.hide();
    
                    }
    
                }
    
            }        
        
        },


        /**
        * @method getMenus
        * @description Returns an array of all menus registered with the 
        * menu manger.
        * @return {Array}
        */
        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 menu to
        * be retrieved.
        * @return {YAHOO.widget.Menu}
        */
        getMenu: function(p_sId) {
    
            if(m_oMenus[p_sId]) {
            
                return m_oMenus[p_sId];
            
            }
        
        },


        /**
        * @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() {

            if(m_oFocusedMenuItem) {

                return (m_oFocusedMenuItem.parent.getRoot());
            
            }

        },

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

    };

}();

})();

Copyright © 2007 Yahoo! Inc. All rights reserved.