(function() {
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event;
/**
* 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
);
};
YAHOO.extend(YAHOO.widget.Menu, YAHOO.widget.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",
// 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 _hideDelayEventHandlersAssigned
* @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
*/
_hideDelayEventHandlersAssigned: 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 Array of menu items.
* @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,
// 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 focus.
* @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.
*/
YAHOO.widget.Menu.superclass.init.call(this, oElement);
this.beforeInitEvent.fire(YAHOO.widget.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.
*/
YAHOO.widget.Menu.superclass.init.call(this, Dom.generateId());
this.beforeInitEvent.fire(YAHOO.widget.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.
*/
YAHOO.widget.Menu.superclass.init.call(this, p_oElement);
this.beforeInitEvent.fire(YAHOO.widget.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) {
var oEl = this.element;
Dom.addClass(oEl, this.CSS_CLASS_NAME);
// Subscribe to Custom Events
this.initEvent.subscribe(this._onInit, this, true);
this.beforeRenderEvent.subscribe(this._onBeforeRender, this, true);
this.renderEvent.subscribe(this._onRender, this, true);
this.beforeShowEvent.subscribe(this._onBeforeShow, this, true);
this.showEvent.subscribe(this._onShow, this, true);
this.beforeHideEvent.subscribe(this._onBeforeHide, this, true);
this.mouseOverEvent.subscribe(this._onMouseOver, this, true);
this.mouseOutEvent.subscribe(this._onMouseOut, this, true);
this.clickEvent.subscribe(this._onClick, this, true);
this.keyDownEvent.subscribe(this._onKeyDown, this, true);
YAHOO.widget.Module.textResizeEvent.subscribe(
this._onTextResize,
this,
true
);
if(p_oConfig) {
this.cfg.applyConfig(p_oConfig, true);
}
// Register the Menu instance with the MenuManager
YAHOO.widget.MenuManager.addMenu(this);
this.initEvent.fire(YAHOO.widget.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 oNode;
if(this.srcElement.tagName == "DIV") {
/*
Populate the collection of item groups and item
group titles
*/
oNode = this.body.firstChild;
var 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(this.srcElement.tagName) {
var sSrcElementTagName = this.srcElement.tagName.toUpperCase();
switch(sSrcElementTagName) {
case "DIV":
if(this._aListElements.length > 0) {
this.logger.log("Found " +
this._aListElements.length +
" item groups to initialize.");
var i = this._aListElements.length - 1;
do {
oNode = this._aListElements[i].firstChild;
this.logger.log("Scanning " +
this._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 " +
this.srcElement.childNodes.length +
" child nodes for items to initialize.");
oNode = this.srcElement.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 nGroups = this._aItemGroups.length,
oItem,
aItemGroup;
for(var i=0; i<nGroups; i++) {
aItemGroup = this._aItemGroups[i];
if(aItemGroup) {
var nItems = aItemGroup.length;
for(var n=0; n<nItems; n++) {
oItem = aItemGroup[n];
if(
!oItem.cfg.getProperty("disabled") &&
oItem.element.style.display != "none"
) {
return oItem;
}
oItem = null;
}
}
}
},
/**
* @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}
*/
_checkPosition: function(p_sPosition) {
if(typeof p_sPosition == "string") {
var sPosition = p_sPosition.toLowerCase();
return ("dynamic,static".indexOf(sPosition) != -1);
}
},
/**
* @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;
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) {
var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0,
aGroup = this._getItemGroup(nGroupIndex),
oGroupItem;
if(!aGroup) {
aGroup = this._createItemGroup(nGroupIndex);
}
if(typeof p_nItemIndex == "number") {
var 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 {
/**
* Returns the next sibling of an item in an array.
* @private
* @param {p_aArray} Array to search.
* @param {p_nStartIndex} Number indicating the index to
* start searching the array.
* @return {Object}
*/
function getNextItemSibling(p_aArray, p_nStartIndex) {
return (
p_aArray[p_nStartIndex] ||
getNextItemSibling(
p_aArray,
(p_nStartIndex+1)
)
);
}
var 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);
return oGroupItem;
}
}
else {
var 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);
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);
if(aGroup) {
var 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
var 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);
// 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);
if(aGroup) {
var nItems = aGroup.length,
nItemIndex = -1;
if(nItems > 0) {
var 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;
if(nItems > 0) {
var i = nItems - 1,
oItem,
oLI;
// 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) {
if(!this._aItemGroups[p_nIndex]) {
this._aItemGroups[p_nIndex] = [];
var 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,
oSubmenu,
true
);
oSubmenu.showEvent.subscribe(
this._onSubmenuShow,
oSubmenu,
true
);
oSubmenu.hideEvent.subscribe(
this._onSubmenuHide,
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.focusEvent.subscribe(this._onMenuItemFocus, p_oItem, this);
p_oItem.blurEvent.subscribe(this._onMenuItemBlur, this, true);
p_oItem.cfg.configChangedEvent.subscribe(
this._onMenuItemConfigChange,
p_oItem,
this
);
},
/**
* @method _getOffsetWidth
* @description Returns the offset width of the menu's
* <code><div></code> element.
* @private
*/
_getOffsetWidth: function() {
var oClone = this.element.cloneNode(true);
Dom.setStyle(oClone, "width", "");
document.body.appendChild(oClone);
var sWidth = oClone.offsetWidth;
document.body.removeChild(oClone);
return sWidth;
},
/**
* @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.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() {
p_oMenu.show();
}
oRoot._nShowDelayId =
window.setTimeout(showMenu, oRoot.cfg.getProperty("showdelay"));
},
// Protected methods
/**
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
* fired the event.
*/
_onMouseOver: function(p_sType, p_aArgs, p_oMenu) {
var oEvent = p_aArgs[0],
oItem = p_aArgs[1],
oTarget = Event.getTarget(oEvent);
if(
!this._bHandledMouseOverEvent &&
(oTarget == this.element || Dom.isAncestor(this.element, oTarget))
) {
this.clearActiveItem();
this._bHandledMouseOverEvent = true;
this._bHandledMouseOutEvent = false;
}
if(
oItem && !oItem.handledMouseOverEvent &&
!oItem.cfg.getProperty("disabled") &&
(oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))
) {
var nShowDelay = this.cfg.getProperty("showdelay"),
bShowDelay = (nShowDelay > 0);
if(bShowDelay) {
this._cancelShowDelay();
}
var oActiveItem = this.activeItem;
if(oActiveItem) {
oActiveItem.cfg.setProperty("selected", false);
var oActiveSubmenu = oActiveItem.cfg.getProperty("submenu");
if(oActiveSubmenu) {
oActiveSubmenu.hide();
}
}
var oItemCfg = oItem.cfg;
// Select and focus the current menu item
oItemCfg.setProperty("selected", true);
oItem.focus();
if(this.cfg.getProperty("autosubmenudisplay")) {
// Show the submenu this menu item
var oSubmenu = oItemCfg.getProperty("submenu");
if(oSubmenu) {
if(bShowDelay) {
this._execShowDelay(oSubmenu);
}
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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
* fired the event.
*/
_onMouseOut: function(p_sType, p_aArgs, p_oMenu) {
var oEvent = p_aArgs[0],
oItem = p_aArgs[1],
oRelatedTarget = Event.getRelatedTarget(oEvent),
bMovingToSubmenu = false;
if(oItem && !oItem.cfg.getProperty("disabled")) {
var 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
)
) {
if(
!oSubmenu ||
(oSubmenu && !oSubmenu.cfg.getProperty("visible"))
) {
oItem.cfg.setProperty("selected", false);
if(
oSubmenu &&
oSubmenu.cfg.getProperty("showdelay") &&
!oSubmenu.cfg.getProperty("visible")
) {
this._cancelShowDelay();
}
}
oItem.handledMouseOutEvent = true;
oItem.handledMouseOverEvent = false;
}
}
if(
!this._bHandledMouseOutEvent &&
(
(
oRelatedTarget != this.element &&
!Dom.isAncestor(this.element, oRelatedTarget)
)
|| bMovingToSubmenu
)
) {
this._bHandledMouseOutEvent = true;
this._bHandledMouseOverEvent = false;
}
},
/**
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
* fired the event.
*/
_onClick: function(p_sType, p_aArgs, p_oMenu) {
var oEvent = p_aArgs[0],
oItem = p_aArgs[1],
oTarget = Event.getTarget(oEvent);
if(oItem && !oItem.cfg.getProperty("disabled")) {
var oItemCfg = oItem.cfg,
oSubmenu = oItemCfg.getProperty("submenu");
/*
ACCESSIBILITY FEATURE FOR SCREEN READERS:
Expand/collapse the submenu when the user clicks
on the submenu indicator image.
*/
if(oTarget == oItem.submenuIndicator && oSubmenu) {
if(oSubmenu.cfg.getProperty("visible")) {
oSubmenu.hide();
}
else {
this.clearActiveItem();
this.activeItem = oItem;
oItem.cfg.setProperty("selected", true);
oSubmenu.show();
}
}
else {
var sURL = oItemCfg.getProperty("url"),
bCurrentPageURL = (sURL.substr((sURL.length-1),1)