YUI recommends YUI 3.
YUI 2 has been deprecated since 2011. This site acts as an archive for files and documentation.
/**
* @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");
}
};
}();
})();