/* Copyright (c) 2008, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.5.2 */ /** * @module menu * @description

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.

The Menu family of controls features:

* * @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 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" }, 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 {HTMLElement} 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), 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); 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_oVisibleMenus) { if (YAHOO.lang.hasOwnProperty(m_oVisibleMenus, i)) { oMenu = m_oVisibleMenus[i]; if (oMenu.cfg.getProperty("clicktohide") && !(oMenu instanceof YAHOO.widget.MenuBar) && oMenu.cfg.getProperty("position") == "dynamic") { oMenu.hide(); } else { if (oMenu.cfg.getProperty("showdelay") > 0) { oMenu._cancelShowDelay(); } if (oMenu.activeItem) { oMenu.activeItem.blur(); oMenu.activeItem.cfg.setProperty("selected", false); oMenu.activeItem = null; } } } } } 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. * @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[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 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; } else if (m_oVisibleMenus[sId]) { delete m_oVisibleMenus[sId]; } } /** * @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); } 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); } } /** * @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); } } } 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);