	var mousexy=new Object();
	var layerleft = 0;
	var layertop = 0;
	var newpage;
	var browser;
	var xmlHttp;
	var response;

function init()
{
  	document.onmousemove = update;
  	update();	
  	BrowserDetect.init();
   	initajax();
}

function gettempXY(e)
{
	if (!e) e = window.event;
	if (e)
	{
		if (e.pageX || e.pageY)
		{
			mousexy.x = e.pageX;
			mousexy.y = e.pageY;
		} else if (e.clientX || e.clientY){
			mousexy.x = e.clientX + document.body.scrollLeft;
			mousexy.y = e.clientY + document.body.scrollTop;
		}
	}
}
function update(e){gettempXY(e);}
function initajax()
{
	try
	{
		xmlHttp=new XMLHttpRequest();
		browser = "STD";
	}
	catch (e)
	{
		try
		{
			xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
			browser = "IE";
		}
		catch (e)
		{
			try
			{
				xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
				browser = "IE";
			}
			catch (e)
			{
				alert("Your browser does not support AJAX!");
				return false;
			}
		}
	}
	}

function changepages(page, post, element, type)
{
	initajax();
	xmlHttp.onreadystatechange=function()
	{
    	if(xmlHttp.readyState==4)
    	{
			//alert(xmlHttp.responseText);
			switch(type)
			{
				case "innerHTML":
					element.innerHTML = xmlHttp.responseText;
					break;
				case "value":
					element.value = xmlHttp.responseText;
					break;
				case "src":
					element.src = xmlHttp.responseText;
					break;
				case "alt":
					element.alt = xmlHttp.responseText;
					break;
				case "captcha":
					document.getElementById('code').src = "/captcha.php?hash=" + xmlHttp.responseText;
					document.getElementById('hash').value = xmlHttp.responseText;
					break;
				case "name2key":
					element.value = xmlHttp.responseText;
					break;
				case "ServerButton":
					ButtonStatus(element,xmlHttp.responseText);
					break;
				case "PSCButton":
					document.serverform.submit();
					break;
				case "OrderServer":
					element.innerHTML = xmlHttp.responseText;
					break;
				case "Verbose":
					//alert(xmlHttp.responseText);
					element.disabled=false;
					break;
				case "Vendor":
					VendorResponse(element,xmlHttp.responseText);
					break;
				case "SaveVendor":
					SaveVendorResponse(element,xmlHttp.responseText);
					break;
				case "custpack":
					//alert(xmlHttp.responseText);
					//RunPacks();
					break;
				case "runpack":
					//alert(xmlHttp.responseText);
					timerID = setInterval("TimedEvent();",1500);
					break;
				case "removescr":
					break;
				case "closepop":
					submitform();
					break;
				case "venrez":
					if(xmlHttp.responseText == "NSR") 
					{ 
						ent.hidePopup();
						alert("No Vendor servers in that region.  Could not complete operation");
					} else {
						submitform();
					}
					break;
				case "pushlist":
					AddToList(post,xmlHttp.responseText,element);
					break;
				case "DeleteEvent":
					document.eventform.submit();
					break;
				case "ObjInv":
					element.innerHTML = xmlHttp.responseText;
					timerID = setInterval("TimedEvent();",1500);
					break;
				case "ScriptState":
					ReturnedScriptState(xmlHttp.responseText,element);
					timerID = setInterval("TimedEvent();",1500);
					break;
				case "DEBUG":
					alert(xmlHttp.responseText);
					break;
				default:
					break;
			}
		}
	}
	if(type == "name2key")
	{
		xmlHttp.open("GET",page+"?"+post,true);
		xmlHttP.send(null);
	} else {
		xmlHttp.open("POST", page ,true);
		xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	    xmlHttp.send(post);
	}
}

function ShowHelp(section,name,anch)
{
	var element = document.getElementById('help');
	element.innerHTML = "";
	help.showPopup(anch);
	changepages('/shared/options.php', 'type=help&section='+section+'&name='+name, element, "innerHTML");
}

function entry_options(entryid, anch)
{
	ent.showPopup(anch);
	document.getElementById('entry_detail').innerHTML = "<center>Loading...... Please Wait</center>";
	changepages('/guest/options.php', 'itemid=' + entryid, document.getElementById('entry_detail'), "innerHTML");
}

function publishstatus(entryid)
{
	var stat;
	var nodename;
	var nodevalue;
	var obj = document.getElementById('publish' + entryid);
	
	nodename = "class";
	
	if  (obj.attributes[nodename].nodeValue == "unpubbutton")
	{
		nodevalue = "pubbutton";
		stat = '1';
	} else {
		nodevalue = "unpubbutton";
		stat = '0';
	}
	var page = "/guest/options.php";
	var post = "itemid=" + entryid + "&status=" + stat;
	changepages(page, post, document.getElementById('entry_detail'), "innerHTML");

	if (BrowserDetect.browser == "Explorer")
	{
		top.location.href = "/home/portal/guestbook/entries";
	} else {
		obj.setAttribute(nodename,nodevalue);
	}
	
}

function deleteentry(entryid, anch)
{
	document.getElementById('entry_detail').innerHTML = "<center>Processing...... Please Wait</center>";
	changepages('/guest/options.php','itemid=' + entryid + "&delete=1", document.getElementById('entry_detail'), "innerHTML");
	top.location.href = "/home/portal/guestbook/entries";
}

function deleteobject(entryid)
{
	changepages('/SCuDS/options.php','itemid=' + entryid + "&delete=1");
}

function ButtonStatus(element, response)
{
	switch(response)
	{
		case '1':
			element.style.backgroundPosition = '0px -20px';
			break
		case '2':
			element.style.backgroundPosition = '0px 0px';
			break
		case '3':
			element.style.backgroundPosition = '0px -40px';
			break
		case '4':
			element.style.backgroundPosition = '0px -60px';
			break
		case '6':
			element.style.backgroundPosition = '0px -100px';
			break
		default:
			element.style.backgroundPosition = '0px -80px';
			break
	}	
}

function VendorResponse(element, response)
{
	//alert(response);
	switch(response)
	{
		case '1':
			break
		case '2':
			element.style.backgroundPosition = '0px -60px';
			break
		case '3':
			break
		case '4':
			break
		case '7':
			element.style.backgroundPosition = '0px 0px';
			break
		case '8':
			element.style.backgroundPosition = '0px -40px';
			break
		case '9':
			break
		default:
			element.style.backgroundPosition = '0px -80px';
			break
	}
}

function SaveVendorResponse(element, response)
{
	//alert(response);
	ent.hidePopup();	
}

function Item_Owner(owner, uid, element, anch)
{
	ent.showPopup(anch);
	element.style.top = mousexy.y+'px';
	element.style.left = mousexy.x+'px';
	element.innerHTML = "<center>Loading...... Please Wait</center>";
	changepages('/SCuDS/options.php', 'customer=' + owner+'&uid='+uid, element, "innerHTML");
}

function test(page)
{
	alert(changepages(page));
}

function MouseOverRegPas(image, x)
{
	//alert(image);
	document.getElementById(image).style.backgroundPosition = '0px -' + x + 'px';
	document.getElementById(image).style.cursor = 'pointer';
	document.getElementById(image).style.color = '#FFFFFF';
	document.getElementById(image).style.textDecoration = 'underline';
}

function MouseOutRegPas(image)
{
	document.getElementById(image).style.backgroundPosition = '0px 0px';
	document.getElementById(image).style.cursor = 'default';
	document.getElementById(image).style.color = '#1576C7';
	document.getElementById(image).style.textDecoration = 'none'
}

function AddToList(name, key, element)
{
	var total = document.getElementById('total');
	var comtot = document.getElementById('comtotals');
	var comtotals = parseInt(total.innerHTML);
	var arr1 = name.split("&");
	var names = arr1[1].split("=");
	var com = arr1[2].split("=");
	//alert(key);
	if(key != "00000")
	{
		if((comtotals + parseInt(com[1])) > 100)
		{
			alert("Totals are over 100%.  "+ names[1] + " has not been added");
			document.getElementById('status').innerHTML = "";
		} else {
			if(element.selectedIndex != -1) var OptSplit = element.options[element.selectedIndex].text.split("(");
			if(element.selectedIndex != -1 && OptSplit[0] == names[1] + " ")
			{
				var percent = parseInt(OptSplit[1]);
				//comtotals = comtotals - percent;
				comtotals += parseInt(com[1]);
				total.innerHTML = comtotals + "%";
				comtot.value = comtotals;
				element.options[element.selectedIndex].text = names[1] + " (" + com[1] + "%)";
				element.selectedIndex = -1;
			}
			 else
			{
				var size  = element.options.length;
				element.options[size] = new Option(names[1] + " (" + com[1] + "%)",key);
				comtotals += parseInt(com[1]);
				total.innerHTML = comtotals + "%";
				comtot.value = comtotals;
			}
			document.getElementById('status').innerHTML = "";
			document.getElementById('mname').value = "";
			document.getElementById('commision').value = "";
		}
	} else {
		alert("Unable to find "+names[1]+"'s key.  This user has not been added to the list");
		document.getElementById('status').innerHTML = "";
	}
}

function RemoveFromList(element)
{
	var optsplit;
	var percent;
	var total = document.getElementById('total');
	var comtot = document.getElementById('comtotals');
	for (i=element.options.length-1; i >= 0;i--) 
	{
		if (element.options[i].selected) 
		{
			optsplit = element.options[i].text.split("(");
			comtotals = parseInt(total.innerHTML);
			percent = parseInt(optsplit[1]);
			comtotals = comtotals - percent;
			total.innerHTML = comtotals + "%";
			comtot.value = comtotals;
			element.options[i] = null;
		}
	}
}

function waitcursor(object)
{
	object.style.cursor =  'pointer';
}

function normalcursor(object)
{
		object.style.cursor = 'default';
}

	
	function SelectAll()
	{
		var element = document.eventform.mark;
		for (var i=0; i < element.length ;i++) 
		{
			if (element[i].checked == false) 
			{
				element[i].checked = true;
			}
		}
	}

	function DeSelectAll()
	{
		var element = document.eventform.mark;
		for (var i=0; i < element.length ;i++) 
		{
			if (element[i].checked == true) 
			{
				element[i].checked = false;
			}
		}
	}

	function popup(mylink, windowname)
	{
		if (! window.focus)return true;
		var href;
		if (typeof(mylink) == 'string')
   			href=mylink;
		else
		   href=mylink.href;
		window.open(href, windowname, 'width=589,height=450,scrollbars=yes');
		//document.scriptform.submit();
		return false;
	}

	function getAttributes(obj){
		for(var i=0;i<obj.attributes.length;i++){
			if(obj.attributes[i].specified){
				alert(obj.attributes[i].nodeName + "=" + obj.attributes[i].nodeValue);
			}
		}
		alert(browser);
	}
	
	function ReturnedScriptState(statestr, element)
	{
		var statearr = statestr.split("|");
		
		switch(statearr[1])
		{
			case '1':
				element.style.backgroundPosition = '0px 0px';
				element.title = 'Stop ' + statearr[0];
				element.onClick = "ScriptState('"+statearr[2]+"', '"+statearr[0]+"', 2, this);";
				break;
			case '2':
				element.style.backgroundPosition = '0px -20px';
				element.title = 'Start ' + statearr[0];
				element.onClick = "ScriptState('"+statearr[2]+"', '"+statearr[0]+"', 1, this);";
				break;
			case '3':
				//element.style.backgroundPosition = '0px 0px';
				//element.title = 'Stop ' + statearr[0];
				//element.onClick = "ScriptState('"+statearr[2]+"', '"+statearr[0]+"', 2, this);";
				break;
			case '4':
				//var list = document.getElementById('row'+statearr[0]);
				//element.removeChild('row'+statearr[0]);
				document.getElementById('row'+statearr[0]).style.visibility = "hidden";
				document.getElementById('row'+statearr[0]).style.position = "absolute";
				//document.getElementById('row'+statearr[0]).style.height = '1px';
				break;
		}
		if(statearr[3] == "F") 
		{
			document.getElementById('names'+statearr[0]).style.color = "#FF0000";
		} else {
			document.getElementById('names'+statearr[0]).style.color = "#000000";
		}
		document.getElementById('names'+statearr[0]).innerHTML = statearr[0];
	}

var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};


function ClearOptions(OptionList) {

   // Always clear an option list from the last entry to the first
   for (x = OptionList.length; x >= 0; x = x - 1) {
      OptionList[x] = null;
   }
}


function AddToOptionList(OptionList, OptionValue, OptionText) {
   // Add option to the bottom of the list
   OptionList[OptionList.length] = new Option(OptionText, OptionValue);
}

var DragDrop = {
	firstContainer : null,
	lastContainer : null,
        parent_id : null,
        parent_group : null,
	makeListContainer : function(list, group) {
		// each container becomes a linked list node
		if (this.firstContainer == null) {
			this.firstContainer = this.lastContainer = list;
			list.previousContainer = null;
			list.nextContainer = null;
		} else {
			list.previousContainer = this.lastContainer;
			list.nextContainer = null;
			this.lastContainer.nextContainer = list;
			this.lastContainer = list;
		}
		
		// these functions are called when an item is draged over
		// a container or out of a container bounds.  onDragOut
		// is also called when the drag ends with an item having
		// been added to the container
		list.onDragOver = new Function();
		list.onDragOut = new Function();
		list.onDragDrop = new Function();
		list.group = group;
		
    	var items = list.getElementsByTagName( "li" );
    	
		for (var i = 0; i < items.length; i++) {
			DragDrop.makeItemDragable(items[i]);
		}
	},
        
        serData : function ( group, theid ) {
                var container = DragDrop.firstContainer;
		var j = 0;
                var string = "";
                
                while (container != null) {
                
                        if(theid != null && container.id != theid)
                        {
                          container = container.nextContainer;
                          continue;
                        }

                        if(group != null && container.group != group)
                        {
                          container = container.nextContainer;
                          continue;
                        }
                
                        j ++;
                        if(j > 1)
                        {
                          string += ":";
                        }
                        string += container.id;
                        
                        var items = container.getElementsByTagName( "li" );
    	                string += "(";
									string = "";
		        for (var i = 0; i < items.length; i++) {
                            if(i > 0)
                            {
                              string += ",";
                            }
			    string += items[i].id;
		        }
                        //string += ")";
                        
			container = container.nextContainer;
		}
                return string;   
        },

	makeItemDragable : function(item) {
		Drag.makeDraggable(item);
		item.setDragThreshold(5);
		
		// tracks if the item is currently outside all containers
		item.isOutside = false;
		
		item.onDragStart = DragDrop.onDragStart;
		item.onDrag = DragDrop.onDrag;
		item.onDragEnd = DragDrop.onDragEnd;
	},

	onDragStart : function(nwPosition, sePosition, nwOffset, seOffset) {
		// update all container bounds, since they may have changed
		// on a previous drag
		//
		// could be more smart about when to do this
		var container = DragDrop.firstContainer;
		while (container != null) {
			container.northwest = Coordinates.northwestOffset( container, true );
			container.southeast = Coordinates.southeastOffset( container, true );
			container = container.nextContainer;
		}
		
		// item starts out over current parent
		this.parentNode.onDragOver();
                parent_id = this.parentNode.id;
                parent_group = this.parentNode.group;
	},

	onDrag : function(nwPosition, sePosition, nwOffset, seOffset) {
		// check if we were nowhere
		if (this.isOutside) {
			// check each container to see if in its bounds
			var container = DragDrop.firstContainer;
			while (container != null) {

				if ((nwOffset.inside( container.northwest, container.southeast ) ||
					seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) {
					// we're inside this one
					container.onDragOver();
					this.isOutside = false;
					
					// since isOutside was true, the current parent is a
					// temporary clone of some previous container node and
					// it needs to be removed from the document
					var tempParent = this.parentNode;
					tempParent.removeChild( this );
					container.appendChild( this );
					tempParent.parentNode.removeChild( tempParent );
					break;
				}
				container = container.nextContainer;
			}
			// we're still not inside the bounds of any container
			if (this.isOutside)
				return;
		
		// check if we're outside our parent's bounds
		} else if (!(nwOffset.inside( this.parentNode.northwest, this.parentNode.southeast ) ||
			seOffset.inside( this.parentNode.northwest, this.parentNode.southeast ))) {
			
			this.parentNode.onDragOut();
			this.isOutside = true;
			
			// check if we're inside a new container's bounds
			var container = DragDrop.firstContainer;
			while (container != null) {
				if ((nwOffset.inside( container.northwest, container.southeast ) ||
					seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) {
					// we're inside this one
					container.onDragOver();
					this.isOutside = false;
					this.parentNode.removeChild( this );
					container.appendChild( this );
					break;
				}
				container = container.nextContainer;
			}
			// if we're not in any container now, make a temporary clone of
			// the previous container node and add it to the document
			if (this.isOutside) {
				var tempParent = this.parentNode.cloneNode( false );
				this.parentNode.removeChild( this );
				tempParent.appendChild( this );
				// body puts a border or item at bottom of page if do not have this
                                tempParent.style.border = 0;
				document.getElementsByTagName( "body" ).item(0).appendChild( tempParent );
				return;
			}
		}
		
		// if we get here, we're inside some container bounds, so we do
		// everything the original dragsort script did to swap us into the
		// correct position
		
		var parent = this.parentNode;
				
		var item = this;
		var next = DragUtils.nextItem(item);
		while (next != null && this.offsetTop >= next.offsetTop - 2) {
			var item = next;
			var next = DragUtils.nextItem(item);
		}
		if (this != item) {
			DragUtils.swap(this, next);
			return;
		}

		var item = this;
		var previous = DragUtils.previousItem(item);
		while (previous != null && this.offsetTop <= previous.offsetTop + 2) {
			var item = previous;
			var previous = DragUtils.previousItem(item);
		}
		if (this != item) {
			DragUtils.swap(this, item);
			return;
		}
	},

	onDragEnd : function(nwPosition, sePosition, nwOffset, seOffset) {
		// if the drag ends and we're still outside all containers
		// it's time to remove ourselves from the document or add 
                // to the trash bin
		if (this.isOutside) {
                        var container = DragDrop.firstContainer;
                        while (container != null) {
                           if(container.id == parent_id)
                           {
                             break;
                           }
                           container = container.nextContainer;
                        }
			this.isOutside = false;
			this.parentNode.removeChild( this );
			container.appendChild( this );
                        this.style["top"] = "0px";
		        this.style["left"] = "0px";
                        //var container = DragDrop.firstContainer;
                        //container.appendChild( this );
			return;
		}
		this.parentNode.onDragOut();
                this.parentNode.onDragDrop();
      		this.style["top"] = "0px";
		this.style["left"] = "0px";
	}
};

var DragUtils = {
	swap : function(item1, item2) {
		var parent = item1.parentNode;
		parent.removeChild(item1);
		parent.insertBefore(item1, item2);

		item1.style["top"] = "0px";
		item1.style["left"] = "0px";
	},

	nextItem : function(item) {
		var sibling = item.nextSibling;
		while (sibling != null) {
			if (sibling.nodeName == item.nodeName) return sibling;
			sibling = sibling.nextSibling;
		}
		return null;
	},

	previousItem : function(item) {
		var sibling = item.previousSibling;
		while (sibling != null) {
			if (sibling.nodeName == item.nodeName) return sibling;
			sibling = sibling.previousSibling;
		}
		return null;
	}		
};

var Drag = {
	BIG_Z_INDEX : 10000,
	group : null,
	isDragging : false,

	makeDraggable : function(group) {
		group.handle = group;
		group.handle.group = group;

		group.minX = null;
		group.minY = null;
		group.maxX = null;
		group.maxY = null;
		group.threshold = 0;
		group.thresholdY = 0;
		group.thresholdX = 0;

		group.onDragStart = new Function();
		group.onDragEnd = new Function();
		group.onDrag = new Function();
		
		// TODO: use element.prototype.myFunc
		group.setDragHandle = Drag.setDragHandle;
		group.setDragThreshold = Drag.setDragThreshold;
		group.setDragThresholdX = Drag.setDragThresholdX;
		group.setDragThresholdY = Drag.setDragThresholdY;
		group.constrain = Drag.constrain;
		group.constrainVertical = Drag.constrainVertical;
		group.constrainHorizontal = Drag.constrainHorizontal;

		group.onmousedown = Drag.onMouseDown;
	},

	constrainVertical : function() {
		var nwOffset = Coordinates.northwestOffset(this, true);
		this.minX = nwOffset.x;
		this.maxX = nwOffset.x;
	},

	constrainHorizontal : function() {
		var nwOffset = Coordinates.northwestOffset(this, true);
		this.minY = nwOffset.y;
		this.maxY = nwOffset.y;
	},

	constrain : function(nwPosition, sePosition) {
		this.minX = nwPosition.x;
		this.minY = nwPosition.y;
		this.maxX = sePosition.x;
		this.maxY = sePosition.y;
	},

	setDragHandle : function(handle) {
		if (handle && handle != null) 
			this.handle = handle;
		else
			this.handle = this;

		this.handle.group = this;
		this.onmousedown = null;
		this.handle.onmousedown = Drag.onMouseDown;
	},

	setDragThreshold : function(threshold) {
		if (isNaN(parseInt(threshold))) return;

		this.threshold = threshold;
	},

	setDragThresholdX : function(threshold) {
		if (isNaN(parseInt(threshold))) return;

		this.thresholdX = threshold;
	},

	setDragThresholdY : function(threshold) {
		if (isNaN(parseInt(threshold))) return;

		this.thresholdY = threshold;
	},

	onMouseDown : function(event) {
		event = Drag.fixEvent(event);
		Drag.group = this.group;

		var group = this.group;
		var mouse = event.windowCoordinate;
		var nwOffset = Coordinates.northwestOffset(group, true);
		var nwPosition = Coordinates.northwestPosition(group);
		var sePosition = Coordinates.southeastPosition(group);
		var seOffset = Coordinates.southeastOffset(group, true);

		group.originalOpacity = group.style.opacity;
		group.originalZIndex = group.style.zIndex;
		group.initialWindowCoordinate = mouse;
		// TODO: need a better name, but don't yet understand how it
		// participates in the magic while dragging 
		group.dragCoordinate = mouse;

		Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);

		group.onDragStart(nwPosition, sePosition, nwOffset, seOffset);

		// TODO: need better constraint API
		if (group.minX != null)
			group.minMouseX = mouse.x - nwPosition.x + 
					group.minX - nwOffset.x;
		if (group.maxX != null) 
			group.maxMouseX = group.minMouseX + group.maxX - group.minX;

		if (group.minY != null)
			group.minMouseY = mouse.y - nwPosition.y + 
					group.minY - nwOffset.y;
		if (group.maxY != null) 
			group.maxMouseY = group.minMouseY + group.maxY - group.minY;

		group.mouseMin = new Coordinate(group.minMouseX, group.minMouseY);
		group.mouseMax = new Coordinate(group.maxMouseX, group.maxMouseY);

		document.onmousemove = Drag.onMouseMove;
		document.onmouseup = Drag.onMouseUp;

		return false;
	},

	showStatus : function(mouse, nwPosition, sePosition, nwOffset, seOffset) {
		/*window.status = 
				"mouse: " + mouse.toString() + "    " + 
				"NW pos: " + nwPosition.toString() + "    " + 
				"SE pos: " + sePosition.toString() + "    " + 
				"NW offset: " + nwOffset.toString() + "    " +
				"SE offset: " + seOffset.toString();*/
	},

	onMouseMove : function(event) {
		event = Drag.fixEvent(event);
		var group = Drag.group;
		var mouse = event.windowCoordinate;
		var nwOffset = Coordinates.northwestOffset(group, true);
		var nwPosition = Coordinates.northwestPosition(group);
		var sePosition = Coordinates.southeastPosition(group);
		var seOffset = Coordinates.southeastOffset(group, true);

		Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);

		if (!Drag.isDragging) {
			if (group.threshold > 0) {
				var distance = group.initialWindowCoordinate.distance(
						mouse);
				if (distance < group.threshold) return true;
			} else if (group.thresholdY > 0) {
				var deltaY = Math.abs(group.initialWindowCoordinate.y - mouse.y);
				if (deltaY < group.thresholdY) return true;
			} else if (group.thresholdX > 0) {
				var deltaX = Math.abs(group.initialWindowCoordinate.x - mouse.x);
				if (deltaX < group.thresholdX) return true;
			}

			Drag.isDragging = true;
			group.style["zIndex"] = Drag.BIG_Z_INDEX;
			group.style["opacity"] = 0.75;
		}

		// TODO: need better constraint API
		var adjusted = mouse.constrain(group.mouseMin, group.mouseMax);
		nwPosition = nwPosition.plus(adjusted.minus(group.dragCoordinate));
		nwPosition.reposition(group);
		group.dragCoordinate = adjusted;

		// once dragging has started, the position of the group
		// relative to the mouse should stay fixed.  They can get out
		// of sync if the DOM is manipulated while dragging, so we
		// correct the error here
		//
		// TODO: what we really want to do is find the offset from
		// our corner to the mouse coordinate and adjust to keep it
		// the same
		
		// changed to be recursive/use absolute offset for corrections
		var offsetBefore = Coordinates.northwestOffset(group, true);
		group.onDrag(nwPosition, sePosition, nwOffset, seOffset);
		var offsetAfter = Coordinates.northwestOffset(group, true);

		if (!offsetBefore.equals(offsetAfter)) {
			var errorDelta = offsetBefore.minus(offsetAfter);
			nwPosition = Coordinates.northwestPosition(group).plus(errorDelta);
			nwPosition.reposition(group);
		}

		return false;
	},

	onMouseUp : function(event) {
		event = Drag.fixEvent(event);
		var group = Drag.group;

		var mouse = event.windowCoordinate;
		var nwOffset = Coordinates.northwestOffset(group, true);
		var nwPosition = Coordinates.northwestPosition(group);
		var sePosition = Coordinates.southeastPosition(group);
		var seOffset = Coordinates.southeastOffset(group, true);

		document.onmousemove = null;
		document.onmouseup   = null;
		group.onDragEnd(nwPosition, sePosition, nwOffset, seOffset);

		if (Drag.isDragging) {
			// restoring zIndex before opacity avoids visual flicker in Firefox
			group.style["zIndex"] = group.originalZIndex;
			group.style["opacity"] = group.originalOpacity;
		}

		Drag.group = null;
		Drag.isDragging = false;

		return false;
	},

	fixEvent : function(event) {
		if (typeof event == 'undefined') event = window.event;
		Coordinates.fixEvent(event);

		return event;
	}
};

var Coordinates = {
	ORIGIN : new Coordinate(0, 0),

	northwestPosition : function(element) {
		var x = parseInt(element.style.left);
		var y = parseInt(element.style.top);

		return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y);
	},

	southeastPosition : function(element) {
		return Coordinates.northwestPosition(element).plus(
				new Coordinate(element.offsetWidth, element.offsetHeight));
	},

	northwestOffset : function(element, isRecursive) {
		var offset = new Coordinate(element.offsetLeft, element.offsetTop);

		if (!isRecursive) return offset;

		var parent = element.offsetParent;
		while (parent) {
			offset = offset.plus(
					new Coordinate(parent.offsetLeft, parent.offsetTop));
			parent = parent.offsetParent;
		}
		return offset;
	},

	southeastOffset : function(element, isRecursive) {
		return Coordinates.northwestOffset(element, isRecursive).plus(
				new Coordinate(element.offsetWidth, element.offsetHeight));
	},

	fixEvent : function(event) {
		event.windowCoordinate = new Coordinate(event.clientX, event.clientY);
	}
};

function Coordinate(x, y) {
	this.x = x;
	this.y = y;
}

Coordinate.prototype.toString = function() {
	return "(" + this.x + "," + this.y + ")";
}

Coordinate.prototype.plus = function(that) {
	return new Coordinate(this.x + that.x, this.y + that.y);
}

Coordinate.prototype.minus = function(that) {
	return new Coordinate(this.x - that.x, this.y - that.y);
}

Coordinate.prototype.distance = function(that) {
	var deltaX = this.x - that.x;
	var deltaY = this.y - that.y;

	return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
}

Coordinate.prototype.max = function(that) {
	var x = Math.max(this.x, that.x);
	var y = Math.max(this.y, that.y);
	return new Coordinate(x, y);
}

Coordinate.prototype.constrain = function(min, max) {
	if (min.x > max.x || min.y > max.y) return this;

	var x = this.x;
	var y = this.y;

	if (min.x != null) x = Math.max(x, min.x);
	if (max.x != null) x = Math.min(x, max.x);
	if (min.y != null) y = Math.max(y, min.y);
	if (max.y != null) y = Math.min(y, max.y);

	return new Coordinate(x, y);
}

Coordinate.prototype.reposition = function(element) {
	element.style["top"] = this.y + "px";
	element.style["left"] = this.x + "px";
}

Coordinate.prototype.equals = function(that) {
	if (this == that) return true;
	if (!that || that == null) return false;

	return this.x == that.x && this.y == that.y;
}

// returns true of this point is inside specified box
Coordinate.prototype.inside = function(northwest, southeast) {
	if ((this.x >= northwest.x) && (this.x <= southeast.x) &&
		(this.y >= northwest.y) && (this.y <= southeast.y)) {
		
		return true;
	}
	return false;
}
