X-Git-Url: https://git.toastfreeware.priv.at/philipp/winterrodeln/wradmin.git/blobdiff_plain/c2d2b07d134a64271aede4886b8c1d0f6960cb4d..582150b643140e3e670d66f244812e314c7aa0c1:/wradmin/static/yui/menu/menu.js diff --git a/wradmin/static/yui/menu/menu.js b/wradmin/static/yui/menu/menu.js new file mode 100644 index 0000000..753e75b --- /dev/null +++ b/wradmin/static/yui/menu/menu.js @@ -0,0 +1,9808 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.7.0 +*/ + + +/** +* @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 _DIV = "DIV", + _HD = "hd", + _BD = "bd", + _FT = "ft", + _LI = "LI", + _DISABLED = "disabled", + _MOUSEOVER = "mouseover", + _MOUSEOUT = "mouseout", + _MOUSEDOWN = "mousedown", + _MOUSEUP = "mouseup", + _FOCUS = YAHOO.env.ua.ie ? "focusin" : "focus", + _CLICK = "click", + _KEYDOWN = "keydown", + _KEYUP = "keyup", + _KEYPRESS = "keypress", + _CLICK_TO_HIDE = "clicktohide", + _POSITION = "position", + _DYNAMIC = "dynamic", + _SHOW_DELAY = "showdelay", + _SELECTED = "selected", + _VISIBLE = "visible", + _UL = "UL", + _MENUMANAGER = "MenuManager", + + + Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Lang = YAHOO.lang; + + + /** + * 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", + "focus": "focusEvent", + "focusin": "focusEvent", + "blur": "blurEvent", + "focusout": "blurEvent" + }, + + + // The element in the DOM that currently has focus + + m_oFocusedElement = null, + + + 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, + returnVal; + + 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) { + + returnVal = oParentNode; + + } + else { + + returnVal = p_oElement; + + } + + break; + + case _LI: + + returnVal = p_oElement; + + break; + + default: + + oParentNode = p_oElement.parentNode; + + if (oParentNode) { + + returnVal = getMenuRootElement(oParentNode); + + } + + break; + + } + + } + + return returnVal; + + } + + + + // 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); + + } + + 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 + */ + + for (var i in m_oVisibleMenus) { + + if (Lang.hasOwnProperty(m_oVisibleMenus, i)) { + + oMenu = m_oVisibleMenus[i]; + + if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) && + !(oMenu instanceof YAHOO.widget.MenuBar) && + oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) { + + oMenu.hide(); + + } + else { + + if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) { + + oMenu._cancelShowDelay(); + + } + + + if (oMenu.activeItem) { + + oMenu.activeItem.blur(); + oMenu.activeItem.cfg.setProperty(_SELECTED, false); + + oMenu.activeItem = null; + + } + + } + + } + + } + + } + else if (p_oEvent.type == _FOCUS) { + + m_oFocusedElement = oTarget; + + } + + } + + + /** + * @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[1]; + + 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 onMenuHide + * @description "hide" 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. + * @param p_oFocusedElement The HTML element that had focus + * prior to the Menu being made visible + */ + function onMenuHide(p_sType, p_aArgs, p_oFocusedElement) { + + /* + Restore focus to the element in the DOM that had focus prior to the Menu + being made visible + */ + + if (p_oFocusedElement && p_oFocusedElement.focus) { + + try { + p_oFocusedElement.focus(); + } + catch(ex) { + } + + } + + this.hideEvent.unsubscribe(onMenuHide, p_oFocusedElement); + + } + + + /** + * @method onMenuShow + * @description "show" 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 onMenuShow(p_sType, p_aArgs) { + + /* + Dynamically positioned, root Menus focus themselves when visible, and will then, + when hidden, restore focus to the UI control that had focus before the Menu was + made visible + */ + + if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) { + + this.hideEvent.subscribe(onMenuHide, m_oFocusedElement); + this.focus(); + + } + + } + + + /** + * @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); + + } + + + /** + * @method removeItem + * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems. + * @private + * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed. + */ + 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); + Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true); + Event.on(oDoc, _KEYUP, onDOMEvent, this, true); + Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true); + + Event.onFocus(oDoc, onDOMEvent, this, true); + Event.onBlur(oDoc, onDOMEvent, this, true); + + m_bInitializedEventHandlers = true; + + + } + + p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange); + p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this); + p_oMenu.itemAddedEvent.subscribe(onItemAdded); + p_oMenu.focusEvent.subscribe(onMenuFocus); + p_oMenu.blurEvent.subscribe(onMenuBlur); + p_oMenu.showEvent.subscribe(onMenuShow); + + + } + + }, + + + /** + * @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) { + + var sId, + aItems, + i; + + if (p_oMenu) { + + sId = p_oMenu.id; + + if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) { + + // Unregister each menu item + + aItems = p_oMenu.getItems(); + + if (aItems && aItems.length > 0) { + + i = aItems.length - 1; + + do { + + removeItem(aItems[i]); + + } + while (i--); + + } + + + // Unregister the menu + + delete m_oMenus[sId]; + + + + /* + Unregister the menu from the collection of + visible menus + */ + + if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) { + + delete m_oVisibleMenus[sId]; + + + } + + + // Unsubscribe event listeners + + if (p_oMenu.cfg) { + + p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE, + onMenuVisibleConfigChange); + + } + + p_oMenu.destroyEvent.unsubscribe(onMenuDestroy, + p_oMenu); + + p_oMenu.itemAddedEvent.unsubscribe(onItemAdded); + p_oMenu.focusEvent.unsubscribe(onMenuFocus); + p_oMenu.blurEvent.unsubscribe(onMenuBlur); + + } + + } + + }, + + + /** + * @method hideVisible + * @description Hides all visible, dynamically positioned menus + * (excluding instances of YAHOO.widget.MenuBar). + */ + hideVisible: function () { + + var oMenu; + + for (var i in m_oVisibleMenus) { + + if (Lang.hasOwnProperty(m_oVisibleMenus, i)) { + + oMenu = m_oVisibleMenus[i]; + + if (!(oMenu instanceof YAHOO.widget.MenuBar) && + oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) { + + oMenu.hide(); + + } + + } + + } + + }, + + + /** + * @method getVisible + * @description Returns a collection of all visible menus registered + * with the menu manger. + * @return {Object} + */ + getVisible: function () { + + return m_oVisibleMenus; + + }, + + + /** + * @method getMenus + * @description Returns a collection of all menus registered with the + * menu manger. + * @return {Object} + */ + 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 + * <div> element representing the menu to + * be retrieved. + * @return {YAHOO.widget.Menu} + */ + getMenu: function (p_sId) { + + var returnVal; + + if (p_sId in m_oMenus) { + + returnVal = m_oMenus[p_sId]; + + } + + return returnVal; + + }, + + + /** + * @method getMenuItem + * @description Returns a menu item with the specified id. + * @param {String} p_sId String specifying the id of the + * <li> element representing the menu item to + * be retrieved. + * @return {YAHOO.widget.MenuItem} + */ + getMenuItem: function (p_sId) { + + var returnVal; + + if (p_sId in m_oItems) { + + returnVal = m_oItems[p_sId]; + + } + + return returnVal; + + }, + + + /** + * @method getMenuItemGroup + * @description Returns an array of menu item instances whose + * corresponding <li> elements are child + * nodes of the <ul> element with the + * specified id. + * @param {String} p_sId String specifying the id of the + * <ul> element representing the group of + * menu items to be retrieved. + * @return {Array} + */ + getMenuItemGroup: function (p_sId) { + + var oUL = Dom.get(p_sId), + aItems, + oNode, + oItem, + sId, + returnVal; + + + if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) { + + oNode = oUL.firstChild; + + if (oNode) { + + aItems = []; + + do { + + sId = oNode.id; + + if (sId) { + + oItem = this.getMenuItem(sId); + + if (oItem) { + + aItems[aItems.length] = oItem; + + } + + } + + } + while ((oNode = oNode.nextSibling)); + + + if (aItems.length > 0) { + + returnVal = aItems; + + } + + } + + } + + return returnVal; + + }, + + + /** + * @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 () { + + var returnVal; + + if (m_oFocusedMenuItem) { + + returnVal = m_oFocusedMenuItem.parent.getRoot(); + + } + + return returnVal; + + }, + + + /** + * @method toString + * @description Returns a string representing the menu manager. + * @return {String} + */ + toString: function () { + + return _MENUMANAGER; + + } + + }; + + }(); + +})(); + + + +(function () { + + var Lang = YAHOO.lang, + + // String constants + + _MENU = "Menu", + _DIV_UPPERCASE = "DIV", + _DIV_LOWERCASE = "div", + _ID = "id", + _SELECT = "SELECT", + _XY = "xy", + _Y = "y", + _UL_UPPERCASE = "UL", + _UL_LOWERCASE = "ul", + _FIRST_OF_TYPE = "first-of-type", + _LI = "LI", + _OPTGROUP = "OPTGROUP", + _OPTION = "OPTION", + _DISABLED = "disabled", + _NONE = "none", + _SELECTED = "selected", + _GROUP_INDEX = "groupindex", + _INDEX = "index", + _SUBMENU = "submenu", + _VISIBLE = "visible", + _HIDE_DELAY = "hidedelay", + _POSITION = "position", + _DYNAMIC = "dynamic", + _STATIC = "static", + _DYNAMIC_STATIC = _DYNAMIC + "," + _STATIC, + _WINDOWS = "windows", + _URL = "url", + _HASH = "#", + _TARGET = "target", + _MAX_HEIGHT = "maxheight", + _TOP_SCROLLBAR = "topscrollbar", + _BOTTOM_SCROLLBAR = "bottomscrollbar", + _UNDERSCORE = "_", + _TOP_SCROLLBAR_DISABLED = _TOP_SCROLLBAR + _UNDERSCORE + _DISABLED, + _BOTTOM_SCROLLBAR_DISABLED = _BOTTOM_SCROLLBAR + _UNDERSCORE + _DISABLED, + _MOUSEMOVE = "mousemove", + _SHOW_DELAY = "showdelay", + _SUBMENU_HIDE_DELAY = "submenuhidedelay", + _IFRAME = "iframe", + _CONSTRAIN_TO_VIEWPORT = "constraintoviewport", + _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap", + _SUBMENU_ALIGNMENT = "submenualignment", + _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay", + _CLICK_TO_HIDE = "clicktohide", + _CONTAINER = "container", + _SCROLL_INCREMENT = "scrollincrement", + _MIN_SCROLL_HEIGHT = "minscrollheight", + _CLASSNAME = "classname", + _SHADOW = "shadow", + _KEEP_OPEN = "keepopen", + _HD = "hd", + _HAS_TITLE = "hastitle", + _CONTEXT = "context", + _EMPTY_STRING = "", + _MOUSEDOWN = "mousedown", + _KEYDOWN = "keydown", + _HEIGHT = "height", + _WIDTH = "width", + _PX = "px", + _EFFECT = "effect", + _MONITOR_RESIZE = "monitorresize", + _DISPLAY = "display", + _BLOCK = "block", + _VISIBILITY = "visibility", + _ABSOLUTE = "absolute", + _ZINDEX = "zindex", + _YUI_MENU_BODY_SCROLLED = "yui-menu-body-scrolled", + _NON_BREAKING_SPACE = " ", + _SPACE = " ", + _MOUSEOVER = "mouseover", + _MOUSEOUT = "mouseout", + _ITEM_ADDED = "itemAdded", + _ITEM_REMOVED = "itemRemoved", + _HIDDEN = "hidden", + _YUI_MENU_SHADOW = "yui-menu-shadow", + _YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + "-visible", + _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + _SPACE + _YUI_MENU_SHADOW_VISIBLE; + + +/** +* The Menu class creates a container that holds a vertical list representing +* a set of options or commands. Menu is the base class for all +* menu containers. +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the <div> element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the <select> element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +* @namespace YAHOO.widget +* @class Menu +* @constructor +* @extends YAHOO.widget.Overlay +*/ +YAHOO.widget.Menu = function (p_oElement, p_oConfig) { + + if (p_oConfig) { + + this.parent = p_oConfig.parent; + this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload; + this.itemData = p_oConfig.itemData || p_oConfig.itemdata; + + } + + + YAHOO.widget.Menu.superclass.constructor.call(this, p_oElement, p_oConfig); + +}; + + + +/** +* @method checkPosition +* @description Checks to make sure that the value of the "position" property +* is one of the supported strings. Returns true if the position is supported. +* @private +* @param {Object} p_sPosition String specifying the position of the menu. +* @return {Boolean} +*/ +function checkPosition(p_sPosition) { + + var returnVal = false; + + if (Lang.isString(p_sPosition)) { + + returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1); + + } + + return returnVal; + +} + + +var Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Module = YAHOO.widget.Module, + Overlay = YAHOO.widget.Overlay, + Menu = YAHOO.widget.Menu, + MenuManager = YAHOO.widget.MenuManager, + CustomEvent = YAHOO.util.CustomEvent, + UA = YAHOO.env.ua, + + m_oShadowTemplate, + + EVENT_TYPES = [ + + ["mouseOverEvent", _MOUSEOVER], + ["mouseOutEvent", _MOUSEOUT], + ["mouseDownEvent", _MOUSEDOWN], + ["mouseUpEvent", "mouseup"], + ["clickEvent", "click"], + ["keyPressEvent", "keypress"], + ["keyDownEvent", _KEYDOWN], + ["keyUpEvent", "keyup"], + ["focusEvent", "focus"], + ["blurEvent", "blur"], + ["itemAddedEvent", _ITEM_ADDED], + ["itemRemovedEvent", _ITEM_REMOVED] + + ], + + VISIBLE_CONFIG = { + key: _VISIBLE, + value: false, + validator: Lang.isBoolean + }, + + CONSTRAIN_TO_VIEWPORT_CONFIG = { + key: _CONSTRAIN_TO_VIEWPORT, + value: true, + validator: Lang.isBoolean, + supercedes: [_IFRAME,"x",_Y,_XY] + }, + + PREVENT_CONTEXT_OVERLAP_CONFIG = { + key: _PREVENT_CONTEXT_OVERLAP, + value: true, + validator: Lang.isBoolean, + supercedes: [_CONSTRAIN_TO_VIEWPORT] + }, + + POSITION_CONFIG = { + key: _POSITION, + value: _DYNAMIC, + validator: checkPosition, + supercedes: [_VISIBLE, _IFRAME] + }, + + SUBMENU_ALIGNMENT_CONFIG = { + key: _SUBMENU_ALIGNMENT, + value: ["tl","tr"] + }, + + AUTO_SUBMENU_DISPLAY_CONFIG = { + key: _AUTO_SUBMENU_DISPLAY, + value: true, + validator: Lang.isBoolean, + suppressEvent: true + }, + + SHOW_DELAY_CONFIG = { + key: _SHOW_DELAY, + value: 250, + validator: Lang.isNumber, + suppressEvent: true + }, + + HIDE_DELAY_CONFIG = { + key: _HIDE_DELAY, + value: 0, + validator: Lang.isNumber, + suppressEvent: true + }, + + SUBMENU_HIDE_DELAY_CONFIG = { + key: _SUBMENU_HIDE_DELAY, + value: 250, + validator: Lang.isNumber, + suppressEvent: true + }, + + CLICK_TO_HIDE_CONFIG = { + key: _CLICK_TO_HIDE, + value: true, + validator: Lang.isBoolean, + suppressEvent: true + }, + + CONTAINER_CONFIG = { + key: _CONTAINER, + suppressEvent: true + }, + + SCROLL_INCREMENT_CONFIG = { + key: _SCROLL_INCREMENT, + value: 1, + validator: Lang.isNumber, + supercedes: [_MAX_HEIGHT], + suppressEvent: true + }, + + MIN_SCROLL_HEIGHT_CONFIG = { + key: _MIN_SCROLL_HEIGHT, + value: 90, + validator: Lang.isNumber, + supercedes: [_MAX_HEIGHT], + suppressEvent: true + }, + + MAX_HEIGHT_CONFIG = { + key: _MAX_HEIGHT, + value: 0, + validator: Lang.isNumber, + supercedes: [_IFRAME], + suppressEvent: true + }, + + CLASS_NAME_CONFIG = { + key: _CLASSNAME, + value: null, + validator: Lang.isString, + suppressEvent: true + }, + + DISABLED_CONFIG = { + key: _DISABLED, + value: false, + validator: Lang.isBoolean, + suppressEvent: true + }, + + SHADOW_CONFIG = { + key: _SHADOW, + value: true, + validator: Lang.isBoolean, + suppressEvent: true, + supercedes: [_VISIBLE] + }, + + KEEP_OPEN_CONFIG = { + key: _KEEP_OPEN, + value: false, + validator: Lang.isBoolean + }; + + + +YAHOO.lang.extend(Menu, Overlay, { + + +// Constants + + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +* menu's <div> element. +* @default "yuimenu" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenu", + + +/** +* @property ITEM_TYPE +* @description Object representing the type of menu item to instantiate and +* add when parsing the child nodes (either <li> element, +* <optgroup> element or <option>) +* of the menu's source HTML element. +* @default YAHOO.widget.MenuItem +* @final +* @type YAHOO.widget.MenuItem +*/ +ITEM_TYPE: null, + + +/** +* @property GROUP_TITLE_TAG_NAME +* @description String representing the tagname of the HTML element used to +* title the menu's item groups. +* @default H6 +* @final +* @type String +*/ +GROUP_TITLE_TAG_NAME: "h6", + + +/** +* @property OFF_SCREEN_POSITION +* @description Array representing the default x and y position that a menu +* should have when it is positioned outside the viewport by the +* "poistionOffScreen" method. +* @default "-999em" +* @final +* @type String +*/ +OFF_SCREEN_POSITION: "-999em", + + +// Private properties + + +/** +* @property _useHideDelay +* @description Boolean indicating if the "mouseover" and "mouseout" event +* handlers used for hiding the menu via a call to "YAHOO.lang.later" have +* already been assigned. +* @default false +* @private +* @type Boolean +*/ +_useHideDelay: false, + + +/** +* @property _bHandledMouseOverEvent +* @description Boolean indicating the current state of the menu's +* "mouseover" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOverEvent: false, + + +/** +* @property _bHandledMouseOutEvent +* @description Boolean indicating the current state of the menu's +* "mouseout" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOutEvent: false, + + +/** +* @property _aGroupTitleElements +* @description Array of HTML element used to title groups of menu items. +* @default [] +* @private +* @type Array +*/ +_aGroupTitleElements: null, + + +/** +* @property _aItemGroups +* @description Multi-dimensional Array representing the menu items as they +* are grouped in the menu. +* @default [] +* @private +* @type Array +*/ +_aItemGroups: null, + + +/** +* @property _aListElements +* @description Array of <ul> elements, each of which is +* the parent node for each item's <li> element. +* @default [] +* @private +* @type Array +*/ +_aListElements: null, + + +/** +* @property _nCurrentMouseX +* @description The current x coordinate of the mouse inside the area of +* the menu. +* @default 0 +* @private +* @type Number +*/ +_nCurrentMouseX: 0, + + +/** +* @property _bStopMouseEventHandlers +* @description Stops "mouseover," "mouseout," and "mousemove" event handlers +* from executing. +* @default false +* @private +* @type Boolean +*/ +_bStopMouseEventHandlers: false, + + +/** +* @property _sClassName +* @description The current value of the "classname" configuration attribute. +* @default null +* @private +* @type String +*/ +_sClassName: null, + + + +// Public properties + + +/** +* @property lazyLoad +* @description Boolean indicating if the menu's "lazy load" feature is +* enabled. If set to "true," initialization and rendering of the menu's +* items will be deferred until the first time it is made visible. This +* property should be set via the constructor using the configuration +* object literal. +* @default false +* @type Boolean +*/ +lazyLoad: false, + + +/** +* @property itemData +* @description Array of items to be added to the menu. The array can contain +* strings representing the text for each item to be created, object literals +* representing the menu item configuration properties, or MenuItem instances. +* This property should be set via the constructor using the configuration +* object literal. +* @default null +* @type Array +*/ +itemData: null, + + +/** +* @property activeItem +* @description Object reference to the item in the menu that has is selected. +* @default null +* @type YAHOO.widget.MenuItem +*/ +activeItem: null, + + +/** +* @property parent +* @description Object reference to the menu's parent menu or menu item. +* This property can be set via the constructor using the configuration +* object literal. +* @default null +* @type YAHOO.widget.MenuItem +*/ +parent: null, + + +/** +* @property srcElement +* @description Object reference to the HTML element (either +* <select> or <div>) used to +* create the menu. +* @default null +* @type HTMLSelectElement|HTMLDivElement +*/ +srcElement: null, + + + +// Events + + +/** +* @event mouseOverEvent +* @description Fires when the mouse has entered the menu. Passes back +* the DOM Event object as an argument. +*/ + + +/** +* @event mouseOutEvent +* @description Fires when the mouse has left the menu. Passes back the DOM +* Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event mouseDownEvent +* @description Fires when the user mouses down on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event mouseUpEvent +* @description Fires when the user releases a mouse button while the mouse is +* over the menu. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event clickEvent +* @description Fires when the user clicks the on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event keyPressEvent +* @description Fires when the user presses an alphanumeric key when one of the +* menu's items has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event keyDownEvent +* @description Fires when the user presses a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event keyUpEvent +* @description Fires when the user releases a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event itemAddedEvent +* @description Fires when an item is added to the menu. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @event itemRemovedEvent +* @description Fires when an item is removed to the menu. +* @type YAHOO.util.CustomEvent +*/ + + +/** +* @method init +* @description The Menu class's initialization method. This method is +* automatically called by the constructor, and sets up all DOM references +* for pre-existing markup, and creates required markup if it is not +* already present. +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the <div> element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the <select> element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +*/ +init: function (p_oElement, p_oConfig) { + + this._aItemGroups = []; + this._aListElements = []; + this._aGroupTitleElements = []; + + if (!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuItem; + + } + + + var oElement; + + if (Lang.isString(p_oElement)) { + + oElement = Dom.get(p_oElement); + + } + else if (p_oElement.tagName) { + + oElement = p_oElement; + + } + + + if (oElement && oElement.tagName) { + + switch(oElement.tagName.toUpperCase()) { + + case _DIV_UPPERCASE: + + this.srcElement = oElement; + + if (!oElement.id) { + + oElement.setAttribute(_ID, Dom.generateId()); + + } + + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + Menu.superclass.init.call(this, oElement); + + this.beforeInitEvent.fire(Menu); + + + break; + + case _SELECT: + + this.srcElement = oElement; + + + /* + The source element is not something that we can use + outright, so we need to create a new Overlay + + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + Menu.superclass.init.call(this, Dom.generateId()); + + this.beforeInitEvent.fire(Menu); + + + break; + + } + + } + else { + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + Menu.superclass.init.call(this, p_oElement); + + this.beforeInitEvent.fire(Menu); + + + } + + + if (this.element) { + + Dom.addClass(this.element, this.CSS_CLASS_NAME); + + + // Subscribe to Custom Events + + this.initEvent.subscribe(this._onInit); + this.beforeRenderEvent.subscribe(this._onBeforeRender); + this.renderEvent.subscribe(this._onRender); + this.beforeShowEvent.subscribe(this._onBeforeShow); + this.hideEvent.subscribe(this._onHide); + this.showEvent.subscribe(this._onShow); + this.beforeHideEvent.subscribe(this._onBeforeHide); + this.mouseOverEvent.subscribe(this._onMouseOver); + this.mouseOutEvent.subscribe(this._onMouseOut); + this.clickEvent.subscribe(this._onClick); + this.keyDownEvent.subscribe(this._onKeyDown); + this.keyPressEvent.subscribe(this._onKeyPress); + this.blurEvent.subscribe(this._onBlur); + + + // Fixes an issue in Firefox 2 and Webkit where Dom's "getX" and "getY" + // methods return values that don't take scrollTop into consideration + + if ((UA.gecko && UA.gecko < 1.9) || UA.webkit) { + + this.cfg.subscribeToConfigEvent(_Y, this._onYChange); + + } + + + if (p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + + // Register the Menu instance with the MenuManager + + MenuManager.addMenu(this); + + + this.initEvent.fire(Menu); + + } + +}, + + + +// Private methods + + +/** +* @method _initSubTree +* @description Iterates the childNodes of the source element to find nodes +* used to instantiate menu and menu items. +* @private +*/ +_initSubTree: function () { + + var oSrcElement = this.srcElement, + sSrcElementTagName, + nGroup, + sGroupTitleTagName, + oNode, + aListElements, + nListElements, + i; + + + if (oSrcElement) { + + sSrcElementTagName = + (oSrcElement.tagName && oSrcElement.tagName.toUpperCase()); + + + if (sSrcElementTagName == _DIV_UPPERCASE) { + + // Populate the collection of item groups and item group titles + + oNode = this.body.firstChild; + + + if (oNode) { + + nGroup = 0; + sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase(); + + do { + + + if (oNode && oNode.tagName) { + + switch (oNode.tagName.toUpperCase()) { + + case sGroupTitleTagName: + + this._aGroupTitleElements[nGroup] = oNode; + + break; + + case _UL_UPPERCASE: + + this._aListElements[nGroup] = oNode; + this._aItemGroups[nGroup] = []; + nGroup++; + + break; + + } + + } + + } + while ((oNode = oNode.nextSibling)); + + + /* + Apply the "first-of-type" class to the first UL to mimic + the ":first-of-type" CSS3 psuedo class. + */ + + if (this._aListElements[0]) { + + Dom.addClass(this._aListElements[0], _FIRST_OF_TYPE); + + } + + } + + } + + + oNode = null; + + + + if (sSrcElementTagName) { + + switch (sSrcElementTagName) { + + case _DIV_UPPERCASE: + + aListElements = this._aListElements; + nListElements = aListElements.length; + + if (nListElements > 0) { + + + i = nListElements - 1; + + do { + + oNode = aListElements[i].firstChild; + + if (oNode) { + + + do { + + if (oNode && oNode.tagName && + oNode.tagName.toUpperCase() == _LI) { + + + this.addItem(new this.ITEM_TYPE(oNode, + { parent: this }), i); + + } + + } + while ((oNode = oNode.nextSibling)); + + } + + } + while (i--); + + } + + break; + + case _SELECT: + + + oNode = oSrcElement.firstChild; + + do { + + if (oNode && oNode.tagName) { + + switch (oNode.tagName.toUpperCase()) { + + case _OPTGROUP: + case _OPTION: + + + this.addItem( + new this.ITEM_TYPE( + oNode, + { parent: this } + ) + ); + + break; + + } + + } + + } + while ((oNode = oNode.nextSibling)); + + break; + + } + + } + + } + +}, + + +/** +* @method _getFirstEnabledItem +* @description Returns the first enabled item in the menu. +* @return {YAHOO.widget.MenuItem} +* @private +*/ +_getFirstEnabledItem: function () { + + var aItems = this.getItems(), + nItems = aItems.length, + oItem, + returnVal; + + + for(var i=0; i= aGroup.length); + + + if (aGroup[p_nItemIndex]) { + + aGroup.splice(p_nItemIndex, 0, oItem); + + } + else { + + aGroup[p_nItemIndex] = oItem; + + } + + + oGroupItem = aGroup[p_nItemIndex]; + + if (oGroupItem) { + + if (bAppend && (!oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11)) { + + this._aListElements[nGroupIndex].appendChild(oGroupItem.element); + + } + else { + + oNextItemSibling = getNextItemSibling(aGroup, (p_nItemIndex+1)); + + if (oNextItemSibling && (!oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11)) { + + this._aListElements[nGroupIndex].insertBefore( + oGroupItem.element, oNextItemSibling.element); + + } + + } + + + oGroupItem.parent = this; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + this._updateItemProperties(nGroupIndex); + + + this.itemAddedEvent.fire(oGroupItem); + this.changeContentEvent.fire(); + + returnVal = oGroupItem; + + } + + } + else { + + nItemIndex = aGroup.length; + + aGroup[nItemIndex] = oItem; + + oGroupItem = aGroup[nItemIndex]; + + + if (oGroupItem) { + + if (!Dom.isAncestor(this._aListElements[nGroupIndex], oGroupItem.element)) { + + this._aListElements[nGroupIndex].appendChild(oGroupItem.element); + + } + + oGroupItem.element.setAttribute(_GROUP_INDEX, nGroupIndex); + oGroupItem.element.setAttribute(_INDEX, nItemIndex); + + oGroupItem.parent = this; + + oGroupItem.index = nItemIndex; + oGroupItem.groupIndex = nGroupIndex; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + if (nItemIndex === 0) { + + Dom.addClass(oGroupItem.element, _FIRST_OF_TYPE); + + } + + + + this.itemAddedEvent.fire(oGroupItem); + this.changeContentEvent.fire(); + + returnVal = oGroupItem; + + } + + } + + } + + return returnVal; + +}, + + +/** +* @method _removeItemFromGroupByIndex +* @description Removes a menu item from a group by index. Returns the menu +* item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the menu +* item belongs. +* @param {Number} p_nItemIndex Number indicating the index of the menu item +* to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) { + + var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0, + aGroup = this._getItemGroup(nGroupIndex), + aArray, + oItem, + oUL; + + if (aGroup) { + + aArray = aGroup.splice(p_nItemIndex, 1); + oItem = aArray[0]; + + if (oItem) { + + // Update the index and className properties of each member + + this._updateItemProperties(nGroupIndex); + + if (aGroup.length === 0) { + + // Remove the UL + + oUL = this._aListElements[nGroupIndex]; + + if (this.body && oUL) { + + this.body.removeChild(oUL); + + } + + // Remove the group from the array of items + + this._aItemGroups.splice(nGroupIndex, 1); + + + // Remove the UL from the array of ULs + + this._aListElements.splice(nGroupIndex, 1); + + + /* + Assign the "first-of-type" class to the new first UL + in the collection + */ + + oUL = this._aListElements[0]; + + if (oUL) { + + Dom.addClass(oUL, _FIRST_OF_TYPE); + + } + + } + + + this.itemRemovedEvent.fire(oItem); + this.changeContentEvent.fire(); + + } + + } + + // Return a reference to the item that was removed + + return oItem; + +}, + + +/** +* @method _removeItemFromGroupByValue +* @description Removes a menu item from a group by reference. Returns the +* menu item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the +* menu item belongs. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) { + + var aGroup = this._getItemGroup(p_nGroupIndex), + nItems, + nItemIndex, + returnVal, + i; + + if (aGroup) { + + nItems = aGroup.length; + nItemIndex = -1; + + if (nItems > 0) { + + i = nItems-1; + + do { + + if (aGroup[i] == p_oItem) { + + nItemIndex = i; + break; + + } + + } + while (i--); + + if (nItemIndex > -1) { + + returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex); + + } + + } + + } + + return returnVal; + +}, + + +/** +* @method _updateItemProperties +* @description Updates the "index," "groupindex," and "className" properties +* of the menu items in the specified group. +* @private +* @param {Number} p_nGroupIndex Number indicating the group of items to update. +*/ +_updateItemProperties: function (p_nGroupIndex) { + + var aGroup = this._getItemGroup(p_nGroupIndex), + nItems = aGroup.length, + oItem, + oLI, + i; + + + if (nItems > 0) { + + i = nItems - 1; + + // Update the index and className properties of each member + + do { + + oItem = aGroup[i]; + + if (oItem) { + + oLI = oItem.element; + + oItem.index = i; + oItem.groupIndex = p_nGroupIndex; + + oLI.setAttribute(_GROUP_INDEX, p_nGroupIndex); + oLI.setAttribute(_INDEX, i); + + Dom.removeClass(oLI, _FIRST_OF_TYPE); + + } + + } + while (i--); + + + if (oLI) { + + Dom.addClass(oLI, _FIRST_OF_TYPE); + + } + + } + +}, + + +/** +* @method _createItemGroup +* @description Creates a new menu item group (array) and its associated +* <ul> element. Returns an aray of menu item groups. +* @private +* @param {Number} p_nIndex Number indicating the group to create. +* @return {Array} +*/ +_createItemGroup: function (p_nIndex) { + + var oUL, + returnVal; + + if (!this._aItemGroups[p_nIndex]) { + + this._aItemGroups[p_nIndex] = []; + + oUL = document.createElement(_UL_LOWERCASE); + + this._aListElements[p_nIndex] = oUL; + + returnVal = this._aItemGroups[p_nIndex]; + + } + + return returnVal; + +}, + + +/** +* @method _getItemGroup +* @description Returns the menu item group at the specified index. +* @private +* @param {Number} p_nIndex Number indicating the index of the menu item group +* to be retrieved. +* @return {Array} +*/ +_getItemGroup: function (p_nIndex) { + + var nIndex = Lang.isNumber(p_nIndex) ? p_nIndex : 0, + aGroups = this._aItemGroups, + returnVal; + + if (nIndex in aGroups) { + + returnVal = aGroups[nIndex]; + + } + + return returnVal; + +}, + + +/** +* @method _configureSubmenu +* @description Subscribes the menu item's submenu to its parent menu's events. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance with the submenu to be configured. +*/ +_configureSubmenu: function (p_oItem) { + + var oSubmenu = p_oItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + /* + Listen for configuration changes to the parent menu + so they they can be applied to the submenu. + */ + + this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange, oSubmenu, true); + + this.renderEvent.subscribe(this._onParentMenuRender, oSubmenu, true); + + } + +}, + + + + +/** +* @method _subscribeToItemEvents +* @description Subscribes a menu to a menu item's event. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance whose events should be subscribed to. +*/ +_subscribeToItemEvents: function (p_oItem) { + + p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this); + p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange, p_oItem, this); + +}, + + +/** +* @method _onVisibleChange +* @description Change event handler for the the menu's "visible" configuration +* property. +* @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. +*/ +_onVisibleChange: function (p_sType, p_aArgs) { + + var bVisible = p_aArgs[0]; + + if (bVisible) { + + Dom.addClass(this.element, _VISIBLE); + + } + else { + + Dom.removeClass(this.element, _VISIBLE); + + } + +}, + + +/** +* @method _cancelHideDelay +* @description Cancels the call to "hideMenu." +* @private +*/ +_cancelHideDelay: function () { + + var oTimer = this.getRoot()._hideDelayTimer; + + if (oTimer) { + + oTimer.cancel(); + + } + +}, + + +/** +* @method _execHideDelay +* @description Hides the menu after the number of milliseconds specified by +* the "hidedelay" configuration property. +* @private +*/ +_execHideDelay: function () { + + this._cancelHideDelay(); + + var oRoot = this.getRoot(); + + oRoot._hideDelayTimer = Lang.later(oRoot.cfg.getProperty(_HIDE_DELAY), this, function () { + + if (oRoot.activeItem) { + + if (oRoot.hasFocus()) { + + oRoot.activeItem.focus(); + + } + + oRoot.clearActiveItem(); + + } + + if (oRoot == this && !(this instanceof YAHOO.widget.MenuBar) && + this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + this.hide(); + + } + + }); + +}, + + +/** +* @method _cancelShowDelay +* @description Cancels the call to the "showMenu." +* @private +*/ +_cancelShowDelay: function () { + + var oTimer = this.getRoot()._showDelayTimer; + + if (oTimer) { + + oTimer.cancel(); + + } + +}, + + +/** +* @method _execSubmenuHideDelay +* @description Hides a submenu after the number of milliseconds specified by +* the "submenuhidedelay" configuration property have ellapsed. +* @private +* @param {YAHOO.widget.Menu} p_oSubmenu Object specifying the submenu that +* should be hidden. +* @param {Number} p_nMouseX The x coordinate of the mouse when it left +* the specified submenu's parent menu item. +* @param {Number} p_nHideDelay The number of milliseconds that should ellapse +* before the submenu is hidden. +*/ +_execSubmenuHideDelay: function (p_oSubmenu, p_nMouseX, p_nHideDelay) { + + p_oSubmenu._submenuHideDelayTimer = Lang.later(50, this, function () { + + if (this._nCurrentMouseX > (p_nMouseX + 10)) { + + p_oSubmenu._submenuHideDelayTimer = Lang.later(p_nHideDelay, p_oSubmenu, function () { + + this.hide(); + + }); + + } + else { + + p_oSubmenu.hide(); + + } + + }); + +}, + + + +// Protected methods + + +/** +* @method _disableScrollHeader +* @description Disables the header used for scrolling the body of the menu. +* @protected +*/ +_disableScrollHeader: function () { + + if (!this._bHeaderDisabled) { + + Dom.addClass(this.header, _TOP_SCROLLBAR_DISABLED); + this._bHeaderDisabled = true; + + } + +}, + + +/** +* @method _disableScrollFooter +* @description Disables the footer used for scrolling the body of the menu. +* @protected +*/ +_disableScrollFooter: function () { + + if (!this._bFooterDisabled) { + + Dom.addClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED); + this._bFooterDisabled = true; + + } + +}, + + +/** +* @method _enableScrollHeader +* @description Enables the header used for scrolling the body of the menu. +* @protected +*/ +_enableScrollHeader: function () { + + if (this._bHeaderDisabled) { + + Dom.removeClass(this.header, _TOP_SCROLLBAR_DISABLED); + this._bHeaderDisabled = false; + + } + +}, + + +/** +* @method _enableScrollFooter +* @description Enables the footer used for scrolling the body of the menu. +* @protected +*/ +_enableScrollFooter: function () { + + if (this._bFooterDisabled) { + + Dom.removeClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED); + this._bFooterDisabled = false; + + } + +}, + + +/** +* @method _onMouseOver +* @description "mouseover" event handler for the menu. +* @protected +* @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. +*/ +_onMouseOver: function (p_sType, p_aArgs) { + + var oEvent = p_aArgs[0], + oItem = p_aArgs[1], + oTarget = Event.getTarget(oEvent), + oRoot = this.getRoot(), + oSubmenuHideDelayTimer = this._submenuHideDelayTimer, + oParentMenu, + nShowDelay, + bShowDelay, + oActiveItem, + oItemCfg, + oSubmenu; + + + var showSubmenu = function () { + + if (this.parent.cfg.getProperty(_SELECTED)) { + + this.show(); + + } + + }; + + + if (!this._bStopMouseEventHandlers) { + + if (!this._bHandledMouseOverEvent && (oTarget == this.element || + Dom.isAncestor(this.element, oTarget))) { + + // Menu mouseover logic + + if (this._useHideDelay) { + this._cancelHideDelay(); + } + + this._nCurrentMouseX = 0; + + Event.on(this.element, _MOUSEMOVE, this._onMouseMove, this, true); + + + /* + If the mouse is moving from the submenu back to its corresponding menu item, + don't hide the submenu or clear the active MenuItem. + */ + + if (!(oItem && Dom.isAncestor(oItem.element, Event.getRelatedTarget(oEvent)))) { + + this.clearActiveItem(); + + } + + + if (this.parent && oSubmenuHideDelayTimer) { + + oSubmenuHideDelayTimer.cancel(); + + this.parent.cfg.setProperty(_SELECTED, true); + + oParentMenu = this.parent.parent; + + oParentMenu._bHandledMouseOutEvent = true; + oParentMenu._bHandledMouseOverEvent = false; + + } + + + this._bHandledMouseOverEvent = true; + this._bHandledMouseOutEvent = false; + + } + + + if (oItem && !oItem.handledMouseOverEvent && !oItem.cfg.getProperty(_DISABLED) && + (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))) { + + // Menu Item mouseover logic + + nShowDelay = this.cfg.getProperty(_SHOW_DELAY); + bShowDelay = (nShowDelay > 0); + + + if (bShowDelay) { + + this._cancelShowDelay(); + + } + + + oActiveItem = this.activeItem; + + if (oActiveItem) { + + oActiveItem.cfg.setProperty(_SELECTED, false); + + } + + + oItemCfg = oItem.cfg; + + // Select and focus the current menu item + + oItemCfg.setProperty(_SELECTED, true); + + + if (this.hasFocus() || oRoot._hasFocus) { + + oItem.focus(); + + oRoot._hasFocus = false; + + } + + + if (this.cfg.getProperty(_AUTO_SUBMENU_DISPLAY)) { + + // Show the submenu this menu item + + oSubmenu = oItemCfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + if (bShowDelay) { + + oRoot._showDelayTimer = + Lang.later(oRoot.cfg.getProperty(_SHOW_DELAY), oSubmenu, showSubmenu); + + } + else { + + oSubmenu.show(); + + } + + } + + } + + oItem.handledMouseOverEvent = true; + oItem.handledMouseOutEvent = false; + + } + + } + +}, + + +/** +* @method _onMouseOut +* @description "mouseout" event handler for the menu. +* @protected +* @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. +*/ +_onMouseOut: function (p_sType, p_aArgs) { + + var oEvent = p_aArgs[0], + oItem = p_aArgs[1], + oRelatedTarget = Event.getRelatedTarget(oEvent), + bMovingToSubmenu = false, + oItemCfg, + oSubmenu, + nSubmenuHideDelay, + nShowDelay; + + + if (!this._bStopMouseEventHandlers) { + + if (oItem && !oItem.cfg.getProperty(_DISABLED)) { + + oItemCfg = oItem.cfg; + oSubmenu = oItemCfg.getProperty(_SUBMENU); + + + if (oSubmenu && (oRelatedTarget == oSubmenu.element || + Dom.isAncestor(oSubmenu.element, oRelatedTarget))) { + + bMovingToSubmenu = true; + + } + + + if (!oItem.handledMouseOutEvent && ((oRelatedTarget != oItem.element && + !Dom.isAncestor(oItem.element, oRelatedTarget)) || bMovingToSubmenu)) { + + // Menu Item mouseout logic + + if (!bMovingToSubmenu) { + + oItem.cfg.setProperty(_SELECTED, false); + + + if (oSubmenu) { + + nSubmenuHideDelay = this.cfg.getProperty(_SUBMENU_HIDE_DELAY); + + nShowDelay = this.cfg.getProperty(_SHOW_DELAY); + + if (!(this instanceof YAHOO.widget.MenuBar) && nSubmenuHideDelay > 0 && + nShowDelay >= nSubmenuHideDelay) { + + this._execSubmenuHideDelay(oSubmenu, Event.getPageX(oEvent), + nSubmenuHideDelay); + + } + else { + + oSubmenu.hide(); + + } + + } + + } + + + oItem.handledMouseOutEvent = true; + oItem.handledMouseOverEvent = false; + + } + + } + + + if (!this._bHandledMouseOutEvent && ((oRelatedTarget != this.element && + !Dom.isAncestor(this.element, oRelatedTarget)) || bMovingToSubmenu)) { + + // Menu mouseout logic + + if (this._useHideDelay) { + this._execHideDelay(); + } + + Event.removeListener(this.element, _MOUSEMOVE, this._onMouseMove); + + this._nCurrentMouseX = Event.getPageX(oEvent); + + this._bHandledMouseOutEvent = true; + this._bHandledMouseOverEvent = false; + + } + + } + +}, + + +/** +* @method _onMouseMove +* @description "click" event handler for the menu. +* @protected +* @param {Event} p_oEvent Object representing the DOM event object passed +* back by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onMouseMove: function (p_oEvent, p_oMenu) { + + if (!this._bStopMouseEventHandlers) { + + this._nCurrentMouseX = Event.getPageX(p_oEvent); + + } + +}, + + +/** +* @method _onClick +* @description "click" event handler for the menu. +* @protected +* @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. +*/ +_onClick: function (p_sType, p_aArgs) { + + var oEvent = p_aArgs[0], + oItem = p_aArgs[1], + bInMenuAnchor = false, + oSubmenu, + oMenu, + oRoot, + sId, + sURL, + nHashPos, + nLen; + + + var hide = function () { + + /* + There is an inconsistency between Firefox for Mac OS X and Firefox Windows + regarding the triggering of the display of the browser's context menu and the + subsequent firing of the "click" event. In Firefox for Windows, when the user + triggers the display of the browser's context menu the "click" event also fires + for the document object, even though the "click" event did not fire for the + element that was the original target of the "contextmenu" event. This is unique + to Firefox on Windows. For all other A-Grade browsers, including Firefox for + Mac OS X, the "click" event doesn't fire for the document object. + + This bug in Firefox for Windows affects Menu as Menu instances listen for + events at the document level and have an internal "click" event handler they + use to hide themselves when clicked. As a result, in Firefox for Windows a + Menu will hide when the user right clicks on a MenuItem to raise the browser's + default context menu, because its internal "click" event handler ends up + getting called. The following line fixes this bug. + */ + + if (!((UA.gecko && this.platform == _WINDOWS) && oEvent.button > 0)) { + + oRoot = this.getRoot(); + + if (oRoot instanceof YAHOO.widget.MenuBar || + oRoot.cfg.getProperty(_POSITION) == _STATIC) { + + oRoot.clearActiveItem(); + + } + else { + + oRoot.hide(); + + } + + } + + }; + + + if (oItem) { + + if (oItem.cfg.getProperty(_DISABLED)) { + + Event.preventDefault(oEvent); + + hide.call(this); + + } + else { + + oSubmenu = oItem.cfg.getProperty(_SUBMENU); + + + /* + Check if the URL of the anchor is pointing to an element that is + a child of the menu. + */ + + sURL = oItem.cfg.getProperty(_URL); + + + if (sURL) { + + nHashPos = sURL.indexOf(_HASH); + + nLen = sURL.length; + + + if (nHashPos != -1) { + + sURL = sURL.substr(nHashPos, nLen); + + nLen = sURL.length; + + + if (nLen > 1) { + + sId = sURL.substr(1, nLen); + + oMenu = YAHOO.widget.MenuManager.getMenu(sId); + + if (oMenu) { + + bInMenuAnchor = + (this.getRoot() === oMenu.getRoot()); + + } + + } + else if (nLen === 1) { + + bInMenuAnchor = true; + + } + + } + + } + + + if (bInMenuAnchor && !oItem.cfg.getProperty(_TARGET)) { + + Event.preventDefault(oEvent); + + + if (UA.webkit) { + + oItem.focus(); + + } + else { + + oItem.focusEvent.fire(); + + } + + } + + + if (!oSubmenu && !this.cfg.getProperty(_KEEP_OPEN)) { + + hide.call(this); + + } + + } + + } + +}, + + +/** +* @method _onKeyDown +* @description "keydown" event handler for the menu. +* @protected +* @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. +*/ +_onKeyDown: function (p_sType, p_aArgs) { + + var oEvent = p_aArgs[0], + oItem = p_aArgs[1], + oSubmenu, + oItemCfg, + oParentItem, + oRoot, + oNextItem, + oBody, + nBodyScrollTop, + nBodyOffsetHeight, + aItems, + nItems, + nNextItemOffsetTop, + nScrollTarget, + oParentMenu; + + + if (this._useHideDelay) { + this._cancelHideDelay(); + } + + + /* + This function is called to prevent a bug in Firefox. In Firefox, + moving a DOM element into a stationary mouse pointer will cause the + browser to fire mouse events. This can result in the menu mouse + event handlers being called uncessarily, especially when menus are + moved into a stationary mouse pointer as a result of a + key event handler. + */ + function stopMouseEventHandlers() { + + this._bStopMouseEventHandlers = true; + + Lang.later(10, this, function () { + + this._bStopMouseEventHandlers = false; + + }); + + } + + + if (oItem && !oItem.cfg.getProperty(_DISABLED)) { + + oItemCfg = oItem.cfg; + oParentItem = this.parent; + + switch(oEvent.keyCode) { + + case 38: // Up arrow + case 40: // Down arrow + + oNextItem = (oEvent.keyCode == 38) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if (oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty(_SELECTED, true); + oNextItem.focus(); + + + if (this.cfg.getProperty(_MAX_HEIGHT) > 0) { + + oBody = this.body; + nBodyScrollTop = oBody.scrollTop; + nBodyOffsetHeight = oBody.offsetHeight; + aItems = this.getItems(); + nItems = aItems.length - 1; + nNextItemOffsetTop = oNextItem.element.offsetTop; + + + if (oEvent.keyCode == 40 ) { // Down + + if (nNextItemOffsetTop >= (nBodyOffsetHeight + nBodyScrollTop)) { + + oBody.scrollTop = nNextItemOffsetTop - nBodyOffsetHeight; + + } + else if (nNextItemOffsetTop <= nBodyScrollTop) { + + oBody.scrollTop = 0; + + } + + + if (oNextItem == aItems[nItems]) { + + oBody.scrollTop = oNextItem.element.offsetTop; + + } + + } + else { // Up + + if (nNextItemOffsetTop <= nBodyScrollTop) { + + oBody.scrollTop = nNextItemOffsetTop - oNextItem.element.offsetHeight; + + } + else if (nNextItemOffsetTop >= (nBodyScrollTop + nBodyOffsetHeight)) { + + oBody.scrollTop = nNextItemOffsetTop; + + } + + + if (oNextItem == aItems[0]) { + + oBody.scrollTop = 0; + + } + + } + + + nBodyScrollTop = oBody.scrollTop; + nScrollTarget = oBody.scrollHeight - oBody.offsetHeight; + + if (nBodyScrollTop === 0) { + + this._disableScrollHeader(); + this._enableScrollFooter(); + + } + else if (nBodyScrollTop == nScrollTarget) { + + this._enableScrollHeader(); + this._disableScrollFooter(); + + } + else { + + this._enableScrollHeader(); + this._enableScrollFooter(); + + } + + } + + } + + + Event.preventDefault(oEvent); + + stopMouseEventHandlers(); + + break; + + + case 39: // Right arrow + + oSubmenu = oItemCfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + if (!oItemCfg.getProperty(_SELECTED)) { + + oItemCfg.setProperty(_SELECTED, true); + + } + + oSubmenu.show(); + oSubmenu.setInitialFocus(); + oSubmenu.setInitialSelection(); + + } + else { + + oRoot = this.getRoot(); + + if (oRoot instanceof YAHOO.widget.MenuBar) { + + oNextItem = oRoot.activeItem.getNextEnabledSibling(); + + if (oNextItem) { + + oRoot.clearActiveItem(); + + oNextItem.cfg.setProperty(_SELECTED, true); + + oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.show(); + oSubmenu.setInitialFocus(); + + } + else { + + oNextItem.focus(); + + } + + } + + } + + } + + + Event.preventDefault(oEvent); + + stopMouseEventHandlers(); + + break; + + + case 37: // Left arrow + + if (oParentItem) { + + oParentMenu = oParentItem.parent; + + if (oParentMenu instanceof YAHOO.widget.MenuBar) { + + oNextItem = + oParentMenu.activeItem.getPreviousEnabledSibling(); + + if (oNextItem) { + + oParentMenu.clearActiveItem(); + + oNextItem.cfg.setProperty(_SELECTED, true); + + oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.show(); + oSubmenu.setInitialFocus(); + + } + else { + + oNextItem.focus(); + + } + + } + + } + else { + + this.hide(); + + oParentItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + stopMouseEventHandlers(); + + break; + + } + + + } + + + if (oEvent.keyCode == 27) { // Esc key + + if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + this.hide(); + + if (this.parent) { + + this.parent.focus(); + + } + + } + else if (this.activeItem) { + + oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.blur(); + this.activeItem.cfg.setProperty(_SELECTED, false); + + } + + } + + + Event.preventDefault(oEvent); + + } + +}, + + +/** +* @method _onKeyPress +* @description "keypress" event handler for a Menu instance. +* @protected +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +*/ +_onKeyPress: function (p_sType, p_aArgs) { + + var oEvent = p_aArgs[0]; + + + if (oEvent.keyCode == 40 || oEvent.keyCode == 38) { + + Event.preventDefault(oEvent); + + } + +}, + + +/** +* @method _onBlur +* @description "blur" event handler for a Menu instance. +* @protected +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +*/ +_onBlur: function (p_sType, p_aArgs) { + + if (this._hasFocus) { + this._hasFocus = false; + } + +}, + +/** +* @method _onYChange +* @description "y" event handler for a Menu instance. +* @protected +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +*/ +_onYChange: function (p_sType, p_aArgs) { + + var oParent = this.parent, + nScrollTop, + oIFrame, + nY; + + + if (oParent) { + + nScrollTop = oParent.parent.body.scrollTop; + + + if (nScrollTop > 0) { + + nY = (this.cfg.getProperty(_Y) - nScrollTop); + + Dom.setY(this.element, nY); + + oIFrame = this.iframe; + + + if (oIFrame) { + + Dom.setY(oIFrame, nY); + + } + + this.cfg.setProperty(_Y, nY, true); + + } + + } + +}, + + +/** +* @method _onScrollTargetMouseOver +* @description "mouseover" event handler for the menu's "header" and "footer" +* elements. Used to scroll the body of the menu up and down when the +* menu's "maxheight" configuration property is set to a value greater than 0. +* @protected +* @param {Event} p_oEvent Object representing the DOM event object passed +* back by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onScrollTargetMouseOver: function (p_oEvent, p_oMenu) { + + var oBodyScrollTimer = this._bodyScrollTimer; + + + if (oBodyScrollTimer) { + + oBodyScrollTimer.cancel(); + + } + + + this._cancelHideDelay(); + + + var oTarget = Event.getTarget(p_oEvent), + oBody = this.body, + nScrollIncrement = this.cfg.getProperty(_SCROLL_INCREMENT), + nScrollTarget, + fnScrollFunction; + + + function scrollBodyDown() { + + var nScrollTop = oBody.scrollTop; + + + if (nScrollTop < nScrollTarget) { + + oBody.scrollTop = (nScrollTop + nScrollIncrement); + + this._enableScrollHeader(); + + } + else { + + oBody.scrollTop = nScrollTarget; + + this._bodyScrollTimer.cancel(); + + this._disableScrollFooter(); + + } + + } + + + function scrollBodyUp() { + + var nScrollTop = oBody.scrollTop; + + + if (nScrollTop > 0) { + + oBody.scrollTop = (nScrollTop - nScrollIncrement); + + this._enableScrollFooter(); + + } + else { + + oBody.scrollTop = 0; + + this._bodyScrollTimer.cancel(); + + this._disableScrollHeader(); + + } + + } + + + if (Dom.hasClass(oTarget, _HD)) { + + fnScrollFunction = scrollBodyUp; + + } + else { + + nScrollTarget = oBody.scrollHeight - oBody.offsetHeight; + + fnScrollFunction = scrollBodyDown; + + } + + + this._bodyScrollTimer = Lang.later(10, this, fnScrollFunction, null, true); + +}, + + +/** +* @method _onScrollTargetMouseOut +* @description "mouseout" event handler for the menu's "header" and "footer" +* elements. Used to stop scrolling the body of the menu up and down when the +* menu's "maxheight" configuration property is set to a value greater than 0. +* @protected +* @param {Event} p_oEvent Object representing the DOM event object passed +* back by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onScrollTargetMouseOut: function (p_oEvent, p_oMenu) { + + var oBodyScrollTimer = this._bodyScrollTimer; + + if (oBodyScrollTimer) { + + oBodyScrollTimer.cancel(); + + } + + this._cancelHideDelay(); + +}, + + + +// Private methods + + +/** +* @method _onInit +* @description "init" event handler for the 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. +*/ +_onInit: function (p_sType, p_aArgs) { + + this.cfg.subscribeToConfigEvent(_VISIBLE, this._onVisibleChange); + + var bRootMenu = !this.parent, + bLazyLoad = this.lazyLoad; + + + /* + Automatically initialize a menu's subtree if: + + 1) This is the root menu and lazyload is off + + 2) This is the root menu, lazyload is on, but the menu is + already visible + + 3) This menu is a submenu and lazyload is off + */ + + + + if (((bRootMenu && !bLazyLoad) || + (bRootMenu && (this.cfg.getProperty(_VISIBLE) || + this.cfg.getProperty(_POSITION) == _STATIC)) || + (!bRootMenu && !bLazyLoad)) && this.getItemGroups().length === 0) { + + if (this.srcElement) { + + this._initSubTree(); + + } + + + if (this.itemData) { + + this.addItems(this.itemData); + + } + + } + else if (bLazyLoad) { + + this.cfg.fireQueue(); + + } + +}, + + +/** +* @method _onBeforeRender +* @description "beforerender" event handler for the menu. Appends all of the +* <ul>, <li> and their accompanying +* title elements to the body element of the 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. +*/ +_onBeforeRender: function (p_sType, p_aArgs) { + + var oEl = this.element, + nListElements = this._aListElements.length, + bFirstList = true, + i = 0, + oUL, + oGroupTitle; + + if (nListElements > 0) { + + do { + + oUL = this._aListElements[i]; + + if (oUL) { + + if (bFirstList) { + + Dom.addClass(oUL, _FIRST_OF_TYPE); + bFirstList = false; + + } + + + if (!Dom.isAncestor(oEl, oUL)) { + + this.appendToBody(oUL); + + } + + + oGroupTitle = this._aGroupTitleElements[i]; + + if (oGroupTitle) { + + if (!Dom.isAncestor(oEl, oGroupTitle)) { + + oUL.parentNode.insertBefore(oGroupTitle, oUL); + + } + + + Dom.addClass(oUL, _HAS_TITLE); + + } + + } + + i++; + + } + while (i < nListElements); + + } + +}, + + +/** +* @method _onRender +* @description "render" event handler for the 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. +*/ +_onRender: function (p_sType, p_aArgs) { + + if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + if (!this.cfg.getProperty(_VISIBLE)) { + + this.positionOffScreen(); + + } + + } + +}, + + + + + +/** +* @method _onBeforeShow +* @description "beforeshow" event handler for the 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. +*/ +_onBeforeShow: function (p_sType, p_aArgs) { + + var nOptions, + n, + oSrcElement, + oContainer = this.cfg.getProperty(_CONTAINER); + + + if (this.lazyLoad && this.getItemGroups().length === 0) { + + if (this.srcElement) { + + this._initSubTree(); + + } + + + if (this.itemData) { + + if (this.parent && this.parent.parent && + this.parent.parent.srcElement && + this.parent.parent.srcElement.tagName.toUpperCase() == + _SELECT) { + + nOptions = this.itemData.length; + + for(n=0; n nContextElY) { + nNewY = (nContextElY - nMenuOffsetHeight); + } + else { // The Menu is above the context element, flip it below + nNewY = (nContextElY + nContextElHeight); + } + + oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true); + + return nNewY; + + }; + + + /* + Uses the context element's position to calculate the availble height + above and below it to display its corresponding Menu. + */ + + var getDisplayRegionHeight = function () { + + // The Menu is below the context element + if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { + return (nBottomRegionHeight - nViewportOffset); + } + else { // The Menu is above the context element + return (nTopRegionHeight - nViewportOffset); + } + + }; + + + /* + Sets the Menu's "y" configuration property to the correct value based on its + current orientation. + */ + + var alignY = function () { + + var nNewY; + + if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { + nNewY = (nContextElY + nContextElHeight); + } + else { + nNewY = (nContextElY - oMenuEl.offsetHeight); + } + + oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true); + + }; + + + // Resets the maxheight of the Menu to the value set by the user + + var resetMaxHeight = function () { + + oMenu._setScrollHeight(this.cfg.getProperty(_MAX_HEIGHT)); + + oMenu.hideEvent.unsubscribe(resetMaxHeight); + + }; + + + /* + Trys to place the Menu in the best possible position (either above or + below its corresponding context element). + */ + + var setVerticalPosition = function () { + + var nDisplayRegionHeight = getDisplayRegionHeight(), + bMenuHasItems = (oMenu.getItems().length > 0), + nMenuMinScrollHeight, + fnReturnVal; + + + if (nMenuOffsetHeight > nDisplayRegionHeight) { + + nMenuMinScrollHeight = + bMenuHasItems ? oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) : nMenuOffsetHeight; + + + if ((nDisplayRegionHeight > nMenuMinScrollHeight) && bMenuHasItems) { + nMaxHeight = nDisplayRegionHeight; + } + else { + nMaxHeight = nInitialMaxHeight; + } + + + oMenu._setScrollHeight(nMaxHeight); + oMenu.hideEvent.subscribe(resetMaxHeight); + + + // Re-align the Menu since its height has just changed + // as a result of the setting of the maxheight property. + + alignY(); + + + if (nDisplayRegionHeight < nMenuMinScrollHeight) { + + if (bFlipped) { + + /* + All possible positions and values for the "maxheight" + configuration property have been tried, but none were + successful, so fall back to the original size and position. + */ + + flipVertical(); + + } + else { + + flipVertical(); + + bFlipped = true; + + fnReturnVal = setVerticalPosition(); + + } + + } + + } + else if (nMaxHeight && (nMaxHeight !== nInitialMaxHeight)) { + + oMenu._setScrollHeight(nInitialMaxHeight); + oMenu.hideEvent.subscribe(resetMaxHeight); + + // Re-align the Menu since its height has just changed + // as a result of the setting of the maxheight property. + + alignY(); + + } + + return fnReturnVal; + + }; + + + // Determine if the current value for the Menu's "y" configuration property will + // result in the Menu being positioned outside the boundaries of the viewport + + if (y < topConstraint || y > bottomConstraint) { + + // The current value for the Menu's "y" configuration property WILL + // result in the Menu being positioned outside the boundaries of the viewport + + if (bCanConstrain) { + + if (oMenu.cfg.getProperty(_PREVENT_CONTEXT_OVERLAP) && bPotentialContextOverlap) { + + // SOLUTION #1: + // If the "preventcontextoverlap" configuration property is set to "true", + // try to flip and/or scroll the Menu to both keep it inside the boundaries of the + // viewport AND from overlaping its context element (MenuItem or MenuBarItem). + + oContextEl = aContext[0]; + nContextElHeight = oContextEl.offsetHeight; + nContextElY = (Dom.getY(oContextEl) - scrollY); + + nTopRegionHeight = nContextElY; + nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight)); + + setVerticalPosition(); + + yNew = oMenu.cfg.getProperty(_Y); + + } + else if (!(oMenu instanceof YAHOO.widget.MenuBar) && + nMenuOffsetHeight >= viewPortHeight) { + + // SOLUTION #2: + // If the Menu exceeds the height of the viewport, introduce scroll bars + // to keep the Menu inside the boundaries of the viewport + + nAvailableHeight = (viewPortHeight - (nViewportOffset * 2)); + + if (nAvailableHeight > oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT)) { + + oMenu._setScrollHeight(nAvailableHeight); + oMenu.hideEvent.subscribe(resetMaxHeight); + + alignY(); + + yNew = oMenu.cfg.getProperty(_Y); + + } + + } + else { + + // SOLUTION #3: + + if (y < topConstraint) { + yNew = topConstraint; + } else if (y > bottomConstraint) { + yNew = bottomConstraint; + } + + } + + } + else { + // The "y" configuration property cannot be set to a value that will keep + // entire Menu inside the boundary of the viewport. Therefore, set + // the "y" configuration property to scrollY to keep as much of the + // Menu inside the viewport as possible. + yNew = nViewportOffset + scrollY; + } + + } + + return yNew; + +}, + + +/** +* @method _onHide +* @description "hide" event handler for the 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. +*/ +_onHide: function (p_sType, p_aArgs) { + + if (this.cfg.getProperty(_POSITION) === _DYNAMIC) { + + this.positionOffScreen(); + + } + +}, + + +/** +* @method _onShow +* @description "show" event handler for the 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. +*/ +_onShow: function (p_sType, p_aArgs) { + + var oParent = this.parent, + oParentMenu, + oElement, + nOffsetWidth, + sWidth; + + + function disableAutoSubmenuDisplay(p_oEvent) { + + var oTarget; + + if (p_oEvent.type == _MOUSEDOWN || (p_oEvent.type == _KEYDOWN && p_oEvent.keyCode == 27)) { + + /* + Set the "autosubmenudisplay" to "false" if the user + clicks outside the menu bar. + */ + + oTarget = Event.getTarget(p_oEvent); + + if (oTarget != oParentMenu.element || !Dom.isAncestor(oParentMenu.element, oTarget)) { + + oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false); + + Event.removeListener(document, _MOUSEDOWN, disableAutoSubmenuDisplay); + Event.removeListener(document, _KEYDOWN, disableAutoSubmenuDisplay); + + } + + } + + } + + + function onSubmenuHide(p_sType, p_aArgs, p_sWidth) { + + this.cfg.setProperty(_WIDTH, _EMPTY_STRING); + this.hideEvent.unsubscribe(onSubmenuHide, p_sWidth); + + } + + + if (oParent) { + + oParentMenu = oParent.parent; + + + if (!oParentMenu.cfg.getProperty(_AUTO_SUBMENU_DISPLAY) && + (oParentMenu instanceof YAHOO.widget.MenuBar || + oParentMenu.cfg.getProperty(_POSITION) == _STATIC)) { + + oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, true); + + Event.on(document, _MOUSEDOWN, disableAutoSubmenuDisplay); + Event.on(document, _KEYDOWN, disableAutoSubmenuDisplay); + + } + + + // The following fixes an issue with the selected state of a MenuItem + // not rendering correctly when a submenu is aligned to the left of + // its parent Menu instance. + + if ((this.cfg.getProperty("x") < oParentMenu.cfg.getProperty("x")) && + (UA.gecko && UA.gecko < 1.9) && !this.cfg.getProperty(_WIDTH)) { + + oElement = this.element; + nOffsetWidth = oElement.offsetWidth; + + /* + Measuring the difference of the offsetWidth before and after + setting the "width" style attribute allows us to compute the + about of padding and borders applied to the element, which in + turn allows us to set the "width" property correctly. + */ + + oElement.style.width = nOffsetWidth + _PX; + + sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX; + + this.cfg.setProperty(_WIDTH, sWidth); + + this.hideEvent.subscribe(onSubmenuHide, sWidth); + + } + + } + +}, + + +/** +* @method _onBeforeHide +* @description "beforehide" event handler for the 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. +*/ +_onBeforeHide: function (p_sType, p_aArgs) { + + var oActiveItem = this.activeItem, + oRoot = this.getRoot(), + oConfig, + oSubmenu; + + + if (oActiveItem) { + + oConfig = oActiveItem.cfg; + + oConfig.setProperty(_SELECTED, false); + + oSubmenu = oConfig.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.hide(); + + } + + } + + + /* + Focus can get lost in IE when the mouse is moving from a submenu back to its parent Menu. + For this reason, it is necessary to maintain the focused state in a private property + so that the _onMouseOver event handler is able to determined whether or not to set focus + to MenuItems as the user is moving the mouse. + */ + + if (UA.ie && this.cfg.getProperty(_POSITION) === _DYNAMIC && this.parent) { + + oRoot._hasFocus = this.hasFocus(); + + } + + + if (oRoot == this) { + + oRoot.blur(); + + } + +}, + + +/** +* @method _onParentMenuConfigChange +* @description "configchange" event handler for a submenu. +* @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_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onParentMenuConfigChange: function (p_sType, p_aArgs, p_oSubmenu) { + + var sPropertyName = p_aArgs[0][0], + oPropertyValue = p_aArgs[0][1]; + + switch(sPropertyName) { + + case _IFRAME: + case _CONSTRAIN_TO_VIEWPORT: + case _HIDE_DELAY: + case _SHOW_DELAY: + case _SUBMENU_HIDE_DELAY: + case _CLICK_TO_HIDE: + case _EFFECT: + case _CLASSNAME: + case _SCROLL_INCREMENT: + case _MAX_HEIGHT: + case _MIN_SCROLL_HEIGHT: + case _MONITOR_RESIZE: + case _SHADOW: + case _PREVENT_CONTEXT_OVERLAP: + + p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue); + + break; + + case _SUBMENU_ALIGNMENT: + + if (!(this.parent.parent instanceof YAHOO.widget.MenuBar)) { + + p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue); + + } + + break; + + } + +}, + + +/** +* @method _onParentMenuRender +* @description "render" event handler for a submenu. Renders a +* submenu in response to the firing of its parent's "render" event. +* @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_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onParentMenuRender: function (p_sType, p_aArgs, p_oSubmenu) { + + var oParentMenu = p_oSubmenu.parent.parent, + oParentCfg = oParentMenu.cfg, + + oConfig = { + + constraintoviewport: oParentCfg.getProperty(_CONSTRAIN_TO_VIEWPORT), + + xy: [0,0], + + clicktohide: oParentCfg.getProperty(_CLICK_TO_HIDE), + + effect: oParentCfg.getProperty(_EFFECT), + + showdelay: oParentCfg.getProperty(_SHOW_DELAY), + + hidedelay: oParentCfg.getProperty(_HIDE_DELAY), + + submenuhidedelay: oParentCfg.getProperty(_SUBMENU_HIDE_DELAY), + + classname: oParentCfg.getProperty(_CLASSNAME), + + scrollincrement: oParentCfg.getProperty(_SCROLL_INCREMENT), + + maxheight: oParentCfg.getProperty(_MAX_HEIGHT), + + minscrollheight: oParentCfg.getProperty(_MIN_SCROLL_HEIGHT), + + iframe: oParentCfg.getProperty(_IFRAME), + + shadow: oParentCfg.getProperty(_SHADOW), + + preventcontextoverlap: oParentCfg.getProperty(_PREVENT_CONTEXT_OVERLAP), + + monitorresize: oParentCfg.getProperty(_MONITOR_RESIZE) + + }, + + oLI; + + + + if (!(oParentMenu instanceof YAHOO.widget.MenuBar)) { + + oConfig[_SUBMENU_ALIGNMENT] = oParentCfg.getProperty(_SUBMENU_ALIGNMENT); + + } + + + p_oSubmenu.cfg.applyConfig(oConfig); + + + if (!this.lazyLoad) { + + oLI = this.parent.element; + + if (this.element.parentNode == oLI) { + + this.render(); + + } + else { + + this.render(oLI); + + } + + } + +}, + + +/** +* @method _onMenuItemDestroy +* @description "destroy" event handler for the menu's items. +* @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. +*/ +_onMenuItemDestroy: function (p_sType, p_aArgs, p_oItem) { + + this._removeItemFromGroupByValue(p_oItem.groupIndex, p_oItem); + +}, + + +/** +* @method _onMenuItemConfigChange +* @description "configchange" event handler for the menu's items. +* @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. +*/ +_onMenuItemConfigChange: function (p_sType, p_aArgs, p_oItem) { + + var sPropertyName = p_aArgs[0][0], + oPropertyValue = p_aArgs[0][1], + oSubmenu; + + + switch(sPropertyName) { + + case _SELECTED: + + if (oPropertyValue === true) { + + this.activeItem = p_oItem; + + } + + break; + + case _SUBMENU: + + oSubmenu = p_aArgs[0][1]; + + if (oSubmenu) { + + this._configureSubmenu(p_oItem); + + } + + break; + + } + +}, + + + +// Public event handlers for configuration properties + + +/** +* @method configVisible +* @description Event handler for when the "visible" configuration property +* the menu changes. +* @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. +*/ +configVisible: function (p_sType, p_aArgs, p_oMenu) { + + var bVisible, + sDisplay; + + if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + Menu.superclass.configVisible.call(this, p_sType, p_aArgs, p_oMenu); + + } + else { + + bVisible = p_aArgs[0]; + sDisplay = Dom.getStyle(this.element, _DISPLAY); + + Dom.setStyle(this.element, _VISIBILITY, _VISIBLE); + + if (bVisible) { + + if (sDisplay != _BLOCK) { + this.beforeShowEvent.fire(); + Dom.setStyle(this.element, _DISPLAY, _BLOCK); + this.showEvent.fire(); + } + + } + else { + + if (sDisplay == _BLOCK) { + this.beforeHideEvent.fire(); + Dom.setStyle(this.element, _DISPLAY, _NONE); + this.hideEvent.fire(); + } + + } + + } + +}, + + +/** +* @method configPosition +* @description Event handler for when the "position" configuration property +* of the menu changes. +* @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. +*/ +configPosition: function (p_sType, p_aArgs, p_oMenu) { + + var oElement = this.element, + sCSSPosition = p_aArgs[0] == _STATIC ? _STATIC : _ABSOLUTE, + oCfg = this.cfg, + nZIndex; + + + Dom.setStyle(oElement, _POSITION, sCSSPosition); + + + if (sCSSPosition == _STATIC) { + + // Statically positioned menus are visible by default + + Dom.setStyle(oElement, _DISPLAY, _BLOCK); + + oCfg.setProperty(_VISIBLE, true); + + } + else { + + /* + Even though the "visible" property is queued to + "false" by default, we need to set the "visibility" property to + "hidden" since Overlay's "configVisible" implementation checks the + element's "visibility" style property before deciding whether + or not to show an Overlay instance. + */ + + Dom.setStyle(oElement, _VISIBILITY, _HIDDEN); + + } + + + if (sCSSPosition == _ABSOLUTE) { + + nZIndex = oCfg.getProperty(_ZINDEX); + + if (!nZIndex || nZIndex === 0) { + + oCfg.setProperty(_ZINDEX, 1); + + } + + } + +}, + + +/** +* @method configIframe +* @description Event handler for when the "iframe" configuration property of +* the menu changes. +* @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. +*/ +configIframe: function (p_sType, p_aArgs, p_oMenu) { + + if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + Menu.superclass.configIframe.call(this, p_sType, p_aArgs, p_oMenu); + + } + +}, + + +/** +* @method configHideDelay +* @description Event handler for when the "hidedelay" configuration property +* of the menu changes. +* @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. +*/ +configHideDelay: function (p_sType, p_aArgs, p_oMenu) { + + var nHideDelay = p_aArgs[0]; + + this._useHideDelay = (nHideDelay > 0); + +}, + + +/** +* @method configContainer +* @description Event handler for when the "container" configuration property +* of the menu changes. +* @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. +*/ +configContainer: function (p_sType, p_aArgs, p_oMenu) { + + var oElement = p_aArgs[0]; + + if (Lang.isString(oElement)) { + + this.cfg.setProperty(_CONTAINER, Dom.get(oElement), true); + + } + +}, + + +/** +* @method _clearSetWidthFlag +* @description Change event listener for the "width" configuration property. This listener is +* added when a Menu's "width" configuration property is set by the "_setScrollHeight" method, and +* is used to set the "_widthSetForScroll" property to "false" if the "width" configuration property +* is changed after it was set by the "_setScrollHeight" method. If the "_widthSetForScroll" +* property is set to "false", and the "_setScrollHeight" method is in the process of tearing down +* scrolling functionality, it will maintain the Menu's new width rather than reseting it. +* @private +*/ +_clearSetWidthFlag: function () { + + this._widthSetForScroll = false; + + this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); + +}, + + +/** +* @method _setScrollHeight +* @description +* @param {String} p_nScrollHeight Number representing the scrolling height of the Menu. +* @private +*/ +_setScrollHeight: function (p_nScrollHeight) { + + var nScrollHeight = p_nScrollHeight, + bRefireIFrameAndShadow = false, + bSetWidth = false, + oElement, + oBody, + oHeader, + oFooter, + fnMouseOver, + fnMouseOut, + nMinScrollHeight, + nHeight, + nOffsetWidth, + sWidth; + + + if (this.getItems().length > 0) { + + oElement = this.element; + oBody = this.body; + oHeader = this.header; + oFooter = this.footer; + fnMouseOver = this._onScrollTargetMouseOver; + fnMouseOut = this._onScrollTargetMouseOut; + nMinScrollHeight = this.cfg.getProperty(_MIN_SCROLL_HEIGHT); + + + if (nScrollHeight > 0 && nScrollHeight < nMinScrollHeight) { + + nScrollHeight = nMinScrollHeight; + + } + + + Dom.setStyle(oBody, _HEIGHT, _EMPTY_STRING); + Dom.removeClass(oBody, _YUI_MENU_BODY_SCROLLED); + oBody.scrollTop = 0; + + + // Need to set a width for the Menu to fix the following problems in + // Firefox 2 and IE: + + // #1) Scrolled Menus will render at 1px wide in Firefox 2 + + // #2) There is a bug in gecko-based browsers where an element whose + // "position" property is set to "absolute" and "overflow" property is + // set to "hidden" will not render at the correct width when its + // offsetParent's "position" property is also set to "absolute." It is + // possible to work around this bug by specifying a value for the width + // property in addition to overflow. + + // #3) In IE it is necessary to give the Menu a width before the + // scrollbars are rendered to prevent the Menu from rendering with a + // width that is 100% of the browser viewport. + + bSetWidth = ((UA.gecko && UA.gecko < 1.9) || UA.ie); + + if (nScrollHeight > 0 && bSetWidth && !this.cfg.getProperty(_WIDTH)) { + + nOffsetWidth = oElement.offsetWidth; + + /* + Measuring the difference of the offsetWidth before and after + setting the "width" style attribute allows us to compute the + about of padding and borders applied to the element, which in + turn allows us to set the "width" property correctly. + */ + + oElement.style.width = nOffsetWidth + _PX; + + sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX; + + + this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); + + + this.cfg.setProperty(_WIDTH, sWidth); + + + /* + Set a flag (_widthSetForScroll) to maintain some history regarding how the + "width" configuration property was set. If the "width" configuration property + is set by something other than the "_setScrollHeight" method, it will be + necessary to maintain that new value and not clear the width if scrolling + is turned off. + */ + + this._widthSetForScroll = true; + + this.cfg.subscribeToConfigEvent(_WIDTH, this._clearSetWidthFlag); + + } + + + if (nScrollHeight > 0 && (!oHeader && !oFooter)) { + + + this.setHeader(_NON_BREAKING_SPACE); + this.setFooter(_NON_BREAKING_SPACE); + + oHeader = this.header; + oFooter = this.footer; + + Dom.addClass(oHeader, _TOP_SCROLLBAR); + Dom.addClass(oFooter, _BOTTOM_SCROLLBAR); + + oElement.insertBefore(oHeader, oBody); + oElement.appendChild(oFooter); + + } + + + nHeight = nScrollHeight; + + + if (oHeader && oFooter) { + nHeight = (nHeight - (oHeader.offsetHeight + oFooter.offsetHeight)); + } + + + if ((nHeight > 0) && (oBody.offsetHeight > nScrollHeight)) { + + + Dom.addClass(oBody, _YUI_MENU_BODY_SCROLLED); + Dom.setStyle(oBody, _HEIGHT, (nHeight + _PX)); + + if (!this._hasScrollEventHandlers) { + + Event.on(oHeader, _MOUSEOVER, fnMouseOver, this, true); + Event.on(oHeader, _MOUSEOUT, fnMouseOut, this, true); + Event.on(oFooter, _MOUSEOVER, fnMouseOver, this, true); + Event.on(oFooter, _MOUSEOUT, fnMouseOut, this, true); + + this._hasScrollEventHandlers = true; + + } + + this._disableScrollHeader(); + this._enableScrollFooter(); + + bRefireIFrameAndShadow = true; + + } + else if (oHeader && oFooter) { + + + + /* + Only clear the the "width" configuration property if it was set the + "_setScrollHeight" method and wasn't changed by some other means after it was set. + */ + + if (this._widthSetForScroll) { + + + this._widthSetForScroll = false; + + this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag); + + this.cfg.setProperty(_WIDTH, _EMPTY_STRING); + + } + + + this._enableScrollHeader(); + this._enableScrollFooter(); + + if (this._hasScrollEventHandlers) { + + Event.removeListener(oHeader, _MOUSEOVER, fnMouseOver); + Event.removeListener(oHeader, _MOUSEOUT, fnMouseOut); + Event.removeListener(oFooter, _MOUSEOVER, fnMouseOver); + Event.removeListener(oFooter, _MOUSEOUT, fnMouseOut); + + this._hasScrollEventHandlers = false; + + } + + oElement.removeChild(oHeader); + oElement.removeChild(oFooter); + + this.header = null; + this.footer = null; + + bRefireIFrameAndShadow = true; + + } + + + if (bRefireIFrameAndShadow) { + + this.cfg.refireEvent(_IFRAME); + this.cfg.refireEvent(_SHADOW); + + } + + } + +}, + + +/** +* @method _setMaxHeight +* @description "renderEvent" handler used to defer the setting of the +* "maxheight" configuration property until the menu is rendered in lazy +* load scenarios. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +* @param {Number} p_nMaxHeight Number representing the value to set for the +* "maxheight" configuration property. +* @private +*/ +_setMaxHeight: function (p_sType, p_aArgs, p_nMaxHeight) { + + this._setScrollHeight(p_nMaxHeight); + this.renderEvent.unsubscribe(this._setMaxHeight); + +}, + + +/** +* @method configMaxHeight +* @description Event handler for when the "maxheight" configuration property of +* a Menu changes. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired +* the event. +*/ +configMaxHeight: function (p_sType, p_aArgs, p_oMenu) { + + var nMaxHeight = p_aArgs[0]; + + if (this.lazyLoad && !this.body && nMaxHeight > 0) { + + this.renderEvent.subscribe(this._setMaxHeight, nMaxHeight, this); + + } + else { + + this._setScrollHeight(nMaxHeight); + + } + +}, + + +/** +* @method configClassName +* @description Event handler for when the "classname" configuration property of +* a menu changes. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. +*/ +configClassName: function (p_sType, p_aArgs, p_oMenu) { + + var sClassName = p_aArgs[0]; + + if (this._sClassName) { + + Dom.removeClass(this.element, this._sClassName); + + } + + Dom.addClass(this.element, sClassName); + this._sClassName = sClassName; + +}, + + +/** +* @method _onItemAdded +* @description "itemadded" event handler for a Menu instance. +* @private +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event +* was fired. +*/ +_onItemAdded: function (p_sType, p_aArgs) { + + var oItem = p_aArgs[0]; + + if (oItem) { + + oItem.cfg.setProperty(_DISABLED, true); + + } + +}, + + +/** +* @method configDisabled +* @description Event handler for when the "disabled" configuration property of +* a menu changes. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. +*/ +configDisabled: function (p_sType, p_aArgs, p_oMenu) { + + var bDisabled = p_aArgs[0], + aItems = this.getItems(), + nItems, + i; + + if (Lang.isArray(aItems)) { + + nItems = aItems.length; + + if (nItems > 0) { + + i = nItems - 1; + + do { + + aItems[i].cfg.setProperty(_DISABLED, bDisabled); + + } + while (i--); + + } + + + if (bDisabled) { + + this.clearActiveItem(true); + + Dom.addClass(this.element, _DISABLED); + + this.itemAddedEvent.subscribe(this._onItemAdded); + + } + else { + + Dom.removeClass(this.element, _DISABLED); + + this.itemAddedEvent.unsubscribe(this._onItemAdded); + + } + + } + +}, + + +/** +* @method configShadow +* @description Event handler for when the "shadow" configuration property of +* a menu changes. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event. +*/ +configShadow: function (p_sType, p_aArgs, p_oMenu) { + + var sizeShadow = function () { + + var oElement = this.element, + oShadow = this._shadow; + + if (oShadow && oElement) { + + // Clear the previous width + + if (oShadow.style.width && oShadow.style.height) { + + oShadow.style.width = _EMPTY_STRING; + oShadow.style.height = _EMPTY_STRING; + + } + + oShadow.style.width = (oElement.offsetWidth + 6) + _PX; + oShadow.style.height = (oElement.offsetHeight + 1) + _PX; + + } + + }; + + + var replaceShadow = function () { + + this.element.appendChild(this._shadow); + + }; + + + var addShadowVisibleClass = function () { + + Dom.addClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE); + + }; + + + var removeShadowVisibleClass = function () { + + Dom.removeClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE); + + }; + + + var createShadow = function () { + + var oShadow = this._shadow, + oElement; + + if (!oShadow) { + + oElement = this.element; + + + if (!m_oShadowTemplate) { + + m_oShadowTemplate = document.createElement(_DIV_LOWERCASE); + m_oShadowTemplate.className = _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE; + + } + + oShadow = m_oShadowTemplate.cloneNode(false); + + oElement.appendChild(oShadow); + + this._shadow = oShadow; + + this.beforeShowEvent.subscribe(addShadowVisibleClass); + this.beforeHideEvent.subscribe(removeShadowVisibleClass); + + + if (UA.ie) { + + /* + Need to call sizeShadow & syncIframe via setTimeout for + IE 7 Quirks Mode and IE 6 Standards Mode and Quirks Mode + or the shadow and iframe shim will not be sized and + positioned properly. + */ + + Lang.later(0, this, function () { + + sizeShadow.call(this); + this.syncIframe(); + + }); + + + this.cfg.subscribeToConfigEvent(_WIDTH, sizeShadow); + this.cfg.subscribeToConfigEvent(_HEIGHT, sizeShadow); + this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, sizeShadow); + this.changeContentEvent.subscribe(sizeShadow); + + Module.textResizeEvent.subscribe(sizeShadow, this, true); + + this.destroyEvent.subscribe(function () { + + Module.textResizeEvent.unsubscribe(sizeShadow, this); + + }); + + } + + this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, replaceShadow); + + } + + }; + + + var onBeforeShow = function () { + + if (this._shadow) { + + // If called because the "shadow" event was refired - just append again and resize + + replaceShadow.call(this); + + if (UA.ie) { + sizeShadow.call(this); + } + + } + else { + + createShadow.call(this); + + } + + this.beforeShowEvent.unsubscribe(onBeforeShow); + + }; + + + var bShadow = p_aArgs[0]; + + + if (bShadow && this.cfg.getProperty(_POSITION) == _DYNAMIC) { + + if (this.cfg.getProperty(_VISIBLE)) { + + if (this._shadow) { + + // If the "shadow" event was refired - just append again and resize + + replaceShadow.call(this); + + if (UA.ie) { + sizeShadow.call(this); + } + + } + else { + createShadow.call(this); + } + + } + else { + + this.beforeShowEvent.subscribe(onBeforeShow); + + } + + } + +}, + + + +// Public methods + + +/** +* @method initEvents +* @description Initializes the custom events for the menu. +*/ +initEvents: function () { + + Menu.superclass.initEvents.call(this); + + // Create custom events + + var i = EVENT_TYPES.length - 1, + aEventData, + oCustomEvent; + + + do { + + aEventData = EVENT_TYPES[i]; + + oCustomEvent = this.createEvent(aEventData[1]); + oCustomEvent.signature = CustomEvent.LIST; + + this[aEventData[0]] = oCustomEvent; + + } + while (i--); + +}, + + +/** +* @method positionOffScreen +* @description Positions the menu outside of the boundaries of the browser's +* viewport. Called automatically when a menu is hidden to ensure that +* it doesn't force the browser to render uncessary scrollbars. +*/ +positionOffScreen: function () { + + var oIFrame = this.iframe, + oElement = this.element, + sPos = this.OFF_SCREEN_POSITION; + + oElement.style.top = _EMPTY_STRING; + oElement.style.left = _EMPTY_STRING; + + if (oIFrame) { + + oIFrame.style.top = sPos; + oIFrame.style.left = sPos; + + } + +}, + + +/** +* @method getRoot +* @description Finds the menu's root menu. +*/ +getRoot: function () { + + var oItem = this.parent, + oParentMenu, + returnVal; + + if (oItem) { + + oParentMenu = oItem.parent; + + returnVal = oParentMenu ? oParentMenu.getRoot() : this; + + } + else { + + returnVal = this; + + } + + return returnVal; + +}, + + +/** +* @method toString +* @description Returns a string representing the menu. +* @return {String} +*/ +toString: function () { + + var sReturnVal = _MENU, + sId = this.id; + + if (sId) { + + sReturnVal += (_SPACE + sId); + + } + + return sReturnVal; + +}, + + +/** +* @method setItemGroupTitle +* @description Sets the title of a group of menu items. +* @param {String} p_sGroupTitle String specifying the title of the group. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to which +* the title belongs. +*/ +setItemGroupTitle: function (p_sGroupTitle, p_nGroupIndex) { + + var nGroupIndex, + oTitle, + i, + nFirstIndex; + + if (Lang.isString(p_sGroupTitle) && p_sGroupTitle.length > 0) { + + nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0; + oTitle = this._aGroupTitleElements[nGroupIndex]; + + + if (oTitle) { + + oTitle.innerHTML = p_sGroupTitle; + + } + else { + + oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME); + + oTitle.innerHTML = p_sGroupTitle; + + this._aGroupTitleElements[nGroupIndex] = oTitle; + + } + + + i = this._aGroupTitleElements.length - 1; + + do { + + if (this._aGroupTitleElements[i]) { + + Dom.removeClass(this._aGroupTitleElements[i], _FIRST_OF_TYPE); + + nFirstIndex = i; + + } + + } + while (i--); + + + if (nFirstIndex !== null) { + + Dom.addClass(this._aGroupTitleElements[nFirstIndex], + _FIRST_OF_TYPE); + + } + + this.changeContentEvent.fire(); + + } + +}, + + + +/** +* @method addItem +* @description Appends an item to the menu. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be added to the menu. +* @param {String} p_oItem String specifying the text of the item to be added +* to the menu. +* @param {Object} p_oItem Object literal containing a set of menu item +* configuration properties. +* @param {Number} p_nGroupIndex Optional. Number indicating the group to +* which the item belongs. +* @return {YAHOO.widget.MenuItem} +*/ +addItem: function (p_oItem, p_nGroupIndex) { + + return this._addItemToGroup(p_nGroupIndex, p_oItem); + +}, + + +/** +* @method addItems +* @description Adds an array of items to the menu. +* @param {Array} p_aItems Array of items to be added to the menu. The array +* can contain strings specifying the text for each item to be created, object +* literals specifying each of the menu item configuration properties, +* or MenuItem instances. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to +* which the items belongs. +* @return {Array} +*/ +addItems: function (p_aItems, p_nGroupIndex) { + + var nItems, + aItems, + oItem, + i, + returnVal; + + + if (Lang.isArray(p_aItems)) { + + nItems = p_aItems.length; + aItems = []; + + for(i=0; i 0) { + + aSubmenus = []; + + for(i=0; i 0) { + + i = nItems - 1; + + do { + + oItem = aItems[i]; + + if (oItem) { + + oSubmenu = oItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + this.cfg.configChangedEvent.unsubscribe( + this._onParentMenuConfigChange, oSubmenu); + + this.renderEvent.unsubscribe(this._onParentMenuRender, + oSubmenu); + + } + + this.removeItem(oItem, oItem.groupIndex); + + } + + } + while (i--); + + } + + + if (oHeader) { + + Event.purgeElement(oHeader); + oElement.removeChild(oHeader); + + } + + + if (oFooter) { + + Event.purgeElement(oFooter); + oElement.removeChild(oFooter); + } + + + if (oBody) { + + Event.purgeElement(oBody); + + oBody.innerHTML = _EMPTY_STRING; + + } + + this.activeItem = null; + + this._aItemGroups = []; + this._aListElements = []; + this._aGroupTitleElements = []; + + this.cfg.setProperty(_WIDTH, null); + +}, + + +/** +* @method destroy +* @description Removes the menu's <div> element +* (and accompanying child nodes) from the document. +*/ +destroy: function () { + + // Remove all items + + this.clearContent(); + + this._aItemGroups = null; + this._aListElements = null; + this._aGroupTitleElements = null; + + + // Continue with the superclass implementation of this method + + Menu.superclass.destroy.call(this); + + +}, + + +/** +* @method setInitialFocus +* @description Sets focus to the menu's first enabled item. +*/ +setInitialFocus: function () { + + var oItem = this._getFirstEnabledItem(); + + if (oItem) { + + oItem.focus(); + + } + +}, + + +/** +* @method setInitialSelection +* @description Sets the "selected" configuration property of the menu's first +* enabled item to "true." +*/ +setInitialSelection: function () { + + var oItem = this._getFirstEnabledItem(); + + if (oItem) { + + oItem.cfg.setProperty(_SELECTED, true); + } + +}, + + +/** +* @method clearActiveItem +* @description Sets the "selected" configuration property of the menu's active +* item to "false" and hides the item's submenu. +* @param {Boolean} p_bBlur Boolean indicating if the menu's active item +* should be blurred. +*/ +clearActiveItem: function (p_bBlur) { + + if (this.cfg.getProperty(_SHOW_DELAY) > 0) { + + this._cancelShowDelay(); + + } + + + var oActiveItem = this.activeItem, + oConfig, + oSubmenu; + + if (oActiveItem) { + + oConfig = oActiveItem.cfg; + + if (p_bBlur) { + + oActiveItem.blur(); + + this.getRoot()._hasFocus = true; + + } + + oConfig.setProperty(_SELECTED, false); + + oSubmenu = oConfig.getProperty(_SUBMENU); + + + if (oSubmenu) { + + oSubmenu.hide(); + + } + + this.activeItem = null; + + } + +}, + + +/** +* @method focus +* @description Causes the menu to receive focus and fires the "focus" event. +*/ +focus: function () { + + if (!this.hasFocus()) { + + this.setInitialFocus(); + + } + +}, + + +/** +* @method blur +* @description Causes the menu to lose focus and fires the "blur" event. +*/ +blur: function () { + + var oItem; + + if (this.hasFocus()) { + + oItem = MenuManager.getFocusedMenuItem(); + + if (oItem) { + + oItem.blur(); + + } + + } + +}, + + +/** +* @method hasFocus +* @description Returns a boolean indicating whether or not the menu has focus. +* @return {Boolean} +*/ +hasFocus: function () { + + return (MenuManager.getFocusedMenu() == this.getRoot()); + +}, + + +/** +* Adds the specified CustomEvent subscriber to the menu and each of +* its submenus. +* @method subscribe +* @param p_type {string} the type, or name of the event +* @param p_fn {function} the function to exectute when the event fires +* @param p_obj {Object} An object to be passed along when the event +* fires +* @param p_override {boolean} If true, the obj passed in becomes the +* execution scope of the listener +*/ +subscribe: function () { + + function onItemAdded(p_sType, p_aArgs, p_oObject) { + + var oItem = p_aArgs[0], + oSubmenu = oItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.subscribe.apply(oSubmenu, p_oObject); + + } + + } + + + function onSubmenuAdded(p_sType, p_aArgs, p_oObject) { + + var oSubmenu = this.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.subscribe.apply(oSubmenu, p_oObject); + + } + + } + + + Menu.superclass.subscribe.apply(this, arguments); + Menu.superclass.subscribe.call(this, _ITEM_ADDED, onItemAdded, arguments); + + + var aItems = this.getItems(), + nItems, + oItem, + oSubmenu, + i; + + + if (aItems) { + + nItems = aItems.length; + + if (nItems > 0) { + + i = nItems - 1; + + do { + + oItem = aItems[i]; + + oSubmenu = oItem.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.subscribe.apply(oSubmenu, arguments); + + } + else { + + oItem.cfg.subscribeToConfigEvent(_SUBMENU, onSubmenuAdded, arguments); + + } + + } + while (i--); + + } + + } + +}, + + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function () { + + Menu.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + + // Module documentation overrides + + /** + * @config effect + * @description Object or array of objects representing the ContainerEffect + * classes that are active for animating the container. When set this + * property is automatically applied to all submenus. + * @type Object + * @default null + */ + + // Overlay documentation overrides + + + /** + * @config x + * @description Number representing the absolute x-coordinate position of + * the Menu. This property is only applied when the "position" + * configuration property is set to dynamic. + * @type Number + * @default null + */ + + + /** + * @config y + * @description Number representing the absolute y-coordinate position of + * the Menu. This property is only applied when the "position" + * configuration property is set to dynamic. + * @type Number + * @default null + */ + + + /** + * @description Array of the absolute x and y positions of the Menu. This + * property is only applied when the "position" configuration property is + * set to dynamic. + * @config xy + * @type Number[] + * @default null + */ + + + /** + * @config context + * @description Array of context arguments for context-sensitive positioning. + * The format is: [id or element, element corner, context corner]. + * For example, setting this property to ["img1", "tl", "bl"] would + * align the Mnu's top left corner to the context element's + * bottom left corner. This property is only applied when the "position" + * configuration property is set to dynamic. + * @type Array + * @default null + */ + + + /** + * @config fixedcenter + * @description Boolean indicating if the Menu should be anchored to the + * center of the viewport. This property is only applied when the + * "position" configuration property is set to dynamic. + * @type Boolean + * @default false + */ + + + /** + * @config iframe + * @description Boolean indicating whether or not the Menu should + * have an IFRAME shim; used to prevent SELECT elements from + * poking through an Overlay instance in IE6. When set to "true", + * the iframe shim is created when the Menu instance is intially + * made visible. This property is only applied when the "position" + * configuration property is set to dynamic and is automatically applied + * to all submenus. + * @type Boolean + * @default true for IE6 and below, false for all other browsers. + */ + + + // Add configuration attributes + + /* + Change the default value for the "visible" configuration + property to "false" by re-adding the property. + */ + + /** + * @config visible + * @description Boolean indicating whether or not the menu is visible. If + * the menu's "position" configuration property is set to "dynamic" (the + * default), this property toggles the menu's <div> + * element's "visibility" style property between "visible" (true) or + * "hidden" (false). If the menu's "position" configuration property is + * set to "static" this property toggles the menu's + * <div> element's "display" style property + * between "block" (true) or "none" (false). + * @default false + * @type Boolean + */ + oConfig.addProperty( + VISIBLE_CONFIG.key, + { + handler: this.configVisible, + value: VISIBLE_CONFIG.value, + validator: VISIBLE_CONFIG.validator + } + ); + + + /* + Change the default value for the "constraintoviewport" configuration + property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property. + */ + + /** + * @config constraintoviewport + * @description Boolean indicating if the menu will try to remain inside + * the boundaries of the size of viewport. This property is only applied + * when the "position" configuration property is set to dynamic and is + * automatically applied to all submenus. + * @default true + * @type Boolean + */ + oConfig.addProperty( + CONSTRAIN_TO_VIEWPORT_CONFIG.key, + { + handler: this.configConstrainToViewport, + value: CONSTRAIN_TO_VIEWPORT_CONFIG.value, + validator: CONSTRAIN_TO_VIEWPORT_CONFIG.validator, + supercedes: CONSTRAIN_TO_VIEWPORT_CONFIG.supercedes + } + ); + + + /* + Change the default value for the "preventcontextoverlap" configuration + property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property. + */ + + /** + * @config preventcontextoverlap + * @description Boolean indicating whether or not a submenu should overlap its parent MenuItem + * when the "constraintoviewport" configuration property is set to "true". + * @type Boolean + * @default true + */ + oConfig.addProperty(PREVENT_CONTEXT_OVERLAP_CONFIG.key, { + + value: PREVENT_CONTEXT_OVERLAP_CONFIG.value, + validator: PREVENT_CONTEXT_OVERLAP_CONFIG.validator, + supercedes: PREVENT_CONTEXT_OVERLAP_CONFIG.supercedes + + }); + + + /** + * @config position + * @description String indicating how a menu should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menus are + * visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menus are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and + * can overlay other elements on the screen. + * @default dynamic + * @type String + */ + oConfig.addProperty( + POSITION_CONFIG.key, + { + handler: this.configPosition, + value: POSITION_CONFIG.value, + validator: POSITION_CONFIG.validator, + supercedes: POSITION_CONFIG.supercedes + } + ); + + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu item. The format is: [itemCorner, submenuCorner]. By default + * a submenu's top left corner is aligned to its parent menu item's top + * right corner. + * @default ["tl","tr"] + * @type Array + */ + oConfig.addProperty( + SUBMENU_ALIGNMENT_CONFIG.key, + { + value: SUBMENU_ALIGNMENT_CONFIG.value, + suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent + } + ); + + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu's items. + * @default true + * @type Boolean + */ + oConfig.addProperty( + AUTO_SUBMENU_DISPLAY_CONFIG.key, + { + value: AUTO_SUBMENU_DISPLAY_CONFIG.value, + validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator, + suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent + } + ); + + + /** + * @config showdelay + * @description Number indicating the time (in milliseconds) that should + * expire before a submenu is made visible when the user mouses over + * the menu's items. This property is only applied when the "position" + * configuration property is set to dynamic and is automatically applied + * to all submenus. + * @default 250 + * @type Number + */ + oConfig.addProperty( + SHOW_DELAY_CONFIG.key, + { + value: SHOW_DELAY_CONFIG.value, + validator: SHOW_DELAY_CONFIG.validator, + suppressEvent: SHOW_DELAY_CONFIG.suppressEvent + } + ); + + + /** + * @config hidedelay + * @description Number indicating the time (in milliseconds) that should + * expire before the menu is hidden. This property is only applied when + * the "position" configuration property is set to dynamic and is + * automatically applied to all submenus. + * @default 0 + * @type Number + */ + oConfig.addProperty( + HIDE_DELAY_CONFIG.key, + { + handler: this.configHideDelay, + value: HIDE_DELAY_CONFIG.value, + validator: HIDE_DELAY_CONFIG.validator, + suppressEvent: HIDE_DELAY_CONFIG.suppressEvent + } + ); + + + /** + * @config submenuhidedelay + * @description Number indicating the time (in milliseconds) that should + * expire before a submenu is hidden when the user mouses out of a menu item + * heading in the direction of a submenu. The value must be greater than or + * equal to the value specified for the "showdelay" configuration property. + * This property is only applied when the "position" configuration property + * is set to dynamic and is automatically applied to all submenus. + * @default 250 + * @type Number + */ + oConfig.addProperty( + SUBMENU_HIDE_DELAY_CONFIG.key, + { + value: SUBMENU_HIDE_DELAY_CONFIG.value, + validator: SUBMENU_HIDE_DELAY_CONFIG.validator, + suppressEvent: SUBMENU_HIDE_DELAY_CONFIG.suppressEvent + } + ); + + + /** + * @config clicktohide + * @description Boolean indicating if the menu will automatically be + * hidden if the user clicks outside of it. This property is only + * applied when the "position" configuration property is set to dynamic + * and is automatically applied to all submenus. + * @default true + * @type Boolean + */ + oConfig.addProperty( + CLICK_TO_HIDE_CONFIG.key, + { + value: CLICK_TO_HIDE_CONFIG.value, + validator: CLICK_TO_HIDE_CONFIG.validator, + suppressEvent: CLICK_TO_HIDE_CONFIG.suppressEvent + } + ); + + + /** + * @config container + * @description HTML element reference or string specifying the id + * attribute of the HTML element that the menu's markup should be + * rendered into. + * @type HTMLElement|String + * @default document.body + */ + oConfig.addProperty( + CONTAINER_CONFIG.key, + { + handler: this.configContainer, + value: document.body, + suppressEvent: CONTAINER_CONFIG.suppressEvent + } + ); + + + /** + * @config scrollincrement + * @description Number used to control the scroll speed of a menu. Used to + * increment the "scrollTop" property of the menu's body by when a menu's + * content is scrolling. When set this property is automatically applied + * to all submenus. + * @default 1 + * @type Number + */ + oConfig.addProperty( + SCROLL_INCREMENT_CONFIG.key, + { + value: SCROLL_INCREMENT_CONFIG.value, + validator: SCROLL_INCREMENT_CONFIG.validator, + supercedes: SCROLL_INCREMENT_CONFIG.supercedes, + suppressEvent: SCROLL_INCREMENT_CONFIG.suppressEvent + } + ); + + + /** + * @config minscrollheight + * @description Number defining the minimum threshold for the "maxheight" + * configuration property. When set this property is automatically applied + * to all submenus. + * @default 90 + * @type Number + */ + oConfig.addProperty( + MIN_SCROLL_HEIGHT_CONFIG.key, + { + value: MIN_SCROLL_HEIGHT_CONFIG.value, + validator: MIN_SCROLL_HEIGHT_CONFIG.validator, + supercedes: MIN_SCROLL_HEIGHT_CONFIG.supercedes, + suppressEvent: MIN_SCROLL_HEIGHT_CONFIG.suppressEvent + } + ); + + + /** + * @config maxheight + * @description Number defining the maximum height (in pixels) for a menu's + * body element (<div class="bd"<). Once a menu's body + * exceeds this height, the contents of the body are scrolled to maintain + * this value. This value cannot be set lower than the value of the + * "minscrollheight" configuration property. + * @default 0 + * @type Number + */ + oConfig.addProperty( + MAX_HEIGHT_CONFIG.key, + { + handler: this.configMaxHeight, + value: MAX_HEIGHT_CONFIG.value, + validator: MAX_HEIGHT_CONFIG.validator, + suppressEvent: MAX_HEIGHT_CONFIG.suppressEvent, + supercedes: MAX_HEIGHT_CONFIG.supercedes + } + ); + + + /** + * @config classname + * @description String representing the CSS class to be applied to the + * menu's root <div> element. The specified class(es) + * are appended in addition to the default class as specified by the menu's + * CSS_CLASS_NAME constant. When set this property is automatically + * applied to all submenus. + * @default null + * @type String + */ + oConfig.addProperty( + CLASS_NAME_CONFIG.key, + { + handler: this.configClassName, + value: CLASS_NAME_CONFIG.value, + validator: CLASS_NAME_CONFIG.validator, + supercedes: CLASS_NAME_CONFIG.supercedes + } + ); + + + /** + * @config disabled + * @description Boolean indicating if the menu should be disabled. + * Disabling a menu disables each of its items. (Disabled menu items are + * dimmed and will not respond to user input or fire events.) Disabled + * menus have a corresponding "disabled" CSS class applied to their root + * <div> element. + * @default false + * @type Boolean + */ + oConfig.addProperty( + DISABLED_CONFIG.key, + { + handler: this.configDisabled, + value: DISABLED_CONFIG.value, + validator: DISABLED_CONFIG.validator, + suppressEvent: DISABLED_CONFIG.suppressEvent + } + ); + + + /** + * @config shadow + * @description Boolean indicating if the menu should have a shadow. + * @default true + * @type Boolean + */ + oConfig.addProperty( + SHADOW_CONFIG.key, + { + handler: this.configShadow, + value: SHADOW_CONFIG.value, + validator: SHADOW_CONFIG.validator + } + ); + + + /** + * @config keepopen + * @description Boolean indicating if the menu should remain open when clicked. + * @default false + * @type Boolean + */ + oConfig.addProperty( + KEEP_OPEN_CONFIG.key, + { + value: KEEP_OPEN_CONFIG.value, + validator: KEEP_OPEN_CONFIG.validator + } + ); + +} + +}); // END YAHOO.lang.extend + +})(); + + + +(function () { + +/** +* Creates an item for a menu. +* +* @param {String} p_oObject String specifying the text of the menu item. +* @param {HTMLLIElement} p_oObject Object specifying +* the <li> element of the menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the <optgroup> element of the menu item. +* @param {HTMLOptionElement} p_oObject Object +* specifying the <option> element of the menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu item. See configuration class documentation +* for more details. +* @class MenuItem +* @constructor +*/ +YAHOO.widget.MenuItem = function (p_oObject, p_oConfig) { + + if (p_oObject) { + + if (p_oConfig) { + + this.parent = p_oConfig.parent; + this.value = p_oConfig.value; + this.id = p_oConfig.id; + + } + + this.init(p_oObject, p_oConfig); + + } + +}; + + +var Dom = YAHOO.util.Dom, + Module = YAHOO.widget.Module, + Menu = YAHOO.widget.Menu, + MenuItem = YAHOO.widget.MenuItem, + CustomEvent = YAHOO.util.CustomEvent, + UA = YAHOO.env.ua, + Lang = YAHOO.lang, + + // Private string constants + + _TEXT = "text", + _HASH = "#", + _HYPHEN = "-", + _HELP_TEXT = "helptext", + _URL = "url", + _TARGET = "target", + _EMPHASIS = "emphasis", + _STRONG_EMPHASIS = "strongemphasis", + _CHECKED = "checked", + _SUBMENU = "submenu", + _DISABLED = "disabled", + _SELECTED = "selected", + _HAS_SUBMENU = "hassubmenu", + _CHECKED_DISABLED = "checked-disabled", + _HAS_SUBMENU_DISABLED = "hassubmenu-disabled", + _HAS_SUBMENU_SELECTED = "hassubmenu-selected", + _CHECKED_SELECTED = "checked-selected", + _ONCLICK = "onclick", + _CLASSNAME = "classname", + _EMPTY_STRING = "", + _OPTION = "OPTION", + _OPTGROUP = "OPTGROUP", + _LI_UPPERCASE = "LI", + _HREF = "href", + _SELECT = "SELECT", + _DIV = "DIV", + _START_HELP_TEXT = "", + _START_EM = "", + _END_EM = "", + _START_STRONG = "", + _END_STRONG = "", + _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap", + _OBJ = "obj", + _SCOPE = "scope", + _NONE = "none", + _VISIBLE = "visible", + _SPACE = " ", + _MENUITEM = "MenuItem", + _CLICK = "click", + _SHOW = "show", + _HIDE = "hide", + _LI_LOWERCASE = "li", + _ANCHOR_TEMPLATE = "", + + EVENT_TYPES = [ + + ["mouseOverEvent", "mouseover"], + ["mouseOutEvent", "mouseout"], + ["mouseDownEvent", "mousedown"], + ["mouseUpEvent", "mouseup"], + ["clickEvent", _CLICK], + ["keyPressEvent", "keypress"], + ["keyDownEvent", "keydown"], + ["keyUpEvent", "keyup"], + ["focusEvent", "focus"], + ["blurEvent", "blur"], + ["destroyEvent", "destroy"] + + ], + + TEXT_CONFIG = { + key: _TEXT, + value: _EMPTY_STRING, + validator: Lang.isString, + suppressEvent: true + }, + + HELP_TEXT_CONFIG = { + key: _HELP_TEXT, + supercedes: [_TEXT], + suppressEvent: true + }, + + URL_CONFIG = { + key: _URL, + value: _HASH, + suppressEvent: true + }, + + TARGET_CONFIG = { + key: _TARGET, + suppressEvent: true + }, + + EMPHASIS_CONFIG = { + key: _EMPHASIS, + value: false, + validator: Lang.isBoolean, + suppressEvent: true, + supercedes: [_TEXT] + }, + + STRONG_EMPHASIS_CONFIG = { + key: _STRONG_EMPHASIS, + value: false, + validator: Lang.isBoolean, + suppressEvent: true, + supercedes: [_TEXT] + }, + + CHECKED_CONFIG = { + key: _CHECKED, + value: false, + validator: Lang.isBoolean, + suppressEvent: true, + supercedes: [_DISABLED, _SELECTED] + }, + + SUBMENU_CONFIG = { + key: _SUBMENU, + suppressEvent: true, + supercedes: [_DISABLED, _SELECTED] + }, + + DISABLED_CONFIG = { + key: _DISABLED, + value: false, + validator: Lang.isBoolean, + suppressEvent: true, + supercedes: [_TEXT, _SELECTED] + }, + + SELECTED_CONFIG = { + key: _SELECTED, + value: false, + validator: Lang.isBoolean, + suppressEvent: true + }, + + ONCLICK_CONFIG = { + key: _ONCLICK, + suppressEvent: true + }, + + CLASS_NAME_CONFIG = { + key: _CLASSNAME, + value: null, + validator: Lang.isString, + suppressEvent: true + }, + + KEY_LISTENER_CONFIG = { + key: "keylistener", + value: null, + suppressEvent: true + }, + + m_oMenuItemTemplate = null, + + CLASS_NAMES = {}; + + +/** +* @method getClassNameForState +* @description Returns a class name for the specified prefix and state. If the class name does not +* yet exist, it is created and stored in the CLASS_NAMES object to increase performance. +* @private +* @param {String} prefix String representing the prefix for the class name +* @param {String} state String representing a state - "disabled," "checked," etc. +*/ +var getClassNameForState = function (prefix, state) { + + var oClassNames = CLASS_NAMES[prefix]; + + if (!oClassNames) { + CLASS_NAMES[prefix] = {}; + oClassNames = CLASS_NAMES[prefix]; + } + + + var sClassName = oClassNames[state]; + + if (!sClassName) { + sClassName = prefix + _HYPHEN + state; + oClassNames[state] = sClassName; + } + + return sClassName; + +}; + + +/** +* @method addClassNameForState +* @description Applies a class name to a MenuItem instance's <LI> and <A> elements +* that represents a MenuItem's state - "disabled," "checked," etc. +* @private +* @param {String} state String representing a state - "disabled," "checked," etc. +*/ +var addClassNameForState = function (state) { + + Dom.addClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state)); + Dom.addClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state)); + +}; + +/** +* @method removeClassNameForState +* @description Removes a class name from a MenuItem instance's <LI> and <A> elements +* that represents a MenuItem's state - "disabled," "checked," etc. +* @private +* @param {String} state String representing a state - "disabled," "checked," etc. +*/ +var removeClassNameForState = function (state) { + + Dom.removeClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state)); + Dom.removeClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state)); + +}; + + +MenuItem.prototype = { + + /** + * @property CSS_CLASS_NAME + * @description String representing the CSS class(es) to be applied to the + * <li> element of the menu item. + * @default "yuimenuitem" + * @final + * @type String + */ + CSS_CLASS_NAME: "yuimenuitem", + + + /** + * @property CSS_LABEL_CLASS_NAME + * @description String representing the CSS class(es) to be applied to the + * menu item's <a> element. + * @default "yuimenuitemlabel" + * @final + * @type String + */ + CSS_LABEL_CLASS_NAME: "yuimenuitemlabel", + + + /** + * @property SUBMENU_TYPE + * @description Object representing the type of menu to instantiate and + * add when parsing the child nodes of the menu item's source HTML element. + * @final + * @type YAHOO.widget.Menu + */ + SUBMENU_TYPE: null, + + + + // Private member variables + + + /** + * @property _oAnchor + * @description Object reference to the menu item's + * <a> element. + * @default null + * @private + * @type HTMLAnchorElement + */ + _oAnchor: null, + + + /** + * @property _oHelpTextEM + * @description Object reference to the menu item's help text + * <em> element. + * @default null + * @private + * @type HTMLElement + */ + _oHelpTextEM: null, + + + /** + * @property _oSubmenu + * @description Object reference to the menu item's submenu. + * @default null + * @private + * @type YAHOO.widget.Menu + */ + _oSubmenu: null, + + + /** + * @property _oOnclickAttributeValue + * @description Object reference to the menu item's current value for the + * "onclick" configuration attribute. + * @default null + * @private + * @type Object + */ + _oOnclickAttributeValue: null, + + + /** + * @property _sClassName + * @description The current value of the "classname" configuration attribute. + * @default null + * @private + * @type String + */ + _sClassName: null, + + + + // Public properties + + + /** + * @property constructor + * @description Object reference to the menu item's constructor function. + * @default YAHOO.widget.MenuItem + * @type YAHOO.widget.MenuItem + */ + constructor: MenuItem, + + + /** + * @property index + * @description Number indicating the ordinal position of the menu item in + * its group. + * @default null + * @type Number + */ + index: null, + + + /** + * @property groupIndex + * @description Number indicating the index of the group to which the menu + * item belongs. + * @default null + * @type Number + */ + groupIndex: null, + + + /** + * @property parent + * @description Object reference to the menu item's parent menu. + * @default null + * @type YAHOO.widget.Menu + */ + parent: null, + + + /** + * @property element + * @description Object reference to the menu item's + * <li> element. + * @default HTMLLIElement + * @type HTMLLIElement + */ + element: null, + + + /** + * @property srcElement + * @description Object reference to the HTML element (either + * <li>, <optgroup> or + * <option>) used create the menu item. + * @default HTMLLIElement|HTMLOptGroupElement|HTMLOptionElement + * @type HTMLLIElement| + * HTMLOptGroupElement|HTMLOptionElement + */ + srcElement: null, + + + /** + * @property value + * @description Object reference to the menu item's value. + * @default null + * @type Object + */ + value: null, + + + /** + * @property browser + * @deprecated Use YAHOO.env.ua + * @description String representing the browser. + * @type String + */ + browser: Module.prototype.browser, + + + /** + * @property id + * @description Id of the menu item's root <li> + * element. This property should be set via the constructor using the + * configuration object literal. If an id is not specified, then one will + * be created using the "generateId" method of the Dom utility. + * @default null + * @type String + */ + id: null, + + + + // Events + + + /** + * @event destroyEvent + * @description Fires when the menu item's <li> + * element is removed from its parent <ul> element. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event mouseOverEvent + * @description Fires when the mouse has entered the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event mouseOutEvent + * @description Fires when the mouse has left the menu item. Passes back + * the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event mouseDownEvent + * @description Fires when the user mouses down on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event mouseUpEvent + * @description Fires when the user releases a mouse button while the mouse + * is over the menu item. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event clickEvent + * @description Fires when the user clicks the on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event keyPressEvent + * @description Fires when the user presses an alphanumeric key when the + * menu item has focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event keyDownEvent + * @description Fires when the user presses a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event keyUpEvent + * @description Fires when the user releases a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event focusEvent + * @description Fires when the menu item receives focus. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @event blurEvent + * @description Fires when the menu item loses the input focus. + * @type YAHOO.util.CustomEvent + */ + + + /** + * @method init + * @description The MenuItem class's initialization method. This method is + * automatically called by the constructor, and sets up all DOM references + * for pre-existing markup, and creates required markup if it is not + * already present. + * @param {String} p_oObject String specifying the text of the menu item. + * @param {HTMLLIElement} p_oObject Object specifying + * the <li> element of the menu item. + * @param {HTMLOptGroupElement} p_oObject Object + * specifying the <optgroup> element of the menu item. + * @param {HTMLOptionElement} p_oObject Object + * specifying the <option> element of the menu item. + * @param {Object} p_oConfig Optional. Object literal specifying the + * configuration for the menu item. See configuration class documentation + * for more details. + */ + init: function (p_oObject, p_oConfig) { + + + if (!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = Menu; + + } + + + // Create the config object + + this.cfg = new YAHOO.util.Config(this); + + this.initDefaultConfig(); + + var oConfig = this.cfg, + sURL = _HASH, + oCustomEvent, + aEventData, + oAnchor, + sTarget, + sText, + sId, + i; + + + if (Lang.isString(p_oObject)) { + + this._createRootNodeStructure(); + + oConfig.queueProperty(_TEXT, p_oObject); + + } + else if (p_oObject && p_oObject.tagName) { + + switch(p_oObject.tagName.toUpperCase()) { + + case _OPTION: + + this._createRootNodeStructure(); + + oConfig.queueProperty(_TEXT, p_oObject.text); + oConfig.queueProperty(_DISABLED, p_oObject.disabled); + + this.value = p_oObject.value; + + this.srcElement = p_oObject; + + break; + + case _OPTGROUP: + + this._createRootNodeStructure(); + + oConfig.queueProperty(_TEXT, p_oObject.label); + oConfig.queueProperty(_DISABLED, p_oObject.disabled); + + this.srcElement = p_oObject; + + this._initSubTree(); + + break; + + case _LI_UPPERCASE: + + // Get the anchor node (if it exists) + + oAnchor = Dom.getFirstChild(p_oObject); + + + // Capture the "text" and/or the "URL" + + if (oAnchor) { + + sURL = oAnchor.getAttribute(_HREF, 2); + sTarget = oAnchor.getAttribute(_TARGET); + + sText = oAnchor.innerHTML; + + } + + this.srcElement = p_oObject; + this.element = p_oObject; + this._oAnchor = oAnchor; + + /* + Set these properties silently to sync up the + configuration object without making changes to the + element's DOM + */ + + oConfig.setProperty(_TEXT, sText, true); + oConfig.setProperty(_URL, sURL, true); + oConfig.setProperty(_TARGET, sTarget, true); + + this._initSubTree(); + + break; + + } + + } + + + if (this.element) { + + sId = (this.srcElement || this.element).id; + + if (!sId) { + + sId = this.id || Dom.generateId(); + + this.element.id = sId; + + } + + this.id = sId; + + + Dom.addClass(this.element, this.CSS_CLASS_NAME); + Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME); + + + i = EVENT_TYPES.length - 1; + + do { + + aEventData = EVENT_TYPES[i]; + + oCustomEvent = this.createEvent(aEventData[1]); + oCustomEvent.signature = CustomEvent.LIST; + + this[aEventData[0]] = oCustomEvent; + + } + while (i--); + + + if (p_oConfig) { + + oConfig.applyConfig(p_oConfig); + + } + + oConfig.fireQueue(); + + } + + }, + + + + // Private methods + + /** + * @method _createRootNodeStructure + * @description Creates the core DOM structure for the menu item. + * @private + */ + _createRootNodeStructure: function () { + + var oElement, + oAnchor; + + if (!m_oMenuItemTemplate) { + + m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE); + m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE; + + } + + oElement = m_oMenuItemTemplate.cloneNode(true); + oElement.className = this.CSS_CLASS_NAME; + + oAnchor = oElement.firstChild; + oAnchor.className = this.CSS_LABEL_CLASS_NAME; + + this.element = oElement; + this._oAnchor = oAnchor; + + }, + + + /** + * @method _initSubTree + * @description Iterates the source element's childNodes collection and uses + * the child nodes to instantiate other menus. + * @private + */ + _initSubTree: function () { + + var oSrcEl = this.srcElement, + oConfig = this.cfg, + oNode, + aOptions, + nOptions, + oMenu, + n; + + + if (oSrcEl.childNodes.length > 0) { + + if (this.parent.lazyLoad && this.parent.srcElement && + this.parent.srcElement.tagName.toUpperCase() == _SELECT) { + + oConfig.setProperty( + _SUBMENU, + { id: Dom.generateId(), itemdata: oSrcEl.childNodes } + ); + + } + else { + + oNode = oSrcEl.firstChild; + aOptions = []; + + do { + + if (oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case _DIV: + + oConfig.setProperty(_SUBMENU, oNode); + + break; + + case _OPTION: + + aOptions[aOptions.length] = oNode; + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + + nOptions = aOptions.length; + + if (nOptions > 0) { + + oMenu = new this.SUBMENU_TYPE(Dom.generateId()); + + oConfig.setProperty(_SUBMENU, oMenu); + + for(n=0; n 0) { + + oAnchor.setAttribute(_TARGET, sTarget); + + } + else { + + oAnchor.removeAttribute(_TARGET); + + } + + }, + + + /** + * @method configEmphasis + * @description Event handler for when the "emphasis" configuration property + * of the menu item changes. + * @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. + */ + configEmphasis: function (p_sType, p_aArgs, p_oItem) { + + var bEmphasis = p_aArgs[0], + oConfig = this.cfg; + + + if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) { + + oConfig.setProperty(_STRONG_EMPHASIS, false); + + } + + + oConfig.refireEvent(_TEXT); + + }, + + + /** + * @method configStrongEmphasis + * @description Event handler for when the "strongemphasis" configuration + * property of the menu item changes. + * @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. + */ + configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) { + + var bStrongEmphasis = p_aArgs[0], + oConfig = this.cfg; + + + if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) { + + oConfig.setProperty(_EMPHASIS, false); + + } + + oConfig.refireEvent(_TEXT); + + }, + + + /** + * @method configChecked + * @description Event handler for when the "checked" configuration property + * of the menu item changes. + * @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. + */ + configChecked: function (p_sType, p_aArgs, p_oItem) { + + var bChecked = p_aArgs[0], + oConfig = this.cfg; + + + if (bChecked) { + + addClassNameForState.call(this, _CHECKED); + + } + else { + + removeClassNameForState.call(this, _CHECKED); + } + + + oConfig.refireEvent(_TEXT); + + + if (oConfig.getProperty(_DISABLED)) { + + oConfig.refireEvent(_DISABLED); + + } + + + if (oConfig.getProperty(_SELECTED)) { + + oConfig.refireEvent(_SELECTED); + + } + + }, + + + + /** + * @method configDisabled + * @description Event handler for when the "disabled" configuration property + * of the menu item changes. + * @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. + */ + configDisabled: function (p_sType, p_aArgs, p_oItem) { + + var bDisabled = p_aArgs[0], + oConfig = this.cfg, + oSubmenu = oConfig.getProperty(_SUBMENU), + bChecked = oConfig.getProperty(_CHECKED); + + + if (bDisabled) { + + if (oConfig.getProperty(_SELECTED)) { + + oConfig.setProperty(_SELECTED, false); + + } + + + addClassNameForState.call(this, _DISABLED); + + + if (oSubmenu) { + + addClassNameForState.call(this, _HAS_SUBMENU_DISABLED); + + } + + + if (bChecked) { + + addClassNameForState.call(this, _CHECKED_DISABLED); + + } + + } + else { + + removeClassNameForState.call(this, _DISABLED); + + + if (oSubmenu) { + + removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED); + + } + + + if (bChecked) { + + removeClassNameForState.call(this, _CHECKED_DISABLED); + + } + + } + + }, + + + /** + * @method configSelected + * @description Event handler for when the "selected" configuration property + * of the menu item changes. + * @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. + */ + configSelected: function (p_sType, p_aArgs, p_oItem) { + + var oConfig = this.cfg, + oAnchor = this._oAnchor, + + bSelected = p_aArgs[0], + bChecked = oConfig.getProperty(_CHECKED), + oSubmenu = oConfig.getProperty(_SUBMENU); + + + if (UA.opera) { + + oAnchor.blur(); + + } + + + if (bSelected && !oConfig.getProperty(_DISABLED)) { + + addClassNameForState.call(this, _SELECTED); + + + if (oSubmenu) { + + addClassNameForState.call(this, _HAS_SUBMENU_SELECTED); + + } + + + if (bChecked) { + + addClassNameForState.call(this, _CHECKED_SELECTED); + + } + + } + else { + + removeClassNameForState.call(this, _SELECTED); + + + if (oSubmenu) { + + removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED); + + } + + + if (bChecked) { + + removeClassNameForState.call(this, _CHECKED_SELECTED); + + } + + } + + + if (this.hasFocus() && UA.opera) { + + oAnchor.focus(); + + } + + }, + + + /** + * @method _onSubmenuBeforeHide + * @description "beforehide" Custom Event handler for a submenu. + * @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. + */ + _onSubmenuBeforeHide: function (p_sType, p_aArgs) { + + var oItem = this.parent, + oMenu; + + function onHide() { + + oItem._oAnchor.blur(); + oMenu.beforeHideEvent.unsubscribe(onHide); + + } + + + if (oItem.hasFocus()) { + + oMenu = oItem.parent; + + oMenu.beforeHideEvent.subscribe(onHide); + + } + + }, + + + /** + * @method configSubmenu + * @description Event handler for when the "submenu" configuration property + * of the menu item changes. + * @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. + */ + configSubmenu: function (p_sType, p_aArgs, p_oItem) { + + var oSubmenu = p_aArgs[0], + oConfig = this.cfg, + bLazyLoad = this.parent && this.parent.lazyLoad, + oMenu, + sSubmenuId, + oSubmenuConfig; + + + if (oSubmenu) { + + if (oSubmenu instanceof Menu) { + + oMenu = oSubmenu; + oMenu.parent = this; + oMenu.lazyLoad = bLazyLoad; + + } + else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) { + + sSubmenuId = oSubmenu.id; + oSubmenuConfig = oSubmenu; + + oSubmenuConfig.lazyload = bLazyLoad; + oSubmenuConfig.parent = this; + + oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig); + + + // Set the value of the property to the Menu instance + + oConfig.setProperty(_SUBMENU, oMenu, true); + + } + else { + + oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this }); + + + // Set the value of the property to the Menu instance + + oConfig.setProperty(_SUBMENU, oMenu, true); + + } + + + if (oMenu) { + + oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true); + + addClassNameForState.call(this, _HAS_SUBMENU); + + + if (oConfig.getProperty(_URL) === _HASH) { + + oConfig.setProperty(_URL, (_HASH + oMenu.id)); + + } + + + this._oSubmenu = oMenu; + + + if (UA.opera) { + + oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide); + + } + + } + + } + else { + + removeClassNameForState.call(this, _HAS_SUBMENU); + + if (this._oSubmenu) { + + this._oSubmenu.destroy(); + + } + + } + + + if (oConfig.getProperty(_DISABLED)) { + + oConfig.refireEvent(_DISABLED); + + } + + + if (oConfig.getProperty(_SELECTED)) { + + oConfig.refireEvent(_SELECTED); + + } + + }, + + + /** + * @method configOnClick + * @description Event handler for when the "onclick" configuration property + * of the menu item changes. + * @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. + */ + configOnClick: function (p_sType, p_aArgs, p_oItem) { + + var oObject = p_aArgs[0]; + + /* + Remove any existing listeners if a "click" event handler has + already been specified. + */ + + if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) { + + this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn, + this._oOnclickAttributeValue.obj); + + this._oOnclickAttributeValue = null; + + } + + + if (!this._oOnclickAttributeValue && Lang.isObject(oObject) && + Lang.isFunction(oObject.fn)) { + + this.clickEvent.subscribe(oObject.fn, + ((_OBJ in oObject) ? oObject.obj : this), + ((_SCOPE in oObject) ? oObject.scope : null) ); + + this._oOnclickAttributeValue = oObject; + + } + + }, + + + /** + * @method configClassName + * @description Event handler for when the "classname" configuration + * property of a menu item changes. + * @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. + */ + configClassName: function (p_sType, p_aArgs, p_oItem) { + + var sClassName = p_aArgs[0]; + + if (this._sClassName) { + + Dom.removeClass(this.element, this._sClassName); + + } + + Dom.addClass(this.element, sClassName); + this._sClassName = sClassName; + + }, + + + /** + * @method _dispatchClickEvent + * @description Dispatches a DOM "click" event to the anchor element of a + * MenuItem instance. + * @private + */ + _dispatchClickEvent: function () { + + var oMenuItem = this, + oAnchor, + oEvent; + + if (!oMenuItem.cfg.getProperty(_DISABLED)) { + + oAnchor = Dom.getFirstChild(oMenuItem.element); + + // Dispatch a "click" event to the MenuItem's anchor so that its + // "click" event handlers will get called in response to the user + // pressing the keyboard shortcut defined by the "keylistener" + // configuration property. + + if (UA.ie) { + oAnchor.fireEvent(_ONCLICK); + } + else { + + if ((UA.gecko && UA.gecko >= 1.9) || UA.opera || UA.webkit) { + + oEvent = document.createEvent("HTMLEvents"); + oEvent.initEvent(_CLICK, true, true); + + } + else { + + oEvent = document.createEvent("MouseEvents"); + oEvent.initMouseEvent(_CLICK, true, true, window, 0, 0, 0, + 0, 0, false, false, false, false, 0, null); + + } + + oAnchor.dispatchEvent(oEvent); + + } + + } + + }, + + + /** + * @method _createKeyListener + * @description "show" event handler for a Menu instance - responsible for + * setting up the KeyListener instance for a MenuItem. + * @private + * @param {String} type String representing the name of the event that + * was fired. + * @param {Array} args Array of arguments sent when the event was fired. + * @param {Array} keyData Array of arguments sent when the event was fired. + */ + _createKeyListener: function (type, args, keyData) { + + var oMenuItem = this, + oMenu = oMenuItem.parent; + + var oKeyListener = new YAHOO.util.KeyListener( + oMenu.element.ownerDocument, + keyData, + { + fn: oMenuItem._dispatchClickEvent, + scope: oMenuItem, + correctScope: true }); + + + if (oMenu.cfg.getProperty(_VISIBLE)) { + oKeyListener.enable(); + } + + + oMenu.subscribe(_SHOW, oKeyListener.enable, null, oKeyListener); + oMenu.subscribe(_HIDE, oKeyListener.disable, null, oKeyListener); + + oMenuItem._keyListener = oKeyListener; + + oMenu.unsubscribe(_SHOW, oMenuItem._createKeyListener, keyData); + + }, + + + /** + * @method configKeyListener + * @description Event handler for when the "keylistener" configuration + * property of a menu item changes. + * @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. + */ + configKeyListener: function (p_sType, p_aArgs) { + + var oKeyData = p_aArgs[0], + oMenuItem = this, + oMenu = oMenuItem.parent; + + if (oMenuItem._keyData) { + + // Unsubscribe from the "show" event in case the keylistener + // config was changed before the Menu was ever made visible. + + oMenu.unsubscribe(_SHOW, + oMenuItem._createKeyListener, oMenuItem._keyData); + + oMenuItem._keyData = null; + + } + + + // Tear down for the previous value of the "keylistener" property + + if (oMenuItem._keyListener) { + + oMenu.unsubscribe(_SHOW, oMenuItem._keyListener.enable); + oMenu.unsubscribe(_HIDE, oMenuItem._keyListener.disable); + + oMenuItem._keyListener.disable(); + oMenuItem._keyListener = null; + + } + + + if (oKeyData) { + + oMenuItem._keyData = oKeyData; + + // Defer the creation of the KeyListener instance until the + // parent Menu is visible. This is necessary since the + // KeyListener instance needs to be bound to the document the + // Menu has been rendered into. Deferring creation of the + // KeyListener instance also improves performance. + + oMenu.subscribe(_SHOW, oMenuItem._createKeyListener, + oKeyData, oMenuItem); + } + + }, + + + // Public methods + + + /** + * @method initDefaultConfig + * @description Initializes an item's configurable properties. + */ + initDefaultConfig : function () { + + var oConfig = this.cfg; + + + // Define the configuration attributes + + /** + * @config text + * @description String specifying the text label for the menu item. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default "" + * @type String + */ + oConfig.addProperty( + TEXT_CONFIG.key, + { + handler: this.configText, + value: TEXT_CONFIG.value, + validator: TEXT_CONFIG.validator, + suppressEvent: TEXT_CONFIG.suppressEvent + } + ); + + + /** + * @config helptext + * @description String specifying additional instructional text to + * accompany the text for the menu item. + * @deprecated Use "text" configuration property to add help text markup. + * For example: oMenuItem.cfg.setProperty("text", "Copy <em + * class=\"helptext\">Ctrl + C</em>"); + * @default null + * @type String| + * HTMLElement + */ + oConfig.addProperty( + HELP_TEXT_CONFIG.key, + { + handler: this.configHelpText, + supercedes: HELP_TEXT_CONFIG.supercedes, + suppressEvent: HELP_TEXT_CONFIG.suppressEvent + } + ); + + + /** + * @config url + * @description String specifying the URL for the menu item's anchor's + * "href" attribute. When building a menu from existing HTML the value + * of this property will be interpreted from the menu's markup. + * @default "#" + * @type String + */ + oConfig.addProperty( + URL_CONFIG.key, + { + handler: this.configURL, + value: URL_CONFIG.value, + suppressEvent: URL_CONFIG.suppressEvent + } + ); + + + /** + * @config target + * @description String specifying the value for the "target" attribute + * of the menu item's anchor element. Specifying a target will + * require the user to click directly on the menu item's anchor node in + * order to cause the browser to navigate to the specified URL. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default null + * @type String + */ + oConfig.addProperty( + TARGET_CONFIG.key, + { + handler: this.configTarget, + suppressEvent: TARGET_CONFIG.suppressEvent + } + ); + + + /** + * @config emphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with emphasis. + * @deprecated Use the "text" configuration property to add emphasis. + * For example: oMenuItem.cfg.setProperty("text", "<em>Some + * Text</em>"); + * @default false + * @type Boolean + */ + oConfig.addProperty( + EMPHASIS_CONFIG.key, + { + handler: this.configEmphasis, + value: EMPHASIS_CONFIG.value, + validator: EMPHASIS_CONFIG.validator, + suppressEvent: EMPHASIS_CONFIG.suppressEvent, + supercedes: EMPHASIS_CONFIG.supercedes + } + ); + + + /** + * @config strongemphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with strong emphasis. + * @deprecated Use the "text" configuration property to add strong emphasis. + * For example: oMenuItem.cfg.setProperty("text", "<strong> + * Some Text</strong>"); + * @default false + * @type Boolean + */ + oConfig.addProperty( + STRONG_EMPHASIS_CONFIG.key, + { + handler: this.configStrongEmphasis, + value: STRONG_EMPHASIS_CONFIG.value, + validator: STRONG_EMPHASIS_CONFIG.validator, + suppressEvent: STRONG_EMPHASIS_CONFIG.suppressEvent, + supercedes: STRONG_EMPHASIS_CONFIG.supercedes + } + ); + + + /** + * @config checked + * @description Boolean indicating if the menu item should be rendered + * with a checkmark. + * @default false + * @type Boolean + */ + oConfig.addProperty( + CHECKED_CONFIG.key, + { + handler: this.configChecked, + value: CHECKED_CONFIG.value, + validator: CHECKED_CONFIG.validator, + suppressEvent: CHECKED_CONFIG.suppressEvent, + supercedes: CHECKED_CONFIG.supercedes + } + ); + + + /** + * @config disabled + * @description Boolean indicating if the menu item should be disabled. + * (Disabled menu items are dimmed and will not respond to user input + * or fire events.) + * @default false + * @type Boolean + */ + oConfig.addProperty( + DISABLED_CONFIG.key, + { + handler: this.configDisabled, + value: DISABLED_CONFIG.value, + validator: DISABLED_CONFIG.validator, + suppressEvent: DISABLED_CONFIG.suppressEvent + } + ); + + + /** + * @config selected + * @description Boolean indicating if the menu item should + * be highlighted. + * @default false + * @type Boolean + */ + oConfig.addProperty( + SELECTED_CONFIG.key, + { + handler: this.configSelected, + value: SELECTED_CONFIG.value, + validator: SELECTED_CONFIG.validator, + suppressEvent: SELECTED_CONFIG.suppressEvent + } + ); + + + /** + * @config submenu + * @description Object specifying the submenu to be appended to the + * menu item. The value can be one of the following:
  • Object + * specifying a Menu instance.
  • Object literal specifying the + * menu to be created. Format: { id: [menu id], itemdata: + * [array of values for + * items] }.
  • String specifying the id attribute + * of the <div> element of the menu.
  • + * Object specifying the <div> element of the + * menu.
+ * @default null + * @type Menu|String|Object| + * HTMLElement + */ + oConfig.addProperty( + SUBMENU_CONFIG.key, + { + handler: this.configSubmenu, + supercedes: SUBMENU_CONFIG.supercedes, + suppressEvent: SUBMENU_CONFIG.suppressEvent + } + ); + + + /** + * @config onclick + * @description Object literal representing the code to be executed when + * the item is clicked. Format:
{
+ * fn: Function, // The handler to call when + * the event fires.
obj: Object, // An + * object to pass back to the handler.
scope: + * Object // The object to use for the scope of the handler. + *
}
+ * @type Object + * @default null + */ + oConfig.addProperty( + ONCLICK_CONFIG.key, + { + handler: this.configOnClick, + suppressEvent: ONCLICK_CONFIG.suppressEvent + } + ); + + + /** + * @config classname + * @description CSS class to be applied to the menu item's root + * <li> element. The specified class(es) are + * appended in addition to the default class as specified by the menu + * item's CSS_CLASS_NAME constant. + * @default null + * @type String + */ + oConfig.addProperty( + CLASS_NAME_CONFIG.key, + { + handler: this.configClassName, + value: CLASS_NAME_CONFIG.value, + validator: CLASS_NAME_CONFIG.validator, + suppressEvent: CLASS_NAME_CONFIG.suppressEvent + } + ); + + + /** + * @config keylistener + * @description Object literal representing the key(s) that can be used + * to trigger the MenuItem's "click" event. Possible attributes are + * shift (boolean), alt (boolean), ctrl (boolean) and keys (either an int + * or an array of ints representing keycodes). + * @default null + * @type Object + */ + oConfig.addProperty( + KEY_LISTENER_CONFIG.key, + { + handler: this.configKeyListener, + value: KEY_LISTENER_CONFIG.value, + suppressEvent: KEY_LISTENER_CONFIG.suppressEvent + } + ); + + }, + + + /** + * @method getNextEnabledSibling + * @description Finds the menu item's next enabled sibling. + * @return YAHOO.widget.MenuItem + */ + getNextEnabledSibling: function () { + + var nGroupIndex, + aItemGroups, + oNextItem, + nNextGroupIndex, + aNextGroup, + returnVal; + + function getNextArrayItem(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || getNextArrayItem(p_aArray, (p_nStartIndex+1)); + + } + + if (this.parent instanceof Menu) { + + nGroupIndex = this.groupIndex; + + aItemGroups = this.parent.getItemGroups(); + + if (this.index < (aItemGroups[nGroupIndex].length - 1)) { + + oNextItem = getNextArrayItem(aItemGroups[nGroupIndex], + (this.index+1)); + + } + else { + + if (nGroupIndex < (aItemGroups.length - 1)) { + + nNextGroupIndex = nGroupIndex + 1; + + } + else { + + nNextGroupIndex = 0; + + } + + aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex); + + // Retrieve the first menu item in the next group + + oNextItem = getNextArrayItem(aNextGroup, 0); + + } + + returnVal = (oNextItem.cfg.getProperty(_DISABLED) || + oNextItem.element.style.display == _NONE) ? + oNextItem.getNextEnabledSibling() : oNextItem; + + } + + return returnVal; + + }, + + + /** + * @method getPreviousEnabledSibling + * @description Finds the menu item's previous enabled sibling. + * @return {YAHOO.widget.MenuItem} + */ + getPreviousEnabledSibling: function () { + + var nGroupIndex, + aItemGroups, + oPreviousItem, + nPreviousGroupIndex, + aPreviousGroup, + returnVal; + + function getPreviousArrayItem(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || getPreviousArrayItem(p_aArray, (p_nStartIndex-1)); + + } + + function getFirstItemIndex(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] ? p_nStartIndex : + getFirstItemIndex(p_aArray, (p_nStartIndex+1)); + + } + + if (this.parent instanceof Menu) { + + nGroupIndex = this.groupIndex; + aItemGroups = this.parent.getItemGroups(); + + + if (this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0)) { + + oPreviousItem = getPreviousArrayItem(aItemGroups[nGroupIndex], + (this.index-1)); + + } + else { + + if (nGroupIndex > getFirstItemIndex(aItemGroups, 0)) { + + nPreviousGroupIndex = nGroupIndex - 1; + + } + else { + + nPreviousGroupIndex = aItemGroups.length - 1; + + } + + aPreviousGroup = getPreviousArrayItem(aItemGroups, + nPreviousGroupIndex); + + oPreviousItem = getPreviousArrayItem(aPreviousGroup, + (aPreviousGroup.length - 1)); + + } + + returnVal = (oPreviousItem.cfg.getProperty(_DISABLED) || + oPreviousItem.element.style.display == _NONE) ? + oPreviousItem.getPreviousEnabledSibling() : oPreviousItem; + + } + + return returnVal; + + }, + + + /** + * @method focus + * @description Causes the menu item to receive the focus and fires the + * focus event. + */ + focus: function () { + + var oParent = this.parent, + oAnchor = this._oAnchor, + oActiveItem = oParent.activeItem; + + + function setFocus() { + + try { + + if (!(UA.ie && !document.hasFocus())) { + + if (oActiveItem) { + + oActiveItem.blurEvent.fire(); + + } + + oAnchor.focus(); + + this.focusEvent.fire(); + + } + + } + catch(e) { + + } + + } + + + if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) && + this.element.style.display != _NONE) { + + + /* + Setting focus via a timer fixes a race condition in Firefox, IE + and Opera where the browser viewport jumps as it trys to + position and focus the menu. + */ + + Lang.later(0, this, setFocus); + + } + + }, + + + /** + * @method blur + * @description Causes the menu item to lose focus and fires the + * blur event. + */ + blur: function () { + + var oParent = this.parent; + + if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) { + + Lang.later(0, this, function () { + + try { + + this._oAnchor.blur(); + this.blurEvent.fire(); + + } + catch (e) { + + } + + }, 0); + + } + + }, + + + /** + * @method hasFocus + * @description Returns a boolean indicating whether or not the menu item + * has focus. + * @return {Boolean} + */ + hasFocus: function () { + + return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this); + + }, + + + /** + * @method destroy + * @description Removes the menu item's <li> element + * from its parent <ul> element. + */ + destroy: function () { + + var oEl = this.element, + oSubmenu, + oParentNode, + aEventData, + i; + + + if (oEl) { + + + // If the item has a submenu, destroy it first + + oSubmenu = this.cfg.getProperty(_SUBMENU); + + if (oSubmenu) { + + oSubmenu.destroy(); + + } + + + // Remove the element from the parent node + + oParentNode = oEl.parentNode; + + if (oParentNode) { + + oParentNode.removeChild(oEl); + + this.destroyEvent.fire(); + + } + + + // Remove CustomEvent listeners + + i = EVENT_TYPES.length - 1; + + do { + + aEventData = EVENT_TYPES[i]; + + this[aEventData[0]].unsubscribeAll(); + + } + while (i--); + + + this.cfg.configChangedEvent.unsubscribeAll(); + + } + + }, + + + /** + * @method toString + * @description Returns a string representing the menu item. + * @return {String} + */ + toString: function () { + + var sReturnVal = _MENUITEM, + sId = this.id; + + if (sId) { + + sReturnVal += (_SPACE + sId); + + } + + return sReturnVal; + + } + +}; + +Lang.augmentProto(MenuItem, YAHOO.util.EventProvider); + +})(); +(function () { + + var _XY = "xy", + _MOUSEDOWN = "mousedown", + _CONTEXTMENU = "ContextMenu", + _SPACE = " "; + +/** +* Creates a list of options or commands which are made visible in response to +* an HTML element's "contextmenu" event ("mousedown" for Opera). +* +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source for the +* context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +* <div> element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the <select> element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +* @class ContextMenu +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) { + + YAHOO.widget.ContextMenu.superclass.constructor.call(this, p_oElement, p_oConfig); + +}; + + +var Event = YAHOO.util.Event, + UA = YAHOO.env.ua, + ContextMenu = YAHOO.widget.ContextMenu, + + + + /** + * Constant representing the name of the ContextMenu's events + * @property EVENT_TYPES + * @private + * @final + * @type Object + */ + EVENT_TYPES = { + + "TRIGGER_CONTEXT_MENU": "triggerContextMenu", + "CONTEXT_MENU": (UA.opera ? _MOUSEDOWN : "contextmenu"), + "CLICK": "click" + + }, + + + /** + * Constant representing the ContextMenu's configuration properties + * @property DEFAULT_CONFIG + * @private + * @final + * @type Object + */ + TRIGGER_CONFIG = { + key: "trigger", + suppressEvent: true + }; + + +/** +* @method position +* @description "beforeShow" event handler used to position the contextmenu. +* @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 {Array} p_aPos Array representing the xy position for the context menu. +*/ +function position(p_sType, p_aArgs, p_aPos) { + + this.cfg.setProperty(_XY, p_aPos); + + this.beforeShowEvent.unsubscribe(position, p_aPos); + +} + + +YAHOO.lang.extend(ContextMenu, YAHOO.widget.Menu, { + + + +// Private properties + + +/** +* @property _oTrigger +* @description Object reference to the current value of the "trigger" +* configuration property. +* @default null +* @private +* @type String|HTMLElement|Array +*/ +_oTrigger: null, + + +/** +* @property _bCancelled +* @description Boolean indicating if the display of the context menu should +* be cancelled. +* @default false +* @private +* @type Boolean +*/ +_bCancelled: false, + + + +// Public properties + + +/** +* @property contextEventTarget +* @description Object reference for the HTML element that was the target of the +* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of +* the context menu. +* @default null +* @type HTMLElement +*/ +contextEventTarget: null, + + + +// Events + + +/** +* @event triggerContextMenuEvent +* @description Custom Event wrapper for the "contextmenu" DOM event +* ("mousedown" for Opera) fired by the element(s) that trigger the display of +* the context menu. +*/ +triggerContextMenuEvent: null, + + + +/** +* @method init +* @description The ContextMenu class's initialization method. This method is +* automatically called by the constructor, and sets up all DOM references for +* pre-existing markup, and creates required markup if it is not already present. +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source for +* the context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +* <div> element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the <select> element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +*/ +init: function(p_oElement, p_oConfig) { + + + // Call the init of the superclass (YAHOO.widget.Menu) + + ContextMenu.superclass.init.call(this, p_oElement); + + + this.beforeInitEvent.fire(ContextMenu); + + + if (p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + this.initEvent.fire(ContextMenu); + +}, + + +/** +* @method initEvents +* @description Initializes the custom events for the context menu. +*/ +initEvents: function() { + + ContextMenu.superclass.initEvents.call(this); + + // Create custom events + + this.triggerContextMenuEvent = this.createEvent(EVENT_TYPES.TRIGGER_CONTEXT_MENU); + + this.triggerContextMenuEvent.signature = YAHOO.util.CustomEvent.LIST; + +}, + + +/** +* @method cancel +* @description Cancels the display of the context menu. +*/ +cancel: function() { + + this._bCancelled = true; + +}, + + + +// Private methods + + +/** +* @method _removeEventHandlers +* @description Removes all of the DOM event handlers from the HTML element(s) +* whose "context menu" event ("click" for Opera) trigger the display of +* the context menu. +* @private +*/ +_removeEventHandlers: function() { + + var oTrigger = this._oTrigger; + + + // Remove the event handlers from the trigger(s) + + if (oTrigger) { + + Event.removeListener(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu); + + if (UA.opera) { + + Event.removeListener(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick); + + } + + } + +}, + + + +// Private event handlers + + + +/** +* @method _onTriggerClick +* @description "click" event handler for the HTML element(s) identified as the +* "trigger" for the context menu. Used to cancel default behaviors in Opera. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerClick: function(p_oEvent, p_oMenu) { + + if (p_oEvent.ctrlKey) { + + Event.stopEvent(p_oEvent); + + } + +}, + + +/** +* @method _onTriggerContextMenu +* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML +* element(s) that trigger the display of the context menu. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerContextMenu: function(p_oEvent, p_oMenu) { + + var aXY; + + if (!(p_oEvent.type == _MOUSEDOWN && !p_oEvent.ctrlKey)) { + + this.contextEventTarget = Event.getTarget(p_oEvent); + + this.triggerContextMenuEvent.fire(p_oEvent); + + + if (!this._bCancelled) { + + /* + Prevent the browser's default context menu from appearing and + stop the propagation of the "contextmenu" event so that + other ContextMenu instances are not displayed. + */ + + Event.stopEvent(p_oEvent); + + + // Hide any other Menu instances that might be visible + + YAHOO.widget.MenuManager.hideVisible(); + + + + // Position and display the context menu + + aXY = Event.getXY(p_oEvent); + + + if (!YAHOO.util.Dom.inDocument(this.element)) { + + this.beforeShowEvent.subscribe(position, aXY); + + } + else { + + this.cfg.setProperty(_XY, aXY); + + } + + + this.show(); + + } + + this._bCancelled = false; + + } + +}, + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the context menu. +* @return {String} +*/ +toString: function() { + + var sReturnVal = _CONTEXTMENU, + sId = this.id; + + if (sId) { + + sReturnVal += (_SPACE + sId); + + } + + return sReturnVal; + +}, + + +/** +* @method initDefaultConfig +* @description Initializes the class's configurable properties which can be +* changed using the context menu's Config object ("cfg"). +*/ +initDefaultConfig: function() { + + ContextMenu.superclass.initDefaultConfig.call(this); + + /** + * @config trigger + * @description The HTML element(s) whose "contextmenu" event ("mousedown" + * for Opera) trigger the display of the context menu. Can be a string + * representing the id attribute of the HTML element, an object reference + * for the HTML element, or an array of strings or HTML element references. + * @default null + * @type String|HTMLElement|Array + */ + this.cfg.addProperty(TRIGGER_CONFIG.key, + { + handler: this.configTrigger, + suppressEvent: TRIGGER_CONFIG.suppressEvent + } + ); + +}, + + +/** +* @method destroy +* @description Removes the context menu's <div> element +* (and accompanying child nodes) from the document. +*/ +destroy: function() { + + // Remove the DOM event handlers from the current trigger(s) + + this._removeEventHandlers(); + + + // Continue with the superclass implementation of this method + + ContextMenu.superclass.destroy.call(this); + +}, + + + +// Public event handlers for configuration properties + + +/** +* @method configTrigger +* @description Event handler for when the value of the "trigger" configuration +* property changes. +* @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.ContextMenu} p_oMenu Object representing the context +* menu that fired the event. +*/ +configTrigger: function(p_sType, p_aArgs, p_oMenu) { + + var oTrigger = p_aArgs[0]; + + if (oTrigger) { + + /* + If there is a current "trigger" - remove the event handlers + from that element(s) before assigning new ones + */ + + if (this._oTrigger) { + + this._removeEventHandlers(); + + } + + this._oTrigger = oTrigger; + + + /* + Listen for the "mousedown" event in Opera b/c it does not + support the "contextmenu" event + */ + + Event.on(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu, this, true); + + + /* + Assign a "click" event handler to the trigger element(s) for + Opera to prevent default browser behaviors. + */ + + if (UA.opera) { + + Event.on(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick, this, true); + + } + + } + else { + + this._removeEventHandlers(); + + } + +} + +}); // END YAHOO.lang.extend + +}()); + + + +/** +* Creates an item for a context menu. +* +* @param {String} p_oObject String specifying the text of the context menu item. +* @param {HTMLLIElement} p_oObject Object specifying the +* <li> element of the context menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the <optgroup> element of the context +* menu item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the <option> element of the context menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu item. See configuration class +* documentation for more details. +* @class ContextMenuItem +* @constructor +* @extends YAHOO.widget.MenuItem +* @deprecated As of version 2.4.0 items for YAHOO.widget.ContextMenu instances +* are of type YAHOO.widget.MenuItem. +*/ +YAHOO.widget.ContextMenuItem = YAHOO.widget.MenuItem; +(function () { + + var Lang = YAHOO.lang, + + // String constants + + _STATIC = "static", + _DYNAMIC_STATIC = "dynamic," + _STATIC, + _DISABLED = "disabled", + _SELECTED = "selected", + _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay", + _SUBMENU = "submenu", + _VISIBLE = "visible", + _SPACE = " ", + _SUBMENU_TOGGLE_REGION = "submenutoggleregion", + _MENUBAR = "MenuBar"; + +/** +* Horizontal collection of items, each of which can contain a submenu. +* +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the <div> element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the <select> element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +* @class MenuBar +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) { + + YAHOO.widget.MenuBar.superclass.constructor.call(this, p_oElement, p_oConfig); + +}; + + +/** +* @method checkPosition +* @description Checks to make sure that the value of the "position" property +* is one of the supported strings. Returns true if the position is supported. +* @private +* @param {Object} p_sPosition String specifying the position of the menu. +* @return {Boolean} +*/ +function checkPosition(p_sPosition) { + + var returnVal = false; + + if (Lang.isString(p_sPosition)) { + + returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1); + + } + + return returnVal; + +} + + +var Event = YAHOO.util.Event, + MenuBar = YAHOO.widget.MenuBar, + + POSITION_CONFIG = { + key: "position", + value: _STATIC, + validator: checkPosition, + supercedes: [_VISIBLE] + }, + + SUBMENU_ALIGNMENT_CONFIG = { + key: "submenualignment", + value: ["tl","bl"] + }, + + AUTO_SUBMENU_DISPLAY_CONFIG = { + key: _AUTO_SUBMENU_DISPLAY, + value: false, + validator: Lang.isBoolean, + suppressEvent: true + }, + + SUBMENU_TOGGLE_REGION_CONFIG = { + key: _SUBMENU_TOGGLE_REGION, + value: false, + validator: Lang.isBoolean + }; + + + +Lang.extend(MenuBar, YAHOO.widget.Menu, { + +/** +* @method init +* @description The MenuBar class's initialization method. This method is +* automatically called by the constructor, and sets up all DOM references for +* pre-existing markup, and creates required markup if it is not already present. +* @param {String} p_oElement String specifying the id attribute of the +* <div> element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +* <select> element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the <div> element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the <select> element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +*/ +init: function(p_oElement, p_oConfig) { + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuBarItem; + + } + + + // Call the init of the superclass (YAHOO.widget.Menu) + + MenuBar.superclass.init.call(this, p_oElement); + + + this.beforeInitEvent.fire(MenuBar); + + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + this.initEvent.fire(MenuBar); + +}, + + + +// Constants + + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the menu +* bar's <div> element. +* @default "yuimenubar" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubar", + + +/** +* @property SUBMENU_TOGGLE_REGION_WIDTH +* @description Width (in pixels) of the area of a MenuBarItem that, when pressed, will toggle the +* display of the MenuBarItem's submenu. +* @default 20 +* @final +* @type Number +*/ +SUBMENU_TOGGLE_REGION_WIDTH: 20, + + +// Protected event handlers + + +/** +* @method _onKeyDown +* @description "keydown" Custom Event handler for the menu bar. +* @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.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) { + + var oEvent = p_aArgs[0], + oItem = p_aArgs[1], + oSubmenu, + oItemCfg, + oNextItem; + + + if(oItem && !oItem.cfg.getProperty(_DISABLED)) { + + oItemCfg = oItem.cfg; + + switch(oEvent.keyCode) { + + case 37: // Left arrow + case 39: // Right arrow + + if(oItem == this.activeItem && !oItemCfg.getProperty(_SELECTED)) { + + oItemCfg.setProperty(_SELECTED, true); + + } + else { + + oNextItem = (oEvent.keyCode == 37) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if(oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty(_SELECTED, true); + + oSubmenu = oNextItem.cfg.getProperty(_SUBMENU); + + if(oSubmenu) { + + oSubmenu.show(); + oSubmenu.setInitialFocus(); + + } + else { + oNextItem.focus(); + } + + } + + } + + Event.preventDefault(oEvent); + + break; + + case 40: // Down arrow + + if(this.activeItem != oItem) { + + this.clearActiveItem(); + + oItemCfg.setProperty(_SELECTED, true); + oItem.focus(); + + } + + oSubmenu = oItemCfg.getProperty(_SUBMENU); + + if(oSubmenu) { + + if(oSubmenu.cfg.getProperty(_VISIBLE)) { + + oSubmenu.setInitialSelection(); + oSubmenu.setInitialFocus(); + + } + else { + + oSubmenu.show(); + oSubmenu.setInitialFocus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + } + + } + + + if(oEvent.keyCode == 27 && this.activeItem) { // Esc key + + oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU); + + if(oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.cfg.setProperty(_SELECTED, false); + this.activeItem.blur(); + + } + + Event.preventDefault(oEvent); + + } + +}, + + +/** +* @method _onClick +* @description "click" event handler for the menu bar. +* @protected +* @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.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onClick: function(p_sType, p_aArgs, p_oMenuBar) { + + MenuBar.superclass._onClick.call(this, p_sType, p_aArgs, p_oMenuBar); + + var oItem = p_aArgs[1], + bReturnVal = true, + oItemEl, + oEvent, + oTarget, + oActiveItem, + oConfig, + oSubmenu, + nMenuItemX, + nToggleRegion; + + + var toggleSubmenuDisplay = function () { + + if(oSubmenu.cfg.getProperty(_VISIBLE)) { + + oSubmenu.hide(); + + } + else { + + oSubmenu.show(); + + } + + }; + + + if(oItem && !oItem.cfg.getProperty(_DISABLED)) { + + oEvent = p_aArgs[0]; + oTarget = Event.getTarget(oEvent); + oActiveItem = this.activeItem; + oConfig = this.cfg; + + + // Hide any other submenus that might be visible + + if(oActiveItem && oActiveItem != oItem) { + + this.clearActiveItem(); + + } + + + oItem.cfg.setProperty(_SELECTED, true); + + + // Show the submenu for the item + + oSubmenu = oItem.cfg.getProperty(_SUBMENU); + + + if(oSubmenu) { + + oItemEl = oItem.element; + nMenuItemX = YAHOO.util.Dom.getX(oItemEl); + nToggleRegion = nMenuItemX + (oItemEl.offsetWidth - this.SUBMENU_TOGGLE_REGION_WIDTH); + + if (oConfig.getProperty(_SUBMENU_TOGGLE_REGION)) { + + if (Event.getPageX(oEvent) > nToggleRegion) { + + toggleSubmenuDisplay(); + + Event.preventDefault(oEvent); + + /* + Return false so that other click event handlers are not called when the + user clicks inside the toggle region. + */ + bReturnVal = false; + + } + + } + else { + + toggleSubmenuDisplay(); + + } + + } + + } + + + return bReturnVal; + +}, + + + +// Public methods + +/** +* @method configSubmenuToggle +* @description Event handler for when the "submenutoggleregion" configuration property of +* a MenuBar changes. +* @param {String} p_sType The name of the event that was fired. +* @param {Array} p_aArgs Collection of arguments sent when the event was fired. +*/ +configSubmenuToggle: function (p_sType, p_aArgs) { + + var bSubmenuToggle = p_aArgs[0]; + + if (bSubmenuToggle) { + + this.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false); + + } + +}, + + +/** +* @method toString +* @description Returns a string representing the menu bar. +* @return {String} +*/ +toString: function() { + + var sReturnVal = _MENUBAR, + sId = this.id; + + if(sId) { + + sReturnVal += (_SPACE + sId); + + } + + return sReturnVal; + +}, + + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu bar's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function() { + + MenuBar.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + // Add configuration properties + + + /* + Set the default value for the "position" configuration property + to "static" by re-adding the property. + */ + + + /** + * @config position + * @description String indicating how a menu bar should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menu bars + * are visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menu bars are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and can + * overlay other elements on the screen. + * @default static + * @type String + */ + oConfig.addProperty( + POSITION_CONFIG.key, + { + handler: this.configPosition, + value: POSITION_CONFIG.value, + validator: POSITION_CONFIG.validator, + supercedes: POSITION_CONFIG.supercedes + } + ); + + + /* + Set the default value for the "submenualignment" configuration property + to ["tl","bl"] by re-adding the property. + */ + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu bar item. The format is: [itemCorner, submenuCorner]. + * @default ["tl","bl"] + * @type Array + */ + oConfig.addProperty( + SUBMENU_ALIGNMENT_CONFIG.key, + { + value: SUBMENU_ALIGNMENT_CONFIG.value, + suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent + } + ); + + + /* + Change the default value for the "autosubmenudisplay" configuration + property to "false" by re-adding the property. + */ + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu bar's items. + * @default false + * @type Boolean + */ + oConfig.addProperty( + AUTO_SUBMENU_DISPLAY_CONFIG.key, + { + value: AUTO_SUBMENU_DISPLAY_CONFIG.value, + validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator, + suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent + } + ); + + + /** + * @config submenutoggleregion + * @description Boolean indicating if only a specific region of a MenuBarItem should toggle the + * display of a submenu. The default width of the region is determined by the value of the + * SUBMENU_TOGGLE_REGION_WIDTH property. If set to true, the autosubmenudisplay + * configuration property will be set to false, and any click event listeners will not be + * called when the user clicks inside the submenu toggle region of a MenuBarItem. If the + * user clicks outside of the submenu toggle region, the MenuBarItem will maintain its + * standard behavior. + * @default false + * @type Boolean + */ + oConfig.addProperty( + SUBMENU_TOGGLE_REGION_CONFIG.key, + { + value: SUBMENU_TOGGLE_REGION_CONFIG.value, + validator: SUBMENU_TOGGLE_REGION_CONFIG.validator, + handler: this.configSubmenuToggle + } + ); + +} + +}); // END YAHOO.lang.extend + +}()); + + + +/** +* Creates an item for a menu bar. +* +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +* <li> element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the <optgroup> element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the <option> element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +* @class MenuBarItem +* @constructor +* @extends YAHOO.widget.MenuItem +*/ +YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) { + + YAHOO.widget.MenuBarItem.superclass.constructor.call(this, p_oObject, p_oConfig); + +}; + +YAHOO.lang.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, { + + + +/** +* @method init +* @description The MenuBarItem class's initialization method. This method is +* automatically called by the constructor, and sets up all DOM references for +* pre-existing markup, and creates required markup if it is not already present. +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +* <li> element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the <optgroup> element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the <option> element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +*/ +init: function(p_oObject, p_oConfig) { + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = YAHOO.widget.Menu; + + } + + + /* + Call the init of the superclass (YAHOO.widget.MenuItem) + Note: We don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject); + + + var oConfig = this.cfg; + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig, true); + + } + + oConfig.fireQueue(); + +}, + + + +// Constants + + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +* <li> element of the menu bar item. +* @default "yuimenubaritem" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubaritem", + + +/** +* @property CSS_LABEL_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +* menu bar item's <a> element. +* @default "yuimenubaritemlabel" +* @final +* @type String +*/ +CSS_LABEL_CLASS_NAME: "yuimenubaritemlabel", + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the menu bar item. +* @return {String} +*/ +toString: function() { + + var sReturnVal = "MenuBarItem"; + + if(this.cfg && this.cfg.getProperty("text")) { + + sReturnVal += (": " + this.cfg.getProperty("text")); + + } + + return sReturnVal; + +} + +}); // END YAHOO.lang.extend +YAHOO.register("menu", YAHOO.widget.Menu, {version: "2.7.0", build: "1799"});