function ZoomMenu(id, isDebugMode) {
	this.ID = id;
	this.IsDebugMode = isDebugMode
	this.MenuItems = new Object();
	this.ImageDirectory = '';
	this.BaseHeight = 35;
	this.BaseWidth = 35;
	this.MaxHeight = 70;
	this.MaxWidth = 70;
	this.IsVertical = 1;
	this.ZoomDistanceMultiplier = 2.5; // the distance away from the current menu item to zoom other items
	this.Steps = 7; // the number of steps between each zoom
	this.TimeoutLength = 5; //15;  // milliseconds between zooms (see this.Velocity in Initialize());
	this.HasImageBorders = 0; // test property
	this.LastMouseMovement = null;

	return this;
};
ZoomMenu.prototype.Initialize = function() {
	this.ClearDebug();
	this.Container = document.getElementById(this.ID);
	this.Container.ZoomMenu = this;
	
	for (var i=0; i<this.MenuItems.length; i++) {
		zoomMenuItem = this.MenuItems[i];
		zoomMenuItem.Index = i;
		zoomMenuItem.ZoomMenu = this;
		zoomMenuItem.Initialize();
		zoomMenuItem.Image.ZoomMenuItem = zoomMenuItem;

		// here 'this' refers to the <img> tag
		zoomMenuItem.Image.onmouseover = function(e) {
			this.ZoomMenuItem.ZoomMenu.OnMenuItem(e, this.ZoomMenuItem);		
		};
		zoomMenuItem.Image.onmouseout = function(e) {
			this.ZoomMenuItem.ZoomMenu.OffMenuItem(e, this.ZoomMenuItem);		
		};
		zoomMenuItem.Image.onmousemove = function(e) {			
			this.ZoomMenuItem.ZoomMenu.MoveMenuItem(e, this.ZoomMenuItem);		
		};		
		zoomMenuItem.Image.onclick = function(e) {
			// TODO: allow URLs!
			eval(this.ZoomMenuItem.Href);	
		};
	}
	
	this.Container.onmouseover = function(e) {
		// here 'this' refers to the <img> tag
		this.ZoomMenu.OnMenu(e);
	};
	//this.Container.onmousemove = function(e) {
		// here 'this' refers to the <img> tag
	//	this.ZoomMenu.OnMenu(e);
	//};	
	this.Container.onmouseout = function(e) {
		// here 'this' refers to the <img> tag
		this.ZoomMenu.OffMenu(e);
	};

	
	// calculate velocities
	this.XVelocity = (this.MaxWidth - this.BaseWidth) / this.Steps;
	this.YVelocity = (this.MaxHeight - this.BaseHeight) / this.Steps;
	
	// calculate the image gradation
	this.ImageWidthIncriment = (this.MaxWidth - this.BaseWidth) / this.MenuItems[0].Images.length;
	this.ImageHeightIncriment = (this.MaxHeight - this.BaseHeight) / this.MenuItems[0].Images.length;
	
	// timeouts
	this.TimeoutLength = null;
	this.HasMovingMenuItems = false;
	
	// size
	//this.Container.style.width = (this.BaseWidth  * (this.MenuItems.length)) + (this.HasImageBorders * this.MenuItems.length * 2) + 'px'
	//this.Container.style.width = (this.BaseWidth  * (this.MenuItems.length)) + 'px'
		
	this.Debug(this.ID + ' initialized');
	
	container = this.GetObjectPosition(this.Container);
	
	this.Debug(container.X + "," + container.Y);
};
ZoomMenu.prototype.MoveMenuItem = function(e, zoomMenuItem) {
	time = new Date();
	if (time - this.LastMouseMovement > 5) 
		this.OnMenuItem(e, zoomMenuItem);
	
}
ZoomMenu.prototype.OnMenuItem = function(e, zoomMenuItem) {
	
	this.HasMovingMenuItems = true;
	zoomMenuItem.IsMoving = true;
	zoomMenuItem.TargetWidth = this.MaxWidth;
	zoomMenuItem.TargetHeight = this.MaxHeight;
	
	// TODO: for vertical menu!!
	
	zoomDistance = this.ZoomDistanceMultiplier * this.MaxWidth;
	
	show = '';
	
	for (var i=0; i<this.MenuItems.length; i++) {
		if (i != zoomMenuItem.Index) {
			currentMenuItem = this.MenuItems[i];
			currentMenuItem.IsMoving = true;
			
			dist = this.GetMouseDistanceToObjectEdge(e,currentMenuItem.Image); 
				
				
			show += i + '=' + dist.X + ',';
			
			if (dist.X < zoomDistance) {
				currentMenuItem.TargetWidth =  this.RoundUp(
					this.BaseWidth +  
					( 
					  (this.MaxWidth - this.BaseWidth)
					  *
					  (zoomDistance-dist.X) / zoomDistance
					)
				);
				
				currentMenuItem.TargetHeight =  this.RoundUp(
					this.BaseHeight +  
					( 
					  (this.MaxHeight - this.BaseHeight)
					  *
					  (zoomDistance-dist.X) / zoomDistance
					)
				);				
				
			}
		}
	}

	//this.Debug(show);
	
	this.MoveMenuItems();
	this.LastMouseMovement = new Date();
	// start the next round
	//this.StartTimeout();	

};
ZoomMenu.prototype.OffMenuItem = function(e, zoomMenuItem) {

};
ZoomMenu.prototype.OnMenu = function(e) {
	//this.ExpandAll();
};
ZoomMenu.prototype.OffMenu = function(e) {
	this.ResetAllMenuItems();
	this.HasMovingMenuItems = true;
	this.StartTimeout();	
};
ZoomMenu.prototype.ExpandToMouse = function() {
	
	// where are we?
	mouse = this.GetMousePositionRelativeToContainer(e);
	
	
	
	
	//for (var i=0; i<this.MenuItems.length; i++) {
	//	zoomMenuItem = this.MenuItems[i];
	//	this.SetImageSize(zoomMenuItem, this.MaxWidth, this.MaxHeight);
	//}
};
ZoomMenu.prototype.ExpandAll = function() {
	for (var i=0; i<this.MenuItems.length; i++) {
		zoomMenuItem = this.MenuItems[i];
		this.SetImageSize(zoomMenuItem, this.MaxWidth, this.MaxHeight);
	}
};
ZoomMenu.prototype.CollapseAll = function() {
	for (var i=0; i<this.MenuItems.length; i++) {
		zoomMenuItem = this.MenuItems[i];
		this.SetImageSize(zoomMenuItem, this.BaseWidth, this.BaseWidth);
	}
};
ZoomMenu.prototype.GetMousePosition = function(e) {
	// from: http://www.quirksmode.org/js/events_properties.html
	var posx = 0;
	var posy = 0;
	if (!e) var e = window.event;
	if (e.pageX || e.pageY)
	{
		posx = e.pageX;
		posy = e.pageY;
	}
	else if (e.clientX || e.clientY)
	{
		posx = e.clientX + document.documentElement.scrollLeft;
		posy = e.clientY + document.documentElement.scrollTop;
	}
	return new Coordinate(posx, posy);
};
ZoomMenu.prototype.GetMousePositionRelativeToContainer = function(e) {
	return this.GetMousePositionRelativeToObject(e, this.Container);
};
ZoomMenu.prototype.GetMousePositionRelativeToObject = function(e, obj) {
	mousePos = this.GetMousePosition(e);
	objPos = this.GetObjectPosition(obj);
	
	return new Coordinate(mousePos.X - objPos.X, mousePos.Y - objPos.Y);	
}; 
ZoomMenu.prototype.GetMouseDistanceToObjectCenter = function(e, obj) {
	objWidth = obj.offsetWidth;
	objHeight = obj.offsetHeight;
	
	mousePos = this.GetMousePosition(e);
	objPos = this.GetObjectPosition(obj);
	
	return new Coordinate(
		Math.abs(mousePos.X - objPos.X) - objWidth/2, 
		Math.abs(mousePos.Y - objPos.Y) - objHeight/2
	);	
};
ZoomMenu.prototype.GetMouseDistanceToObjectEdge = function(e, obj) {
	objWidth = obj.offsetWidth;
	objHeight = obj.offsetHeight;
		
	mousePos = this.GetMousePosition(e);
	objPos = this.GetObjectPosition(obj);
	
	xDist = Math.abs(mousePos.X - objPos.X) - ((objPos.X < mousePos.X) ? objWidth : 0);
	yDist = Math.abs(mousePos.Y - objPos.Y) - ((objPos.Y < mousePos.Y) ? objHeight : 0);
	
	
	
	return new Coordinate(
		xDist,
		yDist
	);	
};
ZoomMenu.prototype.GetObjectPosition = function(source, target) {
	var top = this.GetOffsetAttribute(source,"offsetLeft");
	var left = this.GetOffsetAttribute(source,"offsetTop");
	
	return new Coordinate(top, left);
};
ZoomMenu.prototype.SetObjectPosition = function(source, target) {
	target.style.left=this.GetOffsetAttribute(source,"offsetLeft")+"px";
	target.style.top=this.GetOffsetAttribute(source,"offsetTop")+"px";

};
ZoomMenu.prototype.GetOffsetAttribute = function(object,attribute) {
	var offset=0;
	while (object) {
		offset+=object[attribute]; 
		object=object.offsetParent;
	}
	return offset;
};
ZoomMenu.prototype.StartTimeout = function() {
	if (this.HasMovingMenuItems) {
		_TimeoutManager.AddMethod(this.id + '_MoveMenuItems', this, 'MoveMenuItems', this.TimeoutLength);		
		this.HasMovingMenuItems = true;
	}
};
ZoomMenu.prototype.MoveMenuItems = function() {
	activeItems = false;
	for (var i=0; i<this.MenuItems.length; i++) {
		zoomMenuItem = this.MenuItems[i];
		
		if (zoomMenuItem.IsMoving) {
			currentWidth = zoomMenuItem.Image.width;
			currentHeight = zoomMenuItem.Image.height;
			newWidth = 0;
			newHeight = 0;	
			if (zoomMenuItem.Image.width < zoomMenuItem.TargetWidth) {
				// zoom in
				newWidth = currentWidth + this.XVelocity;
				if (newWidth > zoomMenuItem.TargetWidth) newWidth = zoomMenuItem.TargetWidth;
				newHeight = currentHeight + this.YVelocity;	
				if (newHeight > zoomMenuItem.TargetHeight) newHeight = zoomMenuItem.TargetHeight;
			} else {
				// zoom out
				newWidth = currentWidth - this.XVelocity;
				if (newWidth < zoomMenuItem.TargetWidth) newWidth = zoomMenuItem.TargetWidth;
				newHeight = currentHeight - this.YVelocity;	
				if (newHeight < zoomMenuItem.TargetHeight) newHeight = zoomMenuItem.TargetHeight;
			}	
			this.SetImageSize(zoomMenuItem, newWidth, newHeight);
			
			if (zoomMenuItem.Image.width == zoomMenuItem.TargetWidth) 
				zoomMenuItem.IsMoving = false;
			else 
				activeItems = true;
				
			this.SetImageNumber(zoomMenuItem);
		}
	}	
	this.HasMovingMenuItems = activeItems;
	this.StartTimeout();
};
ZoomMenu.prototype.ResetAllMenuItems = function() {
	for (var i=0; i<this.MenuItems.length; i++) {
		zoomMenuItem = this.MenuItems[i];
		zoomMenuItem.IsMoving = true;
		zoomMenuItem.TargetWidth = this.BaseWidth;
		zoomMenuItem.TargetHeight = this.BaseHeight;		
	}	
};
ZoomMenu.prototype.ZoomIn = function(zoomMenuItem) {
	zoomMenuItem.IsMoving = true;
	zoomMenuItem.TargetWidth = this.MaxWidth;
	zoomMenuItem.TargetHeight = this.MaxHeight;
	
	// set the side items if there are any
	for (var i=this.SizeItemsToZoom; i>=1; i--) {
		leftIndex = zoomMenuItem.Index-i;
		rightIndex = zoomMenuItem.Index+i;
		
		if (leftIndex >= 0) {
			
			leftMenuItem = this.MenuItems[leftIndex];
			//this.Debug(leftMenuItem.ID);
			leftMenuItem.IsMoving = true;
			leftMenuItem.TargetHeight = this.RoundUp(this.BaseHeight + (this.MaxHeight - this.BaseHeight) / (this.SizeItemsToZoom + 1) * (this.SizeItemsToZoom-i+1));
			leftMenuItem.TargetWidth = this.RoundUp(this.BaseWidth + (this.MaxWidth - this.BaseWidth) / (this.SizeItemsToZoom + 1) * (this.SizeItemsToZoom-i+1));	
		}
		
		if (rightIndex < this.MenuItems.length) {
			rightMenuItem = this.MenuItems[rightIndex];
			//this.Debug(rightMenuItem.ID);
			rightMenuItem.IsMoving = true;
			rightMenuItem.TargetHeight = this.RoundUp(this.BaseHeight + (this.MaxHeight - this.BaseHeight) / (this.SizeItemsToZoom + 1) * (this.SizeItemsToZoom-i+1));
			rightMenuItem.TargetWidth = this.RoundUp(this.BaseWidth + (this.MaxWidth - this.BaseWidth) / (this.SizeItemsToZoom + 1) * (this.SizeItemsToZoom-i+1));		
		}		
	}
	// start the next round
	this.HasMovingMenuItems = true;	
	this.StartTimeout();
}
ZoomMenu.prototype.ZoomOut = function(zoomMenuItem) {
	this.ResetAllMenuItems();
	this.HasMovingMenuItems = true;
	this.StartTimeout();
}
ZoomMenu.prototype.SetImageSize = function(zoomMenuItem, width, height, setNumber) {
	
	if (typeof(setNumber) == 'undefined') setNumber = true;

	// controls
	if (width < this.BaseWidth) width = this.BaseWidth;
	if (width > this.MaxWidth) width = this.MaxWidth;
	if (height < this.BaseHeight) width = this.BaseHeight;
	if (height > this.MaxHeight) width = this.MaxHeight;		
	
	// set both the image object and the style
	zoomMenuItem.Image.width = width;
	zoomMenuItem.Image.style.width = width + 'px';
	zoomMenuItem.Image.height = height;
	zoomMenuItem.Image.style.height = height + 'px';
	
	if (setNumber) this.SetImageNumber(zoomMenuItem);
};
ZoomMenu.prototype.SetImageNumber = function(zoomMenuItem) {
	//return;
	currentWidth = zoomMenuItem.Image.width;
	currentHeight = zoomMenuItem.Image.height;
	
	imageIndex = 0;
	if (zoomMenuItem.Image.width == this.BaseWidth) {
		// use the first image in the array
		imageIndex = 0;
	} else if (zoomMenuItem.Image.width == this.MaxWidth) {
		// use the last image in the array
		imageIndex = zoomMenuItem.Images.length-1;
	} else {
		// select 
		
		imageIndex = this.RoundUp((currentWidth - this.BaseHeight) / this.ImageWidthIncriment) - 1;		
	}	
	newImage = zoomMenuItem.Images[imageIndex];
	
	if (newImage) {
		zoomMenuItem.Image.src = newImage.src;
		// reset size b/c new image changes the size		
		this.SetImageSize(zoomMenuItem, currentWidth, currentHeight, false);
	}
};
ZoomMenu.prototype.RoundUp = function(number) {
	rounded = Math.round(number)
	if (rounded < number) 
		return rounded+1;
	else 
		return rounded;
};
ZoomMenu.prototype.RoundDown = function(number) {
	rounded = Math.round(number)
	if (rounded < number) 
		return rounded;
	else 
		return rounded-1;
};
ZoomMenu.prototype.Debug = function(text,addReturn) {
	if (typeof addReturn == "undefined") addReturn = true;
	if (this.IsDebugMode)
		document.getElementById("Debug").value += text + ((addReturn) ? '\r'  : '');
};
ZoomMenu.prototype.ClearDebug = function() {
	debug = document.getElementById("Debug");
	if (debug) debug.value  = '';
};

/* COORDINATE OBJECT */
function Coordinate(x, y) {
	this.X = x;
	this.Y = y;
};


/* MENU ITEM OBJECT */

function ZoomMenuItem(title, href, imageSrcs) {
	this.Title = title;
	this.Href = href;
	this.ImageSrcs = imageSrcs;
	this.Timer = null;
	this.IsMoving = false;
	this.TargetWidth = null;
	this.TargetHeight = null;
	this.Index = 0;
};
ZoomMenuItem.prototype.Initialize = function() {
	this.Images = new Array();
	
	for(var i=0; i<this.ImageSrcs.length; i++) {
		img = new Image();
		img.src = this.ZoomMenu.ImageDirectory + this.ImageSrcs[i];
		img.onerror = 'src="' + this.ImageSrcs[0] + '"';
		this.Images[i] = img;
	}
	
	// add the HTML image
	img  = new Image();
	img.src = this.Images[0].src;
	img.title = img.alt = this.Title;
	img.id = this.ZoomMenu.ID + '_' + this.Index;
	img.width = this.ZoomMenu.BaseWidth;
	img.height = this.ZoomMenu.BaseHeight;
	if (this.ZoomMenu.HasImageBorders) img.style.border = "solid 1px red";
	this.ZoomMenu.Container.appendChild(img);
	this.Image = img;
	
	if (this.ZoomMenu.IsVertical) 
		this.ZoomMenu.Container.appendChild(document.createElement("br"));
};


//* Timeout Manager //

function GlobalTimeoutManager() {
	this.PendingCalls = {};		
};
GlobalTimeoutManager.prototype.AddMethod = function(name,obj,method,delay,arg1,arg2) {
	//document.getElementById('Debug').value += [name,obj,method,delay,arg1,arg2] + '\r';
	this.ClearMethod(name);
	this.PendingCalls[name] = new TimeoutCall(obj,method,arg1,arg2);
	this.PendingCalls[name].Timeout = 
		setTimeout('_TimeoutManager.ExecuteMethod("' + name + '");', delay);
};
GlobalTimeoutManager.prototype.ExecuteMethod = function(name) {
	call = this.PendingCalls[name];
	if (call != null) {
		call.obj[call.method](call.arg1,call.arg2);
		this.ClearMethod(name);
	}
};
GlobalTimeoutManager.prototype.ClearMethod = function(name) {
	if (this.PendingCalls[name]) {
		// delete this.PendingCalls[name];
	}
};
//* Object to hold timeout reference
function TimeoutCall(obj,method,arg1,arg2) {
	this.obj = obj;
	this.method = method;
	this.arg1 = arg1;
	this.arg2 = arg2;
	this.Timeout = null;
};
_TimeoutManager = new GlobalTimeoutManager();