(function () {
/**
* 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
* <code><div></code> element of the menu.
* @param {String} p_oElement String specifying the id attribute of the
* <code><select></code> element to be used as the data source
* for the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
* specifying the <code><div></code> element of the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
* Object specifying the <code><select></code> 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) {
if (typeof p_sPosition == "string") {
return ("dynamic,static".indexOf((p_sPosition.toLowerCase())) != -1);
}
}
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,
Lang = YAHOO.lang,
UA = YAHOO.env.ua,
m_oShadowTemplate,
/**
* Constant representing the name of the Menu's events
* @property EVENT_TYPES
* @private
* @final
* @type Object
*/
EVENT_TYPES = {
"MOUSE_OVER": "mouseover",
"MOUSE_OUT": "mouseout",
"MOUSE_DOWN": "mousedown",
"MOUSE_UP": "mouseup",
"CLICK": "click",
"KEY_PRESS": "keypress",
"KEY_DOWN": "keydown",
"KEY_UP": "keyup",
"FOCUS": "focus",
"BLUR": "blur",
"ITEM_ADDED": "itemAdded",
"ITEM_REMOVED": "itemRemoved"
},
/**
* Constant representing the Menu's configuration properties
* @property DEFAULT_CONFIG
* @private
* @final
* @type Object
*/
DEFAULT_CONFIG = {
"VISIBLE": {
key: "visible",
value: false,
validator: Lang.isBoolean
},
"CONSTRAIN_TO_VIEWPORT": {
key: "constraintoviewport",
value: true,
validator: Lang.isBoolean,
supercedes: ["iframe","x","y","xy"]
},
"POSITION": {
key: "position",
value: "dynamic",
validator: checkPosition,
supercedes: ["visible", "iframe"]
},
"SUBMENU_ALIGNMENT": {
key: "submenualignment",
value: ["tl","tr"],
suppressEvent: true
},
"AUTO_SUBMENU_DISPLAY": {
key: "autosubmenudisplay",
value: true,
validator: Lang.isBoolean,
suppressEvent: true
},
"SHOW_DELAY": {
key: "showdelay",
value: 250,
validator: Lang.isNumber,
suppressEvent: true
},
"HIDE_DELAY": {
key: "hidedelay",
value: 0,
validator: Lang.isNumber,
suppressEvent: true
},
"SUBMENU_HIDE_DELAY": {
key: "submenuhidedelay",
value: 250,
validator: Lang.isNumber,
suppressEvent: true
},
"CLICK_TO_HIDE": {
key: "clicktohide",
value: true,
validator: Lang.isBoolean,
suppressEvent: true
},
"CONTAINER": {
key: "container",
suppressEvent: true
},
"SCROLL_INCREMENT": {
key: "scrollincrement",
value: 1,
validator: Lang.isNumber,
supercedes: ["maxheight"],
suppressEvent: true
},
"MIN_SCROLL_HEIGHT": {
key: "minscrollheight",
value: 90,
validator: Lang.isNumber,
supercedes: ["maxheight"],
suppressEvent: true
},
"MAX_HEIGHT": {
key: "maxheight",
value: 0,
validator: Lang.isNumber,
supercedes: ["iframe"],
suppressEvent: true
},
"CLASS_NAME": {
key: "classname",
value: null,
validator: Lang.isString,
suppressEvent: true
},
"DISABLED": {
key: "disabled",
value: false,
validator: Lang.isBoolean,
suppressEvent: true
}
};
YAHOO.lang.extend(Menu, Overlay, {
// Constants
/**
* @property CSS_CLASS_NAME
* @description String representing the CSS class(es) to be applied to the
* menu's <code><div></code> 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 <code><li></code> element,
* <code><optgroup></code> element or <code><option></code>)
* 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 [-10000, -10000]
* @final
* @type Array
*/
OFF_SCREEN_POSITION: [-10000, -10000],
// Private properties
/**
* @property _nHideDelayId
* @description Number representing the time-out setting used to cancel the
* hiding of a menu.
* @default null
* @private
* @type Number
*/
_nHideDelayId: null,
/**
* @property _nShowDelayId
* @description Number representing the time-out setting used to cancel the
* showing of a menu.
* @default null
* @private
* @type Number
*/
_nShowDelayId: null,
/**
* @property _nSubmenuHideDelayId
* @description Number representing the time-out setting used to cancel the
* hiding of a submenu.
* @default null
* @private
* @type Number
*/
_nSubmenuHideDelayId: null,
/**
* @property _nBodyScrollId
* @description Number representing the time-out setting used to cancel the
* scrolling of the menu's body element.
* @default null
* @private
* @type Number
*/
_nBodyScrollId: null,
/**
* @property _bHideDelayEventHandlersAssigned
* @description Boolean indicating if the "mouseover" and "mouseout" event
* handlers used for hiding the menu via a call to "window.setTimeout" have
* already been assigned.
* @default false
* @private
* @type Boolean
*/
_bHideDelayEventHandlersAssigned: 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 <code><ul></code> elements, each of which is
* the parent node for each item's <code><li></code> 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
* <code><select></code> or <code><div></code>) used to
* create the menu.
* @default null
* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a
* href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.
* html#ID-22445964">HTMLDivElement</a>
*/
srcElement: null,
// Events
/**
* @event mouseOverEvent
* @description Fires when the mouse has entered the menu. Passes back
* the DOM Event object as an argument.
*/
mouseOverEvent: null,
/**
* @event mouseOutEvent
* @description Fires when the mouse has left the menu. Passes back the DOM
* Event object as an argument.
* @type YAHOO.util.CustomEvent
*/
mouseOutEvent: null,
/**
* @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
*/
mouseDownEvent: null,
/**
* @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
*/
mouseUpEvent: null,
/**
* @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
*/
clickEvent: null,
/**
* @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
*/
keyPressEvent: null,
/**
* @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
*/
keyDownEvent: null,
/**
* @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
*/
keyUpEvent: null,
/**
* @event itemAddedEvent
* @description Fires when an item is added to the menu.
* @type YAHOO.util.CustomEvent
*/
itemAddedEvent: null,
/**
* @event itemRemovedEvent
* @description Fires when an item is removed to the menu.
* @type YAHOO.util.CustomEvent
*/
itemRemovedEvent: null,
/**
* @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
* <code><div></code> element of the menu.
* @param {String} p_oElement String specifying the id attribute of the
* <code><select></code> element to be used as the data source
* for the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
* specifying the <code><div></code> element of the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
* Object specifying the <code><select></code> 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 (typeof p_oElement == "string") {
oElement = document.getElementById(p_oElement);
}
else if (p_oElement.tagName) {
oElement = p_oElement;
}
if (oElement && oElement.tagName) {
switch(oElement.tagName.toUpperCase()) {
case "DIV":
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);
this.logger = new YAHOO.widget.LogWriter(this.toString());
this.logger.log("Source element: " + this.srcElement.tagName);
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);
this.logger = new YAHOO.widget.LogWriter(this.toString());
this.logger.log("Source element: " + this.srcElement.tagName);
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);
this.logger = new YAHOO.widget.LogWriter(this.toString());
this.logger.log("No source element found. " +
"Created element with id: " + this.id);
}
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.renderEvent.subscribe(this.onRender);
this.beforeShowEvent.subscribe(this._onBeforeShow);
this.hideEvent.subscribe(this.positionOffScreen);
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);
if (UA.gecko || 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") {
// 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":
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;
this.logger.log("Searching DOM for items to initialize.");
if (sSrcElementTagName) {
switch (sSrcElementTagName) {
case "DIV":
aListElements = this._aListElements;
nListElements = aListElements.length;
if (nListElements > 0) {
this.logger.log("Found " + nListElements +
" item groups to initialize.");
i = nListElements - 1;
do {
oNode = aListElements[i].firstChild;
if (oNode) {
this.logger.log("Scanning " +
aListElements[i].childNodes.length +
" child nodes for items to initialize.");
do {
if (oNode && oNode.tagName &&
oNode.tagName.toUpperCase() == "LI") {
this.logger.log("Initializing " +
oNode.tagName + " node.");
this.addItem(new this.ITEM_TYPE(oNode,
{ parent: this }), i);
}
}
while ((oNode = oNode.nextSibling));
}
}
while (i--);
}
break;
case "SELECT":
this.logger.log("Scanning " +
oSrcElement.childNodes.length +
" child nodes for items to initialize.");
oNode = oSrcElement.firstChild;
do {
if (oNode && oNode.tagName) {
switch (oNode.tagName.toUpperCase()) {
case "OPTGROUP":
case "OPTION":
this.logger.log("Initializing " +
oNode.tagName + " node.");
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;
for(var i=0; i<nItems; i++) {
oItem = aItems[i];
if (oItem && !oItem.cfg.getProperty("disabled") &&
oItem.element.style.display != "none") {
return oItem;
}
}
},
/**
* @method _addItemToGroup
* @description Adds a menu item to a group.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the
* item belongs.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
* instance to be added to the menu.
* @param {String} p_oItem String specifying the text of the item to be added
* to the menu.
* @param {Object} p_oItem Object literal containing a set of menu item
* configuration properties.
* @param {Number} p_nItemIndex Optional. Number indicating the index at
* which the menu item should be added.
* @return {YAHOO.widget.MenuItem}
*/
_addItemToGroup: function (p_nGroupIndex, p_oItem, p_nItemIndex) {
var oItem,
nGroupIndex,
aGroup,
oGroupItem,
bAppend,
oNextItemSibling,
nItemIndex;
function getNextItemSibling(p_aArray, p_nStartIndex) {
return (p_aArray[p_nStartIndex] || getNextItemSibling(p_aArray,
(p_nStartIndex+1)));
}
if (p_oItem instanceof this.ITEM_TYPE) {
oItem = p_oItem;
oItem.parent = this;
}
else if (typeof p_oItem == "string") {
oItem = new this.ITEM_TYPE(p_oItem, { parent: this });
}
else if (typeof p_oItem == "object") {
p_oItem.parent = this;
oItem = new this.ITEM_TYPE(p_oItem.text, p_oItem);
}
if (oItem) {
if (oItem.cfg.getProperty("selected")) {
this.activeItem = oItem;
}
nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
aGroup = this._getItemGroup(nGroupIndex);
if (!aGroup) {
aGroup = this._createItemGroup(nGroupIndex);
}
if (typeof p_nItemIndex == "number") {
bAppend = (p_nItemIndex >= aGroup.length);
if (aGroup[p_nItemIndex]) {
aGroup.splice(p_nItemIndex, 0, oItem);
}
else {
aGroup[p_nItemIndex] = oItem;
}
oGroupItem = aGroup[p_nItemIndex];
if (oGroupItem) {
if (bAppend && (!oGroupItem.element.parentNode ||
oGroupItem.element.parentNode.nodeType == 11)) {
this._aListElements[nGroupIndex].appendChild(
oGroupItem.element);
}
else {
oNextItemSibling = getNextItemSibling(aGroup,
(p_nItemIndex+1));
if (oNextItemSibling && (!oGroupItem.element.parentNode ||
oGroupItem.element.parentNode.nodeType == 11)) {
this._aListElements[nGroupIndex].insertBefore(
oGroupItem.element,
oNextItemSibling.element);
}
}
oGroupItem.parent = this;
this._subscribeToItemEvents(oGroupItem);
this._configureSubmenu(oGroupItem);
this._updateItemProperties(nGroupIndex);
this.logger.log("Item inserted." +
" Text: " + oGroupItem.cfg.getProperty("text") + ", " +
" Index: " + oGroupItem.index + ", " +
" Group Index: " + oGroupItem.groupIndex);
this.itemAddedEvent.fire(oGroupItem);
this.changeContentEvent.fire();
return oGroupItem;
}
}
else {
nItemIndex = aGroup.length;
aGroup[nItemIndex] = oItem;
oGroupItem = aGroup[nItemIndex];
if (oGroupItem) {
if (!Dom.isAncestor(this._aListElements[nGroupIndex],
oGroupItem.element)) {
this._aListElements[nGroupIndex].appendChild(
oGroupItem.element);
}
oGroupItem.element.setAttribute("groupindex", nGroupIndex);
oGroupItem.element.setAttribute("index", nItemIndex);
oGroupItem.parent = this;
oGroupItem.index = nItemIndex;
oGroupItem.groupIndex = nGroupIndex;
this._subscribeToItemEvents(oGroupItem);
this._configureSubmenu(oGroupItem);
if (nItemIndex === 0) {
Dom.addClass(oGroupItem.element, "first-of-type");
}
this.logger.log("Item added." +
" Text: " + oGroupItem.cfg.getProperty("text") + ", " +
" Index: " + oGroupItem.index + ", " +
" Group Index: " + oGroupItem.groupIndex);
this.itemAddedEvent.fire(oGroupItem);
this.changeContentEvent.fire();
return oGroupItem;
}
}
}
},
/**
* @method _removeItemFromGroupByIndex
* @description Removes a menu item from a group by index. Returns the menu
* item that was removed.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the menu
* item belongs.
* @param {Number} p_nItemIndex Number indicating the index of the menu item
* to be removed.
* @return {YAHOO.widget.MenuItem}
*/
_removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) {
var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0,
aGroup = this._getItemGroup(nGroupIndex),
aArray,
oItem,
oUL;
if (aGroup) {
aArray = aGroup.splice(p_nItemIndex, 1);
oItem = aArray[0];
if (oItem) {
// Update the index and className properties of each member
this._updateItemProperties(nGroupIndex);
if (aGroup.length === 0) {
// Remove the UL
oUL = this._aListElements[nGroupIndex];
if (this.body && oUL) {
this.body.removeChild(oUL);
}
// Remove the group from the array of items
this._aItemGroups.splice(nGroupIndex, 1);
// Remove the UL from the array of ULs
this._aListElements.splice(nGroupIndex, 1);
/*
Assign the "first-of-type" class to the new first UL
in the collection
*/
oUL = this._aListElements[0];
if (oUL) {
Dom.addClass(oUL, "first-of-type");
}
}
this.itemRemovedEvent.fire(oItem);
this.changeContentEvent.fire();
// Return a reference to the item that was removed
return oItem;
}
}
},
/**
* @method _removeItemFromGroupByValue
* @description Removes a menu item from a group by reference. Returns the
* menu item that was removed.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the
* menu item belongs.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
* instance to be removed.
* @return {YAHOO.widget.MenuItem}
*/
_removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) {
var aGroup = this._getItemGroup(p_nGroupIndex),
nItems,
nItemIndex,
i;
if (aGroup) {
nItems = aGroup.length;
nItemIndex = -1;
if (nItems > 0) {
i = nItems-1;
do {
if (aGroup[i] == p_oItem) {
nItemIndex = i;
break;
}
}
while(i--);
if (nItemIndex > -1) {
return (this._removeItemFromGroupByIndex(p_nGroupIndex,
nItemIndex));
}
}
}
},
/**
* @method _updateItemProperties
* @description Updates the "index," "groupindex," and "className" properties
* of the menu items in the specified group.
* @private
* @param {Number} p_nGroupIndex Number indicating the group of items to update.
*/
_updateItemProperties: function (p_nGroupIndex) {
var aGroup = this._getItemGroup(p_nGroupIndex),
nItems = aGroup.length,
oItem,
oLI,
i;
if (nItems > 0) {
i = nItems - 1;
// Update the index and className properties of each member
do {
oItem = aGroup[i];
if (oItem) {
oLI = oItem.element;
oItem.index = i;
oItem.groupIndex = p_nGroupIndex;
oLI.setAttribute("groupindex", p_nGroupIndex);
oLI.setAttribute("index", i);
Dom.removeClass(oLI, "first-of-type");
}
}
while(i--);
if (oLI) {
Dom.addClass(oLI, "first-of-type");
}
}
},
/**
* @method _createItemGroup
* @description Creates a new menu item group (array) and its associated
* <code><ul></code> 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;
if (!this._aItemGroups[p_nIndex]) {
this._aItemGroups[p_nIndex] = [];
oUL = document.createElement("ul");
this._aListElements[p_nIndex] = oUL;
return this._aItemGroups[p_nIndex];
}
},
/**
* @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 = ((typeof p_nIndex == "number") ? p_nIndex : 0);
return this._aItemGroups[nIndex];
},
/**
* @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);
oSubmenu.beforeShowEvent.subscribe(this._onSubmenuBeforeShow);
}
},
/**
* @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.focusEvent.subscribe(this._onMenuItemFocus);
p_oItem.blurEvent.subscribe(this._onMenuItemBlur);
p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this);
p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange,
p_oItem, this);
},
/**
* @method _onVisibleChange
* @description Change event handler for the the menu's "visible" configuration
* property.
* @private
* @param {String} p_sType String representing the name of the event that
* was fired.
* @param {Array} p_aArgs Array of arguments sent when the event was fired.
*/
_onVisibleChange: function (p_sType, p_aArgs) {
var bVisible = p_aArgs[0];
if (bVisible) {
Dom.addClass(this.element, "visible");
}
else {
Dom.removeClass(this.element, "visible");
}
},
/**
* @method _cancelHideDelay
* @description Cancels the call to "hideMenu."
* @private
*/
_cancelHideDelay: function () {
var oRoot = this.getRoot();
if (oRoot._nHideDelayId) {
window.clearTimeout(oRoot._nHideDelayId);
}
},
/**
* @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(),
me = this;
function hideMenu() {
if (oRoot.activeItem) {
oRoot.clearActiveItem();
}
if (oRoot == me && !(me instanceof YAHOO.widget.MenuBar) &&
me.cfg.getProperty("position") == "dynamic") {
me.hide();
}
}
oRoot._nHideDelayId =
window.setTimeout(hideMenu, oRoot.cfg.getProperty("hidedelay"));
},
/**
* @method _cancelShowDelay
* @description Cancels the call to the "showMenu."
* @private
*/
_cancelShowDelay: function () {
var oRoot = this.getRoot();
if (oRoot._nShowDelayId) {
window.clearTimeout(oRoot._nShowDelayId);
}
},
/**
* @method _execShowDelay
* @description Shows the menu after the number of milliseconds specified by
* the "showdelay" configuration property have ellapsed.
* @private
* @param {YAHOO.widget.Menu} p_oMenu Object specifying the menu that should
* be made visible.
*/
_execShowDelay: function (p_oMenu) {
var oRoot = this.getRoot();
function showMenu() {
if (p_oMenu.parent.cfg.getProperty("selected")) {
p_oMenu.show();
}
}
oRoot._nShowDelayId =
window.setTimeout(showMenu, oRoot.cfg.getProperty("showdelay"));
},
/**
* @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) {
var me = this;
p_oSubmenu._nSubmenuHideDelayId = window.setTimeout(function () {
if (me._nCurrentMouseX > (p_nMouseX + 10)) {
p_oSubmenu._nSubmenuHideDelayId = window.setTimeout(function () {
p_oSubmenu.hide();
}, p_nHideDelay);
}
else {
p_oSubmenu.hide();
}
}, 50);
},
// 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, "topscrollbar_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, "bottomscrollbar_disabled");
this._bFooterDisabled = true;
}
},
/**
* @method _enableScrollHeader
* @description Enables the header used for scrolling the body of the menu.
* @protected
*/
_enableScrollHeader: