(function () {

	var Event = YAHOO.util.Event,
		Dom = YAHOO.util.Dom,
		Lang = YAHOO.lang,
		UA = YAHOO.env.ua,
		ContextMenu = YAHOO.widget.ContextMenu,		

		MenuPrototype = YAHOO.widget.Menu.prototype,
		fnMenuInitDefaultConfig = MenuPrototype.initDefaultConfig,

		// The currently focused MenuItem label, or the MenuItem label that can be focused 
		// by the user.

		m_oCurrentItemLabel,
		

		// Private constants for strings
		
		_ARIA_PREFIX = "aria-",
		_HAS_POPUP = "haspopup",
		_ROLE = "role",
		_PRESENTATION = "presentation",
		_MENUITEM = "menuitem",
		_HREF = "href",
		_SUBMENU = "submenu",
		_MENU = "menu",
		_MENUBAR = "menubar",
		_LABELLED_BY = "labelledby",
		_FOCUS = "focus", 
		_BLUR = "blur",
		_ITEM_ADDED = "itemAdded",
		_USE_ARIA = "usearia",
		_TRIGGER = "trigger";


	// Menu ARIA plugin		

	var setARIARole = function (element, role) {
	
		element.setAttribute(_ROLE, role);
	
	};


	var setARIAProperty = function (element, property, value) {

		element.setAttribute((_ARIA_PREFIX + property), value);
	
	};


	var addHasPopupRole = function (element) {
	
		if (element.nodeType === 1) {
			setARIAProperty(element, _HAS_POPUP, true);
		}
	
	};


	var removeHasPopupRole = function (element) {

		if (element.nodeType === 1) {
			element.removeAttribute(_ARIA_PREFIX + _HAS_POPUP);
		}
		
	};


	var onDocumentFocus = function (event) {

		// The currently focused element

		var oTarget = Event.getTarget(event);


		if (Dom.isAncestor(this.element, oTarget)) {

			/*
				Modify value of the tabIndex attribute so that the currently 
				focused MenuItem label is in the browser's default tab order.
			*/	

			if (m_oCurrentItemLabel) {
				m_oCurrentItemLabel.tabIndex = -1;
			}

			m_oCurrentItemLabel = oTarget;
			m_oCurrentItemLabel.tabIndex = 0;
		
		}
		else if (m_oCurrentItemLabel && this.getItems().length > 0) {
		
			/*
				If the focus has moved to an element on the page that is not a 
				part of the MenuBar, restore the MenuBar to its original state 
				so that the first item is in the browser's default tab index.
			*/
		
			m_oCurrentItemLabel.tabIndex = -1;

			m_oCurrentItemLabel = Dom.getFirstChild(this.getItem(0).element);
			m_oCurrentItemLabel.tabIndex = 0;
		
		}
	
	};


	var onKeyPress = function (type, args) {

		var oEvent = args[0],
			oMenuItem = args[1],
			oTarget = Event.getTarget(oEvent),
			nCharCode = Event.getCharCode(oEvent);


		if (oMenuItem && nCharCode === 13) {

			if (UA.ie) {
				oTarget.fireEvent("onclick");
			}
			else {

				oEvent = document.createEvent("HTMLEvents");
				oEvent.initEvent("click", true, true);

				oTarget.dispatchEvent(oEvent);

			}
				
		}

	};


	var onConfigSubmenu = function (type, args) {
			
		var oSubmenu = args[0];
			
		if (oSubmenu) {
			addHasPopupRole(Dom.getFirstChild(this.element));
		}
	
	};
	
	
	var addMenuItemRole = function (menuitem) {

		var oMenuItemEl = menuitem.element;

		// For NVDA: add the role of "presentation" to reach LI and UL
		// element to prevent NVDA from announcing the "list" role
		// as well as the menu-related roles.

		setARIARole(oMenuItemEl.parentNode, _PRESENTATION);
		setARIARole(oMenuItemEl, _PRESENTATION);


		// Retrieve a reference to the anchor element that serves as the 
		// label for each MenuItem.

		var oMenuItemLabel = Dom.getFirstChild(oMenuItemEl);


		// Set the "role" attribute of the label to "menuitem"

		setARIARole(oMenuItemLabel, _MENUITEM);


		// Remove the label from the browser's default tab order
		
		oMenuItemLabel.tabIndex = -1;


		// JAWS & NVDA announce the value of each anchor 
		// element's "href" attribute when it recieves focus, so remove  
		// the attribute so that its value isn't announced.

		oMenuItemLabel.removeAttribute(_HREF);


		// If the MenuItem has a submenu, set the "aria-haspopup" 
		// attribute to true so that the screen reader can announce 

		if (menuitem.cfg.getProperty(_SUBMENU)) {
			addHasPopupRole(oMenuItemLabel);
		}
		else {
			menuitem.cfg.subscribeToConfigEvent(_SUBMENU, onConfigSubmenu);
		}
	
	};


	var onItemAdded = function (type, args) {

		addMenuItemRole(args[0]);
	
	};


	MenuPrototype.configUseARIA = function (type, args) {
	
		var bUseARIA = args[0],
			oParent = this.parent,
			oElement = this.element,
			sMenuRole = (this instanceof YAHOO.widget.MenuBar) ? _MENUBAR : _MENU,
			aMenuItems,
			nMenuItems,
			oMenuItem,
			oMenuItemLabel,
			sId,
			i;


		if (bUseARIA) {
	
			// Apply the "role" attribute of "menu" or "menubar" depending on the 
			// type of the Menu control being rendered.

			setARIARole(oElement, sMenuRole);


	
			// Use the "aria-labelledby" attribute to label each submenu.  This 
			// will provide the user with the name of the submenu the first time
			// one of its items receives focus.

			if (oParent) {

				sId = Dom.generateId(Dom.getFirstChild(oParent.element));

				setARIAProperty(oElement, _LABELLED_BY, sId);
				
			}
	
	
			// Apply the appropriate "role" and "aria-[state]" attributes to the 
			// label of each MenuItem instance.

			aMenuItems = this.getItems();
			nMenuItems = aMenuItems.length;

			if (nMenuItems > 0) {

				i = nMenuItems - 1;
					
		
				do {
					oMenuItem = aMenuItems[i];
					addMenuItemRole(oMenuItem);
					i = i - 1;
				}
				while ((i > -1));
					
		
				// Set the "tabindex" of the first MenuItem's label to 0 so the user 
				// can easily tab into and out of the control.
	
				if (this.getRoot() === this) {
					m_oCurrentItemLabel = Dom.getFirstChild(this.getItem(0).element);
					m_oCurrentItemLabel.tabIndex = 0;
				}

			}


			if (this === this.getRoot()) {
				Event.onFocus(document, onDocumentFocus, null, this);
			}
			

			this.subscribe(_ITEM_ADDED, onItemAdded);
			this.subscribe("keypress", onKeyPress);
	
		}	

	};



	// ContextMenu ARIA plugin

	var m_oTriggers = {};

	var toggleARIAForTrigger = function () {

		var sMenuId = this.element.id,
			oTrigger = this.cfg.getProperty(_TRIGGER),
			oCurrentTrigger = m_oTriggers[sMenuId],
			oElement;


		if (oCurrentTrigger) {

			if (Lang.isString(oCurrentTrigger)) {	// String id
	
				oElement = Dom.get(oCurrentTrigger);
				
				if (oElement) {
					removeHasPopupRole(oElement);						
				}
	
			}
			else if (oCurrentTrigger.nodeType === 1) {	// Element reference
				removeHasPopupRole(oCurrentTrigger);
			}				
			else if (oCurrentTrigger.length) { // NodeList or Array
				Dom.batch(oCurrentTrigger, removeHasPopupRole);
			}

		}


		if (oTrigger) {

			if (Lang.isString(oTrigger)) {	// String id

				oElement = Dom.get(oTrigger);
				
				if (oElement) {
					addHasPopupRole(oElement);						
				}

			}
			else if (oTrigger.nodeType === 1) {	// Element reference
				addHasPopupRole(oTrigger);
			}
			else if (oTrigger.length) {	// NodeList or Array
				Dom.batch(oTrigger, addHasPopupRole);
			}
				
			m_oTriggers[sMenuId] = oTrigger;

		}
	
	};


	ContextMenu.prototype.configUseARIA = function (type, args) {

		ContextMenu.superclass.configUseARIA.apply(this, arguments);

		toggleARIAForTrigger.call(this);

		this.cfg.subscribeToConfigEvent(_TRIGGER, toggleARIAForTrigger);
	
	};

}());