/*
TimothyHumphrey.WebControls
Copyright (c) 2003 - 2004 Timothy Humphrey

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

//-------------------- Enumerations --------------------\\
Action = {
	None : 0
	,Link : 1
	,Postback : 2
}

AlignTo = {
	Origin : 0
	,ParentItem : 1
	,Canvas : 2
	,ViewPort : 3
	,Cursor : 4
	,ID : 5
}

Display = {
	None : 0
	,Url : 1
	,Text : 2
}

HorizontalAlign = {
	Left : 0
	,Center : 1
	,Right : 2
}

VerticalAlign = {
	Top : 0
	,Middle : 1
	,Bottom : 2
}

//-------------------- MainMenuEvent Class --------------------\\
function MainMenuEvent(theControl) {
	this.Control = theControl;
	this.Handlers = new HandlerList(this);
	this.Initiators = new InitiatorList(this);
}
MainMenuEvent.prototype = new MainMenuEvent;
MainMenuEvent.prototype.constructor = MainMenuEvent;

MainMenuEvent.prototype.Fire = function(initiator, data) {
	var i;

	for(i = 0; i < this.Initiators.Count; i++) {
		if(this.Initiators.Item(i) != initiator)
			this.Initiators.Item(i).Notify();
	}

	for(i = 0; i < this.Handlers.Count; i++) {
		if(!this.Handlers.Item(i).Play(initiator, data))
			break;
	}
}

//-------------------- Handler Class --------------------\\
function Handler() {
	this.Control = null;
	this.MainMenuEvent = null;
}

Handler.prototype.Load = function() {}
Handler.prototype.Play = function(initiator, data) {}
Handler.prototype.Unload = function() {}

//-------------------- HandlerList Class --------------------\\
function HandlerList(theEvent) {	
	this.base = List;
	this.base();
	delete this.base;

	this.MainMenuEvent = theEvent;
}
HandlerList.prototype = new List;
HandlerList.prototype.constructor = HandlerList;

HandlerList.prototype.Add = function(theHandler) {
	if(theHandler.Load
		&& theHandler.Play
		&& theHandler.Unload
		) {
		this.base = List.prototype.Add;
		this.base(theHandler);
		delete this.base;

		theHandler.Control = this.MainMenuEvent.Control;
		theHandler.MainMenuEvent = this.MainMenuEvent;
	}
}

//-------------------- Initiator Class --------------------\\
function Initiator() {
	this.Control = null;
	this.MainMenuEvent = null;
	this.Installed = false;
}

Initiator.prototype.GetTimeoutString = function(eventName, functionName) {
	var timeoutString = "";
	var found;
	var i;

	found = false;
	for(i = 0; i < this.MainMenuEvent.Initiators.Count; i++) {
		if(this.MainMenuEvent.Initiators.Item(i) == this) {
			found = true;
			break;
		}
	}

	if(found) {
		timeoutString = "MainMenu"
			+ ".MainMenus." + this.Control.MainMenu.ID
			+ "." + ((this.Control.constructor == Menu) ? "Menus." : "Items.") + this.Control.ID
			+ "." + eventName
			+ ".Initiators"
			+ ".Item(" + i + ")"
			+ "." + functionName + "(";
		for(i = 2; i < arguments.length; i++) {
			if(i != 2) timeoutString += ",";
			timeoutString += arguments[i];
		}
		timeoutString += ")";
	}

	return timeoutString;
}

Initiator.prototype.Load = function() {}
Initiator.prototype.Notify = function() {}
Initiator.prototype.Unload = function() {}

//-------------------- InitiatorList Class --------------------\\
function InitiatorList(theEvent) {
	this.base = List;
	this.base();
	delete this.base;

	this.MainMenuEvent = theEvent;
}
InitiatorList.prototype = new List;
InitiatorList.prototype.constructor = InitiatorList;

InitiatorList.prototype.Add = function(theInitiator) {
	if(theInitiator.Load
		&& theInitiator.Notify
		&& theInitiator.Unload
		) {
		this.base = List.prototype.Add;
		this.base(theInitiator);
		delete this.base;

		theInitiator.Control = this.MainMenuEvent.Control;
		theInitiator.MainMenuEvent = this.MainMenuEvent;
	}
}

//-------------------- Control Class --------------------\\
function Control(theMainMenu) {
	this.Data = new Object();
	this.Element = null;
	this.Hot = false;
	this.ID = "";
	this.MainMenu = theMainMenu;
	this.Parent = null;

	this.HotChanged = new Control.HotChangedEvent(this);
	this.Loaded = new MainMenuEvent(this);
}

//-------------------- Control.HotChangedEvent Class --------------------\\
Control.HotChangedEvent = function(theControl) {
	this.base = MainMenuEvent;
	this.base(theControl);
	delete this.base;
}
Control.HotChangedEvent.prototype = new MainMenuEvent;
Control.HotChangedEvent.prototype.constructor = Control.HotChangedEvent;

Control.HotChangedEvent.prototype.Fire = function(initiator, hot) {
	if(hot == this.Control.Hot)
		return;

	this.Control.Hot = hot;

	this.base = MainMenuEvent.prototype.Fire;
	this.base(initiator, hot);
	delete this.base;
}

Control.Type = {
	Menu : 0
	,Item : 1
}

//-------------------- MainMenu Class --------------------\\
function MainMenu(buildData) {
	this.ID = "";
	this.Items = [];
	this.Menus = [];
	this.UniqueID = "";

	this.CreateMainMenu(buildData);
}

MainMenu.MainMenus = [];
MainMenu.PostBackDelimiter = ":";

MainMenu.Add = function(id, uniqueID) {
	var mainMenu = eval(id);
	mainMenu.ID = id;
	mainMenu.UniqueID = uniqueID;
	MainMenu.MainMenus[id] = mainMenu;
	MainMenu.MainMenus[MainMenu.MainMenus.length] = mainMenu;
}

MainMenu.HideAll = function() {
	var i;

	for(i = 0; i < MainMenu.MainMenus.length; i++)
		MainMenu.MainMenus[i].HideAll();
}

MainMenu.prototype.CreateMainMenu = function(buildData) {
	function AssignData(control, defaultControl, objectDef, indexData) {
		var reIdentifierSyntax = /^[A-Za-z_]\w*$/;
		var iDefaultData = 0, iControlData = indexData;
		var dataItemID, dataItemCount;
		var canUseDefault = iDefaultData < defaultControl.Data.length;

		if(indexData + 1 == objectDef.length)
			return;

		while(iControlData < objectDef.length - 1) {
			if(canUseDefault) {
				if(objectDef[iControlData] != null) {
					dataItemID = objectDef[iControlData];
					canUseDefault = dataItemID == defaultControl.Data[iDefaultData];
				}
				else
					dataItemID = defaultControl.Data[iDefaultData];
			}
			else
				dataItemID = objectDef[iControlData];

			iControlData++; iDefaultData++;

			dataItemCount = canUseDefault && objectDef[iControlData] == null
				? defaultControl.Data[iDefaultData]
				: objectDef[iControlData];
			iControlData++; iDefaultData++;
			canUseDefault = canUseDefault && iDefaultData < defaultControl.Data.length;

			if(dataItemID != null && reIdentifierSyntax.test(dataItemID)) {
				if(!dataItemCount)
					dataItemCount = 0;

				if(control.Data[dataItemID] == null) {
					control.Data[dataItemID] = [];
					while(dataItemCount > 0 && iControlData < objectDef.length - 1) {
						control.Data[dataItemID][control.Data[dataItemID].length] = canUseDefault && objectDef[iControlData] == null
							? defaultControl.Data[iDefaultData]
							: objectDef[iControlData];
						iControlData++; iDefaultData++;
						canUseDefault = canUseDefault && iDefaultData < defaultControl.Data.length;
						dataItemCount--;
					}
				}
				else {
					alert("Duplicate data item identifier, '" + dataItemID + "', specified for '" + control.ID + "'.");
					iControlData += dataItemCount;
					iDefaultData += dataItemCount;
					canUseDefault = canUseDefault && iDefaultData < defaultControl.Data.length;
				}
			}
			else {
				alert("Invalid data item identifier, '" + dataItemID + "', specified for '" + control.ID + "'.");
				control.Data = new Object();
				break;
			}
		}
	}

	function AssignHandlerList(list, theEvent) {
		var listArray, aFunction;
		var i;

		listArray = list.split(" ");
		for(i = 0; i < listArray.length; i++) {
			if(eval("typeof " + listArray[i]) == "function") {
				aFunction = eval("new " + listArray[i]);
				theEvent.Handlers.Add(aFunction);
			}
		}
	}

	function AssignInitiatorList(list, theEvent) {
		var listArray, aFunction;
		var i;

		listArray = list.split(" ");
		for(i = 0; i < listArray.length; i++) {
			if(eval("typeof " + listArray[i]) == "function") {
				aFunction = eval("new " + listArray[i]);
				theEvent.Initiators.Add(aFunction);
			}
		}
	}

	//----- Build array enumerations
	var eEnum = 0, eControlEndEnum;

	var eDefinition = eEnum++;

	//Control positions
	var eControlID = eEnum++;
	var eControlParentID = eEnum++;

	var eControlHotChangedHandlers = eEnum++;
	var eControlHotChangedInitiators = eEnum++;
	var eControlLoadedHandlers = eEnum++;
	eControlEndEnum = eEnum;

	//Menu positions
	eEnum = eControlEndEnum;
	var eMenuAlignedItem = eEnum++;
	var eMenuAlignTo = eEnum++;
	var eMenuAlignToID = eEnum++;
	var eMenuElementHorizontalAlign = eEnum++;
	var eMenuElementVerticalAlign = eEnum++;
	var eMenuHideDelay = eEnum++;
	var eMenuMenuHorizontalAlign = eEnum++;
	var eMenuMenuVerticalAlign = eEnum++;
	var eMenuOffsetX = eEnum++;
	var eMenuOffsetY = eEnum++;
	var eMenuPermanent = eEnum++;
	var eMenuShowDelay = eEnum++;

	var eMenuVisibleChangedHandlers = eEnum++;
	var eMenuVisibleChangedInitiators = eEnum++;

	var lengthMenuDefinition = ++eEnum; //include end marker
	var eMenuData = lengthMenuDefinition - 1;

	//Item positions
	eEnum = eControlEndEnum;
	var eItemAction = eEnum++;
	var eItemActionTarget = eEnum++;
	var eItemActionUrl = eEnum++;
	var eItemDisplay = eEnum++;
	var eItemDisplayText = eEnum++;
	var eItemEnabled = eEnum++;
	var eItemKeepHighlight = eEnum++;

	var eItemHighlightChangedHandlers = eEnum++;
	var eItemHighlightChangedInitiators = eEnum++;
	var eItemSelectedHandlers = eEnum++;
	var eItemSelectedInitiators = eEnum++;

	var lengthItemDefinition = ++eEnum; //include end marker
	var eItemData = lengthItemDefinition - 1;

	Definition = {
		MenuDefaults : 0
		,ItemDefaults : 1
		,Menu : 2
		,Item : 3
	}

	//----- Variables
	var objectDef;
	var defaultMenu, defaultItem, menu, item;
	var iBuildData;
	var i, j;

	//----- Create controls
	if(buildData == null)
		return;

	defaultMenu = new Menu(this);
	defaultItem = new Item(this);

	for(iBuildData = 0; iBuildData < buildData.length; iBuildData++) {
		objectDef = buildData[iBuildData];

		switch(objectDef[eDefinition]) {
		case Definition.MenuDefaults:
			if(objectDef[eMenuAlignedItem] != null)
				defaultMenu.AlignedItem = objectDef[eMenuAlignedItem];

			if(objectDef[eMenuAlignTo] != null)
				defaultMenu.AlignTo = objectDef[eMenuAlignTo];

			if(objectDef[eMenuAlignToID] != null)
				defaultMenu.AlignToID = objectDef[eMenuAlignToID];

			if(objectDef[eMenuElementHorizontalAlign] != null)
				defaultMenu.ElementHorizontalAlign = objectDef[eMenuElementHorizontalAlign];

			if(objectDef[eMenuElementVerticalAlign] != null)
				defaultMenu.ElementVerticalAlign = objectDef[eMenuElementVerticalAlign];

			if(objectDef[eMenuHideDelay] != null)
				defaultMenu.HideDelay = objectDef[eMenuHideDelay];

			if(objectDef[eMenuMenuHorizontalAlign] != null)
				defaultMenu.MenuHorizontalAlign = objectDef[eMenuMenuHorizontalAlign];

			if(objectDef[eMenuMenuVerticalAlign] != null)
				defaultMenu.MenuVerticalAlign = objectDef[eMenuMenuVerticalAlign];

			if(objectDef[eMenuOffsetX] != null)
				defaultMenu.OffsetX = objectDef[eMenuOffsetX];

			if(objectDef[eMenuOffsetY] != null)
				defaultMenu.OffsetY = objectDef[eMenuOffsetY];

			if(objectDef[eMenuPermanent] != null)
				defaultMenu.Permanent = defaultMenu.Visible = Boolean(objectDef[eMenuPermanent]);

			if(objectDef[eMenuShowDelay] != null)
				defaultMenu.ShowDelay = objectDef[eMenuShowDelay];


			defaultMenu.HotHandlers = objectDef[eControlHotChangedHandlers] != null
				? objectDef[eControlHotChangedHandlers]
				: null;

			defaultMenu.HotInitiators = objectDef[eControlHotChangedInitiators] != null
				? objectDef[eControlHotChangedInitiators]
				: null;

			defaultMenu.LoadedHandlers = objectDef[eControlLoadedHandlers] != null
				? objectDef[eControlLoadedHandlers]
				: null;

			defaultMenu.VisibleChangedHandlers = objectDef[eMenuVisibleChangedHandlers] != null
				? objectDef[eMenuVisibleChangedHandlers]
				: null;

			defaultMenu.VisibleChangedInitiators = objectDef[eMenuVisibleChangedInitiators] != null
				? objectDef[eMenuVisibleChangedInitiators]
				: null;


			if(objectDef.length > lengthMenuDefinition)
				defaultMenu.Data = objectDef.slice(eMenuData, -1);
			else
				defaultMenu.Data = [];

			break;
		case Definition.ItemDefaults:
			if(objectDef[eItemAction] != null)
				defaultItem.Action = objectDef[eItemAction];

			if(objectDef[eItemActionTarget] != null)
				defaultItem.ActionTarget = objectDef[eItemActionTarget];

			if(objectDef[eItemActionUrl] != null)
				defaultItem.ActionUrl = objectDef[eItemActionUrl];

			if(objectDef[eItemDisplay] != null)
				defaultItem.Display = objectDef[eItemDisplay];

			if(objectDef[eItemDisplayText] != null)
				defaultItem.DisplayText = objectDef[eItemDisplayText];

			if(objectDef[eItemEnabled] != null)
				defaultItem.Enabled = objectDef[eItemEnabled];

			if(objectDef[eItemKeepHighlight] != null)
				defaultItem.KeepHighlight = Boolean(objectDef[eItemKeepHighlight]);


			defaultItem.HighlightChangedHandlers = objectDef[eItemHighlightChangedHandlers] != null
				? objectDef[eItemHighlightChangedHandlers]
				: null;

			defaultItem.HighlightChangedInitiators = objectDef[eItemHighlightChangedInitiators] != null
				? objectDef[eItemHighlightChangedInitiators]
				: null;

			defaultItem.HotHandlers = objectDef[eControlHotChangedHandlers] != null
				? objectDef[eControlHotChangedHandlers]
				: null;

			defaultItem.HotInitiators = objectDef[eControlHotChangedInitiators] != null
				? objectDef[eControlHotChangedInitiators]
				: null;

			defaultItem.LoadedHandlers = objectDef[eControlLoadedHandlers] != null
				? objectDef[eControlLoadedHandlers]
				: null;

			defaultItem.SelectedHandlers = objectDef[eItemSelectedHandlers] != null
				? objectDef[eItemSelectedHandlers]
				: null;

			defaultItem.SelectedInitiators = objectDef[eItemSelectedInitiators] != null
				? objectDef[eItemSelectedInitiators]
				: null;


			if(objectDef.length > lengthItemDefinition)
				defaultItem.Data = objectDef.slice(eItemData, -1);
			else
				defaultItem.Data = [];

			break;
		case Definition.Menu:
			if(!Browser.GetElement(objectDef[eControlID]))
				continue;

			menu = new Menu(this);

			this.Menus[objectDef[eControlID]] = menu;
			this.Menus[this.Menus.length] = menu;

			menu.ID = objectDef[eControlID];
			menu.ParentID = objectDef[eControlParentID];
			menu.Element = Browser.GetElement(objectDef[eControlID]);

			menu.AlignedItem = objectDef[eMenuAlignedItem] != null
				? objectDef[eMenuAlignedItem]
				: defaultMenu.AlignedItem;

			menu.AlignTo = objectDef[eMenuAlignTo] != null
				? objectDef[eMenuAlignTo]
				: defaultMenu.AlignTo;

			menu.AlignToID = objectDef[eMenuAlignToID] != null
				? objectDef[eMenuAlignToID]
				: defaultMenu.AlignToID;

			menu.ElementHorizontalAlign = objectDef[eMenuElementHorizontalAlign] != null
				? objectDef[eMenuElementHorizontalAlign]
				: defaultMenu.ElementHorizontalAlign;

			menu.ElementVerticalAlign = objectDef[eMenuElementVerticalAlign] != null
				? objectDef[eMenuElementVerticalAlign]
				: defaultMenu.ElementVerticalAlign;

			menu.HideDelay = objectDef[eMenuHideDelay] != null
				? objectDef[eMenuHideDelay]
				: defaultMenu.HideDelay;

			menu.MenuHorizontalAlign = objectDef[eMenuMenuHorizontalAlign] != null
				? objectDef[eMenuMenuHorizontalAlign]
				: defaultMenu.MenuHorizontalAlign;

			menu.MenuVerticalAlign = objectDef[eMenuMenuVerticalAlign] != null
				? objectDef[eMenuMenuVerticalAlign]
				: defaultMenu.MenuVerticalAlign;

			menu.OffsetX = objectDef[eMenuOffsetX] != null
				? objectDef[eMenuOffsetX]
				: defaultMenu.OffsetX;

			menu.OffsetY = objectDef[eMenuOffsetY] != null
				? objectDef[eMenuOffsetY]
				: defaultMenu.OffsetY;

			menu.ShowDelay = objectDef[eMenuShowDelay] != null
				? objectDef[eMenuShowDelay]
				: defaultMenu.ShowDelay;

			menu.Permanent = menu.Visible = objectDef[eMenuPermanent] != null
				? Boolean(objectDef[eMenuPermanent])
				: defaultMenu.Permanent;


			if(objectDef[eControlHotChangedHandlers] != null)
				AssignHandlerList(objectDef[eControlHotChangedHandlers], menu.HotChanged);
			else if(defaultMenu.HotHandlers != null)
				AssignHandlerList(defaultMenu.HotHandlers, menu.HotChanged);

			if(objectDef[eControlHotChangedInitiators] != null)
				AssignInitiatorList(objectDef[eControlHotChangedInitiators], menu.HotChanged);
			else if(defaultMenu.HotInitiators != null)
				AssignInitiatorList(defaultMenu.HotInitiators, menu.HotChanged);

			if(objectDef[eControlLoadedHandlers] != null)
				AssignHandlerList(objectDef[eControlLoadedHandlers], menu.Loaded);
			else if(defaultMenu.LoadedHandlers != null)
				AssignHandlerList(defaultMenu.LoadedHandlers, menu.Loaded);

			if(objectDef[eMenuVisibleChangedHandlers] != null)
				AssignHandlerList(objectDef[eMenuVisibleChangedHandlers], menu.VisibleChanged);
			else if(defaultMenu.VisibleChangedHandlers != null)
				AssignHandlerList(defaultMenu.VisibleChangedHandlers, menu.VisibleChanged);

			if(objectDef[eMenuVisibleChangedInitiators] != null)
				AssignInitiatorList(objectDef[eMenuVisibleChangedInitiators], menu.VisibleChanged);
			else if(defaultMenu.VisibleChangedInitiators != null)
				AssignInitiatorList(defaultMenu.VisibleChangedInitiators, menu.VisibleChanged);


			AssignData(menu, defaultMenu, objectDef, eMenuData);

			break;
		case Definition.Item:
			if(!Browser.GetElement(objectDef[eControlID]))
				continue;

			item = new Item(this);

			this.Items[objectDef[eControlID]] = item;
			this.Items[this.Items.length] = item;

			item.ID = objectDef[eControlID];
			item.ParentID = objectDef[eControlParentID];
			item.Element = Browser.GetElement(objectDef[eControlID]);

			item.Action = objectDef[eItemAction] != null
				? objectDef[eItemAction]
				: defaultItem.Action;

			item.ActionTarget = objectDef[eItemActionTarget] != null
				? objectDef[eItemActionTarget]
				: defaultItem.ActionTarget;

			item.ActionUrl = objectDef[eItemActionUrl] != null
				? objectDef[eItemActionUrl]
				: defaultItem.ActionUrl;

			item.Display = objectDef[eItemDisplay] != null
				? objectDef[eItemDisplay]
				: defaultItem.Display;

			item.DisplayText = objectDef[eItemDisplayText] != null
				? objectDef[eItemDisplayText]
				: defaultItem.DisplayText;

			item.Enabled = objectDef[eItemEnabled] != null
				? objectDef[eItemEnabled]
				: defaultItem.Enabled;

			item.KeepHighlight = objectDef[eItemKeepHighlight] != null
				? Boolean(objectDef[eItemKeepHighlight])
				: defaultItem.KeepHighlight;


			if(objectDef[eItemHighlightChangedHandlers] != null)
				AssignHandlerList(objectDef[eItemHighlightChangedHandlers], item.HighlightChanged);
			else if(defaultItem.HighlightChangedHandlers != null)
				AssignHandlerList(defaultItem.HighlightChangedHandlers, item.HighlightChanged);

			if(objectDef[eItemHighlightChangedInitiators] != null)
				AssignInitiatorList(objectDef[eItemHighlightChangedInitiators], item.HighlightChanged);
			else if(defaultItem.HighlightChangedInitiators != null)
				AssignInitiatorList(defaultItem.HighlightChangedInitiators, item.HighlightChanged);

			if(objectDef[eControlHotChangedHandlers] != null)
				AssignHandlerList(objectDef[eControlHotChangedHandlers], item.HotChanged);
			else if(defaultItem.HotHandlers != null)
				AssignHandlerList(defaultItem.HotHandlers, item.HotChanged);

			if(objectDef[eControlHotChangedInitiators] != null)
				AssignInitiatorList(objectDef[eControlHotChangedInitiators], item.HotChanged);
			else if(defaultItem.HotInitiators != null)
				AssignInitiatorList(defaultItem.HotInitiators, item.HotChanged);

			if(objectDef[eControlLoadedHandlers] != null)
				AssignHandlerList(objectDef[eControlLoadedHandlers], item.Loaded);
			else if(defaultItem.LoadedHandlers != null)
				AssignHandlerList(defaultItem.LoadedHandlers, item.Loaded);

			if(objectDef[eItemSelectedHandlers] != null)
				AssignHandlerList(objectDef[eItemSelectedHandlers], item.Selected);
			else if(defaultItem.SelectedHandlers != null)
				AssignHandlerList(defaultItem.SelectedHandlers, item.Selected);

			if(objectDef[eItemSelectedInitiators] != null)
				AssignInitiatorList(objectDef[eItemSelectedInitiators], item.Selected);
			else if(defaultItem.SelectedInitiators != null)
				AssignInitiatorList(defaultItem.SelectedInitiators, item.Selected);


			AssignData(item, defaultItem, objectDef, eItemData);

			break;
		}
	}

	//----- Initialize control properties that require all controls to be created
	//Set Parent property
	for(i = 0; i < this.Menus.length; i++) {
		menu = this.Menus[i];
		if(menu.ParentID && this.Items[menu.ParentID])
			menu.Parent = this.Items[menu.ParentID];
		delete menu.ParentID;
	}

	for(i = 0; i < this.Items.length; i++) {
		item = this.Items[i];
		if(this.Menus[item.ParentID])
			item.Parent = this.Menus[item.ParentID];
		delete item.ParentID;
	}

	//Set menus' Items property
	for(i = 0; i < this.Items.length; i++) {
		this.Items[i].Parent.Items[this.Items[i].Parent.Items.length] = this.Items[i];
	}

	//Set items' ChildMenu property
	for(i = 0; i < this.Menus.length; i++) {
		if(this.Menus[i].Parent != null)
			this.Items[this.Menus[i].Parent.ID].ChildMenu = this.Menus[i];
	}

	//----- Fire Loaded event handlers
	for(i = 0; i < this.Menus.length; i++)
		this.Menus[i].Loaded.Fire();

	for(i = 0; i < this.Items.length; i++)
		this.Items[i].Loaded.Fire();

	//----- Load Initiators
	for(i = 0; i < this.Menus.length; i++) {
		for(j = 0; j < this.Menus[i].HotChanged.Initiators.Count; j++)
			this.Menus[i].HotChanged.Initiators.Item(j).Load();

		for(j = 0; j < this.Menus[i].VisibleChanged.Initiators.Count; j++)
			this.Menus[i].VisibleChanged.Initiators.Item(j).Load();
	}

	for(i = 0; i < this.Items.length; i++) {
		for(j = 0; j < this.Items[i].HighlightChanged.Initiators.Count; j++)
			this.Items[i].HighlightChanged.Initiators.Item(j).Load();

		for(j = 0; j < this.Items[i].HotChanged.Initiators.Count; j++)
			this.Items[i].HotChanged.Initiators.Item(j).Load();

		for(j = 0; j < this.Items[i].Selected.Initiators.Count; j++)
			this.Items[i].Selected.Initiators.Item(j).Load();
	}

	//----- Load Handlers
	for(i = 0; i < this.Menus.length; i++) {
		for(j = 0; j < this.Menus[i].HotChanged.Handlers.Count; j++)
			this.Menus[i].HotChanged.Handlers.Item(j).Load();

		for(j = 0; j < this.Menus[i].VisibleChanged.Handlers.Count; j++)
			this.Menus[i].VisibleChanged.Handlers.Item(j).Load();
	}

	for(i = 0; i < this.Items.length; i++) {
		for(j = 0; j < this.Items[i].HighlightChanged.Handlers.Count; j++)
			this.Items[i].HighlightChanged.Handlers.Item(j).Load();

		for(j = 0; j < this.Items[i].HotChanged.Handlers.Count; j++)
			this.Items[i].HotChanged.Handlers.Item(j).Load();

		for(j = 0; j < this.Items[i].Selected.Handlers.Count; j++)
			this.Items[i].Selected.Handlers.Item(j).Load();
	}
}

MainMenu.prototype.HideAll = function() {
	var i;

	for(i = 0; i < this.Menus.length; i++) {
		if(!this.Menus[i].Permanent)
			this.Menus[i].VisibleChanged.Fire(this, false);
	}
}

//-------------------- Item Class --------------------\\
function Item(theMainMenu) {
	this.base = Control;
	this.base(theMainMenu);
	delete this.base;

	this.Action = Action.Link;
	this.ActionTarget = "";
	this.ActionUrl = "";
	this.ChildMenu = null;
	this.Display = Display.Url;
	this.DisplayText = "";
	this.Enabled = true;
	this.Highlighted = false;
	this.KeepHighlight = true;

	this.HotChanged = new Item.HotChangedEvent(this);
	this.HighlightChanged = new Item.HighlightChangedEvent(this);
	this.Selected = new Item.SelectedEvent(this);
}
Item.prototype = new Control;
Item.prototype.constructor = Item;

//-------------------- Item.HighlightChangedEvent Class --------------------\\
Item.HighlightChangedEvent = function(theControl) {
	this.base = MainMenuEvent;
	this.base(theControl);
	delete this.base;
}
Item.HighlightChangedEvent.prototype = new MainMenuEvent;
Item.HighlightChangedEvent.prototype.constructor = Item.HighlightChangedEvent;

Item.HighlightChangedEvent.prototype.Fire = function(initiator, highlighted) {
	var item = this.Control;
	var i;

	if(!item.Enabled || highlighted == item.Highlighted)
		return;

	if(highlighted && item.Parent.HighlightedItem)
		item.Parent.HighlightedItem.HighlightChanged.Fire("Item.HighlightChangedEvent_HideSiblings", false);

	item.Highlighted = highlighted;
	item.Parent.HighlightedItem = highlighted ? item : null;

	this.base = MainMenuEvent.prototype.Fire;
	this.base(initiator, highlighted);
	delete this.base;
}

//-------------------- Item.HotChangedEvent Class --------------------\\
Item.HotChangedEvent = function(theControl) {
	this.base = Control.HotChangedEvent;
	this.base(theControl);
	delete this.base;
}
Item.HotChangedEvent.prototype = new Control.HotChangedEvent;
Item.HotChangedEvent.prototype.constructor = Item.HotChangedEvent

Item.HotChangedEvent.prototype.Fire = function(initiator, data) {
	if(this.Control.Enabled) {
		this.base = Control.HotChangedEvent.prototype.Fire;
		this.base(initiator, data);
		delete this.base;
	}
}

//-------------------- Item.SelectedEvent Class --------------------\\
Item.SelectedEvent = function(theControl) {
	this.base = MainMenuEvent;
	this.base(theControl);
	delete this.base;
}
Item.SelectedEvent.prototype = new MainMenuEvent;
Item.SelectedEvent.prototype.constructor = Item.SelectedEvent

Item.SelectedEvent.prototype.Fire = function(initiator, eventData) {
	if(this.Control.Enabled) {
		this.base = MainMenuEvent.prototype.Fire;
		this.base(initiator, eventData);
		delete this.base;
	}
}

//-------------------- Menu Class --------------------\\
function Menu(theMainMenu) {
	this.base = Control;
	this.base(theMainMenu);
	delete this.base;

	this.AlignedItem = 0;
	this.AlignTo = AlignTo.ParentItem;
	this.AlignToID = "";
	this.ElementHorizontalAlign = HorizontalAlign.Right;
	this.ElementVerticalAlign = VerticalAlign.Top;
	this.HideDelay = 500;
	this.HighlightedItem = null;
	this.Items = [];
	this.MenuHorizontalAlign = HorizontalAlign.Left;
	this.MenuVerticalAlign = VerticalAlign.Top;
	this.OffsetX = 0;
	this.OffsetY = 0;
	this.Permanent = false;
	this.ShowDelay = 0;
	this.Visible = false;

	this.HotChanged = new Menu.HotChangedEvent(this);
	this.VisibleChanged = new Menu.VisibleChangedEvent(this);
}
Menu.prototype = new Control;
Menu.prototype.constructor = Menu;

Menu.prototype.GetAlignmentPoint = function() {
	function Align(elementHorizontalAlign, elementVerticalAlign, menuHorizontalAlign, menuVerticalAlign) {
		//Get base point
		switch(elementHorizontalAlign) {
		case HorizontalAlign.Left:
			x = elementRect.Left;
			break;
		case HorizontalAlign.Center:
			x = elementRect.Left + Math.floor(elementRect.Width / 2);
			break;
		case HorizontalAlign.Right:
			x = elementRect.Left + elementRect.Width;
			break;
		}

		switch(elementVerticalAlign) {
		case VerticalAlign.Top:
			y = elementRect.Top;
			break;
		case VerticalAlign.Middle:
			y = elementRect.Top + Math.floor(elementRect.Height / 2);
			break;
		case VerticalAlign.Bottom:
			y = elementRect.Top + elementRect.Height;
			break;
		}

		//Offset base point to accommodate menu
		switch(menuHorizontalAlign) {
		case HorizontalAlign.Left:
			break;
		case HorizontalAlign.Center:
			x -= Math.floor(menuRect.Width / 2);
			break;
		case HorizontalAlign.Right:
			x -= menuRect.Width;
			break;
		}

		switch(menuVerticalAlign) {
		case VerticalAlign.Top:
			break;
		case VerticalAlign.Middle:
			y -= Math.floor(menuRect.Height / 2);
			break;
		case VerticalAlign.Bottom:
			y -= menuRect.Height;
			break;
		}

		if(itemRect) {
			x -= itemRect.Left - menuRect.Left;
			y -= itemRect.Top - menuRect.Top;
		}

		x += offsetX;
		y += offsetY;
	}

	var viewPortRect, elementRect, menuRect, itemRect, newRect;
	var cursorPoint;
	var element;
	var newMenuHorizontalAlign, newMenuVerticalAlign;
	var newElementHorizontalAlign, newElementVerticalAlign;
	var offsetX = this.OffsetX;
	var offsetY = this.OffsetY;
	var origX, origY;
	var x, y;
	var altAlignIgnored;

	x = y = 0;

	//Get dimensions of objects
	viewPortRect = Browser.GetViewPort();
	menuRect = Browser.GetDimensions(this.Element);
	if(this.AlignTo == AlignTo.ParentItem
		&& this.AlignedItem >= 0
		&& this.AlignedItem < this.Items.length
		)
		itemRect = Browser.GetDimensions(this.Items[this.AlignedItem].Element);

	switch(this.AlignTo) {
	case AlignTo.Origin:
		elementRect = new Rect(0, 0, 0, 0);
		break;
	case AlignTo.ParentItem:
		elementRect = this.Parent != null
			? Browser.GetDimensions(this.Parent.Element)
			: new Rect(0, 0, 0, 0);
		break;
	case AlignTo.Canvas:
		elementRect = Browser.GetCanvas();
		viewPortRect = elementRect;
		break;
	case AlignTo.ViewPort:
		elementRect = viewPortRect;
		break;
	case AlignTo.Cursor:
		elementRect = Browser.GetCursor();
		break;
	case AlignTo.ID:
		element = Browser.GetElement(this.AlignToID);
		elementRect = element
			? Browser.GetDimensions(element)
			: new Rect(0, 0, 0, 0);
		break;
	}

	Align(this.ElementHorizontalAlign, this.ElementVerticalAlign, this.MenuHorizontalAlign, this.MenuVerticalAlign);
	newRect = new Rect(x, y, menuRect.Width, menuRect.Height);
	origX = x;
	origY = y;

	//Try an alternate position if the preferred can't be accommodated
	if(!viewPortRect.Contains(newRect)) {
		altAlignIgnored = false;

		if(viewPortRect.Contains(newRect, Axis.X)) {
			newMenuHorizontalAlign = this.MenuHorizontalAlign;
			newElementHorizontalAlign = this.ElementHorizontalAlign;
		}
		else {
			switch(this.MenuHorizontalAlign) {
			case HorizontalAlign.Left:
				newMenuHorizontalAlign = HorizontalAlign.Right;
				break;
			case HorizontalAlign.Center:
				altAlignIgnored = true;
				break;
			case HorizontalAlign.Right:
				newMenuHorizontalAlign = HorizontalAlign.Left;
				break;
			}

			switch(this.ElementHorizontalAlign) {
			case HorizontalAlign.Left:
				newElementHorizontalAlign = HorizontalAlign.Right;
				break;
			case HorizontalAlign.Center:
				newElementHorizontalAlign = HorizontalAlign.Center;
				break;
			case HorizontalAlign.Right:
				newElementHorizontalAlign = HorizontalAlign.Left;
				break;
			}
		}

		if(!altAlignIgnored) {
			if(viewPortRect.Contains(newRect, Axis.Y)) {
				newMenuVerticalAlign = this.MenuVerticalAlign;
				newElementVerticalAlign = this.ElementVerticalAlign;
			}
			else {
				switch(this.MenuVerticalAlign) {
				case VerticalAlign.Left:
					newMenuVerticalAlign = VerticalAlign.Bottom;
					break;
				case VerticalAlign.Center:
					altAlignIgnored = true;
					break;
				case VerticalAlign.Right:
					newMenuVerticalAlign = VerticalAlign.Top;
					break;
				}

				switch(this.ElementVerticalAlign) {
				case VerticalAlign.Left:
					newElementVerticalAlign = VerticalAlign.Bottom;
					break;
				case VerticalAlign.Center:
					newElementVerticalAlign = VerticalAlign.Middle;
					break;
				case VerticalAlign.Right:
					newElementVerticalAlign = VerticalAlign.Top;
					break;
				}
			}
		}

		if(!altAlignIgnored) {
			Align(newElementHorizontalAlign, newElementVerticalAlign, newMenuHorizontalAlign, newMenuVerticalAlign);
			newRect.Left = x;
			newRect.Top = y;
		}

		//If preferred and alternative positions fail position menu as best as possible 
		if(altAlignIgnored || !viewPortRect.Contains(newRect)) {
			newRect.Left = origX;
			newRect.Top = origY;

			if(!viewPortRect.Contains(newRect, Axis.X)) {
				if(newRect.Width <= viewPortRect.Width) {
					if(newRect.Left < viewPortRect.Left)
						x = viewPortRect.Left;
					else
						x = viewPortRect.Left + viewPortRect.Width - newRect.Width;
				}
				else {
					switch(this.MenuHorizontalAlign) {
					case HorizontalAlign.Left:
						x = viewPortRect.Left;
						break;
					case HorizontalAlign.Center:
						x = viewPortRect.Left - Math.floor((newRect.Width - viewPortRect.Width) / 2);
						break;
					case HorizontalAlign.Right:
						x = viewPortRect.Left + viewPortRect.Width - newRect.Width;
						break;
					}
				}
			}

			if(!viewPortRect.Contains(newRect, Axis.Y)) {
				if(newRect.Height <= viewPortRect.Height) {
					if(newRect.Top < viewPortRect.Top)
						y = viewPortRect.Top;
					else
						y = viewPortRect.Top + viewPortRect.Height - newRect.Height;
				}
				else {
					switch(this.MenuVerticalAlign) {
					case VerticalAlign.Top:
						y = viewPortRect.Top;
						break;
					case VerticalAlign.Center:
						y = viewPortRect.Top - Math.floor((newRect.Height - viewPortRect.Height) / 2);
						break;
					case VerticalAlign.Right:
						y = viewPortRect.Top + viewPortRect.Height - newRect.Height;
						break;
					}
				}
			}
		}
	}

	return new Point(x, y);
}

Menu.prototype.HideTopInactiveAncestorOrSelf = function() {
	var currMenu = this;
	var foundMenu;

	do {
		if(currMenu.Visible && !(currMenu.Hot || currMenu.Permanent))
			foundMenu = currMenu;
		else
			break;
		currMenu = currMenu.Parent ? currMenu.Parent.Parent : null;
	} while(currMenu)

	if(foundMenu)
		foundMenu.VisibleChanged.Fire("Menu.HideTopInactiveAncestorOrSelf", false);
}

Menu.prototype.IsDescendentOrSelfHot = function() {
	var menu = this;
	var found = false, searching = true;
	var i;

	while(searching) {
		if(menu.Hot) {
			found = true;
			break;
		}
		searching = false;
		for(i = 0; i < menu.Items.length; i++) {
			if(menu.Items[i].ChildMenu && menu.Items[i].ChildMenu.Visible) {
				menu = menu.Items[i].ChildMenu;
				searching = true;
				break;
			}
		}
	}

	return found;
}

Menu.prototype.NotifyAncestorsAndSelf = function() {
	var menu = this;
	var i;

	do {
		for(i = 0; i < menu.VisibleChanged.Initiators.Count; i++)
			menu.VisibleChanged.Initiators.Item(i).Notify();
		menu = menu.Parent ? menu.Parent.Parent : null;
	} while(menu)
}

//-------------------- Menu.HotChangedEvent Class --------------------\\
Menu.HotChangedEvent = function(theControl) {
	this.base = Control.HotChangedEvent;
	this.base(theControl);
	delete this.base;
}
Menu.HotChangedEvent.prototype = new Control.HotChangedEvent;
Menu.HotChangedEvent.prototype.constructor = Menu.HotChangedEvent

Menu.HotChangedEvent.prototype.Fire = function(initiator, hot) {
	var item;

	if(hot == this.Control.Hot)
		return;

	if(hot) {
		item = this.Control.Parent;
		while(item) {
			if(item.KeepHighlight)
				item.HighlightChanged.Fire("Menu.HotChangedEvent_KeepHighlight", true);
			item = item.Parent.Parent;
		}
	}

	this.base = Control.HotChangedEvent.prototype.Fire;
	this.base(initiator, hot);
	delete this.base;
}

//-------------------- Menu.VisibleChangedEvent Class --------------------\\
Menu.VisibleChangedEvent = function(theControl) {
	this.base = MainMenuEvent;
	this.base(theControl);
	delete this.base;
}
Menu.VisibleChangedEvent.prototype = new MainMenuEvent;
Menu.VisibleChangedEvent.prototype.constructor = Menu.VisibleChangedEvent;

Menu.VisibleChangedEvent.prototype.Fire = function(initiator, visible) {
	var menu = this.Control;
	var items, item;
	var i;

	if(menu.Permanent || visible == menu.Visible || (menu.Parent && !menu.Parent.Enabled))
		return;

	//Hide other MainMenus
	if(visible) {
		for(i = 0; i < MainMenu.MainMenus.length; i++) {
			if(MainMenu.MainMenus[i] != menu.MainMenu)
				MainMenu.MainMenus[i].HideAll();
		}
	}

	//Hide sibling menus
	if(visible && menu.Parent) {
		items = menu.Parent.Parent.Items;
		for(i = 0; i < items.length; i++) {
			if(items[i].ChildMenu && items[i].ChildMenu != menu && items[i].ChildMenu.Visible) {
				items[i].ChildMenu.VisibleChanged.Fire("Menu.VisibleChangedEvent_HideSiblings", false);
				break;
			}
		}
	}

	menu.Visible = visible;

	this.base = MainMenuEvent.prototype.Fire;
	this.base(initiator, visible);
	delete this.base;

	//Hide submenus
	if(!visible) {
		items = menu.Items;
		for(i = 0; i < items.length; i++) {
			if(items[i].ChildMenu && items[i].ChildMenu.Visible) {
				items[i].ChildMenu.VisibleChanged.Fire("Menu.VisibleChangedEvent_HideSubmenus", false);
				break;
			}
		}
	}

	//Handle keepHighlight
	if(!visible && menu.Parent && menu.Parent.KeepHighlight)
		menu.Parent.HighlightChanged.Fire("Menu.VisibleChangedEvent_KeepHighlight", false);
}

