//	Copyright (c) 2008, State of Illinois, Department of Human Services. All rights reserved.
//	Developed by: MSF&W Accessibility Solutions, http://www.msfw.com/accessibility
//	Subject to University of Illinois/NCSA Open Source License
//	See: http://www.dhs.state.il.us/opensource
//	Version Date: 2008-10-20
//
//	Accessible Dynamic Menu
//	This script makes unordered list into dynamic menus that are usable with the mouse and keyboard.
//
//	To make a unordered list of links into a dynamic menu add the class:
//		class="dropdownMenu"
//		class="flyoutMenu"
//		class="accordianMenu"
//	And call:
//		DynamicMenu.initAll()
//
//	The following style rules are suggested for dropdown menus:
//		#[Dropdown menu Container ID] { clear: both; width: 100%; min-height: 2.35em; _height: 2.35em; background: #c84b13; }
//		#[Dropdown menu Container ID] h2 { position: absolute; left: -10000px; }
//		ul.dropdownMenu { float: right; margin: 6px 3px 0 3px; padding: 0; list-style: none; }
//		ul.dropdownMenu li { z-index: 1; position: relative; float: left; width: 11em; margin: 0 1px; padding: 0; text-align: right; }
//		ul.dropdownMenu li a { display: block; position: relative; color: #fff; font-weight: bold; text-decoration: none; }
//		ul.dropdownMenu li.openMenu ul { left: 1px; }
//		ul.dropdownMenu li ul { display: none; position: absolute; left: -10000px; margin: 0; padding: 0 0 1px 0; border: 1px solid #e87713; border-top-width: 0; background: #df7012; list-style: none; }
//		ul.dropdownMenu li ul li { width: 10.7em; margin: 0; _margin-left /**/: -16px; padding: 0; text-align: left; }
//		ul.dropdownMenu li ul li a { display: block; margin: 0; padding: 3px 9px; background: #df7012; color: #fff; text-decoration: none; }
//		ul.dropdownMenu li ul li a:hover, ul.dropdownMenu li ul li a:focus, ul.dropdownMenu li ul li a:active { background: #e58735; }
//
//		ul.dropdownMenu li.openMenu ul li.openMenu ul { display: block; position: absolute; top: 0; left: 100%; }
//
//		ul.dropdownMenu li.closeMenu ul,
//		ul.dropdownMenu li.openMenu ul li.openMenu ul li.closeMenu ul { position: absolute; left: -10000px; }
//
//	The following style rules are suggested for flyout menus:
//		NOTE: These styles seem to work OK on the few test pages I worked on. However, they can probably be cleaned up.
//		TODO: Fix up the recommended style sheet rules for flyout menus
//
//		#[Flyout menu Container ID] h2 { position: absolute; left: -10000px; }
//		ul.flyoutMenu { clear: both; background: #C84B13; }
//		ul.flyoutMenu { margin: 6px 3px 0 3px; padding: 0; list-style: none; }
//		ul.flyoutMenu li { margin: 0 1px; padding: 0; }
//		ul.flyoutMenu li a { color: #fff; font-weight: bold; text-decoration: none; }
//		ul.flyoutMenu li.openMenu { position: relative; }
//		ul.flyoutMenu li.openMenu ul { position: absolute; display: block; top: 0; left: 100%; }
//		ul.flyoutMenu li ul { display: none; position: absolute; left: -10000px; margin: 0; padding: 0 0 1px 0; border: 1px solid #e87713; border-top-width: 0; background: #df7012; list-style: none; }
//		ul.flyoutMenu li ul li { width: 10.7em; margin: 0; _margin-left /**/: -16px; padding: 0; text-align: left; }
//		ul.flyoutMenu li ul li a:hover, ul.flyoutMenu li ul li a:focus, ul.flyoutMenu li ul li a:active { background: #e58735; }
//		ul.flyoutMenu li ul li a { display: block; margin: 0; padding: 3px 9px; background: #df7012; color: #fff; text-decoration: none; }
//
//		ul.flyoutMenu li.closeMenu ul { position: absolute; left: -10000px; }
//
//	The following style rules are suggested for accordian menus:
//		TODO: come up with some BETTER style guidelines
//
//		#[Accordian menu Container ID] h2 { position: absolute; left: -10000px; }
//		ul.accordianMenu { clear: both; background: #C84B13; }
//		ul.accordianMenu { margin: 6px 3px 0 3px; padding: 0; list-style: none; }
//		ul.accordianMenu li { margin: 0 1px; padding: 0; }
//		ul.accordianMenu li a { color: #fff; font-weight: bold; text-decoration: none; }
//		ul.accordianMenu li.openMenu { }
//		ul.accordianMenu li.openMenu ul { display: block; }
//		ul.accordianMenu li ul { display: none; margin: 0; padding: 0 0 1px 0; border: 1px solid #e87713; border-top-width: 0; background: #df7012; list-style: none; }
//		ul.accordianMenu li ul li { margin: 0; _margin-left /**/: -16px; padding: 0; text-align: left; }
//		ul.accordianMenu li ul li a:hover, ul.accordianMenu li ul li a:focus, ul.accordianMenu li ul li a:active { background: #e58735; }
//		ul.accordianMenu li ul li a { display: block; margin: 0; padding: 3px 9px; background: #df7012; color: #fff; text-decoration: none; }
//
//		ul.accordianMenu li.closeMenu ul { position: absolute; left: -10000px; }
//
//	Based on the mode of the tab bar menu each arrow key does a different operation.
//		Dropdown menu item has focus
//			Up Arrow - Last sub menu item
//			Down Arrow - First sub menu item
//			Left Arrow - Previous menu item
//			Right Arrow - Next menu item
//			Esc - Does nothing
//			Home - First menu item 
//			End - Last menu item
//		Dropdown sub menu has focus
//			Up Arrow - Previous sub menu item or menu item if on first sub menu item
//			Down Arrow - Next sub menu item or menu item if on last sub menu item
//			Left Arrow - Previous menu item
//			Right Arrow - First sub-sub menu item or next menu item.
//			Esc - Menu item
//			Home - Menu item
//			End - Last sub menu item
//		Dropdown sub-sub menu has focus (acts like flyout menu)
//			Up Arrow - Previous sub menu item or last sub menu item if on first sub menu item
//			Down Arrow - Next sub menu item or first sub menu item if on last sub menu item
//			Left Arrow - Closes menu and goes to parent sub menu item
//			Right Arrow - First sub-sub menu item or next menu item.
//			Esc - Closes menu and goes to parent sub menu item
//			Home - First sub-sub menu item
//			End - Last sub-sub menu item
//		Flyout menu item has focus
//			Up Arrow - Previous menu item
//			Down Arrow - Next menu item
//			Left Arrow - Does nothing
//			Right Arrow - First sub menu item
//			Esc - Does nothing
//			Home - First menu item
//			End - Last menu item
//		Flyout menu sub menu item has focus
//			Up Arrow - Previous sub menu item, or last sub menu item if first sub menu item has focus
//			Down Arrow - Next sub menu item, or first sub menu item if last sub menu item has focus
//			Left Arrow - Current menu item
//			Right Arrow - First sub-sub menu item or does nothing
//			Esc - Menu item
//			Home - First sub menu item
//			End - Last sub menu item
//		Accordian menu item has focus
//			Up Arrow - Previous menu item
//			Down Arrow - Next menu item 
//			Left Arrow - Does nothing
//			Right Arrow - First sub menu item
//			Esc - Does nothing
//			Home - First menu item
//			End - Last menu item
//		Accordian menu sub menu item has focus
//			Up Arrow - Previous sub menu item, or menu item if first sub menu item has focus
//			Down Arrow - Next sub menu item, or next menu item if last sub menu item has focus
//			Left Arrow - Current menu item
//			Right Arrow - First sub-sub menu item or does nothing
//			Esc - Menu item
//			Home - First sub menu item
//			End - Last sub menu item

/*global window, document, navigator, screen */

var SnapClassifieds = SnapClassifieds || {};
var OneNet = SnapClassifieds;

OneNet.Modules = OneNet.Modules || {};
OneNet.Modules.Common = OneNet.Modules.Common || {};

OneNet.Modules.Common.DynamicMenuSettings = OneNet.Modules.Common.DynamicMenuSettings ||
{
	DropDownMenuClassName: "dropdownMenu",
	FlyoutMenuClassName: "flyoutMenu",
	AccordianMenuClassName: "accordianMenu",
	OpenMenuClassName: "openMenu",
	CloseMenuClassName: "closeMenu",
	UseFilter: false,
	MenuFilter: "progid:DXImageTransform.Microsoft.GradientWipe(GradientSize=.1, wipestyle=1, motion=forward)"
};

var DynamicMenuSettings = OneNet.Modules.Common.DynamicMenuSettings;

OneNet.Modules.Common.DynamicMenu = OneNet.Modules.Common.DynamicMenu || function(dropDownMenu, mode, useFilter)
{
	/// <summary>Turns the list passed in into a drop down menu</summary>
	/// <param name="dropDownMenu" type="DomElement">List to make into a drop down menu</param>
	/// <param name="mode" type="string">Type of drop down menu. This can be DynamicMenuSettings.DropDownMenuClassName or DynamicMenuSettings.FlyoutMenuClassName</param>
	/// <param name="useFilter" type="bool">Whether or not to use the filter defined for DynamicMenuSettings.MenuFilter. NOTE: the filter only works in IE</param>

	this._dropDownMenu = dropDownMenu;
	
	if (typeof mode === "undefined" || (mode !== DynamicMenuSettings.DropDownMenuClassName && mode !== DynamicMenuSettings.FlyoutMenuClassName && mode !== DynamicMenuSettings.AccordianMenuClassName))
	{
		throw new Error("mode parameter needs to be set to " + DynamicMenuSettings.DropDownMenuClassName + ", " + DynamicMenuSettings.FlyoutMenuClassName + ", or " + DynamicMenuSettings.AccordianMenuClassName + " for the class OneNet.Modules.Common.DynamicMenu");
	}
	
	this._mode = mode;
	this._useFilter = useFilter;
};

var DynamicMenu = OneNet.Modules.Common.DynamicMenu;

OneNet.Modules.Common.DynamicMenu.prototype =
{
	init: function()
	{
		/// <summary>Initializes the drop down menu</summary>
				
		var subLists = this._dropDownMenu.getElementsByTagName("ul");
		
		for (var i = 0, n = subLists.length; i < n; i++)
		{
			subLists[i].style.display = "block";

			if (this._mode === DynamicMenuSettings.AccordianMenuClassName || this.isMenuItem(subLists[i].parentNode) === false)
			{
				OneNet.Utility.DOM.addClass(subLists[i].parentNode, DynamicMenuSettings.CloseMenuClassName);
			}

			var menuItem = OneNet.Utility.DOM.getAncestorWithTagName(subLists[i], "li");

			if (typeof(menuItem.onmouseenter) !== "undefined")
			{
				menuItem.onmouseenter = this._useFilter === true ? Function.createDelegateWithEvent(this, this.openMenuWithFilter, menuItem) : Function.createDelegateWithEvent(this, this.openMenu, menuItem);
				menuItem.onmouseleave = Function.createDelegateWithEvent(this, this.closeMenu, menuItem);
			}

			else
			{
				menuItem.onmouseover = Function.createDelegateWithEvent(this, this.openMenu, menuItem);
				menuItem.onmouseout = Function.createDelegateWithEvent(this, this.closeMenu, menuItem);
			}			

			// keyboard events
			var links = menuItem.getElementsByTagName("a");

			for (var j = 0, m = links.length; j < m; j++)
			{
				links[j].onfocus = Function.createDelegateWithEvent(this, this.openMenu, menuItem, links[j]);
				links[j].onblur = Function.createDelegateWithEvent(this, this.closeMenu, menuItem, links[j]);
				links[j].onkeydown = Function.createDelegateWithEvent(this, this.onMenuKeyPress, links[j]);
			}
		}
	},
	
	openMenuWithFilter: function(e, menuItem)
	{
		/// <summary>Opens a menu using the menu filter defined for DynamicMenuSettings.MenuFilter</summary>
		/// <param name="e" type="Event">The event information for this event handler</param>
		
		var subMenu = menuItem.getElementsByTagName("ul")[0];
		
		if (menuItem && subMenu)
		{
			subMenu.style.visibility = "hidden";
			this.openMenu(e, menuItem);

			try
			{
				subMenu.style.filter = OneNet.Modules.Common.DynamicMenuSettings.MenuFilter;
				subMenu.filters[0].apply();
				subMenu.style.visibility = "visible";
				subMenu.filters[0].play();
			}
			
			catch(error)
			{
				subMenu.style.visibility = "visible";		
			}
		}
	},
	
	openMenu: function(e, menuItem, linkWithFocus)
	{
		/// <summary>Opens a menu without the menu filter</summary>
		/// <param name="e" type="Event">The event information for this event handler</param>

		OneNet.Utility.DOM.addClass(menuItem, OneNet.Modules.Common.DynamicMenuSettings.OpenMenuClassName);
		OneNet.Utility.DOM.removeClass(menuItem, DynamicMenuSettings.CloseMenuClassName);
		
		if (linkWithFocus)
		{
			var currentListItem = linkWithFocus.parentNode;
			
			while (currentListItem)
			{
				currentListItem = OneNet.Utility.DOM.getAncestorWithTagName(currentListItem.parentNode, "li");

				if (currentListItem)
				{
					this.openMenu(e, currentListItem);
				}
			}
		}
	},
	
	closeMenu: function(e, menuItem, linkLosingFocus)
	{
		/// <summary>Closes a menu</summary>
		/// <param name="e" type="Event">The event information for this event handler</param>

		e = OneNet.Utility.Events.getEvent();

		OneNet.Utility.DOM.removeClass(menuItem, OneNet.Modules.Common.DynamicMenuSettings.OpenMenuClassName);
		OneNet.Utility.DOM.addClass(menuItem, DynamicMenuSettings.CloseMenuClassName);

		if (linkLosingFocus)
		{
			var currentListItem = linkLosingFocus.parentNode;
			
			while (currentListItem)
			{
				currentListItem = OneNet.Utility.DOM.getAncestorWithTagName(currentListItem.parentNode, "li");

				if (currentListItem)
				{
					this.closeMenu(e, currentListItem);
				}
			}
		}
		
		e.stopPropagation();
	},

	onMenuKeyPress: function(e, link)
	{
		/// <summary>Event handler that fires whenever a key is pressed when a link in the menu has focus. This event handler sees if any of the key presses should move focus to another link in the drop down menu.</summary>
		/// <param name="e" type="Event">The event information for this event handler</param>
		/// <param name="link" type="DomElement">The link that has focus when a key was pressed</param>

		e = OneNet.Utility.Events.getEvent();
		var menuItem = null;

		if (this._mode === DynamicMenuSettings.DropDownMenuClassName)
		{
			switch (e.keyCode)
			{
				case 27: //esc
					menuItem = this.isMenuItem(link) ? null : this.getParentMenuItem(link);
					break;
				case 35: //end
					menuItem = this.getLastMenuItemAtCurrentLevel(link);
					break;
				case 36: //home
					menuItem = this.isMenuItem(link) || this.inSubSubMenu(link) ? this.getFirstMenuItemAtCurrentLevel(link) : this.getParentMenuItem(link);
					break;
				case 37: //left arrow
					menuItem = this.inSubSubMenu(link) ? this.getParentMenuItem(link) : this.getPreviousMenuItem(link);
					break;
				case 38: //up arrow
					menuItem = this.inSubSubMenu(link) ? this.getPreviousSubMenuItem(link, DynamicMenuSettings.FlyoutMenuClassName) : this.getPreviousSubMenuItem(link);
					break;
				case 39: //right arrow
					menuItem = this.hasSubSubMenu(link) ? this.getFirstSubSubMenuItem(link) : this.getNextMenuItem(link);
					break;
				case 40: //down arrow
					menuItem = this.inSubSubMenu(link) ? this.getNextSubMenuItem(link, DynamicMenuSettings.FlyoutMenuClassName) : this.getNextSubMenuItem(link);
					break;
				default:
					return true;
			}
		}
		
		else if (this._mode === DynamicMenuSettings.FlyoutMenuClassName)
		{
			switch (e.keyCode)
			{
				case 27: //esc
					menuItem = this.isMenuItem(link) ? null : this.getParentMenuItem(link);
					break;
				case 35: //end
					menuItem = this.getLastMenuItemAtCurrentLevel(link);
					break;
				case 36: //home
					menuItem = this.getFirstMenuItemAtCurrentLevel(link);
					break;
				case 37: //left arrow
					menuItem = this.getParentMenuItem(link);
					break;
				case 38: //up arrow
					menuItem = this.isMenuItem(link) ? this.getPreviousMenuItem(link) : this.getPreviousSubMenuItem(link);
					break;
				case 39: //right arrow
					menuItem = this.getFirstSubSubMenuItem(link);
					break;
				case 40: //down arrow
					menuItem = this.isMenuItem(link) ? this.getNextMenuItem(link) : this.getNextSubMenuItem(link);
					break;
				default:
					return true;
			}
		}
		
		else if (this._mode === DynamicMenuSettings.AccordianMenuClassName)
		{
			switch (e.keyCode)
			{
				case 27: //esc
					menuItem = this.isMenuItem(link) ? null : this.getParentMenuItem(link);
					break;
				case 35: //end
					menuItem = this.getLastMenuItemAtCurrentLevel(link);
					break;
				case 36: //home
					menuItem = this.getFirstMenuItemAtCurrentLevel(link);
					break;
				case 37: //left arrow
					menuItem = this.getParentMenuItem(link);
					break;
				case 38: //up arrow
					menuItem = this.isMenuItem(link) ? this.getPreviousMenuItem(link) : this.getPreviousSubMenuItem(link);
					break;
				case 39: //right arrow
					menuItem = this.getFirstSubSubMenuItem(link);
					break;
				case 40: //down arrow
					//NOTE: bug when there are multiple sub sub levels and pressing the down key should go to a menu item that is 2 generations higher...
					menuItem = this.isMenuItem(link) ? this.getNextMenuItem(link) : this.getNextSubMenuItem(link);
					break;
				default:
					return true;
			}
		}

		if(menuItem !== null && OneNet.Utility.DOM.isAncestor(menuItem, this._dropDownMenu) === true)
		{
			menuItem.getElementsByTagName("a")[0].focus();
		}

		e.preventDefault();
		e.stopPropagation();
		return false;
	},

	isMenuItem: function(menuItemLink)
	{
		/// <summary>Returns true if the link passed in is in a menu item and not a sub menu item.</summary>
		/// <param name="menuItemLink" type="DomElement">The link to check.</param>
		/// <returns type="Boolean">True if the link passed in is in a menu item else false if it is in a sub menu item</returns>

		return OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink, "ul") === this._dropDownMenu;
	},

//DELETE THIS FUNCTION?!
	isSubMenuItem: function(menuItemLink)
	{
		/// <summary>Returns boolean indicating if the link passed in is for a sub menu item that has a sub sub menu (or a sub sub menu item that has its own sub menu)</summary>
		/// <param name="menuItemLink" type="DomElement">The link to check.</param>
		/// <returns type="Boolean">True if the link passed in is in a sub menu item or sub sub menu item else false if it is in a menu item</returns>
		//return isMenuItem(menuItemLink) ? false :
	},
	
	hasSubSubMenu: function(menuItemLink)
	{
		/// <summary>Returns boolean indicating if the link passed in is for a sub menu item or sub sub menu item</summary>
		/// <param name="menuItemLink" type="DomElement">The link to check.</param>
		/// <returns type="Boolean">True if the link passed in is in a sub menu item that has its own sub menu (sub sub menu item) else false</returns>

		return this.isMenuItem(menuItemLink) ? false : OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").getElementsByTagName("ul").length > 0;
	},
	
	inSubSubMenu: function(menuItemLink)
	{
		/// <summary>Returns boolean indicating if the link passed in is for a sub sub menu</summary>
		/// <param name="menuItemLink" type="DomElement">The link to check.</param>
		/// <returns type="Boolean">True if the link passed in is in a sub sub menu item else false</returns>
		
		var currentList = OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ul");
		return currentList !== this._dropDownMenu && OneNet.Utility.DOM.getAncestorWithTagName(currentList.parentNode, "ul") !== this._dropDownMenu;
	},
	
	getFirstMenuItemAtCurrentLevel: function(menuItemLink)
	{
		return OneNet.Utility.DOM.getChildElements(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ul"))[0];
	},
	
	getLastMenuItemAtCurrentLevel: function(menuItemLink)
	{
		var menuItems = OneNet.Utility.DOM.getChildElements(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ul"));
		return menuItems[menuItems.length - 1];	
	},
	
	getPreviousMenuItem: function(menuItemLink)
	{
		/// <summary>Returns the previous menu item for the menu item / sub menu item link passed in.</summary>
		/// <param name="subMenuItemLink" type="DomElement">The current link.</param>

		return this.isMenuItem(menuItemLink) === true ?
			OneNet.Utility.DOM.getPreviousSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li")) :
			OneNet.Utility.DOM.getPreviousSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li"));
	},
	
	getNextMenuItem: function(menuItemLink)
	{
		/// <summary>Returns the next menu item for the menu item / sub menu item link passed in.</summary>
		/// <param name="menuItemLink" type="DomElement">The current link.</param>

		return this.isMenuItem(menuItemLink) === true ?
			OneNet.Utility.DOM.getNextSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li")) :
			OneNet.Utility.DOM.getNextSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li"));
	},
	
	getFirstSubSubMenuItem: function(menuItemLink)
	{
		/// <summary>Returns the first sub sub menu item for a link in a sub menu item.</summary>
		/// <param name="menuItemLink" type="DomElement">The current sub menu link.</param>
		/// <remarks>This method assums that this.hasSubSubMenu returns true.</remarks>

		//return the first sub sub menu link.
		return OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").getElementsByTagName("li")[0];
	},

	getPreviousSubMenuItem: function(menuItemLink, modeToUse)
	{
		/// <summary>Returns the sub menu item for the previous menu item.</summary>
		/// <param name="menuItemLink" type="DomElement">The current link.</param>
		/// <param name="modeToUse" type="String" optional="true">The overriden menu mode to use for this instance (for example sometimes a dropdown menu acts like a flyout menu when it has sub sub menus). If this isn't defined then the current mode will be used.</param>
		
		if (!modeToUse)
		{
			modeToUse = this._mode;
		}

		var menuItems;
		
		//if the user is on a top level item move focus to the last menu item in the submenu.
		if(this.isMenuItem(menuItemLink) === true)
		{
			var subList = OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").getElementsByTagName("ul")[0];
			
			if (!subList)
			{
				return null;
			}
			
			else
			{
				menuItems = OneNet.Utility.DOM.getChildElements(subList);
				return menuItems[menuItems.length - 1];
			}
		}

		else if (modeToUse === DynamicMenuSettings.DropDownMenuClassName || modeToUse === DynamicMenuSettings.AccordianMenuClassName)
		{		
			//see if the link is the first one in the sub menu, if so move focus to the menu item.		
			if(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ol", "ul").getElementsByTagName("li")[0].getElementsByTagName("a")[0] === menuItemLink)
			{
				return OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li");
			}

			else
			{
				return OneNet.Utility.DOM.getPreviousSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li"));
			}
		}
		
		else if (modeToUse === DynamicMenuSettings.FlyoutMenuClassName)
		{
			var menuItem = OneNet.Utility.DOM.getPreviousSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li"));
			
			if (menuItem !== null)
			{
				return menuItem;
			}

			menuItems = OneNet.Utility.DOM.getChildElements(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ul"));
			return menuItems[menuItems.length - 1];
		}
	},
	
	getNextSubMenuItem: function(menuItemLink, modeToUse)
	{
		/// <summary>Returns the sub menu item for the next menu item.</summary>
		/// <param name="menuItemLink" type="DomElement">The current link.</param>
		/// <param name="modeToUse" type="String" optional="true">The overriden menu mode to use for this instance (for example sometimes a dropdown menu acts like a flyout menu when it has sub sub menus). If this isn't defined then the current mode will be used.</param>
		var menuItem;
		
		if (!modeToUse)
		{
			modeToUse = this._mode;
		}

		if(this.isMenuItem(menuItemLink) === true)
		{
			return OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").getElementsByTagName("li")[0];
		}

		else if (modeToUse === DynamicMenuSettings.DropDownMenuClassName)
		{
			//see if it is the last menu item in the menu.
			var menuItems = OneNet.Utility.DOM.getChildElements(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "ul"));
			
			if (menuItems[menuItems.length - 1] === OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li"))
			{
				return OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li");
			}

			else
			{
				return OneNet.Utility.DOM.getNextSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li"));
			}
		}
		
		else if (modeToUse === DynamicMenuSettings.FlyoutMenuClassName)
		{
			menuItem = OneNet.Utility.DOM.getNextSiblingElement(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li"));
			return menuItem ? menuItem : OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li").getElementsByTagName("li")[0];
		}
		
		else if (modeToUse === DynamicMenuSettings.AccordianMenuClassName)
		{
			//try to get the next sibling, if it doesn't exist go to the parent and try its next sibling (until there are no next siblings to try).
			var currentMenuItem = OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li");
			
			while (currentMenuItem)
			{
				menuItem = OneNet.Utility.DOM.getNextSiblingElement(currentMenuItem);
				
				if (menuItem)
				{
					return menuItem;
				}
				
				currentMenuItem = OneNet.Utility.DOM.getAncestorWithTagName(currentMenuItem.parentNode, "li");
			}
			
			return null;
		}
	},
	
	getParentMenuItem: function(menuItemLink)
	{
		/// <summary>Returns the parent menu item for the current link (for ex. if in a sub menu item, the menu item is returned. If in a sub sub menu item the sub menu item is returned).</summary>
		/// <param name="menuItemLink" type="DomElement">The current link.</param>
		/// <returns type="DOMElement">The parent menu item element if the menuItemLink is for a sub menu item or sub sub menu item, else null if menuItemLink is for a menu item.</returns>

		return this.isMenuItem(menuItemLink) === false ?
			OneNet.Utility.DOM.getAncestorWithTagName(OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").parentNode, "li") :
			null;
	},
//TODO CAN WE GET RID OF THIS FUNCTION IF WE USE getFirstSubSubMenuItem INSTEAD? IF SO PROBABLY WANT TO RENAME getFirstSubSubMenuItem
	getFirstSubMenuItem: function(menuItemLink)
	{
		/// <summary>Returns the first sub menu item for a menu item.</summary>
		/// <param name="menuItemLink" type="DomElement">A link in the drop down menu.</param>
		/// <returns type="DomElement">The first sub menu item if menuItemLink is for a menu item, else null if menuItemLink is for a sub menu item</returns>

		return this.isMenuItem(menuItemLink) === true ? 
			OneNet.Utility.DOM.getAncestorWithTagName(menuItemLink.parentNode, "li").getElementsByTagName("li")[0] : 
			null;
	}
};

Function.prototype.createDelegateWithEvent = Function.prototype.createDelegateWithEvent || function(object, method)
{
	/// <summary>Creates a delegate to allow the specified method to run in the context of the specified instance.</summary>
	/// <param name="object" type="Object"></param>
	/// <param name="method" type="Function"></param>
	/// <param name="arguments" type="optional arguments">Optional extra arguments to pass into the method when it is called</param>
	/// <remarks>Necessary to allow "this" in event handlers to point to the specified object rather than the event source element.</remarks>
	/// <remarks>Use: button.onclick = Function.createDelegateWithEvent(this, this.method)</remarks>
	/// <remarks>Use: button.onclick = Function.createDelegateWithEvent(this, this.method, optionalArgument1, optionalArgument2, ... optionalArgumentN)</remarks>
	/// <remarks>NOTE: IE 5.0 doesn't understand Function.apply if we need to support this browser we will need to define it (perhaps define it in terms of Function.call)</remarks>
	/// <remarks>
	///	The delegate function returned will have the event object as the first parameter (or an empty object in browsers that do not pass event objects as the first parameter like IE).
	///	If the delegate function does not need access to event information you can use Function.createDelegate instead.
	/// </remarks>
	/// <remarks>
	///	NOTE: There is a bug in IE with this function where if the first known parameter (at the Function.createDelegate call)
	///	or first local parameter (when there are no known parameters) is an event object then it will not be passed to the delegate function.
	///	If an event object needs to be used with createDelegate make sure it isn't the first parameter or if there are no other parameters pass
	///	in a dummy value like null or new Object()
	/// </remarks>
	var functionArguments = arguments;

	return function()
	{
		var callerArguments = [];

		//If there are no arguments or the first parameter isn't an event add a dummy object as the first parameter to the delegate function (needed for IE)
		var skipFirstLocalArgument = false;

		if (arguments.length === 0 || !arguments[0].target)
		{
			callerArguments.push({});
		}

		//the first local parameter is an event, add it first to the callerArguments.
		else if (arguments.length > 0 && arguments[0].target)
		{
			callerArguments.push(arguments[0]);
			skipFirstLocalArgument = true;
		}
		
		var i, n;

		//Add arguments passed into the createDelegate method.
		for (i = 2, n = functionArguments.length; i < n; i++)
		{
			callerArguments.push(functionArguments[i]);
		}

		//Add any local arguments that might of been added during the method call (i.e. not the arguments defined at the creation of createDelegate)
		for (i = skipFirstLocalArgument ? 1 : 0, n = arguments.length; i < n; i++)
		{
			callerArguments.push(arguments[i]);
		}

		return method.apply(object, callerArguments);
	};
};


OneNet.Utility = OneNet.Utility || {};
OneNet.Utility.DOM = OneNet.Utility.DOM || {};

OneNet.Utility.DOM.addClass = OneNet.Utility.DOM.addClass || function(element)
{
	/// <param name="element" type="DomElement">Element to add the classes to.</param>
	/// <param type="Array of strings" optional="true">The list of classes to add to the element</param>
	/// <remarks>NOTE: if the class name to add is already associated with the element it will not be added a second time.</remarks>

	for (var i = 1, n = arguments.length; i < n; i++)
	{
		if (!OneNet.Utility.DOM.hasClass(element, arguments[i]))
		{
			element.className = element.className === "" ? arguments[i] : element.className + " " + arguments[i];
		}
	}
};

OneNet.Utility.DOM.hasClass = OneNet.Utility.DOM.hasClass || function(element)
{
	/// <param name="element" type="DomElement">Element to check for the class.</param>
	/// <param type="Array of strings" optional="true">The list of classes to check for</param>
	/// <returns>true if the element passed in is in one of the classes in the argument list, else false.</returns>

	var elementClassNames = " " + element.className + " ";

	for (var i = 1, n = arguments.length; i < n; i++)
	{
		if (elementClassNames.indexOf(" " + arguments[i] + " ") !== -1)
		{
			return true;
		}
	}

	return false;
};

OneNet.Utility.DOM.removeClass = OneNet.Utility.DOM.removeClass || function(element)
{
	/// <param name="element" type="DomElement">Element to remove classes from.</param>
	/// <param type="Array of strings" optional="true">The list of classes to remove from the element</param>

	for (var i = 1, n = arguments.length; i < n; i++)
	{
		element.className = OneNet.Utility.String.trim(element.className.replace(new RegExp("\\b" + arguments[i] + "\\b"), ""));
	}
};

OneNet.Utility.DOM.isAncestor = OneNet.Utility.DOM.isAncestor || function(element, possibleAncestor)
{
	/// <summary>Indicates whether possibleAncestor is an ancestor of element.</summary>
	/// <remarks>An element is considered to be an ancestor of itself</remarks>
	/// <param name="element" type="DomElement"></param>
	/// <param name="possibleAncestor" type="DomElement"></param>
	/// <returns>True if possibleAncestor is an ancestor of element, else false.</returns>

	var ancestor = element;

	while (ancestor)
	{
		if (ancestor === possibleAncestor)
		{
			return true;
		}

		ancestor = ancestor.parentNode;
	}

	return false;	
};

OneNet.Utility.DOM.getAncestorWithTagName = OneNet.Utility.DOM.getAncestorWithTagName || function(element)
{
	/// <summary>Returns the element's most immediate ancestor with a given tagName.</summary>
	/// <remarks>An element is considered to be an ancestor of itself</remarks>
	/// <param name="element" type="DomElement"></param>
	/// <param type="Array of strings" optional="true">The list of tag names to look for</param>
	/// <returns>The closest ancestor with one of the tag names passed in else null</returns>
	
	var ancestor = element;
	var argumentsLength = arguments.length;

	while (ancestor)
	{
		//NOTE: need to check that tagName is supported browsers like Mozilla include the document object for a parent node and this property is not defined for it.
		if (ancestor.tagName)
		{
			var tagName = ancestor.tagName.toLowerCase();

			for (var i = 1; i < argumentsLength; i++)
			{
				if (tagName === arguments[i])
				{
					return ancestor;
				}
			}
		}

		ancestor = ancestor.parentNode;
	}

	return ancestor;
};

OneNet.Utility.DOM.getNextSiblingElement = OneNet.Utility.DOM.getNextSiblingElement || function(element)
{
	/// <summary>Gets the next sibling that is an element.</summary>
	/// <remarks>Mimic the behavior of domElement.nextSibling in IE</remarks>
	/// <param name="element" type="DomElement"></param>
	/// <returns>The next sibling element else null</returns>
	
	var current = element.nextSibling;

	while (current !== null && current.nodeType !== 1)
	{
		current = current.nextSibling;
	}

	return current;
};

OneNet.Utility.DOM.getPreviousSiblingElement = OneNet.Utility.DOM.getPreviousSiblingElement || function(element)
{
	/// <summary>Gets the previous sibling that is an element.</summary>
	/// <remarks>Mimic the behavior of domElement.previousSibling in IE</remarks>
	/// <param name="element" type="DomElement"></param>
	/// <returns>The previous sibling element else null</returns>

	var current = element.previousSibling;

	while (current !== null && current.nodeType !== 1)
	{
		current = current.previousSibling;
	}

	return current;
};

OneNet.Utility.DOM.getChildElements = OneNet.Utility.DOM.getChildElements || function(element)
{
	/// <summary>Returns the child elements for the element passed in.</summary>
	/// <remarks>This differs from element.childNodes since this function will not return text nodes.</remarks>
	/// <returns type="Array of DomElements">The child elements of the element passed in</returns>

	var childElements = [];
	
	for (var i = 0, n = element.childNodes.length; i < n; i++)
	{
		if (element.childNodes[i].nodeType === 1)
		{
			childElements.push(element.childNodes[i]);
		}
	}

	return childElements;
};

OneNet.Utility.Events = OneNet.Utility.Events || {};

OneNet.Utility.Events.formatEvent = OneNet.Utility.Events.formatEvent || function(e)
{
	/// <summary>Formats an IE event object to act like a DOM event object</summary>
	/// <param name="element" type="EventElement">The event to format</param>

	e.charCode = e.type === "keypress" || e.type === "keydown" || e.type === "keyup" ? e.keyCode : 0;

	try
	{
		e.eventPhase = 2;
	}
	
	catch(ex1)
	{
	}

	e.isChar = (e.charCode > 0);

	try
	{
		e.pageX = e.clientX + document.body.scrollLeft;
		e.pageY = e.clientY + document.body.scrollTop;
	}
	
	catch(ex2)
	{
	}

	e.preventDefault = function ()
	{
		this.returnValue = false;
	};
	
	try
	{
		if (e.type === "mouseout")
		{
			e.relatedTarget = e.toElement;
		}

		else if (e.type === "mouseover")
		{
			e.relatedTarget = e.fromElement;
		}
	}
	
	catch(ex3)
	{
	}

	e.stopPropagation = function ()
	{
		this.cancelBubble = true;
	};
	
	try
	{
		e.target = e.srcElement;
	}
	
	catch(ex4)
	{
	}

	e.time = (new Date()).getTime();

	return e;
};

OneNet.Utility.Events.getEvent = OneNet.Utility.Events.getEvent || function()
{
	/// <summary>Returns an event object for the calling function.</summary>
	return window.event ? this.formatEvent(window.event) : OneNet.Utility.Events.getEvent.caller.arguments[0];
};

OneNet.Utility.Events.addEventHandler = OneNet.Utility.Events.addEventHandler || function(target, eventType, functionHandler)
{
	/// <summary>Adds an event handler to the target passed in while preserving existing event handler of the same type</summary>
	/// <param name="target" type="DomElement">The element to add the event to</param>
	/// <param name="eventType" type="string">The type of event</param>
	/// <param name="functionHandler" type="Function">The function to call for the event handler</param>

	if (target.addEventListener)
	{
		target.addEventListener(eventType, functionHandler, false);
	}

	else if (target.attachEvent)
	{
		target.attachEvent("on" + eventType, functionHandler);
	}

	else
	{
		target["on" + eventType] = functionHandler;
	}
};


OneNet.Utility.String = OneNet.Utility.String || {};

OneNet.Utility.String.trim = OneNet.Utility.String.trim || function(value)
{
	/// <summary>Removes leading, trailing, and extraneous white space characters from a string.</summary>
	/// <param name="value" type="string">The string to trim</param>
	/// <returns>A trimmed string</returns>

	if (value !== null)
	{
		return value.toString().replace(/\s{2,}/g, " ").replace(/[\t\v\f\n\r]/g, " ").replace(/^\s+|\s+$/g, "");
	}

	else
	{
		return "";
	}
};

//Initialization Code

DynamicMenu.initAll = function()
{
	/// <summary>Initializes all the drop down menus for the document.</summary>

	var lists = document.getElementsByTagName("ul");
	
	for (var i = 0, n = lists.length; i < n; i++)
	{
		var dropDownMenu;
		
		if (OneNet.Utility.DOM.hasClass(lists[i], DynamicMenuSettings.DropDownMenuClassName) === true)
		{
			dropDownMenu = new DynamicMenu(lists[i], DynamicMenuSettings.DropDownMenuClassName, DynamicMenuSettings.UseFilter);
			dropDownMenu.init();
		}
		
		else if (OneNet.Utility.DOM.hasClass(lists[i], DynamicMenuSettings.FlyoutMenuClassName) === true)
		{
			dropDownMenu = new DynamicMenu(lists[i], DynamicMenuSettings.FlyoutMenuClassName, DynamicMenuSettings.UseFilter);
			dropDownMenu.init();
		}
		
		else if (OneNet.Utility.DOM.hasClass(lists[i], DynamicMenuSettings.AccordianMenuClassName) === true)
		{
			dropDownMenu = new DynamicMenu(lists[i], DynamicMenuSettings.AccordianMenuClassName, DynamicMenuSettings.UseFilter);
			dropDownMenu.init();
		}		
	}
};

OneNet.Utility.Events.addEventHandler(window, "load", DynamicMenu.initAll);
