Yahoo! UI Library

Menu Library 

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

/**
* @module menu
* @description <p>The Menu Library features a collection of widgets that make 
* it easy to add menus to your website or web application.  With the Menu 
* Library you can create website fly-out menus, customized context menus, or 
* application-style menu bars with just a small amount of scripting.</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 Library
* @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 = {},


        // Logger

        m_oLogger = new YAHOO.widget.LogWriter(this.toString()),


        me = this;


    // 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 sYUIId = Dom.generateId();

        if(p_oItem && m_oItems[sYUIId] != p_oItem) {

            p_oItem.element.setAttribute("yuiid", sYUIId);
    
            m_oItems[sYUIId] = p_oItem;
    
            p_oItem.destroyEvent.subscribe(onItemDestroy, p_oItem);

            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 sYUIId = p_oItem.element.getAttribute("yuiid");

        if(sYUIId && m_oItems[sYUIId]) {

            delete m_oItems[sYUIId];

            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, "bd") && 
                        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 sYUIId = oElement.getAttribute("yuiid");
        
                if(sYUIId) {
        
                    oMenuItem = m_oItems[sYUIId];
                    oMenu = oMenuItem.parent;
        
                }
            
            }
            else if(sTagName == "DIV") {
            
                if(oElement.id) {
                
                    oMenu = m_oMenus[oElement.id];
                
                }
            
            }

        }

        if(oMenu) {

            // Map of DOM event names to CustomEvent names
        
            var oEventTypes =  {
                    "click": "clickEvent",
                    "mousedown": "mouseDownEvent",
                    "mouseup": "mouseUpEvent",
                    "mouseover": "mouseOverEvent",
                    "mouseout": "mouseOutEvent",
                    "keydown": "keyDownEvent",
                    "keyup": "keyUpEvent",
                    "keypress": "keyPressEvent"
                },
    
                sCustomEventType = oEventTypes[p_oEvent.type];


            // Fire the Custom Even 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
            */
            
            var oActiveItem;
    
            for(var i in m_oMenus) {
    
                if(m_oMenus.hasOwnProperty(i)) {
    
                    oMenu = m_oMenus[i];
    
                    if(
                        oMenu.cfg.getProperty("clicktohide") && 
                        oMenu.cfg.getProperty("position") == "dynamic"
                    ) {
    
                        oMenu.hide();
    
                    }
                    else {

                        oMenu.clearActiveItem(true);
    
                    }
    
                }
    
            } 

        }

    }


    /**
    * @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 Object representing the menu that 
    * fired the event.
    */
    function onMenuDestroy(p_sType, p_aArgs, 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 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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
    * that fired the event.
    */
    function onItemDestroy(p_sType, p_aArgs, p_oItem) {

        var sYUIId = p_oItem.element.getAttribute("yuiid");

        if(sYUIId) {

            delete m_oItems[sYUIId];

        }

    }


    /**
    * @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.
    * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
    * fired the event.
    */
    function onMenuVisibleConfigChange(p_sType, p_aArgs, p_oMenu) {

        var bVisible = p_aArgs[0];
        
        if(bVisible) {

            m_oVisibleMenus[p_oMenu.id] = p_oMenu;
            
            m_oLogger.log("Menu: " + 
                p_oMenu.toString() + 
                " registered with the collection of visible menus.");
        
        }
        else if(m_oVisibleMenus[p_oMenu.id]) {
        
            delete m_oVisibleMenus[p_oMenu.id];
            
            m_oLogger.log("Menu: " + 
                p_oMenu.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.addListener(oDoc, "mouseover", onDOMEvent, me, true);
                    Event.addListener(oDoc, "mouseout", onDOMEvent, me, true);
                    Event.addListener(oDoc, "mousedown", onDOMEvent, me, true);
                    Event.addListener(oDoc, "mouseup", onDOMEvent, me, true);
                    Event.addListener(oDoc, "click", onDOMEvent, me, true);
                    Event.addListener(oDoc, "keydown", onDOMEvent, me, true);
                    Event.addListener(oDoc, "keyup", onDOMEvent, me, true);
                    Event.addListener(oDoc, "keypress", onDOMEvent, me, true);
        
                    m_bInitializedEventHandlers = true;
                    
                    m_oLogger.log("DOM event handlers initialized.");
        
                }
        
                p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, me);
                
                p_oMenu.cfg.subscribeToConfigEvent(
                    "visible", 
                    onMenuVisibleConfigChange, 
                    p_oMenu
                );
        
                p_oMenu.itemAddedEvent.subscribe(onItemAdded);
                p_oMenu.itemRemovedEvent.subscribe(onItemRemoved);
    
                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(m_oVisibleMenus.hasOwnProperty(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 toString
        * @description Returns a string representing the menu manager.
        * @return {String}
        */
        toString: function() {
        
            return ("MenuManager");
        
        }

    };

}();

})();

Copyright © 2006 Yahoo! Inc. All rights reserved.