(
	function MOJO_TWEEN(){
		
		/**
		 * 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 {DOMElement} htmlElement
		 * @param {Function} tweenFunc
		 * @param {Integer} frameDelay
		 * @param {Integer} frameCount
		 * @param {Integer} currentFrame
		 * @param {Function} 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( htmlElement );
			}
				
		};
	
		_.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;
		};

		/**
		 * Stop an elements tween dead in it's tracks
		 * @param {Object} htmlElement
		 */		
		_.stopTween = function ( htmlElement ) {
			if( htmlElement.tween )clearTimeout(htmlElement.tween);
		};
		
		//-----------------------------------------------------
		// Extra features
		//-----------------------------------------------------
		_.fadeIn = function ( element, easing, whenDone ) {
			_.runTween( element, 
				function ( htmlElement, currentFrame, totalFrames ) {
					_.alpha( htmlElement, easing(currentFrame, 0, 100, totalFrames ) );
				},
				10,
				10,
				whenDone );
		};
		
		
		_.fadeOut = function ( element, easing ) {
			_.runTween( element, 
				function ( htmlElement, currentFrame, totalFrames ) {
					_.alpha( htmlElement, easing(currentFrame, 100, -100, totalFrames ) );
				},
				10,
				10,
				whenDone );
		};
		
		//-----------------------------------------------------
		// Tween Frame Functions
		//-----------------------------------------------------
		
		/**
		 * 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 );
			};
		};
	
			
		/**
		 * 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;
		    };	
	}
)();
