# Yahoo! UI Library

### Menu 2.6.0

Yahoo! UI Library > menu > menuitem.js (source view)

Show Private Show Protected Show Deprecated

    (function () {

    /**
    * Creates an item for a menu.
    *
    * @param {String} p_oObject String specifying the text of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying
    * the <code>&#60;li&#62;</code> element of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
    * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object
    * specifying the <code>&#60;option&#62;</code> 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",
    	_LI_LOWERCASE = "li",
    	_HREF = "href",
    	_ANCHOR_TEMPLATE = "<a href=\"#\"></a>",
    	_SELECT = "SELECT",
    	_DIV = "DIV",
    	_START_HELP_TEXT = "<em class=\"helptext\">",
    	_START_EM = "<em>",
    	_END_EM = "</em>",
    	_START_STRONG = "<strong>",
    	_END_STRONG = "</strong>",
    	_PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
    	_OBJ = "obj",
    	_SCOPE = "scope",
    	_NONE = "none",
    	_VISIBLE = "visible",
    	_SPACE = " ",
    	_MENUITEM = "MenuItem",

        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
    	},

        CLASS_NAMES = {},

        m_oMenuItemTemplate;

    /**
    * @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 &#60;LI&#62; and &#60;A&#62; 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 &#60;LI&#62; and &#60;A&#62; 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
        * <code>&#60;li&#62;</code> 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 <code>&#60;a&#62;</code> 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
        * <code>&#60;a&#62;</code> element.
        * @default null
        * @private
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-48250443">HTMLAnchorElement</a>
        */
        _oAnchor: null,

        /**
        * @property _oHelpTextEM
        * @description Object reference to the menu item's help text
        * <code>&#60;em&#62;</code> element.
        * @default null
        * @private
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-58190037">HTMLElement</a>
        */
        _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
        * <code>&#60;li&#62;</code> element.
        * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level
        * -one-html.html#ID-74680021">HTMLLIElement</a>
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-74680021">HTMLLIElement</a>
        */
        element: null,

        /**
        * @property srcElement
        * @description Object reference to the HTML element (either
        * <code>&#60;li&#62;</code>, <code>&#60;optgroup&#62;</code> or
        * <code>&#60;option&#62;</code>) used create the menu item.
        * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.
        * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"
        * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
        * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3.
        * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247">
        * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
        * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
        */
        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 <code>&#60;li&#62;</code>
        * 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 <code>&#60;li&#62;</code>
        * element is removed from its parent <code>&#60;ul&#62;</code> 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 {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying
        * the <code>&#60;li&#62;</code> element of the menu item.
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
        * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
        * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object
        * specifying the <code>&#60;option&#62;</code> element of the menu item.
        * @param {Object} p_oConfig Optional. Object literal specifying the
        * configuration for the menu item. See configuration class documentation
        * for more details.
        */
        init: function (p_oObject, p_oConfig) {

            if (!this.SUBMENU_TYPE) {

                this.SUBMENU_TYPE = Menu;

            }

            // Create the config object

            this.cfg = new YAHOO.util.Config(this);

            this.initDefaultConfig();

            var oConfig = this.cfg,
                sURL = _HASH,
                oCustomEvent,
    			aEventData,
                oAnchor,
                sTarget,
                sText,
                sId,
                i;

            if (Lang.isString(p_oObject)) {

                this._createRootNodeStructure();

                oConfig.queueProperty(_TEXT, p_oObject);

            }
            else if (p_oObject && p_oObject.tagName) {

                switch(p_oObject.tagName.toUpperCase()) {

                    case _OPTION:

                        this._createRootNodeStructure();

                        oConfig.queueProperty(_TEXT, p_oObject.text);
                        oConfig.queueProperty(_DISABLED, p_oObject.disabled);

                        this.value = p_oObject.value;

                        this.srcElement = p_oObject;

                    break;

                    case _OPTGROUP:

                        this._createRootNodeStructure();

                        oConfig.queueProperty(_TEXT, p_oObject.label);
                        oConfig.queueProperty(_DISABLED, p_oObject.disabled);

                        this.srcElement = p_oObject;

                        this._initSubTree();

                    break;

                    case _LI_UPPERCASE:

                        // Get the anchor node (if it exists)

                        oAnchor = Dom.getFirstChild(p_oObject);

                        // Capture the "text" and/or the "URL"

                        if (oAnchor) {

                            sURL = oAnchor.getAttribute(_HREF, 2);
                            sTarget = oAnchor.getAttribute(_TARGET);

                            sText = oAnchor.innerHTML;

                        }

                        this.srcElement = p_oObject;
                        this.element = p_oObject;
                        this._oAnchor = oAnchor;

                        /*
                            Set these properties silently to sync up the
                            configuration object without making changes to the
                            element's DOM
                        */

                        oConfig.setProperty(_TEXT, sText, true);
                        oConfig.setProperty(_URL, sURL, true);
                        oConfig.setProperty(_TARGET, sTarget, true);

                        this._initSubTree();

                    break;

                }

            }

            if (this.element) {

                sId = (this.srcElement || this.element).id;

                if (!sId) {

                    sId = this.id || Dom.generateId();

                    this.element.id = sId;

                }

                this.id = sId;

                Dom.addClass(this.element, this.CSS_CLASS_NAME);
                Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME);

    			i = EVENT_TYPES.length - 1;

    			do {

    				aEventData = EVENT_TYPES[i];

    				oCustomEvent = this.createEvent(aEventData[1]);
    				oCustomEvent.signature = CustomEvent.LIST;

    				this[aEventData[0]] = oCustomEvent;

    			}
    			while (i--);

                if (p_oConfig) {

                    oConfig.applyConfig(p_oConfig);

                }

                oConfig.fireQueue();

            }

        },

        // Private methods

        /**
        * @method _createRootNodeStructure
        * @description Creates the core DOM structure for the menu item.
        * @private
        */
        _createRootNodeStructure: function () {

            var oElement,
                oAnchor;

            if (!m_oMenuItemTemplate) {

                m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE);
                m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE;

            }

            oElement = m_oMenuItemTemplate.cloneNode(true);
            oElement.className = this.CSS_CLASS_NAME;

            oAnchor = oElement.firstChild;
            oAnchor.className = this.CSS_LABEL_CLASS_NAME;

            this.element = oElement;
            this._oAnchor = oAnchor;

        },

        /**
        * @method _initSubTree
        * @description Iterates the source element's childNodes collection and uses
        * the child nodes to instantiate other menus.
        * @private
        */
        _initSubTree: function () {

            var oSrcEl = this.srcElement,
                oConfig = this.cfg,
                oNode,
                aOptions,
                nOptions,
                oMenu,
                n;

            if (oSrcEl.childNodes.length > 0) {

                if (this.parent.lazyLoad && this.parent.srcElement &&
                    this.parent.srcElement.tagName.toUpperCase() == _SELECT) {

                    oConfig.setProperty(
                            _SUBMENU,
                            { id: Dom.generateId(), itemdata: oSrcEl.childNodes }
                        );

                }
                else {

                    oNode = oSrcEl.firstChild;
                    aOptions = [];

                    do {

                        if (oNode && oNode.tagName) {

                            switch(oNode.tagName.toUpperCase()) {

                                case _DIV:

                                    oConfig.setProperty(_SUBMENU, oNode);

                                break;

                                case _OPTION:

                                    aOptions[aOptions.length] = oNode;

                                break;

                            }

                        }

                    }
                    while((oNode = oNode.nextSibling));

                    nOptions = aOptions.length;

                    if (nOptions > 0) {

                        oMenu = new this.SUBMENU_TYPE(Dom.generateId());

                        oConfig.setProperty(_SUBMENU, oMenu);

                        for(n=0; n<nOptions; n++) {

                            oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n])));

                        }

                    }

                }

            }

        },

        // Event handlers for configuration properties

        /**
        * @method configText
        * @description Event handler for when the "text" configuration property of
        * the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configText: function (p_sType, p_aArgs, p_oItem) {

            var sText = p_aArgs[0],
                oConfig = this.cfg,
                oAnchor = this._oAnchor,
                sHelpText = oConfig.getProperty(_HELP_TEXT),
                sHelpTextHTML = _EMPTY_STRING,
                sEmphasisStartTag = _EMPTY_STRING,
                sEmphasisEndTag = _EMPTY_STRING;

            if (sText) {

                if (sHelpText) {

                    sHelpTextHTML = _START_HELP_TEXT + sHelpText + _END_EM;

                }

                if (oConfig.getProperty(_EMPHASIS)) {

                    sEmphasisStartTag = _START_EM;
                    sEmphasisEndTag = _END_EM;

                }

                if (oConfig.getProperty(_STRONG_EMPHASIS)) {

                    sEmphasisStartTag = _START_STRONG;
                    sEmphasisEndTag = _END_STRONG;

                }

                oAnchor.innerHTML = (sEmphasisStartTag + sText + sEmphasisEndTag + sHelpTextHTML);

            }

        },

        /**
        * @method configHelpText
        * @description Event handler for when the "helptext" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configHelpText: function (p_sType, p_aArgs, p_oItem) {

            this.cfg.refireEvent(_TEXT);

        },

        /**
        * @method configURL
        * @description Event handler for when the "url" configuration property of
        * the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configURL: function (p_sType, p_aArgs, p_oItem) {

            var sURL = p_aArgs[0];

            if (!sURL) {

                sURL = _HASH;

            }

            var oAnchor = this._oAnchor;

            if (UA.opera) {

                oAnchor.removeAttribute(_HREF);

            }

            oAnchor.setAttribute(_HREF, sURL);

        },

        /**
        * @method configTarget
        * @description Event handler for when the "target" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configTarget: function (p_sType, p_aArgs, p_oItem) {

            var sTarget = p_aArgs[0],
                oAnchor = this._oAnchor;

            if (sTarget && sTarget.length > 0) {

                oAnchor.setAttribute(_TARGET, sTarget);

            }
            else {

                oAnchor.removeAttribute(_TARGET);

            }

        },

        /**
        * @method configEmphasis
        * @description Event handler for when the "emphasis" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configEmphasis: function (p_sType, p_aArgs, p_oItem) {

            var bEmphasis = p_aArgs[0],
                oConfig = this.cfg;

            if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) {

                oConfig.setProperty(_STRONG_EMPHASIS, false);

            }

            oConfig.refireEvent(_TEXT);

        },

        /**
        * @method configStrongEmphasis
        * @description Event handler for when the "strongemphasis" configuration
        * property of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) {

            var bStrongEmphasis = p_aArgs[0],
                oConfig = this.cfg;

            if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) {

                oConfig.setProperty(_EMPHASIS, false);

            }

            oConfig.refireEvent(_TEXT);

        },

        /**
        * @method configChecked
        * @description Event handler for when the "checked" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configChecked: function (p_sType, p_aArgs, p_oItem) {

            var bChecked = p_aArgs[0],
                oConfig = this.cfg;

            if (bChecked) {

                addClassNameForState.call(this, _CHECKED);

            }
            else {

                removeClassNameForState.call(this, _CHECKED);
            }

            oConfig.refireEvent(_TEXT);

            if (oConfig.getProperty(_DISABLED)) {

                oConfig.refireEvent(_DISABLED);

            }

            if (oConfig.getProperty(_SELECTED)) {

                oConfig.refireEvent(_SELECTED);

            }

        },

        /**
        * @method configDisabled
        * @description Event handler for when the "disabled" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configDisabled: function (p_sType, p_aArgs, p_oItem) {

            var bDisabled = p_aArgs[0],
                oConfig = this.cfg,
                oSubmenu = oConfig.getProperty(_SUBMENU),
                bChecked = oConfig.getProperty(_CHECKED);

            if (bDisabled) {

                if (oConfig.getProperty(_SELECTED)) {

                    oConfig.setProperty(_SELECTED, false);

                }

    			addClassNameForState.call(this, _DISABLED);

                if (oSubmenu) {

    				addClassNameForState.call(this, _HAS_SUBMENU_DISABLED);

                }

                if (bChecked) {

    				addClassNameForState.call(this, _CHECKED_DISABLED);

                }

            }
            else {

    			removeClassNameForState.call(this, _DISABLED);

                if (oSubmenu) {

    				removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED);

                }

                if (bChecked) {

    				removeClassNameForState.call(this, _CHECKED_DISABLED);

                }

            }

        },

        /**
        * @method configSelected
        * @description Event handler for when the "selected" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configSelected: function (p_sType, p_aArgs, p_oItem) {

            var oConfig = this.cfg,
            	oAnchor = this._oAnchor,

                bSelected = p_aArgs[0],
                bChecked = oConfig.getProperty(_CHECKED),
                oSubmenu = oConfig.getProperty(_SUBMENU);

            if (UA.opera) {

                oAnchor.blur();

            }

            if (bSelected && !oConfig.getProperty(_DISABLED)) {

    			addClassNameForState.call(this, _SELECTED);

                if (oSubmenu) {

    				addClassNameForState.call(this, _HAS_SUBMENU_SELECTED);

                }

                if (bChecked) {

    				addClassNameForState.call(this, _CHECKED_SELECTED);

                }

            }
            else {

    			removeClassNameForState.call(this, _SELECTED);

                if (oSubmenu) {

    				removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED);

                }

                if (bChecked) {

    				removeClassNameForState.call(this, _CHECKED_SELECTED);

                }

            }

            if (this.hasFocus() && UA.opera) {

                oAnchor.focus();

            }

        },

        /**
        * @method _onSubmenuBeforeHide
        * @description "beforehide" Custom Event handler for a submenu.
        * @private
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        */
        _onSubmenuBeforeHide: function (p_sType, p_aArgs) {

            var oItem = this.parent,
                oMenu;

            function onHide() {

                oItem._oAnchor.blur();
                oMenu.beforeHideEvent.unsubscribe(onHide);

            }

            if (oItem.hasFocus()) {

                oMenu = oItem.parent;

                oMenu.beforeHideEvent.subscribe(onHide);

            }

        },

        /**
        * @method configSubmenu
        * @description Event handler for when the "submenu" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configSubmenu: function (p_sType, p_aArgs, p_oItem) {

            var oSubmenu = p_aArgs[0],
                oConfig = this.cfg,
                bLazyLoad = this.parent && this.parent.lazyLoad,
                oMenu,
                sSubmenuId,
                oSubmenuConfig;

            if (oSubmenu) {

                if (oSubmenu instanceof Menu) {

                    oMenu = oSubmenu;
                    oMenu.parent = this;
                    oMenu.lazyLoad = bLazyLoad;

                }
                else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) {

                    sSubmenuId = oSubmenu.id;
                    oSubmenuConfig = oSubmenu;

                    oSubmenuConfig.lazyload = bLazyLoad;
                    oSubmenuConfig.parent = this;

                    oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig);

                    // Set the value of the property to the Menu instance

                    oConfig.setProperty(_SUBMENU, oMenu, true);

                }
                else {

                    oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this });

                    // Set the value of the property to the Menu instance

                    oConfig.setProperty(_SUBMENU, oMenu, true);

                }

                if (oMenu) {

    				oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true);

                    addClassNameForState.call(this, _HAS_SUBMENU);

    				if (oConfig.getProperty(_URL) === _HASH) {

    					oConfig.setProperty(_URL, (_HASH + oMenu.id));

    				}

                    this._oSubmenu = oMenu;

                    if (UA.opera) {

                        oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide);

                    }

                }

            }
            else {

    			removeClassNameForState.call(this, _HAS_SUBMENU);

                if (this._oSubmenu) {

                    this._oSubmenu.destroy();

                }

            }

            if (oConfig.getProperty(_DISABLED)) {

                oConfig.refireEvent(_DISABLED);

            }

            if (oConfig.getProperty(_SELECTED)) {

                oConfig.refireEvent(_SELECTED);

            }

        },

        /**
        * @method configOnClick
        * @description Event handler for when the "onclick" configuration property
        * of the menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configOnClick: function (p_sType, p_aArgs, p_oItem) {

            var oObject = p_aArgs[0];

            /*
                Remove any existing listeners if a "click" event handler has
                already been specified.
            */

            if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) {

                this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn,
                                    this._oOnclickAttributeValue.obj);

                this._oOnclickAttributeValue = null;

            }

            if (!this._oOnclickAttributeValue && Lang.isObject(oObject) &&
                Lang.isFunction(oObject.fn)) {

                this.clickEvent.subscribe(oObject.fn,
                    ((_OBJ in oObject) ? oObject.obj : this),
                    ((_SCOPE in oObject) ? oObject.scope : null) );

                this._oOnclickAttributeValue = oObject;

            }

        },

        /**
        * @method configClassName
        * @description Event handler for when the "classname" configuration
        * property of a menu item changes.
        * @param {String} p_sType String representing the name of the event that
        * was fired.
        * @param {Array} p_aArgs Array of arguments sent when the event was fired.
        * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
        * that fired the event.
        */
        configClassName: function (p_sType, p_aArgs, p_oItem) {

            var sClassName = p_aArgs[0];

            if (this._sClassName) {

                Dom.removeClass(this.element, this._sClassName);

            }

            Dom.addClass(this.element, sClassName);
            this._sClassName = sClassName;

        },

        // Public methods

    	/**
        * @method initDefaultConfig
    	* @description Initializes an item's configurable properties.
    	*/
    	initDefaultConfig : function () {

            var oConfig = this.cfg;

            // Define the configuration attributes

            /**
            * @config text
            * @description String specifying the text label for the menu item.
            * When building a menu from existing HTML the value of this property
            * will be interpreted from the menu's markup.
            * @default ""
            * @type String
            */
            oConfig.addProperty(
                TEXT_CONFIG.key,
                {
                    handler: this.configText,
                    value: TEXT_CONFIG.value,
                    validator: TEXT_CONFIG.validator,
                    suppressEvent: TEXT_CONFIG.suppressEvent
                }
            );

            /**
            * @config helptext
            * @description String specifying additional instructional text to
            * accompany the text for the menu item.
            * @deprecated Use "text" configuration property to add help text markup.
            * For example: <code>oMenuItem.cfg.setProperty("text", "Copy &#60;em
            * class=\"helptext\"&#62;Ctrl + C&#60;/em&#62;");</code>
            * @default null
            * @type String|<a href="http://www.w3.org/TR/
            * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
            * HTMLElement</a>
            */
            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. <strong>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.</strong>
            * 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: <code>oMenuItem.cfg.setProperty("text", "&#60;em&#62;Some
            * Text&#60;/em&#62;");</code>
            * @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: <code>oMenuItem.cfg.setProperty("text", "&#60;strong&#62;
            * Some Text&#60;/strong&#62;");</code>
            * @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: <ul><li>Object
            * specifying a Menu instance.</li><li>Object literal specifying the
            * menu to be created.  Format: <code>{ id: [menu id], itemdata:
            * [<a href="YAHOO.widget.Menu.html#itemData">array of values for
            * items</a>] }</code>.</li><li>String specifying the id attribute
            * of the <code>&#60;div&#62;</code> element of the menu.</li><li>
            * Object specifying the <code>&#60;div&#62;</code> element of the
            * menu.</li></ul>
            * @default null
            * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/
            * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
            * HTMLElement</a>
            */
            oConfig.addProperty(
                SUBMENU_CONFIG.key,
                {
                    handler: this.configSubmenu,
                    supercedes: SUBMENU_CONFIG.supercedes,
                    suppressEvent: SUBMENU_CONFIG.suppressEvent
                }
            );

            /**
            * @config onclick
            * @description Object literal representing the code to be executed when
            * the item is clicked.  Format:<br> <code> {<br>
            * <strong>fn:</strong> Function,   &#47;&#47; The handler to call when
            * the event fires.<br> <strong>obj:</strong> Object, &#47;&#47; An
            * object to  pass back to the handler.<br> <strong>scope:</strong>
            * Object &#47;&#47; The object to use for the scope of the handler.
            * <br> } </code>
            * @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
            * <code>&#60;li&#62;</code> 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
                }
            );

    	},

        /**
        * @method getNextEnabledSibling
        * @description Finds the menu item's next enabled sibling.
        * @return YAHOO.widget.MenuItem
        */
        getNextEnabledSibling: function () {

            var nGroupIndex,
                aItemGroups,
                oNextItem,
                nNextGroupIndex,
                aNextGroup,
                returnVal;

            function getNextArrayItem(p_aArray, p_nStartIndex) {

                return p_aArray[p_nStartIndex] || getNextArrayItem(p_aArray, (p_nStartIndex+1));

            }

            if (this.parent instanceof Menu) {

                nGroupIndex = this.groupIndex;

                aItemGroups = this.parent.getItemGroups();

                if (this.index < (aItemGroups[nGroupIndex].length - 1)) {

                    oNextItem = getNextArrayItem(aItemGroups[nGroupIndex],
                            (this.index+1));

                }
                else {

                    if (nGroupIndex < (aItemGroups.length - 1)) {

                        nNextGroupIndex = nGroupIndex + 1;

                    }
                    else {

                        nNextGroupIndex = 0;

                    }

                    aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex);

                    // Retrieve the first menu item in the next group

                    oNextItem = getNextArrayItem(aNextGroup, 0);

                }

                returnVal = (oNextItem.cfg.getProperty(_DISABLED) ||
                    oNextItem.element.style.display == _NONE) ?
                    oNextItem.getNextEnabledSibling() : oNextItem;

            }

            return returnVal;

        },

        /**
        * @method getPreviousEnabledSibling
        * @description Finds the menu item's previous enabled sibling.
        * @return {YAHOO.widget.MenuItem}
        */
        getPreviousEnabledSibling: function () {

            var nGroupIndex,
                aItemGroups,
                oPreviousItem,
                nPreviousGroupIndex,
                aPreviousGroup,
                returnVal;

            function getPreviousArrayItem(p_aArray, p_nStartIndex) {

                return p_aArray[p_nStartIndex] || getPreviousArrayItem(p_aArray, (p_nStartIndex-1));

            }

            function getFirstItemIndex(p_aArray, p_nStartIndex) {

                return p_aArray[p_nStartIndex] ? p_nStartIndex :
                    getFirstItemIndex(p_aArray, (p_nStartIndex+1));

            }

           if (this.parent instanceof Menu) {

                nGroupIndex = this.groupIndex;
                aItemGroups = this.parent.getItemGroups();

                if (this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0)) {

                    oPreviousItem = getPreviousArrayItem(aItemGroups[nGroupIndex],
                            (this.index-1));

                }
                else {

                    if (nGroupIndex > getFirstItemIndex(aItemGroups, 0)) {

                        nPreviousGroupIndex = nGroupIndex - 1;

                    }
                    else {

                        nPreviousGroupIndex = aItemGroups.length - 1;

                    }

                    aPreviousGroup = getPreviousArrayItem(aItemGroups,
                        nPreviousGroupIndex);

                    oPreviousItem = getPreviousArrayItem(aPreviousGroup,
                            (aPreviousGroup.length - 1));

                }

                returnVal = (oPreviousItem.cfg.getProperty(_DISABLED) ||
                    oPreviousItem.element.style.display == _NONE) ?
                    oPreviousItem.getPreviousEnabledSibling() : oPreviousItem;

            }

            return returnVal;

        },

        /**
        * @method focus
        * @description Causes the menu item to receive the focus and fires the
        * focus event.
        */
        focus: function () {

            var oParent = this.parent,
                oAnchor = this._oAnchor,
                oActiveItem = oParent.activeItem;

            function setFocus() {

                try {

                    if (!(UA.ie && !document.hasFocus())) {

    					if (oActiveItem) {

    						oActiveItem.blurEvent.fire();

    					}

    					oAnchor.focus();

    					this.focusEvent.fire();

                    }

                }
                catch(e) {

                }

            }

            if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) &&
                this.element.style.display != _NONE) {

                /*
                    Setting focus via a timer fixes a race condition in Firefox, IE
                    and Opera where the browser viewport jumps as it trys to
                    position and focus the menu.
                */

                Lang.later(0, this, setFocus);

            }

        },

        /**
        * @method blur
        * @description Causes the menu item to lose focus and fires the
        * blur event.
        */
        blur: function () {

            var oParent = this.parent;

            if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) {

                Lang.later(0, this, function () {

                    try {

                        this._oAnchor.blur();
                        this.blurEvent.fire();

                    }
                    catch (e) {

                    }

                }, 0);

            }

        },

        /**
        * @method hasFocus
        * @description Returns a boolean indicating whether or not the menu item
        * has focus.
        * @return {Boolean}
        */
        hasFocus: function () {

            return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this);

        },

    	/**
        * @method destroy
    	* @description Removes the menu item's <code>&#60;li&#62;</code> element
    	* from its parent <code>&#60;ul&#62;</code> 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);

    })();

Copyright © 2008 Yahoo! Inc. All rights reserved.