/*****************************************************
 * Menu.js
 * 04.23.2003
 * Eddie Lim <elim@eecs.harvard.edu>
 * www.netsymbiosis.com
 *****************************************************/

Menu.Registry = [];

// menu item types.
Menu.TEXT_MENU_ITEM = 0;
Menu.ICON_MENU_ITEM = 1;
Menu.SUBMENU_MENU_ITEM = 2;

MenuItem.onClickHandlers = [];
MenuItem.READ_ONLY = 1;
MenuItem.ALIGN_RIGHT = 3;
MenuItem.ALIGN_LEFT = 4;
function MenuItem(id, title, menuItemType)
{
	var menuItem = document.createElement("div");
	this.obj = menuItem;
	menuItem.setAttribute("id", id);
	menuItem.className = "menuItem";
	menuItem.style.padding = "2px 2px 2px 10px";
	menuItem.style.whiteSpace = "nowrap";
//	menuItem.style.overflow = "visible";
	menuItem.style.width = "auto";

	var menuItemText = document.createElement("span");
	menuItemText.style.whitespace = "nowrap";
	menuItemText.style.padding = "0px 0px 0px 8px";
//	menuItemText.style.overflow = "visible";
	menuItemText.innerHTML = title;

	menuItem.menuItemText = menuItemText;
	menuItem.appendChild(menuItemText);

	// this should be called every time the containing menu re-appears.
	menuItem.setLookAndFeel = function(lf)
	{
		this.menuItemText.style.fontStyle = lf.fontStyle;
		this.menuItemText.style.fontSize = lf.fontSize;
		this.menuItemText.style.fontFamily = lf.fontFamily;
		this.menuItemText.style.color = lf.fontColor;

		if(this.icon)
		{
			this.icon.setLookAndFeel(lf);
		}

	};

	menuItem.setLookAndFeel(LookAndFeel.getMenuDefaultLookAndFeel());

	menuItem.isListeningToReadOnly = false;
	menuItem.setListener = function(action)
	{
		switch(action)
		{
			case MenuItem.READ_ONLY:
				this.isListeningToReadOnly = true;
				break;
			default:
		}
	};

	menuItem.isListening = function(action)
	{
		switch(action)
		{
			case MenuItem.READ_ONLY:
				return this.isListeningToReadOnly;
				break;
			default:
		}

		return false;
	};

	menuItem.repaint = function(colorScheme)
	{
		var o = this;
		o.style.backgroundColor = colorScheme.inActiveMenuColor;	
		if(o.memo)
		{				
			if(o.memo.isHidden())
			{		
				this.hideIcon();
			}

			// the icon is shown in fadeIn()
		}
	};
	
	menuItem.menuItemType = menuItemType;
	
	menuItem.onmouseover = function(e)
	{
		this.style.background = this.parent.colorScheme.activeMenuColor;

		// in mozilla, the onmouseout handler is not always invoked properly.
		for(var i = 0; i < this.parent.childNodes.length; i++)
		{
			var sibling = this.parent.childNodes[i];
			if(this == sibling || sibling.className == "separator") { continue; }			

			sibling.style.background = this.parent.colorScheme.inActiveMenuColor;
			if(sibling.menuItemType == Menu.SUBMENU_MENU_ITEM && sibling.subMenu)
			{
				sibling.subMenu.hide();	
			}
		}		

		// if there is a submenu below this guy, show it.
		if(this.menuItemType == Menu.SUBMENU_MENU_ITEM && this.subMenu)
		{
			// need a special check for the memos sub menu.
			if(this.id == "menuItemMemos")
			{
				var memoCount = Memo.getUserMemoCount();
				if(memoCount > 0)
				{
					this.subMenu.show();
				}
			}
			else
			{
				this.subMenu.show();
			}
		}
	};

	menuItem.onmouseout = function(e)
	{
		this.style.background = this.parent.colorScheme.inActiveMenuColor;
	};

	menuItem.defaultOnClickHandler = "EmptyHandler";
}

// abusing the definition of a menu item here...
MenuItem.prototype.setMemo = function(memo)
{
	this.obj.memo = memo;
}

MenuItem.prototype.setText = function(text)
{
	this.obj.menuItemText.innerHTML = text;
}

MenuItem.prototype.addOnClickHandler = function(onClickHandlerKey, args)
{
	this.obj.defaultOnClickHandler = onClickHandlerKey;

	// some shadiness here with default parameters...
	if(args != null && args != "undefined")
	{
		this.obj.defaultArgs = args;
	}
	this.obj.onmousedown = function(e)
	{	
		MenuItem.onClickHandlers[onClickHandlerKey](o, e, args);
	};
}

MenuItem.prototype.setListener = function(action)
{
	this.obj.setListener(action);
}

/**
 * TO DO: currently this can only be called _after_ the menuItem has been 
 * inserted into the menu, because we haven't figured out yet how to center the image automatically...
 */
MenuItem.prototype.addIcon = function(iconSrc, iconPos, width, height, iconSrcReadOnly)
{
	var iconEl = document.createElement("img");
	iconEl.setAttribute("src", iconSrc);	
	iconEl.style.position = "absolute";
	iconEl.style.width = width + "px";
	iconEl.style.height = height + "px";

	// vertically align the icon.
	var marginTop = Math.floor((this.obj.offsetHeight - height) / 2);
	if(marginTop > 0) { marginTop--; }

	// browser support sucks.
	if(Browser.isMozilla && Browser.version < "1.3")
	{
		marginTop += 4;
	}
	iconEl.style.marginTop = marginTop + "px";
	
	if(iconPos == MenuItem.ALIGN_LEFT)
	{
		iconEl.style.left = "6px";
		this.obj.insertBefore(iconEl, this.obj.firstChild);
	} 
	else if(iconPos == MenuItem.ALIGN_RIGHT)
	{
		iconEl.style.right = "10px";
		this.obj.appendChild(iconEl);
	}

	// too bad css doesn't support arbitrary shapes [e.g. triangles] for clipping...
	iconEl.iconSrc = iconSrc;
	iconEl.iconSrcReadOnly = iconSrcReadOnly;
	iconEl.setLookAndFeel = function(lf)
	{
		switch(lf.type)
		{
			case LookAndFeel.MENU_READONLY:
				iconEl.src = this.iconSrcReadOnly;
				break;
			default:
				iconEl.src = this.iconSrc;
				break;
		}
	}

	/**
	 * the routines that call these methods are a bit tricky to understand...
	 * we hide/show the memo menu item icon depending on whether it's hidden or on display,
	 * but because of a mozilla we also have to hide/show the icon whenever the menu hides/shows.
	 */
	this.obj.icon = iconEl;
	this.obj.showIcon = function()
	{
		this.icon.style.visibility = "visible";		
	}

	this.obj.hideIcon = function()
	{
		this.icon.style.visibility = "hidden";
	}
}

MenuItem.prototype.showIcon = function()
{
	this.obj.showIcon();
}
MenuItem.prototype.hideIcon = function()
{
	this.obj.hideIcon();
}

MenuItem.onClickHandlers = {
		
	EmptyHandler : function(o, e) { },

	MemosHandler : function(o, e, args)
	{
		var id = "";
		if(checkArgs(args, 1))
		{
			id = args[0];
		}

		var memo = Memo.Registry[id];
		if(memo)
		{
			memo.show();

			// this needs to be delayed to work properly.
			window.setTimeout("Memo.Registry['" + id + "'].bringToFront()", 100);
		}
	},

	CascadeHandler : function(o, e)
	{
		var memos = new Array();
		for(var memoId in Memo.Registry)
		{
			var memo = Memo.Registry[memoId];
			if(memo.isHidden()) { continue; }
			memos.push(memo);
		}
		
		// sort by z-index.
		memos.sort(function(a, b) { return a.getZIndex() - b.getZIndex(); }); 

		// reposition and resize.
		var offsetTop = 25;
		var offsetLeft = 23;
		for(var i = 0; i < memos.length; i++)
		{			
			memos[i].hide();
			if(o.isClipped)
			{
				o.titleAreaEl.handleClip();
			}
			memos[i].setTop(offsetTop);
			memos[i].setLeft(offsetLeft);
			memos[i].resizeWidth(340);
			memos[i].resizeHeight(200);
			offsetTop += 25;
			offsetLeft += 23;
		}

		// now a staggered cascade.
		for(var i = 0; i < memos.length; i++)
		{
			window.setTimeout("Memo.Registry['" + memos[i].id + "'].show();", 100 * i + 200); 
		}
	},

	NewHandler : function(o, e, args)
	{	
		if(checkArgs(args, 1))
		{
			switch(args[0])
			{
				case Memo.SAME_COLOR:
				case Memo.CHANCE_COLOR:
					_newMemoType = args[0];
					break;

				// invalid.
				default:
					return;
			}
		}
		
		_newDialogEl.setInputValue("");
		_newDialogEl.repaint(); 
		_newDialogEl.show();
		window.setTimeout("_newDialogEl.inputFocus();", 50);
	},

	RenameHandler : function(o, e)
	{
		_renameDialogEl.setInputValue(_activeObj ? _activeObj.getTitle() : "");
		_renameDialogEl.repaint(); 
		_renameDialogEl.show();
		window.setTimeout("_renameDialogEl.inputFocus();", 50);
	},

	PreferencesHandler : function(o, e)
	{
		var selectEl;

		// textarea font.
		selectEl = _preferencesDialogEl.selectTextAreaFont;
		for(var i = 0; i < selectEl.options.length; i++)
		{
			optionEl = selectEl.options[i];
			if(optionEl.value == Preferences.getTextAreaFontFamily())
			{
				optionEl.selected = true;
			}
			else
			{
				optionEl.selected = false;
			}
		}
	
		// textarea size.
		selectEl = _preferencesDialogEl.selectTextAreaSize;
		for(var i = 0; i < selectEl.options.length; i++)
		{
			optionEl = selectEl.options[i];
			if(optionEl.value == Preferences.getTextAreaFontSize())
			{
				optionEl.selected = true;
			}
			else
			{
				optionEl.selected = false;
			}
		}

		// title font.
		selectEl = _preferencesDialogEl.selectTitleFont;
		for(var i = 0; i < selectEl.options.length; i++)
		{
			optionEl = selectEl.options[i];
			if(optionEl.value == Preferences.getTitleFontFamily())
			{
				optionEl.selected = true;
			}
			else
			{
				optionEl.selected = false;
			}
		}
	
		// title size.
		selectEl = _preferencesDialogEl.selectTitleSize;
		for(var i = 0; i < selectEl.options.length; i++)
		{
			optionEl = selectEl.options[i];
			if(optionEl.value == Preferences.getTitleFontSize())
			{
				optionEl.selected = true;
			}
			else
			{
				optionEl.selected = false;
			}
		}

		_preferencesDialogEl.repaint(); 
		_preferencesDialogEl.show();
	},

	DeleteHandler : function(o, e)
	{		
		_deleteDialogEl.setText("Are you sure you want to delete " + (_activeObj ? "'" + _activeObj.getTitle() + "'" : "this memo") + "?");
		_deleteDialogEl.repaint(); 
		_deleteDialogEl.show();
	},

	HideHandler : function(o, e)
	{		
		var id = "";
		if(_activeObj)
		{
			id = _activeObj.memoId;
		}

		var memo = Memo.Registry[id];
		if(memo)
		{
			memo.hide();
		}

		if((Memo.getVisibleCount()) == 0)
		{
			_mainMenu.displayRootless();
		}
	},

	AboutHandler : function(o, e)
	{
		_aboutDialogEl.repaint(); 
		_aboutDialogEl.show();
	}
}

// constructor
function Menu(id, parent, width, height)
{	
	this.id = id;	

	// make the relation reciprocal.
	this.parent = parent;
	if(this.parent != null) { this.parent.subMenu = this; }

	// add this menu object to an internal list of all menus.
	Menu.Registry[id] = this;

	var menu = document.createElement("div");
	menu.setAttribute("id", id);
	menu.className = "menu";
	menu.style.position = "absolute";
	menu.style.overflow = "hidden";
	menu.style.visibility = "hidden";
	menu.style.width = (width != "") ? width + "px" : "auto";
	menu.style.height = (height != "") ? height + "px" : "auto";		
	document.body.insertBefore(menu, null);
	
	this.menu =  menu;
	this.childNodes = menu.childNodes;
	this.style = this.menu.style;
}

Menu.prototype.addMenuItem = function(menuItem, insertIndex)
{
	menuItem.obj.parent = this;

	if(insertIndex && typeof insertIndex == "number")
	{
		for(var i = 0; i < this.childNodes.length; i++)
		{
			if(i == insertIndex)
			{
				this.menu.insertBefore(menuItem.obj, this.childNodes[i]);
			}
		}
	}
	else
	{
		this.menu.appendChild(menuItem.obj);
	}

	this.adjustToFit();
}

Menu.prototype.removeMenuItem = function(menu)
{
	this.menu.removeChild(menu);
}

Menu.prototype.addSeparator = function()
{
	var s = document.createElement("div");
	s.className = "separator";
	s.style.width = "100%";
	s.style.height = "1px";
	s.style.overflow = "hidden";
	s.style.margin = "4px 0px 4px 0px";
//	s.style.background = "#000000";
	this.menu.appendChild(s);
}

Menu.prototype.addFiller = function(height)
{
	var s = document.createElement("div");
	s.className = "filler";
	s.style.width = "100%";	
	s.style.overflow = "hidden";
	s.style.height = height + "px";
	this.menu.appendChild(s);
}

// should be called whenever a new menu item is added, or when a menu item is renamed.
Menu.prototype.adjustToFit = function()
{
	var maxWidth = 0;
	var widestMenu = null;
	for(var i = 0; i < this.childNodes.length; i++)
	{
		var child = this.childNodes[i];
		if(child.menuItemText) 
		{
			var menuItemWidth = child.menuItemText.offsetWidth;
			if(menuItemWidth > maxWidth) 
			{ 
				widestMenu = child;
				maxWidth = menuItemWidth; 
			}
		}
	}

	// now fix the menu width to fit. compensate for an icon.
	if(widestMenu.menuType == MenuItem.SUBMENU_MENU_ITEM || widestMenu.menuType == MenuItem.ICON_MENU_ITEM)
	{
		maxWidth += 40;
	}

	this.menu.style.width = maxWidth + "px";
}

Menu.prototype.setColorScheme = function(colorScheme)
{
	this.colorScheme = colorScheme;
}

Menu.prototype.setZIndex = function(zIndex)
{
	this.style.zIndex = zIndex;
}

Menu.prototype.isOpen = function()
{
	return this.style.visibility == "visible";
}

// show this above the active memo.
Menu.prototype.show = function()
{	
	var o = this;
	if(_activeObj == null || o.isOpen()) { return; }
	o.setZIndex(_activeObj.getZIndex() + 1);
	o.fadeIn();
}

// should only be used called by the root menu.
Menu.prototype.getParent = function()
{
	return this.parent;
}

// should only be used called by the root menu.
Menu.prototype.setParent = function(parent)
{
	this.parent = parent;
}

// recursive.
Menu.prototype.repaint = function()
{	
	var o = this;

	// this is a dirty dirty way of achieving the layout i want.
	if(o == _mainMenu && _activeObj)
	{
		o.style.left = (parseInt(_activeObj.style.left) + parseInt(_activeObj.menuIconEl.style.left) - 2) + "px";
		o.style.top = (parseInt(_activeObj.style.top) + parseInt(_activeObj.menuIconEl.style.top) + parseInt(_activeObj.menuIconEl.style.height) + 2) + "px";
	}
	else
	{
		// TO DO: take a look at this again.
		o.style.left = (parseInt(_mainMenu.style.left) + parseInt(o.parent.offsetWidth) - 8) + "px";
		o.style.top = (parseInt(_mainMenu.style.top) + parseInt(o.parent.offsetTop) - 4) + "px";
	}

	o.style.backgroundColor = o.colorScheme.menuContentColor;
	o.style.borderTop = "1px solid " + o.colorScheme.menuBorderColor;
	o.style.borderBottom = "1px solid " + o.colorScheme.menuBorderColor;
	o.style.borderLeft = "1px solid " + o.colorScheme.menuBorderColor;
	o.style.borderRight = "1px solid " + o.colorScheme.menuBorderColor;
	
	for(var i = 0; i < o.childNodes.length; i++)
	{
		var child = o.childNodes[i];		
		if(child.className == "separator")
		{
			child.style.backgroundColor = o.colorScheme.menuSeparatorColor;
		}	
		if(child.className == "filler")
		{
			child.style.backgroundColor = o.colorScheme.menuContentColor;
		}	
		if(child.className == "menuItem")
		{	
			child.repaint(o.colorScheme);
			if(child.menuItemType == Menu.SUBMENU_MENU_ITEM)
			{
				/**
				 * dirty little hack to disable the memo sublist if it's empty.
				 * we should really have every menu item implement a separate onmouseover handler.
				 */		
				if(child.id == "menuItemMemos")
				{
					var memoCount = Memo.getUserMemoCount();
					if(memoCount > 0)
					{
						child.setLookAndFeel(LookAndFeel.getMenuDefaultLookAndFeel());				
					}
					else
					{
						child.setLookAndFeel(LookAndFeel.getMenuReadOnlyLookAndFeel());			
					}
				}

				child.subMenu.setColorScheme(o.colorScheme);
				child.subMenu.repaint();			
			}
		}
	}
}

Menu.prototype.setLookAndFeel = function(readOnly)
{
	var o = this;	
	for(var i = 0; i < o.childNodes.length; i++)
	{
		var child = o.childNodes[i];		

		if(child.className == "menuItem")
		{		
			if(readOnly && child.isListening(MenuItem.READ_ONLY))
			{
				child.setLookAndFeel(LookAndFeel.getMenuReadOnlyLookAndFeel());								
			}
			else
			{
				child.setLookAndFeel(LookAndFeel.getMenuDefaultLookAndFeel());
			}

			// right now the only handler we care about for read-only is the onmousedown handler
			child.onmousedown = function(e)
			{	
				var onClickHandlerKey = (readOnly && this.isListening(MenuItem.READ_ONLY)) ? "EmptyHandler" : this.defaultOnClickHandler;
				var args = this.defaultArgs ? this.defaultArgs : [_activeObj.memoId];
				MenuItem.onClickHandlers[onClickHandlerKey](this, e, args);
			};
			
			if(child.menuItemType == Menu.SUBMENU_MENU_ITEM)
			{
				child.subMenu.setLookAndFeel(readOnly);			
			}
		}
	}
}

Menu.prototype.fadeIn = function()
{
	var o = this;
	o.style.visibility = "visible";

	// bug fix for mozilla. 
	for(var i = 0; i < o.childNodes.length; i++)
	{
		var child = o.childNodes[i];	
		if(child.className == "menuItem" && child.menuItemType == Menu.ICON_MENU_ITEM && child.memo && !child.memo.isHidden())
		{
			child.showIcon();
		}
	}

	o.style.filter = "alpha (opacity = 0)";
	o.style.MozOpacity = 0;	 
	for(var i = 40; i < 100; i += 2)
	{
		if(Browser.isMozilla)
		{
			window.setTimeout("Menu.Registry['" + this.id + "'].style.MozOpacity = " + (i * .01), i * 5 - 160);
		}
		else
		{
			window.setTimeout("Menu.Registry['" + this.id + "'].style.filter = \"alpha (opacity = " + i + ")\"", i * 5 - 160);
		}
	}
}

// recursive.
Menu.prototype.hide = function()
{
	var o = this;
	o.style.visibility = "hidden";
	for(var i = 0; i < o.childNodes.length; i++)
	{
		var child = o.childNodes[i];				
		if(child.className == "menuItem" && child.menuItemType == Menu.SUBMENU_MENU_ITEM)
		{			
			child.subMenu.hide();			
		}

		// bug fix for mozilla. images don't get hidden correctly when containing parent elements are hidden.
		else if(child.className == "menuItem" && child.menuItemType == Menu.ICON_MENU_ITEM)
		{
			child.hideIcon();
		}
	}
}

// currently this only works for the root menu.
Menu.prototype.displayRootless = function()
{
	var o = this;
	_debugPane.setTop(100);
	_debugPane.setLeft(100);
	_activeObj = _debugPane.root;
	o.hide();
	o.setParent(_activeObj);
	o.setColorScheme(_activeObj.colorScheme);				
	o.setLookAndFeel(_activeObj.readOnly);
	o.repaint();
	window.setTimeout("_mainMenu.show()", 500);			
}


