/**
 * @author grillion
 */
var undefined;

function delegate(obj, method) {
	return function(){ return method.apply(obj, arguments ); }
}

function trim(value) {
	if( !isString(value) ) return value; //we can only trim strings
	var temp = value;
	var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
	if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
	var obj = / +/g;
	temp = temp.replace(obj, " ");
	if (temp == " ") { temp = ""; }
	return temp;
}
function isString(a) { return typeof a == 'string'; }
function isEmpty(str){ str = trim(str); return (str == null) || (str == 'null') || (str.length == 0) || (str == undefined);}

var _;

function mojoInit(){ _ = new MOJO(); }

function MOJO(){
	
	this.px = 'px';
	
		//client wide storage
	this.highZ 	  	= 100;
	this.documentW 	= 0;
	this.documentH 	= 0;
	this.viewportW 	= 0;
	this.viewportH 	= 0;
	this.scrollLeft = 0;
	this.scrollTop 	= 0;
	this.dragObject = null;		//currently dragging object

	this.ns  = !!(document.layers && typeof document.classes != dhNull);
	
		//client wide events
	window.onresize = delegate( this, this.__resize);
	window.onscroll = delegate( this, this.__scroll);	
	document.onmousemove = delegate( this, this.__mouseMove);
	document.onmouseup = delegate( this, this.__mouseUp);
	
	this.__resize();
	this.__scroll();
}
MOJO.prototype = {	
	__resize : function  (){
	 	var pageSizes = this.getPageSize();
		this.documentW = pageSizes[0];
		this.documentH = pageSizes[1];
		this.viewportW = pageSizes[2];
		this.viewportH = pageSizes[3];
		
		if( this._resize ) this._resize();
	},
		
	__scroll : function  (){
		var scrollValues = this.getPageScroll();
		this.scrollLeft = scrollValues[0]
		this.scrollTop = scrollValues[1];
	},

	/*
	 * Mouse Events
	 */
	__mouseMove : function __mouseMove(e){
		//build event object
		if(!e) e = window.event;
		e = this.fixEventObject(e);

		if( this.dragObject ) {
			this.__drag(e);
			this.stopEvent(e.event);							//stop normal event operation, c'mon! we're draggin
		}
		
		if( this.resizeObject && this.resizeFunc ) {
			this.resizeFunc( e );
			this.stopEvent(e.event);
		}
	},
	
	__mouseUp : function __mouseUp( e ){
		
		//build event object
		if(!e) e = window.event;
		e = this.fixEventObject(e);
		
		//stop drag if we're dragging anything
		if( this.dragObject )	this.stopDrag(e);
		
		//stop resizing anything if we're resizing.
		if( this.resizeObject ) this.endResize( e );
		
	},
	
	//drag innerworkings
	//incomming call to move our currently dragging object
	__drag : function __drag( e ){
		
		var newX = (e.mouseX - this.dragObject.dsMx) + this.dragObject.dsx;
		var newY = (e.mouseY - this.dragObject.dsMy) + this.dragObject.dsy;
		
		//captureToParent
		if( this.dragObject.captureToParent ) {
			newX = Math.min( Math.max( newX , 0 ), this.innerW( this.dragObject.offsetParent ) - this.dragObject.dsw );
			newY = Math.min( Math.max( newY , 0 ), this.h( this.dragObject.offsetParent ) - this.dragObject.dsh );
		}

		if( this.dragObject.__drag ){
			this.dragObject.__drag( e );
		}

		this.x( this.dragObject, newX );
		this.y( this.dragObject, newY );
	},
	
	//-----------------------------------------------
	// Info functions
	//-----------------------------------------------
	/**	
	 * Core code from - quirksmode.com
	 * Edit for Firefox by pHaez
	 * 
	 * getPageSize()
	 * returns array with page width, height and window width, height
	 */	
	getPageSize : function getPageSize(){
		
		var xScroll, yScroll;

		var docBody = (document.compatMode && document.compatMode.toLowerCase() != "backcompat")?
			document.documentElement : (document.body || null);
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = window.innerWidth + window.scrollMaxX;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (docBody.scrollHeight > docBody.offsetHeight){ // all but Explorer Mac
			xScroll = docBody.scrollWidth;
			yScroll = docBody.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}
		
		var windowWidth, windowHeight;

		if (self.innerHeight) {	// all except Explorer
			if(document.documentElement.clientWidth){
				windowWidth = document.documentElement.clientWidth; 
			} else {
				windowWidth = self.innerWidth;
			}
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	
		
		// for small pages with total height less then height of the viewport
		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}

		// for small pages with total width less then width of the viewport
		if(xScroll < windowWidth){	
			pageWidth = xScroll;		
		} else {
			pageWidth = windowWidth;
		}

		var arrayPageSize = new Array(pageWidth, pageHeight, windowWidth, windowHeight) 
		return arrayPageSize;
	},
	
	/**	
	 * Core code from - quirksmode.com
	 * getPageScroll()
	 * returns array with x,y page scroll values.
	 */	
	getPageScroll : function getPageScroll(){
	
		var xScroll, yScroll;
	
		if (self.pageYOffset) {
			yScroll = self.pageYOffset;
			xScroll = self.pageXOffset;
		} else if (document.documentElement && document.documentElement.scrollTop){	 // Explorer 6 Strict
			yScroll = document.documentElement.scrollTop;
			xScroll = document.documentElement.scrollLeft;
		} else if (document.body) {// all other Explorers
			yScroll = document.body.scrollTop;
			xScroll = document.body.scrollLeft;	
		}
	
		var arrayPageScroll = new Array(xScroll, yScroll) 
		return arrayPageScroll;
	},
	
	//******************************************************************************************
	//Element Creation Destruction and finding
	//******************************************************************************************
	create : function create(tag, id, classname, parent, innerhtml, value){

		var newElem = document.createElement(tag);
		
		//if it's a form element we need to set the values now
		if (tag == 'input') {
			newElem.value = value;
		}
		
		//set all other values provided
		if (id) 
			newElem.id = id;
		if (classname) 
			newElem.className = classname;
		if (parent) 
			_.setParent(newElem, parent);
		if (innerhtml) 
			newElem.innerHTML = innerhtml;
		
		return newElem;
	},
	
	get : function get(elementName){
		if (document.getElementById) {
			return document.getElementById(elementName);
		} else {
			if (document.all) { 		return document.all[elementName]; }
			else if (document.layers) { return document.layers[elementName]; }
		}
	},
	
	destroy : function destroy( element ){	
		try{ element.parentNode.removeChild( element );
		} catch ( ex ){ return false;
		} return true;
	},
	
	setParent : function setParent( elementA, elementB ){
		if(elementA.parentNode){
			var elementRef = elementA.parentNode.removeChild(elementA);
			elementB.appendChild(elementRef);
		} else {
			elementB.appendChild(elementA);
		}
	},
	
	//******************************************************************************************
	//Event Managing
	//******************************************************************************************
	//event 
	fixEventObject : function fixEventObject(e) {
		if(!e) e = window.event;
		
		newEvent = {};
		
		try{
			newEvent.targetElement = e.srcElement || e.target;
			newEvent.targetID = newEvent.targetElement.id || newEvent.targetElement.name;
			
			//update general mouse movement stats
			//this will only use scrollX and y ifthey are filled
			newEvent.mouseX = (e.pageX || e.clientX + this.scrollLeft);
			newEvent.mouseY = (e.pageY || e.clientY + this.scrollTop);
	
			//mouse placement overan element
			newEvent.offsetX  = (e.offsetX || e.layerX);
			newEvent.offsetY = (e.offsetY || e.layerY);
			
			newEvent.event = e;
		}
		catch ( ex ){ alert(dumpObj(ex)); }
		return newEvent; //don;t know if this is necessary
	},

	//
	stopEvent : function  stopEvent(e){
		if(!e) e = window.event;
		try{ e.cancelBubble = true;	}catch(ex){ }
		try{ e.stopPropagation();	}catch(ex){ }
		try{ e.preventDefault();	}catch(ex){ }
		try{ e.returnValue = false;	}catch(ex){ }
    },
	
	
	

	

	//******************************************************************************************
	//Element Sizing
	//****************************************************************************************** 
	w : function w(element, value){
		if( value ){
			value = parseInt(value);
			value = Math.max( value, ( element.minx || 0) );
			try{ element.style.width = value + this.px; }
			catch (e){}
		}
		try{
			return (this.ns) ?
				(element) ?	element.clip.width : 0
				: (element) ? (element.offsetWidth || element.style.pixelWidth || element.style.width || 0) : 0;
		} catch (e){ } return false;
	},
	
	sH :function(element, value){
		value = Math.max( value, ( element.miny || 0));
		try{ element.style.height = value + this.px; }
		catch (e){}
	},
			
	h : function h(element, value){
		if( value ){
			value = parseInt(value);
			value = Math.max( value, ( element.miny || 0));
			try{ element.style.height = value + this.px; }
			catch (e){}
		}
		try{
			return (this.ns) ?
				(element) ?	element.clip.height : 0
				: (element) ? (element.offsetHeight || element.style.pixelHeight || element.style.height || 0) : 0;
		} catch (e){ } return false;
	},
	
		
	/**	gets the client(inner) width of an element ( scrollbars not included )
	 * getvW
	 * @param {element} element		dom element to calculate for
	 * returns number
	 */
	innerW : function innerW( element ) {
		try{
		return (element.clientWidth || 0);
		} catch (e) {} return false;
	},


	/**	gets the client(inner) height of an element ( scrollbars not included )
	 * getH
	 * @param {element} element		dom element to calculate for.
	 * returns number
	 */
	innerH : function innerH( element ) {
		try{
		return (element.clientHeight || 0);
		} catch (e) {} return false;
	},

	//******************************************************************************************
	//Element Positioning
	//******************************************************************************************
	x : function x(element, value){
		if (value != undefined ) {
			return parseInt(element.style.left = parseInt(value) + this.px) || 0;	}
		else {
			return parseInt(element.offsetLeft) || 0;
		}
	},
	
	y : function y(element, value){
		if (value != undefined ) {
			return parseInt(element.style.top = parseInt(value) + this.px) || 0;	}
		else {
			return parseInt(element.offsetTop) || 0;
		}
	},
	
	z : function z(element, value){
		//console.log(arguments);
		element.style.zIndex = value;
	},

	/**	gets the absolute X position of any element to the origin
	 * getAbsX
	 * @param {element} element		dom element to calculate for.
	 * returns number
	 */
	absX : function absX( element ){
		var curleft = 0;
		var tmpobj = element;
		if (tmpobj.offsetParent) {
			while (tmpobj.offsetParent) {
					curleft += tmpobj.offsetLeft;
					if( tmpobj.tagName.toLowerCase() == 'div' && element != tmpobj ){ curleft -= tmpobj.scrollLeft; }
					tmpobj = tmpobj.offsetParent;
				}
		}
		else if (tmpobj.x) { curleft += tmpobj.x; }
		return curleft;
	},
	
	/**	gets the absolute Y position of any element to the origin
	 * getAbsX
	 * @param {element} element		dom element to calculate for.
	 * returns number
	 */
	absY : function absY(element){
		var curtop = 0;
		var tmpobj = element;
		if (tmpobj.offsetParent) {
			while (tmpobj.offsetParent) {
				curtop += tmpobj.offsetTop
				if( tmpobj.tagName.toLowerCase() == 'div' && element != tmpobj ){ curtop -= tmpobj.scrollTop; }
				tmpobj = tmpobj.offsetParent;
			}
		}
		else if (tmpobj.y) { curtop += tmpobj.y; }
		return curtop;
	},
	
	
	/**	gets the absolute X position of any element to the origin
	 * getAbsX
	 * @param {element} element		dom element to calculate for.
	 * returns number
	 */
	relativeAbsX : function relativeAbsX( elementA, elementB ){
		var curleft = 0;
		var tmpobj = elementA;
		if (tmpobj.offsetParent) {
			while (tmpobj.offsetParent && tmpobj != elementB) {
					curleft += tmpobj.offsetLeft;
					if( tmpobj.tagName.toLowerCase() == 'div' && elementA != tmpobj ){ curleft -= tmpobj.scrollLeft; }
					tmpobj = tmpobj.offsetParent;
				}
		}
		else if (tmpobj.x) { curleft += tmpobj.x; }
		return curleft;
	},

	/**	gets the absolute Y position of any element to the origin
	 * getAbsX
	 * @param {element} element		dom element to calculate for.
	 * returns number
	 */
	relativeAbsY : function relativeAbsY(elementA, elementB){
		var curtop = 0;
		var tmpobj = elementA;
		if (tmpobj.offsetParent) {
			while (tmpobj.offsetParent && tmpobj != elementB) {
				curtop += tmpobj.offsetTop
				if( tmpobj.tagName.toLowerCase() == 'div' && elementA != tmpobj ){ curtop -= tmpobj.scrollTop; }
				tmpobj = tmpobj.offsetParent;
			}
		}
		else if (tmpobj.y) { curtop += tmpobj.y; }
		return curtop;
	},
	
	//******************************************************************************************
	//Graphical Procedures
	//******************************************************************************************
	alpha :	function alpha(element, percent){
		try{
			if(typeof element.style.MozOpacity != undefined){	element.style.MozOpacity = percent / 100; }
			if(typeof element.style.filter != undefined){	element.style.filter = "Alpha(opacity="+parseInt(percent)+")";	}
			element.style.opacity = percent / 100;
			} catch (e) { return false; }
	},
	
	showBlock : function showBlock	( element ){	element.style.display = 'block';		},
	hideBlock : function hideBlock	( element ){	element.style.display = 'none';			},
	
	visible : 	function visible	( element ){	element.style.visibility = 'visible';	},
	invisible : function invisible	( element ){	element.style.visibility = 'hidden';	},
	
	show : function show		( element ){	this.showBlock(element);	this.visible(element);	},
	hide : function hide		( element ){	this.hideBlock(element);	this.invisible(element);	},
	
	fadeIn : function fadeIn( element ){
		setTimeout( function(){	_.alpha( element, 10 );	},	0);
		setTimeout( function(){ _.alpha( element, 20 ); },	50);
		setTimeout( function(){ _.alpha( element, 30 ); },	100);
		setTimeout( function(){ _.alpha( element, 40 ); },	150);
		setTimeout( function(){	_.alpha( element, 50 );	},	200);
		setTimeout( function(){ _.alpha( element, 60 ); },	250);
		setTimeout( function(){ _.alpha( element, 70 ); },	300);
		setTimeout( function(){ _.alpha( element, 80 ); },	350);
		setTimeout( function(){ _.alpha( element, 90 ); },	400);
		setTimeout( function(){ _.alpha( element, 100 );}, 	450);
	},
	
	fadeOut : function fadeOut( element ){
		setTimeout( function(){ _.alpha( element, 90 ); },	0);
		setTimeout( function(){ _.alpha( element, 80 ); },	50);
		setTimeout( function(){ _.alpha( element, 70 ); },	100);
		setTimeout( function(){	_.alpha( element, 60 );	},	150);
		setTimeout( function(){ _.alpha( element, 50 ); },	200);
		setTimeout( function(){ _.alpha( element, 40 ); },	250);
		setTimeout( function(){ _.alpha( element, 30 ); },	300);
		setTimeout( function(){ _.alpha( element, 20 ); },	350);
		setTimeout( function(){ _.alpha( element, 10 );}, 	400);
		setTimeout( function(){ _.alpha( element, 0 );}, 	450);
	},
	
	bringToFront : function bringToFront( element ){
		
		this.highZ++;
		this.z( element, this.highZ );
		
	},
	
	/* ***********************************************************************************
	 * Drag control (public)
	 *********************************************************************************** */
	setDraggable : function ( element, draggable ){
		element.draggable = 		draggable;		
		if( !draggable ) {
			element.isDragHandle = null;
		}
	},
	addDragHandle : function ( HandleHTMLElement, DestHTMLElement ){
		HandleHTMLElement.isDragHandle = DestHTMLElement;
		//this.setDraggable( DestHTMLElement , true );

		//HandleHTMLElement.onmousedown = function(e){ dD.startDrag( e, HandleHTMLElement ); };
	},
	
	startDrag : function( e, element, captureToParent ) {
		//build event object
		if(!e) e = window.event;
		e = this.fixEventObject(e);
		
		//are we a drag handle for something else or are we a drag object
		if( element.isDragHandle ){
			//isDragHandle will be missing or an object which should be dragged

			//switch the drag object
			element = element.isDragHandle;
			
		//we're supposed to be draggable?
		} else {

			//make sure the event was given directly to this element
			if( e.targetElement != element ) return true;
			

		}

		//now set the values
		element.oX = e.offsetX;
		element.oY = e.offsetY;
		
		element.dsMx = e.mouseX;
		element.dsMy = e.mouseY
		
		element.dsx = this.x(element);
		element.dsy = this.y(element);
		
		element.dsw = this.w(element);
		element.dsh = this.h(element);
		
		element.captureToParent = 	captureToParent;
		
		this.dragObject = element;
				
		if ( this.dragObject.__startDrag ) this.dragObject.__startDrag();
		
		this.bringToFront(this.dragObject);
		
		this.stopEvent(e.event);
	},
	startDragFromHandle : function ( e, dragHandle ){
		
	},
	stopDrag : function( ) {

		if( this.dragObject ){
			
			//can't stop now, already let go of the mouse.
			//so ignore return values
			if ( this.dragObject.__stopDrag ) this.dragObject.__stopDrag();

			//clear drag info
			this.dragObject.oX = 	null;
			this.dragObject.oY = 	null;
			this.dragObject.dsMx = 	null;
			this.dragObject.dsMy = 	null;
			this.dragObject.dsx = 	null;
			this.dragObject.dsy = 	null;
			this.dragObject.dsw = 	null;
			this.dragObject.dsh = 	null;
			
			this.dragObject = null;
 		}
	},
	
		
	/* ***********************************************************************************
	 * Resize control (public)
	 *********************************************************************************** */
	//relative parent will remove the ABS X and Y or the relative parent
	//from the result before setting
	startResize : function ( element, resizeEdge ){

		var world = this.get('canvas');
		
		//store the beginnings
		element.rsx = this.relativeAbsX( element, world );	//resize start x
		element.rsy = this.relativeAbsY( element, world );	//resize start y
		element.rsw = this.w(element);
		element.rsh = this.h(element);

		//create a nicely sizeable layer
		var resizeAnimation = this.create('div', 'resizeAnimation', 'a bgLB a50 highZ', world );

		if( element.minw ) resizeAnimation.minw = element.minw;
		if( element.minh ) resizeAnimation.minh = element.minh;

		//prepare our animation
		this.x( resizeAnimation , element.rsx );							//X
		this.y( resizeAnimation , element.rsy );							//Y
		this.w( resizeAnimation , element.rsw );							//W
		this.h( resizeAnimation , element.rsh );							//H
		
		//bring to front
		this.resizeEdge = 		resizeEdge;
		this.resizeFunc = 		this.getResizeFunc( resizeEdge );		//get the appropiate perPixelFunction
		this.resizeAnimation = 	this.get('resizeAnimation');
		this.resizeObject = 	element;
		
		_.bringToFront( this.resizeObject );
	},
	
	//return a proper per pexel resizer for a specific edge	
	getResizeFunc : function ( resizeEdge ) {
		switch( resizeEdge ) {
			case edges.topLeft :
				return resizeFunctions.topLeft;
				break;
			case edges.top :
				return resizeFunctions.top;
				break;
			case edges.topRight :
				return resizeFunctions.topRight;
				break;
			case edges.right :
				return resizeFunctions.right;
				break;
			case edges.bottomRight :
				return resizeFunctions.bottomRight;
				break;
			case edges.bottom :
				return resizeFunctions.bottom;
				break;
			case edges.bottomLeft :
				return resizeFunctions.bottomLeft;
				break;
			case edges.left:
				return resizeFunctions.left;
				break;
		}
		return false;	//thats an error!
	},
	
	endResize : function ( e ){
		var resizeAnimation = this.get('resizeAnimation');
/*
		X( this.resizeObject , getRelativeAbsX( resizeAnimation, windowspace ) - getAbsX( this.resizeObject.offsetParent ) );		//X
		Y( this.resizeObject , getRelativeAbsY( resizeAnimation, windowspace ) - getAbsY( this.resizeObject.offsetParent ) );		//Y
		W( this.resizeObject , getW( resizeAnimation ) )	;								//W
		H( this.resizeObject , getH( resizeAnimation ) );									//H
*/	
		resizeFunctions.applyResize( resizeAnimation, this.resizeObject, _.get('canvas'), this.resizeEdge );
		
		this.resizeEdge = null;
		this.resizeFunc = null;
		
		//if the element has a resize handler call it.
		if( this.resizeObject.__resize ) this.resizeObject.__resize();
		
		this.destroy( resizeAnimation );
		this.resizeObject = null;
	},
	
	/**
	 * Build a tween and run it immediately
	 * 
	 *	quickTween(
	 *		htmlElement,
	 *		{
	 *			destX : this.dragStartX,
	 *			destY : this.dragStartY,
	 *			diffX : this.dragStartX - absX,
	 *			diffY : this.dragStartY - absY,
	 *			destAlpha : 100,
	 *			diffAlpha : 0
	 *		},
	 *		EasingFunction
	 *		50,
	 *		5,
	 *		function(){
	 *			if( dhNode.cloneParent ) dhNode.cloneParent.setOpacity(100);
	 *			dhNode.destroy();
	 *		}
	 *	);
	 * 
	 * @param {Object} htmlElement			An associated DOM element (or node)
	 * @param {Object} destinationValues	An array of values to specify the actions
	 * @param {Object} frameDelay			ms between frames
	 * @param {Object} frameCount			the number of frames
	 * @param {Object} whenDone				a function to run when the animation is compelted
	 */
	quickTween : function quickTween( htmlElement, destinationValues, easingFunc, frameDelay, frameCount, whenDone){
		
		//get the built frame handler
		var tweenFunc = this.compileTween( htmlElement, destinationValues, easingFunc);
		
		//start running the animation
		this.runTween( htmlElement, tweenFunc, frameDelay, frameCount, whenDone );
	},

	/**
	 * Begin Running a Tween
	 * @param {Object} htmlElement
	 * @param {Object} tweenFunc
	 * @param {Object} frameDelay
	 * @param {Object} frameCount
	 * @param {Object} whenDone
	 */
	runTween : function( htmlElement, tweenFunc, frameDelay, totalFrames, whenDone ){
		
		if( htmlElement.tween ) clearTimeout( htmlElement.tween );
		
		//save the timer and start the first Frame
		htmlElement.tween = setTimeout( 
		
			//the first frame on a new thread
			function () { 
				_.runTweenFrame( 
					htmlElement,
					tweenFunc,
					frameDelay,
					totalFrames,
					0,				//start frame
					whenDone
				 );
			},
			0 
		);	//end of timeout defintion
	},

	/**
	 * Run a siungle tween frame
	 * @param {Object} htmlElement
	 * @param {Object} tweenFunc
	 * @param {Object} frameDelay
	 * @param {Object} frameCount
	 * @param {Object} currentFrame
	 * @param {Object} whenDone
	 */
	runTweenFrame: function ( htmlElement, tweenFunc, frameDelay, totalFrames, currentFrame, whenDone  ){
		
		//run the frame
		tweenFunc( htmlElement, currentFrame, totalFrames );
		
		//only make the next frame if it's not the end
		if (currentFrame < totalFrames) {
			
			
			
			//next frame.
			htmlElement.tween = setTimeout(function(){
				_.runTweenFrame(htmlElement, tweenFunc, frameDelay, totalFrames, currentFrame + 1, whenDone);
			}, frameDelay);
		}
		else {
			whenDone();
		}
			
	},

	compileTween : function ( htmlElement, differences, easingFunc ) {
		
		//A single frame hanlder, compiled. Initial value is an empty function to prevent errors
		var finalFunction = function () {};
		
		if (differences.diffX)
			finalFunction = this.tweenXFrame( finalFunction, easingFunc, differences.startX, differences.diffX );
			
		if (differences.diffY)
			finalFunction = this.tweenYFrame( finalFunction, easingFunc, differences.startY, differences.diffY );
			
		if (differences.diffW) 
			finalFunction = this.tweenWFrame( finalFunction, easingFunc, differences.startW, differences.diffW );
			
		if (differences.diffH) 
			finalFunction = this.tweenHFrame( finalFunction, easingFunc, differences.startH, differences.diffH );
			
		if (differences.diffAlpha) 
			finalFunction = this.tweenAlphaFrame( finalFunction, easingFunc, differences.startAlpha, differences.diffAlpha );
					
		return finalFunction;
	},
	
	/**
	 * A single frame for X value tween
	 * @param {Object} htmlElement
	 * @param {Object} currentFrame
	 * @param {Object} startX
	 * @param {Object} diffX
	 * @param {Object} frameCount
	 * @param {Object} nextStep
	 */
	tweenXFrame : function( nextStep, easing, start, diff ) {
		//the function to animate an X frame to spec
		return function ( htmlElement, currentFrame, totalFrames ) {
			_.x( htmlElement, easing(currentFrame, start, diff, totalFrames ) );
			nextStep( htmlElement, currentFrame, totalFrames, start, diff );
		};
	},
	
	tweenYFrame : function( nextStep, easing, start, diff ){
		//the function to animate an X frame to spec
		return function ( htmlElement, currentFrame, totalFrames ) {
			_.y( htmlElement, easing(currentFrame, start, diff, totalFrames ) );
			nextStep( htmlElement, currentFrame, totalFrames, start, diff );
		};
	},
	
	tweenWFrame : function( nextStep, easing, start, diff ){
		//the function to animate an X frame to spec
		return function ( htmlElement, currentFrame, totalFrames ) {
			_.w( htmlElement, easing(currentFrame, start, diff, totalFrames ) );
			nextStep( htmlElement, currentFrame, totalFrames, start, diff );
		};
	},
	
	tweenHFrame : function( nextStep, easing, start, diff ){
		//the function to animate an X frame to spec
		return function ( htmlElement, currentFrame, totalFrames ) {
			_.h( htmlElement, easing(currentFrame, start, diff, totalFrames ) );
			nextStep( htmlElement, currentFrame, totalFrames, start, diff );
		};
	},
	
	tweenAlphaFrame : function( nextStep, easing, start, diff ){
		//the function to animate an X frame to spec
		return function ( htmlElement, currentFrame, totalFrames ) {
			_.alpha( htmlElement, easing(currentFrame, start, diff, totalFrames ) );
			nextStep( htmlElement, currentFrame, totalFrames, start, diff );
		};
	}
}

//an editable list of resizeedges
//the numbers works like so...
//
// for the automatic lines   if (srcEdge + destEdge) % 2 then it's a horizontal or vertical line
// if not, then it's cap to side or side to cap.
//
// side to cap example
//  _
// |_|---:      []--[]  side to side
//       |
//      |_|
//
//
//
var edges = {

topLeft : 1,
top : 2,
topRight : 3,
right : 5,
bottomRight : 6,
bottom : 8,
bottomLeft : 7,
left: 9

};
/*
* These functinos will resize an element for each pixel moved.
*/
var resizeFunctions = {

	topLeft: function(e){
		//figure it out
		var diffX = e.mouseX - _.resizeObject.rsx;
		var diffY = e.mouseY - _.resizeObject.rsy;
		
		var newX = _.resizeObject.rsx + diffX;
		var newY = _.resizeObject.rsy + diffY;
		var newW = Math.max((_.resizeObject.minw || 0), _.resizeObject.rsw - diffX);
		var newH = Math.max((_.resizeObject.minh || 0), _.resizeObject.rsh - diffY);
		
		//show the results
		_.w(_.resizeAnimation, newW);
		_.h(_.resizeAnimation, newH);
		
		if (_.resizeAnimation.minh) 
			if (newH <= _.resizeAnimation.minh) 
				newY = _.resizeObject.rsy + (_.resizeObject.rsh - _.resizeObject.minh);
		
		if (_.resizeAnimation.minw) 
			if (newW <= _.resizeAnimation.minw) 
				newX = _.resizeObject.rsx + (_.resizeObject.rsw - _.resizeObject.minw);
		
		
		_.x(_.resizeAnimation, newX);
		_.y(_.resizeAnimation, newY);
		
		
	},
	top: function(e){
		//figure it out
		var diffY = e.mouseY - _.resizeObject.rsy;
		var newY = _.resizeObject.rsy + diffY;
		var newH = Math.max((_.resizeObject.minh || 0), _.resizeObject.rsh - diffY);
		
		//show the results
		_.h(_.resizeAnimation, newH);
		
		//minimum size fixes for wandering resize animations
		//this will adjust the y value so it doesn't drift
		if (_.resizeAnimation.minh) 
			if (newH <= _.resizeAnimation.minh) 
				newY = _.resizeObject.rsy + (_.resizeObject.rsh - _.resizeObject.minh);
		
		_.y(_.resizeAnimation, newY);
		
	},
	topRight: function(e){
		var diffY = e.mouseY - _.resizeObject.rsy;
		
		var newY = _.resizeObject.rsy + diffY;
		var newH = Math.max((_.resizeObject.minh || 0), _.resizeObject.rsh - diffY);
		var newW = e.mouseX - _.resizeObject.rsx;
		
		if (newW < _.resizeObject.minw) 
			newW = _.resizeObject.minw;
		
		if (_.resizeAnimation.minh) 
			if (newH <= _.resizeAnimation.minh) 
				newY = _.resizeObject.rsy + (_.resizeObject.rsh - _.resizeObject.minh);
		
		//show the results
		_.w(_.resizeAnimation, newW);
		_.h(_.resizeAnimation, newH);
		_.y(_.resizeAnimation, newY);
		
	},
	right: function(e){
		//figure it out
		var newW = e.mouseX - _.resizeObject.rsx;
		
		if (newW < _.resizeObject.minw) 
			newW = _.resizeObject.minw;
		
		//show the results
		_.w(_.resizeAnimation, newW);
	},
	bottomRight: function(e){
		var newW = e.mouseX - _.resizeObject.rsx;
		var newH = e.mouseY - _.resizeObject.rsy;
		
		if (newW < _.resizeObject.minw) 
			newW = _.resizeObject.minw;
		if (newH < _.resizeObject.minh) 
			newH = _.resizeObject.minh;
		
		//show the results
		_.w(_.resizeAnimation, newW);
		_.h(_.resizeAnimation, newH);
	},
	bottom: function(e){
		//figure it out
		var newH = e.mouseY - _.resizeObject.rsy;
		
		if (newH < _.resizeObject.minh) 
			newH = _.resizeObject.minh;
		
		//show the results
		_.h(_.resizeAnimation, newH);
	},
	bottomLeft: function(e){
		//figure it out
		var diffX = e.mouseX - _.resizeObject.rsx;
		var newX = _.resizeObject.rsx + diffX;
		var newW = Math.max((_.resizeObject.minw || 0), _.resizeObject.rsw - diffX);
		var newH = e.mouseY - _.resizeObject.rsy;
		
		if (newH < _.resizeObject.minh) 
			newH = _.resizeObject.minh;
		
		if (_.resizeAnimation.minw) 
			if (newW <= _.resizeAnimation.minw) 
				newX = _.resizeObject.rsx + (_.resizeObject.rsw - _.resizeObject.minw);
		
		//show the results
		_.w(_.resizeAnimation, newW);
		_.h(_.resizeAnimation, newH);
		_.x(_.resizeAnimation, newX);
		
	},
	left: function(e){
		var diffX = e.mouseX - _.resizeObject.rsx;
		var newX = _.resizeObject.rsx + diffX;
		var newW = Math.max((_.resizeObject.minw || 0), _.resizeObject.rsw - diffX);
		
		_.w(_.resizeAnimation, newW);
		
		if (_.resizeAnimation.minw) 
			if (newW <= _.resizeAnimation.minw) 
				newX = _.resizeObject.rsx + (_.resizeObject.rsw - _.resizeObject.minw);
		
		_.x(_.resizeAnimation, newX);
		
	},
	
	/* one last function to apply the results */
	applyResize: function(srcElement, destElement, relativeTo, resizeEdge){
	
		var relativeOffsetX = 0;
		var relativeOffsetY = 0;
		
		if (relativeTo) {
			relativeOffsetX = _.absX(relativeTo);
			relativeOffsetY = _.absY(relativeTo);
		}
		
		switch (resizeEdge) {
		
			case edges.topLeft:
				
				//show the results
				_.w(destElement, _.w(srcElement));
				_.h(destElement, _.h(srcElement));
				_.x(destElement, _.absX(srcElement) - relativeOffsetX);
				_.y(destElement, _.absY(srcElement) - relativeOffsetY);
				
				break;
			case edges.top:
				
				_.h(destElement, _.h(srcElement));
				_.y(destElement, _.absY(srcElement) - relativeOffsetY);
				
				break;
			case edges.topRight:
				
				_.y(destElement, _.absY(srcElement) - relativeOffsetY);
				_.w(destElement, _.w(srcElement));
				_.h(destElement, _.h(srcElement));
				
				break;
			case edges.right:
				
				_.w(destElement, _.w(srcElement));
				
				break;
			case edges.bottomRight:
				
				_.w(destElement, _.w(srcElement));
				_.h(destElement, _.h(srcElement));
				
				break;
			case edges.bottom:
				
				_.h(destElement, _.h(srcElement));
				
				break;
			case edges.bottomLeft:
				
				_.w(destElement, _.w(srcElement));
				_.h(destElement, _.h(srcElement));
				_.x(destElement, _.absX(srcElement) - relativeOffsetX);
				
				break;
			case edges.left:
				
				_.x(destElement, _.absX(srcElement) - relativeOffsetX);
				_.w(destElement, _.w(srcElement));
				
				break;
		}
	}
}

/**
 * The Equations below are used for tweening purposes and were found at 
 * http://www.gizma.com/easing/
 * Easing Equations by Robert Penner
 * 
 * @param {Object} t - Current Time
 * @param {Object} b - Start Value
 * @param {Object} c - Change in Value
 * @param {Object} d - Duration
 */

//simple linear tweening - no easing, no acceleration
    Math.linearTween = function (t, b, c, d) {
    	return c*t/d + b;
    };

// quadratic easing in - accelerating from zero velocity
    Math.easeInQuad = function (t, b, c, d) {
    	t /= d;
    	return c*t*t + b;
    };

// quadratic easing out - decelerating to zero velocity
    Math.easeOutQuad = function (t, b, c, d) {
    	t /= d;
    	return -c * t*(t-2) + b;
    };

// quadratic easing in/out - acceleration until halfway, then deceleration
    Math.easeInOutQuad = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return c/2*t*t + b;
    	t--;
    	return -c/2 * (t*(t-2) - 1) + b;
    };

// cubic easing in - accelerating from zero velocity
    Math.easeInCubic = function (t, b, c, d) {
    	t /= d;
    	return c*t*t*t + b;
    };

// cubic easing out - decelerating to zero velocity
    Math.easeOutCubic = function (t, b, c, d) {
    	t /= d;
    	t--;
    	return c*(t*t*t + 1) + b;
    };

// cubic easing in/out - acceleration until halfway, then deceleration
    Math.easeInOutCubic = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return c/2*t*t*t + b;
    	t -= 2;
    	return c/2*(t*t*t + 2) + b;
    };
    	
// quartic easing in - accelerating from zero velocity
    Math.easeInQuart = function (t, b, c, d) {
    	t /= d;
    	return c*t*t*t*t + b;
    };

// quartic easing out - decelerating to zero velocity
    Math.easeOutQuart = function (t, b, c, d) {
    	t /= d;
    	t--;
    	return -c * (t*t*t*t - 1) + b;
    };

// quartic easing in/out - acceleration until halfway, then deceleration
    Math.easeInOutQuart = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return c/2*t*t*t*t + b;
    	t -= 2;
    	return -c/2 * (t*t*t*t - 2) + b;
    };

// quintic easing in - accelerating from zero velocity
    Math.easeInQuint = function (t, b, c, d) {
    	t /= d;
    	return c*t*t*t*t*t + b;
    };

// quintic easing out - decelerating to zero velocity
    Math.easeOutQuint = function (t, b, c, d) {
    	t /= d;
    	t--;
    	return c*(t*t*t*t*t + 1) + b;
    };

// quintic easing in/out - acceleration until halfway, then deceleration
    Math.easeInOutQuint = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return c/2*t*t*t*t*t + b;
    	t -= 2;
    	return c/2*(t*t*t*t*t + 2) + b;
    };
    		
// sinusoidal easing in - accelerating from zero velocity
    Math.easeInSine = function (t, b, c, d) {
    	return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
    };

// sinusoidal easing out - decelerating to zero velocity
    Math.easeOutSine = function (t, b, c, d) {
    	return c * Math.sin(t/d * (Math.PI/2)) + b;
    };

// sinusoidal easing in/out - accelerating until halfway, then decelerating
    Math.easeInOutSine = function (t, b, c, d) {
    	return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
    };

// exponential easing in - accelerating from zero velocity
    Math.easeInExpo = function (t, b, c, d) {
    	return c * Math.pow( 2, 10 * (t/d - 1) ) + b;
    };

// exponential easing out - decelerating to zero velocity
    Math.easeOutExpo = function (t, b, c, d) {
    	return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;
    };

// exponential easing in/out - accelerating until halfway, then decelerating
    Math.easeInOutExpo = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;
    	t--;
    	return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;
    };

// circular easing in - accelerating from zero velocity
    Math.easeInCirc = function (t, b, c, d) {
    	t /= d;
    	return -c * (Math.sqrt(1 - t*t) - 1) + b;
    };

// circular easing out - decelerating to zero velocity
    Math.easeOutCirc = function (t, b, c, d) {
    	t /= d;
    	t--;
    	return c * Math.sqrt(1 - t*t) + b;
    };

// circular easing in/out - acceleration until halfway, then deceleration
    Math.easeInOutCirc = function (t, b, c, d) {
    	t /= d/2;
    	if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
    	t -= 2;
    	return c/2 * (Math.sqrt(1 - t*t) + 1) + b;
    };