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;
var 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 = new function() {

    // Private member variables


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

    var m_bInitializedEventHandlers = false;


    // Collection of menus

    var m_oMenus = {};
    
    
    //  Collection of menu items 

    var m_oItems = {};


    // Collection of visible menus
    
    var m_oVisibleMenus = {};


    // Logger

    var m_oLogger = new YAHOO.widget.LogWriter(this.toString());



    // Private methods


    /**
    * 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.
    */
    var addItem = function(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.");

        }
    
    };


    /**
    * 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.
    */
    var removeItem = function(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.");

        }
    
    };


    /**
    * 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.
    */
    var getMenuRootElement = function(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


    /**
    * 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).
    */
    var onDOMEvent = function(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

        var oElement = getMenuRootElement(oTarget);
    
        var oMenuItem;
        var 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"
                };
    
            var 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);
    
                    }
    
                }
    
            } 

        }

    };


    /**
    * "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.
    */
    var onMenuDestroy = function(p_sType, p_aArgs, p_oMenu) {
        
        this.removeMenu(p_oMenu);

    };


    /**
    * "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.
    */
    var onItemDestroy = function(p_sType, p_aArgs, p_oItem) {

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

        if(sYUIId) {

            delete m_oItems[sYUIId];

        }

    };


    /**
    * 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.
    */
    var onMenuVisibleConfigChange = function(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.");
        
        }
    
    };


    /**
    * "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.
    */
    var onItemAdded = function(p_sType, p_aArgs) {
    
        addItem(p_aArgs[0]);
    
    };
    

    /**
    * "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.
    */
    var onItemRemoved = function(p_sType, p_aArgs) {

        removeItem(p_aArgs[0]);
    
    };



    // 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.
    */
    this.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, this, true);
                Event.addListener(oDoc, "mouseout", onDOMEvent, this, true);
                Event.addListener(oDoc, "mousedown", onDOMEvent, this, true);
                Event.addListener(oDoc, "mouseup", onDOMEvent, this, true);
                Event.addListener(oDoc, "click", onDOMEvent, this, true);
                Event.addListener(oDoc, "keydown", onDOMEvent, this, true);
                Event.addListener(oDoc, "keyup", onDOMEvent, this, true);
                Event.addListener(oDoc, "keypress", onDOMEvent, this, true);
    
                m_bInitializedEventHandlers = true;
                
                m_oLogger.log("DOM event handlers initialized.");
    
            }
    
            p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
            
            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.
    */
    this.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.
    */
    this.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}
    */
    this.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}
    */
    this.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}
    */
    this.toString = function() {
    
        return ("MenuManager");
    
    };

};

})();

Copyright © 2006 Yahoo! Inc. All rights reserved.