/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * @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:
*<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,
_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,
bFocusListenerInitialized = false,
oFocusedElement,
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
};
function onDocFocus(event) {
oFocusedElement = Event.getTarget(event);
}
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);
YAHOO.log("Source element: " + this.srcElement.tagName, "info", this.toString());
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);
YAHOO.log("Source element: " + this.srcElement.tagName, "info", this.toString());
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);
YAHOO.log("No source element found. Created element with id: " + this.id, "info", this.toString());
}
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);
if (!bFocusListenerInitialized) {
Event.onFocus(document, onDocFocus);
bFocusListenerInitialized = true;
}
// 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;
YAHOO.log("Searching DOM for items to initialize.", "info", this.toString());
if (sSrcElementTagName) {
switch (sSrcElementTagName) {
case _DIV_UPPERCASE:
aListElements = this._aListElements;
nListElements = aListElements.length;
if (nListElements > 0) {
YAHOO.log("Found " + nListElements + " item groups to initialize.",
"info", this.toString());
i = nListElements - 1;
do {
oNode = aListElements[i].firstChild;
if (oNode) {
YAHOO.log("Scanning " +
aListElements[i].childNodes.length +
" child nodes for items to initialize.", "info", this.toString());
do {
if (oNode && oNode.tagName &&
oNode.tagName.toUpperCase() == _LI) {
YAHOO.log("Initializing " +
oNode.tagName + " node.", "info", this.toString());
this.addItem(new this.ITEM_TYPE(oNode,
{ parent: this }), i);
}
}
while ((oNode = oNode.nextSibling));
}
}
while (i--);
}
break;
case _SELECT:
YAHOO.log("Scanning " +
oSrcElement.childNodes.length +
" child nodes for items to initialize.", "info", this.toString());
oNode = oSrcElement.firstChild;
do {
if (oNode && oNode.tagName) {
switch (oNode.tagName.toUpperCase()) {
case _OPTGROUP:
case _OPTION:
YAHOO.log("Initializing " +
oNode.tagName + " node.", "info", this.toString());
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<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 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 () {
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,
oFocusedEl;
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 {
// Focus the element that previously had focus
oFocusedEl = this._focusedElement;
if (oFocusedEl && oFocusedEl.focus) {
try {
oFocusedEl.focus();
}
catch(ex) {
}
}
}
}
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<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);
YAHOO.log("Destroyed.", "info", this.toString());
},
/**
* @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());
},
_doItemSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) {
var oItem = p_aArgs[0],
oSubmenu = oItem.cfg.getProperty(_SUBMENU);
if (oSubmenu) {
oSubmenu.subscribe.apply(oSubmenu, p_oObject);
}
},
_doSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) {
var oSubmenu = this.cfg.getProperty(_SUBMENU);
if (oSubmenu) {
oSubmenu.subscribe.apply(oSubmenu, p_oObject);
}
},
/**
* 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 () {
// Subscribe to the event for this Menu instance
Menu.superclass.subscribe.apply(this, arguments);
// Subscribe to the "itemAdded" event so that all future submenus
// also subscribe to this event
Menu.superclass.subscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, 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, this._doSubmenuSubscribe, arguments);
}
}
while (i--);
}
}
},
unsubscribe: function () {
// Remove the event for this Menu instance
Menu.superclass.unsubscribe.apply(this, arguments);
// Remove the "itemAdded" event so that all future submenus don't have
// the event handler
Menu.superclass.unsubscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, 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.unsubscribe.apply(oSubmenu, arguments);
}
else {
oItem.cfg.unsubscribeFromConfigEvent(_SUBMENU, this._doSubmenuSubscribe, 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 Menu'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; noMenuItem.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: { id: [menu id], itemdata:
* [array of values for
* items] }
.<div>
element of the menu.<div>
element of the
* menu. {
* 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 getNextSibling
* @description Finds the menu item's next sibling.
* @return YAHOO.widget.MenuItem
*/
getNextSibling: function () {
var isUL = function (el) {
return (el.nodeName.toLowerCase() === "ul");
},
menuitemEl = this.element,
next = Dom.getNextSibling(menuitemEl),
parent,
sibling,
list;
if (!next) {
parent = menuitemEl.parentNode;
sibling = Dom.getNextSiblingBy(parent, isUL);
if (sibling) {
list = sibling;
}
else {
list = Dom.getFirstChildBy(parent.parentNode, isUL);
}
next = Dom.getFirstChild(list);
}
return YAHOO.widget.MenuManager.getMenuItem(next.id);
},
/**
* @method getNextEnabledSibling
* @description Finds the menu item's next enabled sibling.
* @return YAHOO.widget.MenuItem
*/
getNextEnabledSibling: function () {
var next = this.getNextSibling();
return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getNextEnabledSibling() : next;
},
/**
* @method getPreviousSibling
* @description Finds the menu item's previous sibling.
* @return {YAHOO.widget.MenuItem}
*/
getPreviousSibling: function () {
var isUL = function (el) {
return (el.nodeName.toLowerCase() === "ul");
},
menuitemEl = this.element,
next = Dom.getPreviousSibling(menuitemEl),
parent,
sibling,
list;
if (!next) {
parent = menuitemEl.parentNode;
sibling = Dom.getPreviousSiblingBy(parent, isUL);
if (sibling) {
list = sibling;
}
else {
list = Dom.getLastChildBy(parent.parentNode, isUL);
}
next = Dom.getLastChild(list);
}
return YAHOO.widget.MenuManager.getMenuItem(next.id);
},
/**
* @method getPreviousEnabledSibling
* @description Finds the menu item's previous enabled sibling.
* @return {YAHOO.widget.MenuItem}
*/
getPreviousEnabledSibling: function () {
var next = this.getPreviousSibling();
return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getPreviousEnabledSibling() : next;
},
/**
* @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.8.0r4", build: "2449"});