//************************************** TYPE UTILS ******************************

function inheritFrom(aThis, aParent)
{
  var excp;

  for (var property in aParent)
  {
    try
    {
      aThis[property] = aParent[property];
    }
    catch(excp)
    {
    }
  }
}

function allProperties(obj)
{
	var ret="";
	for (var property in obj)
	{
		var current=property;
		
		ret+=current+" = "+obj[current]+"<br>";
	}
	
	return ret;
}

function isDefined(v)
{
	if (v)
	{
		
		return true;
	}

	if (v==null || v==0 || v==false )
	{
		return true;
	}
	
	/* //purposefully disabled, i guess
	if ( isNaN(val))
	{
		return true;
	}
	*/
	
	

	return false;
	
}

//************************************** MATH *****************************

function signum(num)
{
	if (num<0)
		return -1;
	else if (num>0)
		return 1;
		
	return 0;
}


function extentOutsideBox(div, x1, y1, x2, y2)
{
	var divX1=parseInt(div.style.left);
	var divY1=parseInt(div.style.top);
	var divWidth=parseInt(div.style.width);
	var divHeight=parseInt(div.style.height);

	return extentOutsideBoxCoords(
		divX1, divY1, 
		divX1+divWidth,divY1+divHeight,
		x1,y1,x2,y2);
}

//Returns a 2 dim array, with a[0] being the x oob and a[1] being the y oob
function extentOutsideBoxCoords(
	x1, y1, x2, y2,
	boxX1, boxY1, boxX2, boxY2)
{
	var ret=new Array(2);

	if (x1<boxX1)
	{
		ret[0]=x1-boxX1;
	} else if (x2>boxX2)
	{
		ret[0]=x2-boxX2;
	} else
	{
		ret[0]=0;
	}
	
	if (y1<boxY1)
	{
		ret[1]=y1-boxY1;
	} else if (y2>boxY2)
	{
		ret[1]=y2-boxY2;
	} else
	{
		ret[1]=0;
	}
	
	return ret;
}

function abs(num)
{
	if (num<0)
	{
		return num*-1;
	}
	
	return num;
}


//*************************************** LINKEDLIST **********************


function Comparator()
{
	this.compare=baseCompare;
}

function baseCompare(o1, o2)
{
	return 1;
}

function NumComparator()
{
	this.compare=numCompare;
}
NumComparator.prototype=new Comparator();

function numCompare(o1, o2)
{
	return signum(o1- o2);
}

function Node(data)
{
	this.data=data;
	this.next=null;
}

function nodeToString()
{
	return this.data.toString();
}
Node.prototype.toString=nodeToString;

function LinkedList()
{

}
LinkedList.Root=null;
LinkedList.End=null;

// create the object method
function add(data)
{
	var node=new Node(data);	

	if (this.Root==null)
	{
		this.Root=node;
		node.next=null;
		this.End=node;	
		return;
	}
	
	this.End.next=node;	
	this.End=node;
	
}
LinkedList.prototype.add=add;

//Only removes the first such instance
function removeObject(data)
{
	var iterator=new Iterator(this);
	
	var previous=null;
	while (iterator.hasNext())
	{
		var current=iterator.nextNode();
		
		if (current.data==data)
		{
			if (previous==null)
			{
				this.Root=current.next;
			} else
			{
				previous.next=current.next;
			}
		}
		
		previous=current;
	}
}
LinkedList.prototype.removeObject=removeObject;

function listToString()
{
	var ret="[";
	
	var iterator=new Iterator(this);
	
	while (iterator.hasNext())
	{
		var current=iterator.next();
		
		ret+=current.toString();
		
		if (iterator.hasNext())
		{
			ret+=", ";
		}
	}
	/*
	var current=this.Root;
	
	while (current!=null)
	{
		ret+=current;
		
		current=current.next;
		
		if (current!=null)
		{
			ret+=", ";
		}
	}
	*/
	ret+="]";
	return ret;

//	return "list toString";
}
LinkedList.prototype.toString=listToString;

function Iterator(list)
{
	this.list=list;
	this.currentNode=list.Root;
}

function hasNext()
{
	if (this.currentNode!=null)
	{
		return true;
	}
	
	return false;
}
Iterator.prototype.hasNext=hasNext;

function next()
{
	var ret=this.currentNode;
	
	this.currentNode=ret.next;
	
	return ret.data;
}
Iterator.prototype.next=next;

function nextNode()
{
	var ret=this.currentNode;
	
	this.currentNode=ret.next;
	
	return ret;
}
Iterator.prototype.nextNode=nextNode;

//*********************************** LOG ************************************
var logSizeLimit=2000;

var logDiv=null;
function log(str)
{

	if (logDiv==null)
	{
		logDiv=document.getElementById("log");
	}
	
	log2(str, document.getElementById("log"));
}

function log2(str, div)
{
	currentString=div.innerHTML;
	
	if (currentString.length>logSizeLimit)
	{
		currentString=currentString.substring(0,logSizeLimit);
	}
	
	div.innerHTML=str+"<br>"+currentString;
}
//************************************ DOM *********************************

function findPos(obj)
{
 var left = !!obj.offsetLeft ? obj.offsetLeft : 0;
 var top = !!obj.offsetTop ? obj.offsetTop : 0;

 while(obj = obj.offsetParent)
 {
  left += !!obj.offsetLeft ? obj.offsetLeft : 0;
  top += !!obj.offsetTop ? obj.offsetTop : 0;
 }

 return{x:left, y:top};
}

//returns percent position
function mousePositionWithinElement(element)
{
	var o_element=document.getElementById(element);
	
	var pos=findPos(o_element);
	
	var yPer=(lastMouseY-pos.y)/o_element.offsetHeight;
	var xPer=(lastMouseX-pos.x)/o_element.offsetWidth;
	
	return {xPer:xPer,yPer:yPer};
}

function moveDiv(div, hor, vert)
{
	if (vert!=0)
	{
		var x=divY(div);
		div.style.top=x+vert;
	} 
	
	if (hor!=0)
	{
		var x=divX(div);
		div.style.left=x+hor;
	}
}

function divWidth(div)
{
	return parseInt(div.style.width);
}

function divHeight(div)
{
	return parseInt(div.style.height);
}

function divFarX(div)
{
	return divX(div)+divWidth(div);
}

function divFarY(div)
{
	return divY(div)+divHeight(div);
}

function divX(div)
{
	return parseInt(div.style.left);
}

function divY(div)
{
	return parseInt(div.style.top);
}

function getter(anything)
{
	return function () {return anything;};
}


function getDivIfString(div)
{
	if (typeof(div)=='string')
	{
		div=document.getElementById(div);
	}
	
	return div;
}

function divWidthFunction(div)
{
	div=getDivIfString(div);

	return function () {return divWidth(div)};

}

function divHeightFunction(div)
{
	div=getDivIfString(div);

	return function () {return divHeight(div)};
}

function divXFunction(div)
{
	div=getDivIfString(div);

	return function () {return divX(div)};

}

function divYFunction(div)
{
	div=getDivIfString(div);

	return function () {return divY(div)};
}


function setDiv(div, left, top)
{
	div.style.top=top;
	div.style.left=left;
	//div.style.top=parseInt(top);
	//div.style.left=parseInt(left);
}

function addDiv(parentId, newDivId) {
  var newDiv = document.createElement("div");
  var div_parent=document.getElementById(parentId);
  
  
  if(newDivId)
    newDiv.id = newDivId;
    /*
  if(style)
    for(var x in style)
      r.style[x] = style[x];
  if(children)
    for(var i = 0; i < children.length; ++i)
      r.appendChild(children);
      */
  div_parent.appendChild(newDiv);
  return newDiv;
}

/*
function addDiv(parent, id, style, children) {
  var r = document.createElement("div");
  if(id)
    r.id = id;
  if(style)
    for(var x in style)
      r.style[x] = style[x];
  if(children)
    for(var i = 0; i < children.length; ++i)
      r.appendChild(children);
  parent.appendChild(r);
  return r;
}
*/

//********************************** UI EVENTS ******************************

document.onmousemove=getMouseCoordinates;
document.onkeydown=onkeypress;

var lastMouseX=null;
var lastMouseY=null;
var keyListeners=new LinkedList();

function getMouseCoordinates(event)
{
	ev = event || window.event;
	lastMouseX=
		ev.clientX;
		//ev.pageX;
	
	lastMouseY=
		ev.clientY;
		//ev.pageY;
}

function getMouseX()
{
	return lastMouseX;
}

function getMouseY()
{
	return lastMouseY;
}

function listenToKeys(listener)
{
	keyListeners.add(listener);
}

function onkeypress(e)
{
	var e=window.event || e;
 	log("CharCode value: "+e.keyCode);
 	log("Character: "+String.fromCharCode(e.keyCode));

 	var it=new Iterator(keyListeners);
	
	while (it.hasNext())
	{
		var obj=it.next();
		obj.onkeypress(e);
	}
}

//********************************* STRING *******************************
function getProperties(str)
{
	var ret=new Array();
	var key="";
	var val="";
	var inKey=true;
	for (var index=0;index<str.length;index++)
	{
		var ch=str[index];
		
		if (ch=='=')
		{
			inKey=false;
		} else if (ch==',')
		{
			ret[key]=val;			
			inKey=true;
			key="";
			val="";
		} else
		{
			if (inKey)
			{
				key+=ch;
			} else
			{
				val+=ch;
			}
		}
	}
	
	ret[key]=val;
	
	return ret;
}
