/***********************************************************************
 * Memo.js
 * 03.29.2003
 * Eddie Lim <elim@eecs.harvard.edu>
 * www.netsymbiosis.com
 *
 **************************** mini-blog ********************************
 *
 * 03.29.2003 
 * # started to build the foundation for divs that drag and resize, 
 * based on youngpup.net's dom-drag. 
 *
 ***********************************************************************
 * 04.03.2003 - 04.06.2003
 * # new york city.
 *
 ***********************************************************************
 * 04.09.2003 
 * # second pass at drag and resize, this time using prototyped objects.
 * 
 ***********************************************************************
 * 04.11.2003 
 * # fundamental problems with the previous framework. now setup a new 
 * infrastructure for drag and resize that uses double dispatch. 
 * # also added ondblclick handler for titleBar.
 *
 ***********************************************************************
 * 04.12.2003 
 * # filled in double-dispatch code for drag and resize.
 * # added drop-shadow div. the drop-shadow is actually the containing 
 * div, but to preserve the structure of the code we treat it as a child
 * of the memo pad root.
 * # added menu icon and minimize icon [both are actually just divs].
 *
 ***********************************************************************/

var Compass = { NONE: 0, NORTHWEST: 1, NORTH: 2, NORTHEAST: 3, EAST: 4, SOUTHEAST: 5, SOUTH: 6, SOUTHWEST: 7, WEST: 8 };

var _dragObj = null;
var _activeObj = null;

// use this for an IE bug where mouse events in a scroll bar aren't propagated properly.
var _scrollBarDrag = false;

// need this because moz takes a performance beating when resizing the title.
Memo.updateTitleCount = 0;

Memo.menuIconClicked = false;

Memo.Registry = [];
Memo.memoId = 0;
Memo.bestZIndex = 0;

Memo.MIN_WIDTH = 120;
Memo.MIN_HEIGHT = 80;

// global workaround here to pass arguments between unrelated event handlers.
var _newMemoType = -1;
Memo.SAME_COLOR = 0;
Memo.CHANCE_COLOR = 1;

Memo.INTERNAL_MEMO = 0;
Memo.USER_MEMO = 1;

Memo.TITLE_AREA_HEIGHT = 24;

// it'd be nice if this was in sync with the insets for the menu icon and minimize icon...
Memo.resizeTolerance = 4; 

if(Browser.isMozilla)
{
	Memo.PADDING_LEFT = 4;
	Memo.PADDING_RIGHT = 0;
	Memo.PADDING_OFFSET_WIDTH = 2 + Memo.PADDING_LEFT + Memo.PADDING_RIGHT;
	Memo.PADDING_OFFSET_HEIGHT = 4;
}
else
{
	Memo.PADDING_LEFT = 4;
	Memo.PADDING_RIGHT = 4;
	Memo.PADDING_OFFSET_WIDTH = 4 + Memo.PADDING_LEFT + Memo.PADDING_RIGHT;
	Memo.PADDING_OFFSET_HEIGHT = 9;
}

function Memo(type, title, colorSchemeId, top, left, width, height, isVisible, readOnly)
{
	this.type = type;
	var id = "memo" + Memo.memoId++;
	Memo.Registry[id] = this;
	this.id = id;
	
	var colorScheme = ColorScheme.Registry[colorSchemeId];
	if(colorScheme == null)
	{
		colorScheme = ColorScheme.getRandomColorScheme();
	}

	// this is the main canvas where all the action happens.
	var containerEl = document.createElement("div");
	containerEl.setAttribute("id", id);
	containerEl.colorSchemeId = colorSchemeId;
	
	containerEl.style.position = "absolute";	
	containerEl.style.top = "200px";
	containerEl.style.left = "320px";
	containerEl.style.width = "200px";
	containerEl.style.height = "120px";
//	containerEl.style.overflow = "hidden";

	containerEl.style.visibility = isVisible ? "visible" : "hidden";

	var titleAreaEl = document.createElement("div");
	titleAreaEl.style.position = "absolute";

	titleAreaEl.style.width = "100%";
	titleAreaEl.style.height = Memo.TITLE_AREA_HEIGHT + "px";

	titleAreaEl.style.top = "0px";
	titleAreaEl.style.scrollbars = "no";
	titleAreaEl.style.paddingLeft = Memo.PADDING_LEFT + "px"; 
	titleAreaEl.style.paddingRight = Memo.PADDING_RIGHT + "px";
	titleAreaEl.style.borderTop = "1px solid " + colorScheme.titleAreaBorderColor;
	titleAreaEl.style.borderBottom = "1px solid " + colorScheme.titleAreaBorderColor;
	titleAreaEl.style.borderLeft = "1px solid " + colorScheme.titleAreaBorderColor;
	titleAreaEl.style.borderRight = "2px solid " + colorScheme.titleAreaBorderColor;
	titleAreaEl.style.background = colorScheme.titleAreaColor;
	titleAreaEl.style.overflow = "hidden";

	var menuIconEl = document.createElement("div");
	menuIconEl.style.position = "absolute";
	menuIconEl.style.top = "6px";
	menuIconEl.style.left = "6px";
	menuIconEl.style.border = "1px solid #000";
	menuIconEl.style.overflow = "hidden";
	menuIconEl.style.background = "#E0FF00";
	menuIconEl.style.width = "9px";
	menuIconEl.style.height = "9px";
	titleAreaEl.appendChild(menuIconEl);

	var simulatedTitleTextEl = document.createElement("input");
	simulatedTitleTextEl.style.position = "absolute";
	simulatedTitleTextEl.style.top = "0px";
	simulatedTitleTextEl.style.left = (parseInt(menuIconEl.style.left) + parseInt(menuIconEl.style.width) + 2) + "px";
	if(Browser.isIE)
	{
		simulatedTitleTextEl.style.width = "0px";
	}

	simulatedTitleTextEl.style.fontSize = Preferences.getTitleFontSize();
	simulatedTitleTextEl.style.fontStyle = Preferences.getTitleFontStyle();
	simulatedTitleTextEl.style.fontFamily = Preferences.getTitleFontFamily();
	simulatedTitleTextEl.style.fontColor = Preferences.getTitleFontColor();
	
	simulatedTitleTextEl.style.padding = "2px 0px 2px 6px";
	simulatedTitleTextEl.style.overflow = "visible";
	simulatedTitleTextEl.style.border = "0px";
	simulatedTitleTextEl.style.visibility = "hidden";
	simulatedTitleTextEl.style.zIndex = "-1";
	titleAreaEl.appendChild(simulatedTitleTextEl);
	
	var titleTextEl = document.createElement("input");
	titleTextEl.style.position = "absolute";
	titleTextEl.style.left = (parseInt(menuIconEl.style.left) + parseInt(menuIconEl.style.width) + 2) + "px";

	titleTextEl.style.fontSize = Preferences.getTitleFontSize();
	titleTextEl.style.fontStyle = Preferences.getTitleFontStyle();
	titleTextEl.style.fontFamily = Preferences.getTitleFontFamily();
	titleTextEl.style.fontColor = Preferences.getTitleFontColor();

	titleTextEl.style.padding = "2px 0px 2px 6px";
	titleTextEl.style.overflow = "visible";
	titleTextEl.style.border = "0px";
	titleTextEl.style.background = colorScheme.titleAreaColor;
	titleTextEl.style.verticalAlign = "top";
	titleTextEl.readOnly = true;		
	titleAreaEl.appendChild(titleTextEl);
		
	var minIconEl = document.createElement("div");
	minIconEl.style.position = "absolute";
	minIconEl.style.width = "9px";
	minIconEl.style.height = "9px";
	minIconEl.style.top = "6px";
	minIconEl.style.border = "1px solid #000";	

	// add 6 for resizeTolerance. add 2 for borders. minus padding.
	minIconEl.style.left = (parseInt(containerEl.style.width) - (6 + parseInt(minIconEl.style.width) + 2 - (Memo.PADDING_LEFT + Memo.PADDING_RIGHT))) + "px"; 
	minIconEl.style.overflow = "hidden";
	minIconEl.style.background = "transparent";

	var minIconBar = document.createElement("div");
	minIconBar.style.position = "absolute";
	minIconBar.style.width = "100%";
	minIconBar.style.height = "2px";	
	minIconBar.style.top = "7px";	
	minIconBar.style.border = "0px";
	minIconBar.style.background = "#000000";
	minIconBar.style.overflow = "hidden";

	minIconEl.appendChild(minIconBar);
	titleAreaEl.appendChild(minIconEl);

	var textAreaEl = document.createElement("textarea");
	textAreaEl.style.position = "absolute";
	textAreaEl.style.width = "100%";
	textAreaEl.style.height = (parseInt(containerEl.style.height) - parseInt(titleAreaEl.style.height)) + "px";		
	textAreaEl.style.top = "26px";
	if(Browser.isMozilla)
	{
		textAreaEl.style.top = (parseInt(textAreaEl.style.top) - 1) + "px";
	}

	textAreaEl.style.paddingLeft = Memo.PADDING_LEFT + "px";
	textAreaEl.style.paddingRight = Memo.PADDING_RIGHT + "px";

	// these only work in IE.
	textAreaEl.style.scrollbarArrowColor = colorScheme.activeMenuColor;
	textAreaEl.style.scrollbarFaceColor = colorScheme.textAreaColor;
	textAreaEl.style.scrollbarBaseColor = colorScheme.activeMenuColor; 
	textAreaEl.style.scrollbar3dlightColor = colorScheme.textAreaColor;
	textAreaEl.style.scrollbarDarkshadowColor = colorScheme.textAreaColor;
	textAreaEl.style.scrollbarShadowColor = colorScheme.textAreaColor;
	
	textAreaEl.style.background = colorScheme.textAreaColor;
	textAreaEl.style.scrollbars = "auto";
	textAreaEl.style.borderTop = "0px";
	textAreaEl.style.borderBottom = "2px solid " + colorScheme.textAreaBorderColor;
	textAreaEl.style.borderLeft = "1px solid " + colorScheme.textAreaBorderColor;
	textAreaEl.style.borderRight = "2px solid " + colorScheme.textAreaBorderColor;

	textAreaEl.style.fontSize = Preferences.getTextAreaFontSize();
	textAreaEl.style.fontStyle = Preferences.getTextAreaFontStyle();
	textAreaEl.style.fontFamily = Preferences.getTextAreaFontFamily();
	textAreaEl.style.fontColor = Preferences.getTextAreaFontColor();

	textAreaEl.readOnly = readOnly;
		
	containerEl.appendChild(titleAreaEl);
	containerEl.appendChild(textAreaEl);
	document.body.insertBefore(containerEl, null);

	MemoRoot.init(containerEl, titleAreaEl, simulatedTitleTextEl, titleTextEl, textAreaEl, minIconEl, menuIconEl, id, colorScheme, readOnly);
	MemoMinimizeIcon.init(minIconEl, containerEl);
	MemoMenuIcon.init(menuIconEl, containerEl);
	MemoTitleArea.init(titleAreaEl, containerEl);
	MemoTextArea.init(textAreaEl, containerEl);
	MemoTitleText.init(titleTextEl, containerEl);

	this.root = containerEl;
	this.titleAreaEl = titleAreaEl;
	this.titleTextEl = titleTextEl;
	this.simulatedTitleTextEl = simulatedTitleTextEl;
	this.textAreaEl = textAreaEl;
	this.minIconEl = minIconEl;
	this.menuIconEl = menuIconEl;

	// create an entry in the appropriate menu.
	if(type == Memo.USER_MEMO)
	{
		var menuItem = new MenuItem("menuItemMemo" + id, title, Menu.ICON_MENU_ITEM);
		menuItem.addOnClickHandler("MemosHandler", [id]);
		menuItem.setMemo(this);
		_memoMenu.addMenuItem(menuItem,  _memoMenu.menu.childNodes.length - 1); // add right before the filler element at the end.
		menuItem.addIcon("images/checkmark.gif", MenuItem.ALIGN_LEFT, 7, 7); 
	}
	else
	{
		var menuItem = new MenuItem("menuItemFrames" + id, title, Menu.ICON_MENU_ITEM);
		menuItem.addOnClickHandler("MemosHandler", [id]);
		menuItem.setMemo(this);
		_frameMenu.addMenuItem(menuItem,  _frameMenu.menu.childNodes.length - 1); // add right before the filler element at the end.
		menuItem.addIcon("images/checkmark.gif", MenuItem.ALIGN_LEFT, 7, 7); 
	}
	this.menuItem = menuItem;

	// set position and size.
	this.setTop(top);
	this.setLeft(left);
	this.resizeWidth(width);
	this.resizeHeight(height);

	// set the title.
	containerEl.setTitle(title);
	this.updateTitle(true);
	this.verticallyAlignTitle();

	// bring this guy to the front.
	this.bringToFront();
}

Memo.getUserMemoCount = function()
{
	var memoCount = 0;
	for(var memo in Memo.Registry)
	{
		if(Memo.Registry[memo].type == Memo.USER_MEMO)
		{
			memoCount++;
		}
	}
	return memoCount;
}

Memo.getVisibleCount = function()
{
	var memoCount = 0;
	for(var memo in Memo.Registry)
	{
		if(!Memo.Registry[memo].isHidden())
		{
			memoCount++;
		}
	}
	return memoCount;
}

Memo.prototype.show = function()
{
	this.root.show();
}

Memo.prototype.isHidden = function()
{
	return this.root.isHidden();
}

Memo.prototype.hide = function()
{
	this.root.hide();
}

// call this when initializing z-indices...
Memo.prototype.setZIndex = function(zIndex)
{
	this.root.setZIndex(zIndex);
	if(zIndex > Memo.bestZIndex)
	{
		Memo.bestZIndex = zIndex;
	}
}

Memo.prototype.getZIndex = function()
{
	return this.root.getZIndex();
}

Memo.prototype.bringToFront = function()
{
	this.root.setZIndex(++Memo.bestZIndex);
	this.root.textAreaFocus();
}

Memo.prototype.setText = function(text)
{
	this.root.textAreaEl.value = text;
}

Memo.prototype.updateTitle = function(force)
{
	this.root.updateTitle(force);
}

Memo.prototype.setTitle = function(title)
{
	this.root.setTitle(title);	
}

Memo.prototype.getTitle = function()
{
	return this.root.getTitle();
}

Memo.prototype.setTop = function(top)
{
	this.root.setTop(top);
}

Memo.prototype.setLeft = function(left)
{
	this.root.setLeft(left);
}

Memo.prototype.resizeWidth = function(width)
{
	this.root.resizeWidth(width);
}

Memo.prototype.resizeHeight = function(height)
{
	this.root.resizeHeight(height);
}

Memo.prototype.retrofitMozillaWidth = function()
{
	this.root.retrofitMozillaWidth();
}

Memo.prototype.retrofitMozillaHeight = function()
{
	this.root.retrofitMozillaHeight();
}	

// should be called after initialization, and anytime the title's font changes.
Memo.prototype.verticallyAlignTitle = function()
{
	this.root.verticallyAlignTitle();
}

Memo.prototype.appendText = function(str)
{
	this.root.textAreaEl.value += str + "\n";
}

Memo.prototype.clear = function(str)
{
	this.root.textAreaEl.value = "";
}

var MemoRoot = 
{	
	init : function(o, titleAreaEl, simulatedTitleTextEl, titleTextEl, textAreaEl, minIconEl, menuIconEl, id, colorScheme, readOnly)
	{
		o.onmousedown = MemoRoot.start;
		o.onmouseover = MemoRoot.onmouseover;
		o.onmousemove = MemoRoot.onmousemove;

		o.titleAreaEl = titleAreaEl;
		o.simulatedTitleTextEl = simulatedTitleTextEl;
		o.titleTextEl = titleTextEl;
		o.textAreaEl = textAreaEl;
		o.minIconEl = minIconEl;
		o.menuIconEl = menuIconEl;
		o.memoId = id;
		o.colorScheme = colorScheme;
		o.readOnly = readOnly;
		o.compass = "NONE";
		o.isClipped = false;
		o.lastWidth = o.style.width;
		o.lastHeight = o.style.height;				

		o.show = function()
		{
			var o = this;
			o.style.visibility = "visible";	
			o.textAreaEl.style.visibility = "visible";
			if(o.isClipped)
			{
				o.titleAreaEl.handleClip();
			}
		}

		o.hide = function()
		{
			this.style.visibility = "hidden";	
			this.textAreaEl.blur();
			this.textAreaEl.style.visibility = "hidden";
			
		};

		o.isHidden = function()
		{
			return this.style.visibility == "hidden";
		};

		o.setTitle = function(title)
		{
			if(title == null || title == "")
			{
				title = this.id;
			}			

			// we name it "_title" here, because IE will show the "title" variable as a tooltip on mouseovers
			this._title = title;
			this.updateTitle(true);
		};

		o.getTitle = function()
		{
			return this._title;
		}

		o.updateTitle = function(force)
		{
			var title = this._title;
			var croppedTitle = title;

			// in moz, this routine is really really slow. so we only call it periodically.
			Memo.updateTitleCount++;
			if(!force && Browser.isMozilla && Memo.updateTitleCount % 10)
			{
				return;
			}

			this.simulatedTitleTextEl.value = title;
			if(Browser.isMozilla)
			{
				this.simulatedTitleTextEl.size = title.length + 1;
			}

			var curWidth =  this.simulatedTitleTextEl.offsetWidth;
			var availableWidth = parseInt(this.style.width) - (parseInt(this.menuIconEl.style.width) + parseInt(this.menuIconEl.style.left) + parseInt(this.minIconEl.style.width) + 2);
			availableWidth -= 4; // take some extra off for room.
	
			var lastCurWidth = curWidth;			
			while(curWidth >= availableWidth)
			{
				croppedTitle = croppedTitle.substring(0, croppedTitle.length - 2);
				title = croppedTitle + "...";
				if(Browser.isMozilla)
				{
					this.simulatedTitleTextEl.size = title.length + 1;
				}

				this.simulatedTitleTextEl.value = title;
				curWidth = this.simulatedTitleTextEl.offsetWidth;

/*
				if(curWidth == lastCurWidth)
				{
					debug("@@@ infinite loop at : " + curWidth);
					break;
				}
*/
				lastCurWidth = curWidth;
			}

			if(Browser.isMozilla)
			{
				this.titleTextEl.size = title.length + 1;
			}
			this.titleTextEl.title = title;	
			this.titleTextEl.value = title;

			if(force)
			{
				Memo.updateTitleCount = 0;
			}
		}

		o.verticallyAlignTitle = function()
		{
			var o = this;
			o.titleTextEl.style.top = Math.floor((parseInt(o.titleAreaEl.style.height ) - o.titleTextEl.offsetHeight) / 2) + "px";			
		};
	
		o.setZIndex = function(zIndex)
		{
			this.style.zIndex = zIndex;	
		};

		o.getZIndex = function()
		{
			return this.style.zIndex;
		};

		o.textAreaFocus = function()
		{			
			// make sure it's not clipped.
			window.setTimeout("try { if(Memo.Registry['" + this.memoId + "'].textAreaEl.style.visibility !='hidden') { Memo.Registry['" + this.memoId + "'].textAreaEl.focus(); } } catch (e) {}", 50);
		};

		/**
		 * i think that events will actually be short-circuited before 
         * reaching this handler, but just to be safe...
		 */
		o.handleMouseEvent = function(e)			
		{
			var o = this;		
			if(o.isClipped) 
			{ 
				o.style.cursor = "default";
				return;
			}

			e = fixE(e);
			var y = parseInt(o.style.top);
			var x = parseInt(o.style.left);		
			var width = parseInt(o.style.width);
			var height = parseInt(o.style.height);

			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.NORTHWEST:
					o.style.cursor = "nw-resize";
					break;
				case Compass.NORTH:
					o.style.cursor = "n-resize";
					break;
				case Compass.NORTHEAST:
					o.style.cursor = "ne-resize";
					break;
				case Compass.EAST:
					o.style.cursor = "e-resize";
					break;
				case Compass.SOUTHEAST:
					o.style.cursor = "se-resize";
					break;
				case Compass.SOUTH:
					o.style.cursor = "s-resize";
					break;
				case Compass.SOUTHWEST:
					o.style.cursor = "sw-resize";
					break;
				case Compass.WEST:
					o.style.cursor = "w-resize";
					break;
				default:
					o.style.cursor = "default";
					break;
			}
		};

		o.start = function(e)
		{
			var o = _dragObj = this;
			if(o.getZIndex() < Memo.bestZIndex)
			{
				o.setZIndex(++Memo.bestZIndex);
			}
			if(o.isClipped) 
			{ 
				o.style.cursor = "default";
				_bodyEl.style.cursor = "default";
				document.onmousemove = MemoRoot.drag;
				document.onmouseup = MemoRoot.end;		
				return;
			}

			var y = parseInt(o.style.top);
			var x = parseInt(o.style.left);		
			var width = parseInt(o.style.width) + Memo.PADDING_OFFSET_WIDTH;
			var height = parseInt(o.style.height) + Memo.PADDING_OFFSET_HEIGHT;
		
			o.lastMouseX = e.clientX;
			o.lastMouseY = e.clientY;

			var compass = Compass.getCompass(e, x, y, width, height);			
			switch(compass)
			{
				case Compass.NORTHWEST:
					o.style.cursor = "nw-resize";		
					_bodyEl.style.cursor = "nw-resize";
					break;
				case Compass.NORTH:
					o.style.cursor = "n-resize";
					_bodyEl.style.cursor = "n-resize";
					break;
				case Compass.NORTHEAST:					
					o.style.cursor = "ne-resize";		
					_bodyEl.style.cursor = "ne-resize";	
					break;
				case Compass.EAST:
					o.style.cursor = "e-resize";		
					_bodyEl.style.cursor = "e-resize";	
					break;
				case Compass.SOUTHEAST:
					o.style.cursor = "se-resize";		
					_bodyEl.style.cursor = "se-resize";	
					break;
				case Compass.SOUTH:
					o.style.cursor = "s-resize";		
					_bodyEl.style.cursor = "s-resize";	
					break;
				case Compass.SOUTHWEST:
					o.style.cursor = "sw-resize";		
					_bodyEl.style.cursor = "sw-resize";	
					break;
				case Compass.WEST:
					o.style.cursor = "w-resize";		
					_bodyEl.style.cursor = "w-resize";	
					break;
				default:
					o.style.cursor = "default";
					_bodyEl.style.cursor = "default";
					break;
			}
		
			o.compass = compass;
			debug("root: drag start, direction = " + o.compass);
			if(_scrollBarDrag)
			{
				o.end();
				_scrollBarDrag = false;
				return;
			}

			document.onmousemove = MemoRoot.drag;
			document.onmouseup = MemoRoot.end;		
		};

		o.setTop = function(top)
		{
			this.style.top = top + "px";
		};

		o.setLeft = function(left)
		{
			this.style.left = left + "px";
		};

		o.resizeWidth = function(width)
		{
			var o = this;
			o.style["width"] = width + "px"; 
			o.minIconEl.style["left"] = (parseInt(o.style["width"]) - (6 + parseInt(o.minIconEl.style["width"]) + 2 - (Memo.PADDING_LEFT + Memo.PADDING_RIGHT))) + "px"; 
			o.updateTitle();
		};

		o.resizeHeight = function(height)
		{
			var o = this;
			o.style["height"] = height + "px"; 
			o.textAreaEl.style["height"] = (height - Memo.TITLE_AREA_HEIGHT) + "px";
		};

		o.drag = function(e)
		{
			var o = this;
			if(o.isClipped) { return; }

			var ey	= e.clientY;
			var ex	= e.clientX;
			var y = parseInt(o.style.top);
			var x = parseInt(o.style.left);				
			var nx = x + (ex - o.lastMouseX);
			var ny = y + (ey - o.lastMouseY);

			var width = parseInt(o.style.width);
			var height = parseInt(o.style.height);
			var newWidth, newHeight;		

			switch(o.compass)
			{			
				case Compass.NORTHWEST:		
					newWidth = width + (o.lastMouseX - ex);
					newHeight = height + (o.lastMouseY - ey);
					if(newWidth > Memo.MIN_WIDTH) 
					{ 
						o.style["left"] = nx + "px"; 
						o.resizeWidth(newWidth);
					}
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.style["top"] = ny + "px"; 
						o.resizeHeight(newHeight);
					}
					break;

				case Compass.NORTH:
					newHeight = height + (o.lastMouseY - ey);
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.style["top"] = ny + "px"; 
						o.resizeHeight(newHeight);
					}
					break;
				
				case Compass.NORTHEAST:
					newWidth = width + (ex - o.lastMouseX);
					newHeight = height + (o.lastMouseY - ey);
					if(newWidth > Memo.MIN_WIDTH) 
					{ 					
						o.resizeWidth(newWidth);
					}
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.style["top"] = ny + "px"; 
						o.resizeHeight(newHeight);				
					}
					break;

				case Compass.EAST:
					newWidth = width + (ex - o.lastMouseX);			
					if(newWidth > Memo.MIN_WIDTH) 
					{ 					
						o.resizeWidth(newWidth);
					}		
					break;
				
				case Compass.SOUTHEAST:
					newWidth = width + (ex - o.lastMouseX);
					newHeight = height + (ey - o.lastMouseY);
					if(newWidth > Memo.MIN_WIDTH) 
					{ 
						o.resizeWidth(newWidth);
					}
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.resizeHeight(newHeight);
					}
					break;
				
				case Compass.SOUTH:
					newHeight = height + (ey - o.lastMouseY);
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.resizeHeight(newHeight);
					}
					break;
				
				case Compass.SOUTHWEST:
					newWidth = width + (o.lastMouseX - ex);
					newHeight = height + (ey - o.lastMouseY);
					if(newWidth > Memo.MIN_WIDTH) 
					{ 
						o.style["left"] = nx + "px"; 
						o.resizeWidth(newWidth);
					}
					if(newHeight > Memo.MIN_HEIGHT) 
					{ 
						o.resizeHeight(newHeight);
					}
					break;
				
				case Compass.WEST:
					newWidth = width + (o.lastMouseX - ex);					
					if(newWidth > Memo.MIN_WIDTH) 
					{ 
						o.style["left"] = nx + "px"; 
						o.resizeWidth(newWidth);
					}				
					break;
	
				default:					
			}

			// retrofit mozilla.
			/*
			if(Browser.isMozilla)
			{
				if(newWidth != undefined && newWidth > Memo.MIN_WIDTH)
				{
					o.retrofitMozillaWidth();
				}
				if(newHeight != undefined && newHeight > Memo.MIN_HEIGHT)
				{
					o.retrofitMozillaHeight();
				}				
			}
			*/

			if(newWidth != undefined && newWidth > Memo.MIN_WIDTH)
			{
				o.updateTitle();			
			}

			o.lastMouseX = ex;
			o.lastMouseY = ey;
		};

		o.end = function()
		{			
			debug("root: drag end");	
			o.updateTitle(true);			
			document.onmousemove = null;
			document.onmouseup   = null;			
			this.compass = Compass.NONE;
			this.style.cursor = "default";
			_bodyEl.style.cursor = "default";		
			_dragObj = null;
		}
	},

	onmouseover : function(e)
	{		
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	},

	onmousemove : function(e)
	{
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	},

	start : function(e)
	{
		if(_activeDialog)
		{
			_activeDialog.flicker();
			return;
		}			

		if(_dragObj) { return; }
		
		var o = this;
		e = fixE(e);
		o.start(e);
	},

	drag : function(e)
	{	
		var o = this;
		
		// prevent cascading drags. dispatch drag event to the right object.
		if(o != _dragObj) 
		{ 			
			o = _dragObj;
		}
		
		e = fixE(e);
		o.drag(e);
	},

	end : function()
	{	
		_dragObj.end();
	}
};

var MemoTitleArea = 
{	
	init : function(o, oRoot)
	{
		o.onmousedown = MemoTitleArea.start;
		o.onmouseover = MemoTitleArea.onmouseover;
		o.onmousemove = MemoTitleArea.onmousemove;		
		
		o.root = oRoot;		

		o.handleMouseEvent = function(e)			
		{
			var o = this;		
			if(o.root.isClipped) 
			{ 
				o.style.cursor = "move";
				return;
			}

			e = fixE(e);

			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		
		
			// need these offsets because of the padding.
			var width = parseInt(o.root.style.width) + Memo.PADDING_OFFSET_WIDTH;
			var height = parseInt(o.root.style.height) + Memo.PADDING_OFFSET_HEIGHT;

			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.NORTHWEST:
					o.style.cursor = "nw-resize";
					break;
				case Compass.NORTH:
					o.style.cursor = "n-resize";
					break;
				case Compass.NORTHEAST:
					o.style.cursor = "ne-resize";
					break;
				case Compass.EAST:
					o.style.cursor = "e-resize";
					break;
				case Compass.WEST:
					o.style.cursor = "w-resize";
					break;

				// the rest of the directions don't apply.
				default:
					o.style.cursor = "move";
					break;
			}
		};

		o.start = function(e)
		{
			var o = this;
			e = fixE(e);
		
			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		
			
			// need these offsets because of the padding.
			var width = parseInt(o.root.style.width) + Memo.PADDING_OFFSET_WIDTH;
			var height = parseInt(o.root.style.height) + Memo.PADDING_OFFSET_HEIGHT;

			o.lastMouseX = e.clientX;
			o.lastMouseY = e.clientY;

			/**
			 * first check if this is a resize event, and if so, dispatch  
			 * the event to the parent.
			 */
			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.NORTHWEST:				
				case Compass.NORTH:				
				case Compass.NORTHEAST:				
				case Compass.EAST:				
				case Compass.WEST:		

					_dragObj = o.root;
					o.root.start(e);
					break;

				// otherwise this is a regular drag.
				default:
					if(o.root.getZIndex() < Memo.bestZIndex)
					{
						o.root.setZIndex(++Memo.bestZIndex);
					}
					document.onmousemove = MemoTitleArea.drag;
					document.onmouseup = MemoTitleArea.end;
					_dragObj = o;
			}
		};

		o.drag = function(e)
		{
			var o = this;
			var ey	= e.clientY;
			var ex	= e.clientX;
			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		
			var nx = x + (ex - o.lastMouseX);
			var ny = y + (ey - o.lastMouseY);
	
			o.root.style["left"] = nx + "px";
			o.root.style["top"] = ny + "px";
		
			o.lastMouseX = ex;
			o.lastMouseY = ey;
		};

		o.end = function()
		{
			document.onmousemove = null;
			document.onmouseup   = null;
			this.style.cursor = "default";		
			_dragObj = null;
		};

		/**
		 * double-clicking on the title bar will do three things:
		 * 1) hide the text area 
		 * 2) add an extra pixel to the border around the title bar 
		 * 3) re-position the root container to compensate for the extra border.
		 */
		o.ondblclick = function(e)
		{			
			if(_activeDialog) { return; }
			debug("title area: double-click...");
			var o = this;
			o.handleClip();
		};

		o.handleClip = function()
		{
			if(o.root.isClipped)
			{				
				o.root.style.left = (parseInt(o.root.style.left) + 1) + "px";
				o.root.style.top = (parseInt(o.root.style.top) + 1) + "px";
				o.root.titleAreaEl.style.borderTop = "1px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderBottom = "1px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderLeft = "1px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderRight = "2px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.textAreaEl.style.visibility = "visible";
				o.root.isClipped = false;
			}
			else
			{					
				o.root.style.left = (parseInt(o.root.style.left) - 1) + "px";
				o.root.style.top = (parseInt(o.root.style.top) - 1) + "px";
				o.root.titleAreaEl.style.borderTop = "2px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderRight = "2px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderLeft = "2px solid " + o.root.colorScheme.titleAreaBorderColor;
				o.root.titleAreaEl.style.borderBottom = "2px solid " + o.root.colorScheme.titleAreaBorderColor;	
				o.root.textAreaEl.style.visibility = "hidden";
				o.root.isClipped = true;
			}						
		};
	},

	onmouseover : function(e)
	{		
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	},

	onmousemove : function(e)
	{
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	},

	start : function(e)
	{
		if(_dragObj || _activeDialog) { return; }		

		var o = this;
		e = fixE(e);
		o.start(e);
	},

	drag : function(e)
	{			
		var o = this;
		
		// prevent cascading drags. dispatch drag event to the right object.
		if(o != _dragObj) 
		{ 			
			o = _dragObj;
		}
		
		e = fixE(e);
		o.drag(e);
	},

	end : function()
	{	
		_dragObj.end();
	}

};

var MemoTitleText = 
{	
	init : function(o, oRoot)
	{
				
		o.root = oRoot;		

		o.onmousedown = function(e)
		{
			if(_dragObj || _activeDialog) { return; }		

			var o = this;
			e = fixE(e);
		
			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		
			
			var width = parseInt(o.root.style.width);
			var height = parseInt(o.root.style.height);

			o.lastMouseX = e.clientX;
			o.lastMouseY = e.clientY;

			/**
			 * first check if this is a resize event, and if so, dispatch  
			 * the event to the parent.
			 */
			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.NORTHWEST:				
				case Compass.NORTH:				
				case Compass.NORTHEAST:				
				case Compass.EAST:				
				case Compass.WEST:		

					_dragObj = o.root;
					o.root.start(e);
					break;

				// otherwise dispatch to the parent element.
				default:
					o.root.titleAreaEl.start(e);
			}
		};

		o.onmouseover = function(e)
		{		
			var o = this;
			e = fixE(e);
				
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return;
			}

			o.handleMouseEvent(e);
		},

		o.onmousemove = function(e)
		{
			var o = this;
			e = fixE(e);
				
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return; 
			}

			o.handleMouseEvent(e);
		};

		o.handleMouseEvent = function(e)			
		{
			var o = this;		
			e = fixE(e);

			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);
		
			var width = parseInt(o.root.style.width);
			var height = parseInt(o.root.style.height);

			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.NORTHWEST:
					o.style.cursor = "nw-resize";
					break;
				case Compass.NORTH:
					o.style.cursor = "n-resize";
					break;
				case Compass.NORTHEAST:
					o.style.cursor = "ne-resize";
					break;

				// the rest of the directions don't apply.
				default:
					o.style.cursor = "move";
					break;
			}
		};
	}
};

var MemoTextArea = 
{	
	init : function(o, oRoot)
	{
		o.onmousedown = function(e)
		{
			if(_dragObj || _activeDialog) { return; }		
		
			var o = this;
			e = fixE(e);
			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		

			// need extra offset to prevent conflict with scrollbar.
			var width = parseInt(o.root.style.width) + Memo.PADDING_OFFSET_WIDTH + 2; 
			var height = parseInt(o.root.style.height) + Memo.PADDING_OFFSET_HEIGHT;

			o.lastMouseX = e.clientX;
			o.lastMouseY = e.clientY;

			/**
			 * first check if this is a resize event, and if so, dispatch  
			 * the event to the parent.
			 */
			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.SOUTHWEST:				
				case Compass.SOUTH:				
				case Compass.SOUTHEAST:				
				case Compass.EAST:				
				case Compass.WEST:		
					_dragObj = o.root;
					o.root.start(e);
					break;

				// otherwise do nothing.
				default:		
//					debug("textAreaEl.start():  default start");					
					_scrollBarDrag = true;
			}

			if(Browser.isMozilla)
			{
//				debug("moz: forcing focus to text area");
				o.root.textAreaFocus();
			}
		};

		o.onmouseover = MemoTextArea.onmouseover;
		o.onmousemove = MemoTextArea.onmousemove;		

		o.onfocus = function(e)
		{
			if(_activeDialog)
			{
				this.blur();
			}
		}
		
		o.root = oRoot;		

		o.handleMouseEvent = function(e)			
		{
			var o = this;		
			e = fixE(e);
			var y = parseInt(o.root.style.top);
			var x = parseInt(o.root.style.left);		
		
			var width = parseInt(o.root.style.width) + Memo.PADDING_OFFSET_WIDTH + 2; // need extra offset to prevent conflict with scrollbar.
			var height = parseInt(o.root.style.height) + Memo.PADDING_OFFSET_HEIGHT;

			var compass = Compass.getCompass(e, x, y, width, height);
			switch(compass)
			{
				case Compass.SOUTHWEST:
					o.style.cursor = "sw-resize";
					break;
				case Compass.SOUTH:
					o.style.cursor = "s-resize";
					break;
				case Compass.SOUTHEAST:
					o.style.cursor = "se-resize";
					break;
				case Compass.EAST:
					o.style.cursor = "e-resize";
					break;
				case Compass.WEST:
					o.style.cursor = "w-resize";
					break;

				// the rest of the directions don't apply.
				default:
					o.style.cursor = "default";
					break;
			}
		};

	},

	onmouseover : function(e)
	{		
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	},

	onmousemove : function(e)
	{
		var o = this;
			
		// once dragging starts, drag routines determine the cursor styles...
		if(_dragObj) 
		{ 	
			o.style.cursor = _dragObj.style.cursor;
			return; 
		}

		o.handleMouseEvent(e);
	}
};

var MemoMinimizeIcon = 
{	
	init : function(o, oRoot)
	{
		o.root = oRoot;		

		o.onmousedown = function(e)
		{
			if(_activeDialog) {	return; }			
			o.root.hide();
			if((Memo.getVisibleCount()) == 0)
			{
				_mainMenu.displayRootless();
			}
		}

		o.onmouseover = function(e)
		{
			var o = this;
			e = fixE(e);
			
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return; 
			}
			o.handleMouseEvent(e);
		};

		o.onmousemove = function(e)
		{
			var o = this;
			e = fixE(e);
			
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return; 
			}
			o.handleMouseEvent(e);
		};

		o.handleMouseEvent = function(e)			
		{
			var o = this;		
			e = fixE(e);
			o.style.cursor = "pointer";
		};
	}
};

var MemoMenuIcon = 
{	
	init : function(o, oRoot)
	{
		o.root = oRoot;		

		o.onmousedown = function(e)
		{
			if(_dragObj) { return; }

			// we need this to prevent the body event handler from closing the menu.
			Memo.menuIconClicked = true;
		};

		o.onmouseover = function(e)
		{
			var o = this;
			e = fixE(e);
			
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return; 
			}
			o.handleMouseEvent(e);
		};

		o.onmousemove = function(e)
		{
			var o = this;
			e = fixE(e);
			
			// once dragging starts, drag routines determine the cursor styles...
			if(_dragObj) 
			{ 	
				o.style.cursor = _dragObj.style.cursor;
				return; 
			}
			o.handleMouseEvent(e);
		};

		o.handleMouseEvent = function(e)			
		{
			o.style.cursor = "pointer";
		};

		o.onclick = function(e)
		{
			if(_dragObj) { return; }

			var o = this;
			e = fixE(e);
			_activeObj = o.root;

			if(_activeDialog)
			{
				return;
			}			
			else if(_mainMenu.isOpen() && _mainMenu.parent == _activeObj)
			{
				// ignore.	
			} 
			else 
			{
				// the menu is already open over another memo.
				if(_mainMenu.isOpen())
				{
					_mainMenu.hide();		
				}

				_mainMenu.setParent(_activeObj);
				_mainMenu.setColorScheme(_activeObj.colorScheme);	
				_mainMenu.setLookAndFeel(_activeObj.readOnly);
				_mainMenu.repaint();
				_mainMenu.show();
			}
		}
	}
};

function getCompass(e, x, y, width, height)
{	
	clientX = e.clientX + getViewportScrollX();
	clientY = e.clientY + getViewportScrollY();

	// NW corner.
	if(clientX - x < Memo.resizeTolerance && clientY - y < Memo.resizeTolerance)
	{
		return Compass.NORTHWEST;				
	} 

	// NE corner
	else if((x + width) - clientX < Memo.resizeTolerance && clientY - y < Memo.resizeTolerance)
	{
		return Compass.NORTHEAST;				
	}
					
	// North border.
	else if(clientY - y < Memo.resizeTolerance)
	{
		return Compass.NORTH;		
	}

	// SE border.
	else if((x + width) - clientX < Memo.resizeTolerance && (y + height) - clientY < Memo.resizeTolerance)
	{
		return Compass.SOUTHEAST;
	}

	// East border.
	else if((x + width) - clientX < Memo.resizeTolerance)
	{
		return Compass.EAST;
	}

	// SW border.
	else if(clientX - x < Memo.resizeTolerance && (y + height) - clientY < Memo.resizeTolerance)
	{
		return Compass.SOUTHWEST;
	}

	// South border.
	else if((y + height) - clientY < Memo.resizeTolerance)
	{
		return Compass.SOUTH;
	}

	// West border.
	else if(clientX - x < Memo.resizeTolerance)
	{
		return Compass.WEST;
	}

	return Compass.NONE;		
}

Compass.getCompass = getCompass;
