//------------------------------------------------------------------------
// Name: LW_Animation_Controller
// Desc: Stores the animations parameters.
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: LW_Animation_Controller()
// Desc: Class constructor.
//------------------------------------------------------------------------
function LW_Animation_Controller()
{

	// Store the default values.
	this.move              = { to:    new LW_Structure_Point(),
	                           by:    new LW_Structure_Point(),
							   ease:  LW_Animation.EASE_NONE };

	this.width             = { to:    null,
	                           by:    null,
							   ease:  LW_Animation.EASE_NONE };

	this.height            = { to:    null,
	                           by:    null,
							   ease:  LW_Animation.EASE_NONE };

	this.opacity           = { to:    null,
	                           by:    null,
							   ease:  LW_Animation.EASE_NONE };

	this.background_color  = { to:    null,
	                           by:    null };

	this.border_color      = { to:    null,
	                           by:    null };

	this.text_color        = { to:    null,
	                           by:    null };

	this.delay             = 0;

}


//------------------------------------------------------------------------
// Name: LW_Animation
// Desc: Holds a single animation for an element and the necessary methods
//       to play it out.
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Class Constants
//------------------------------------------------------------------------
LW_Animation.EASE_NONE          = 0;
LW_Animation.EASE_IN            = 1;
LW_Animation.EASE_OUT           = 2;
LW_Animation.EASE_BOTH          = 3;
LW_Animation.STRONG_EASE_IN     = 4;
LW_Animation.STRONG_EASE_OUT    = 5;
LW_Animation.STRONG_EASE_BOTH   = 6;
LW_Animation.BACK_EASE_IN       = 7;
LW_Animation.BACK_EASE_OUT      = 8;
LW_Animation.BACK_EASE_BOTH     = 9;
LW_Animation.BOUNCE_EASE_IN     = 10;
LW_Animation.BOUNCE_EASE_OUT    = 11;
LW_Animation.BOUNCE_EASE_BOTH   = 12;
LW_Animation.ELASTIC_EASE_IN    = 13;
LW_Animation.ELASTIC_EASE_OUT   = 14;
LW_Animation.ELASTIC_EASE_BOTH  = 15;


//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: LW_Animation()
// Desc: Class constructor.
//------------------------------------------------------------------------
function LW_Animation( element, run_time )
{

	// Store the values.
	this.element               = element;
	this.element_properties    = Object();
	this.run_time              = run_time;
	this.controller            = new LW_Animation_Controller();

	this.onStart               = null;
	this.onInterval            = null;
	this.onAdvance             = null;
	this.onEventFrame          = null;
	this.onStop                = null;
	this.onFinish              = null;

	// These values are used by the LW_Animation system internally.
	this.status                = false;
	this.event_frames          = new Array();

	this.start_time            = null;
	this.current_time          = null;

	this.begin_width           = null;
	this.begin_height          = null;
	this.begin_pos             = null;
	this.begin_back_color      = null;
	this.begin_border_color    = null;
	this.begin_text_color      = null;
	this.begin_opacity         = null;

	this.offset_width          = null;
	this.offset_height         = null;
	this.offset_pos            = new LW_Structure_Point();
	this.offset_back_color     = new LW_Structure_Color();
	this.offset_border_color   = new LW_Structure_Color();
	this.offset_text_color     = new LW_Structure_Color();
	this.offset_opacity        = null;

	this.desired_width         = null;
	this.desired_height        = null;
	this.desired_pos           = new LW_Structure_Point();
	this.desired_back_color    = null;
	this.desired_border_color  = null;
	this.desired_text_color    = null;
	this.desired_opacity       = null;

}


//------------------------------------------------------------------------
// Name: addEventFrame()
// Desc: Adds an event frame to the animation.
//------------------------------------------------------------------------
LW_Animation.prototype.addEventFrame = function( time_offset, event_func )
{

  // Add the new event frame to the animation.
	this.event_frames.push( { time_offset: time_offset, event_func: event_func, triggered: false } );

}


//------------------------------------------------------------------------
// Name: start()
// Desc: Starts the animation.
//------------------------------------------------------------------------
LW_Animation.prototype.start = function()
{

	// Don't start if in the middle of playing.
	if ( this.status ) return;

	// Set the necessary values.
	this.status                      = true;
	this.start_time                  = new Date();

	// Width.
	if ( this.controller.width.to != null || this.controller.width.by != null )
	{

		// Get the element's width.
		this.begin_width = LW_DOM_Library.getWidth( this.element );

		// Get the desired width and offset width.
		this.desired_width  = (this.controller.width.to != null) ? (this.controller.width.to) : (this.begin_width + this.controller.width.by);
		this.offset_width   = this.desired_width - this.begin_width;

	}

	// Height.
	if ( this.controller.height.to != null || this.controller.height.by != null )
	{

		// Get the element's height.
		this.begin_height = LW_DOM_Library.getHeight( this.element );

		// Get the desired height and offset height.
		this.desired_height  = (this.controller.height.to != null) ? (this.controller.height.to) : (this.begin_height + this.controller.height.by);
		this.offset_height   = this.desired_height - this.begin_height;

	}

	// Position.
	if ( this.controller.move.to.X != null || this.controller.move.to.Y != null || this.controller.move.by.X != null || this.controller.move.by.Y != null )
	{

		// Get the element's position.
		this.begin_pos = LW_DOM_Library.getXY( this.element );

		// Get the desired X position and X offset position.
		if ( this.controller.move.to.X != null || this.controller.move.by.X != null )
		{

			this.desired_pos.X  = (this.controller.move.to.X != null) ? (this.controller.move.to.X) : (this.begin_pos.X + this.controller.move.by.X);
			this.offset_pos.X   = this.desired_pos.X - this.begin_pos.X;

		}

		// Get the desired Y position and Y offset position.
		if ( this.controller.move.to.Y != null || this.controller.move.by.Y != null )
		{

			this.desired_pos.Y  = (this.controller.move.to.Y != null) ? (this.controller.move.to.Y) : (this.begin_pos.Y + this.controller.move.by.Y);
			this.offset_pos.Y   = this.desired_pos.Y - this.begin_pos.Y;

		}

	}

	// Opacity.
	if ( this.controller.opacity.to != null || this.controller.opacity.by != null )
	{
		
		// Get the element's opacity.
		this.begin_opacity = LW_DOM_Library.getStyle( this.element, "opacity" );
		
		// Get the desired opacity and offset opacity.
		this.desired_opacity  = (this.controller.opacity.to != null) ? (this.controller.opacity.to) : (this.begin_opacity + this.controller.opacity.by);
		this.offset_opacity   = this.desired_opacity - this.begin_opacity;
		
	}

	// Background Color.
	if ( this.controller.background_color.to != null || this.controller.background_color.by != null )
	{

		// Get the element's background color.
		this.begin_back_color   = new LW_Structure_Color( LW_DOM_Library.getStyle( this.element, "backgroundColor" ).substring( 1 ) );
		this.desired_back_color = new LW_Structure_Color( this.begin_back_color.toHexString() );

		// Get the desired red value and offset value.
		if ( this.controller.background_color.to.R != null || this.controller.background_color.by.R != null )
		{

			this.desired_back_color.R  = (this.controller.background_color.to.R != null) ? (this.controller.background_color.to.R) : (this.begin_back_color.R + this.controller.background_color.by.R);
			this.offset_back_color.R   = this.desired_back_color.R - this.begin_back_color.R;

		}

		// Get the desired green value and offset value.
		if ( this.controller.background_color.to.G != null || this.controller.background_color.by.G != null )
		{

			this.desired_back_color.G  = (this.controller.background_color.to.G != null) ? (this.controller.background_color.to.G) : (this.begin_back_color.G + this.controller.background_color.by.G);
			this.offset_back_color.G   = this.desired_back_color.G - this.begin_back_color.G;

		}

		// Get the desired blue value and offset value.
		if ( this.controller.background_color.to.B != null || this.controller.background_color.by.B != null )
		{

			this.desired_back_color.B  = (this.controller.background_color.to.B != null) ? (this.controller.background_color.to.B) : (this.begin_back_color.B + this.controller.background_color.by.B);
			this.offset_back_color.B   = this.desired_back_color.B - this.begin_back_color.B;

		}

	}

	// Border Color.
	if ( this.controller.border_color.to != null || this.controller.border_color.by != null )
	{

		// Get the element's border color.
		this.begin_border_color   = new LW_Structure_Color( LW_DOM_Library.getStyle( this.element, "borderColor" ).substring( 1 ) );
		this.desired_border_color = new LW_Structure_Color( this.begin_border_color.toHexString() );

		// Get the desired red value and offset value.
		if ( this.controller.border_color.to.R != null || this.controller.border_color.by.R != null )
		{

			this.desired_border_color.R  = (this.controller.border_color.to.R != null) ? (this.controller.border_color.to.R) : (this.begin_border_color.R + this.controller.border_color.by.R);
			this.offset_border_color.R   = this.desired_border_color.R - this.begin_border_color.R;

		}

		// Get the desired green value and offset value.
		if ( this.controller.border_color.to.G != null || this.controller.border_color.by.G != null )
		{

			this.desired_border_color.G  = (this.controller.border_color.to.G != null) ? (this.controller.border_color.to.G) : (this.begin_border_color.G + this.controller.border_color.by.G);
			this.offset_border_color.G   = this.desired_border_color.G - this.begin_border_color.G;

		}

		// Get the desired blue value and offset value.
		if ( this.controller.border_color.to.B != null || this.controller.border_color.by.B != null )
		{

			this.desired_border_color.B  = (this.controller.border_color.to.B != null) ? (this.controller.border_color.to.B) : (this.begin_border_color.B + this.controller.border_color.by.B);
			this.offset_border_color.B   = this.desired_border_color.B - this.begin_border_color.B;

		}

	}

	// Text Color.
	if ( this.controller.text_color.to != null || this.controller.text_color.by != null )
	{

		// Get the element's text color.
		this.begin_text_color   = new LW_Structure_Color( LW_DOM_Library.getStyle( this.element, "color" ).substring( 1 ) );
		this.desired_text_color = new LW_Structure_Color( this.begin_text_color.toHexString() );

		// Get the desired red value and offset value.
		if ( this.controller.text_color.to.R != null || this.controller.text_color.by.R != null )
		{

			this.desired_text_color.R  = (this.controller.text_color.to.R != null) ? (this.controller.text_color.to.R) : (this.begin_text_color.R + this.controller.text_color.by.R);
			this.offset_text_color.R   = this.desired_text_color.R - this.begin_text_color.R;

		}

		// Get the desired green value and offset value.
		if ( this.controller.text_color.to.G != null || this.controller.text_color.by.G != null )
		{

			this.desired_text_color.G  = (this.controller.text_color.to.G != null) ? (this.controller.text_color.to.G) : (this.begin_text_color.G + this.controller.text_color.by.G);
			this.offset_text_color.G   = this.desired_text_color.G - this.begin_text_color.G;

		}

		// Get the desired blue value and offset value.
		if ( this.controller.text_color.to.B != null || this.controller.text_color.by.B != null )
		{

			this.desired_text_color.B  = (this.controller.text_color.to.B != null) ? (this.controller.text_color.to.B) : (this.begin_text_color.B + this.controller.text_color.by.B);
			this.offset_text_color.B   = this.desired_text_color.B - this.begin_text_color.B;

		}

	}

	// Call the onStart function if there is any.
	if ( this.onStart != null )
	  this.onStart( this );

	// Call the incrementAnimation function. It will start the animation.
	LW_Animation_Manager.addAnimation( this );

}


//------------------------------------------------------------------------
// Name: advanceWidth()
// Desc: Increments the width.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceWidth = function()
{

  // Get the new width.
	var new_width = LW_Animation.tweenValue( this.controller.width.ease, (this.current_time - this.controller.delay), this.run_time, this.begin_width, this.offset_width );

	// Set the new width on the element.
	LW_DOM_Library.setStyle( this.element, "width", new_width + "px" );

}


//------------------------------------------------------------------------
// Name: advanceHeight()
// Desc: Increments the height.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceHeight = function()
{

	// Get the new height.
	var new_height = LW_Animation.tweenValue( this.controller.height.ease, (this.current_time - this.controller.delay), this.run_time, this.begin_height, this.offset_height );

	// Set the new height on the element.
	LW_DOM_Library.setStyle( this.element, "height", new_height + "px" );

}


//------------------------------------------------------------------------
// Name: advancePosition()
// Desc: Increments the position.
//------------------------------------------------------------------------
LW_Animation.prototype.advancePosition = function()
{

	// Updating X position?
	if ( this.offset_pos.X != null )
	{

		// Get the new X position.
		var new_X_pos = LW_Animation.tweenValue( this.controller.move.ease, (this.current_time - this.controller.delay), this.run_time, this.begin_pos.X, this.offset_pos.X );

		// Set the new X position on the element.
		LW_DOM_Library.setX( this.element, new_X_pos );

	}  // End if updating X position.

	// Updating Y position?
	if ( this.offset_pos.Y != null )
	{

		// Get the new Y position.
		var new_Y_pos = LW_Animation.tweenValue( this.controller.move.ease, (this.current_time - this.controller.delay), this.run_time, this.begin_pos.Y, this.offset_pos.Y );

		// Set the new Y position on the element.
		LW_DOM_Library.setY( this.element, new_Y_pos );

	}  // End if updating Y position.

}


//------------------------------------------------------------------------
// Name: advanceOpacity()
// Desc: Increments the opacity.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceOpacity = function()
{

	// Get the new opacity.
	var new_opacity = (LW_Animation.tweenValue( this.controller.opacity.ease, (this.current_time - this.controller.delay), this.run_time, (this.begin_opacity * 100), (this.offset_opacity * 100) ) / 100);
	
	// Set the new opacity on the element.
	LW_DOM_Library.setStyle( this.element, "opacity", new_opacity );

}


//------------------------------------------------------------------------
// Name: advanceBackgroundColor()
// Desc: Increments the background color.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceBackgroundColor = function()
{

	// Set the new back color as the beginning color.
	var new_back_color = new LW_Structure_Color( this.begin_back_color.toHexString() );

	// Updating red value?
	if ( this.offset_back_color.R != null )
	{

		// Get the new background color.
	  new_back_color.R = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_back_color.R, this.offset_back_color.R ) );
	}  // End if updating red value.

	// Updating green value?
	if ( this.offset_back_color.G != null )
	{

		// Get the new background color.
	  new_back_color.G = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_back_color.G, this.offset_back_color.G ) );

	}  // End if updating red value.

	// Updating blue value?
	if ( this.offset_back_color.B != null )
	{

		// Get the new background color.
	  new_back_color.B = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_back_color.B, this.offset_back_color.B ) );

	}  // End if updating red value.

	// Set the new background color on the element.
	LW_DOM_Library.setStyle( this.element, "backgroundColor", "#" + new_back_color.toHexString() );

}


//------------------------------------------------------------------------
// Name: advanceBorderColor()
// Desc: Increments the border color.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceBorderColor = function()
{

	// Set the new border color as the beginning color.
	var new_border_color = new LW_Structure_Color( this.begin_border_color.toHexString() );

	// Updating red value?
	if ( this.offset_border_color.R != null )
	{

		// Get the new border color.
	  new_border_color.R = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_border_color.R, this.offset_border_color.R ) );

	}  // End if updating red value.

	// Updating green value?
	if ( this.offset_back_color.G != null )
	{

		// Get the new border color.
	  new_border_color.G = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_border_color.G, this.offset_border_color.G ) );

	}  // End if updating red value.

	// Updating blue value?
	if ( this.offset_border_color.B != null )
	{

		// Get the new border color.
	  new_border_color.B = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_border_color.B, this.offset_border_color.B ) );

	}  // End if updating red value.

	// Set the new border color on the element.
	LW_DOM_Library.setStyle( this.element, "borderColor", "#" + new_border_color.toHexString() );

}


//------------------------------------------------------------------------
// Name: advanceTextColor()
// Desc: Increments the text color.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceTextColor = function()
{

	// Set the new text color as the beginning color.
	var new_text_color = new LW_Structure_Color( this.begin_text_color.toHexString() );

	// Updating red value?
	if ( this.offset_text_color.R != null )
	{

		// Get the new text color.
	  new_text_color.R = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_text_color.R, this.offset_text_color.R ) );

	}  // End if updating red value.

	// Updating green value?
	if ( this.offset_text_color.G != null )
	{

		// Get the new text color.
	  new_text_color.G = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_text_color.G, this.offset_text_color.G ) );

	}  // End if updating red value.

	// Updating blue value?
	if ( this.offset_text_color.B != null )
	{

		// Get the new text color.
	  new_text_color.B = Math.ceil( LW_Animation.tweenValue( LW_Animation.EASE_NONE, (this.current_time - this.controller.delay), this.run_time, this.begin_text_color.B, this.offset_text_color.B ) );

	}  // End if updating red value.

	// Set the new text color on the element.
	LW_DOM_Library.setStyle( this.element, "color", "#" + new_text_color.toHexString() );

}


//------------------------------------------------------------------------
// Name: advanceFrame()
// Desc: Carries out the next frame of the animation.
//------------------------------------------------------------------------
LW_Animation.prototype.advanceFrame = function()
{

	// If the animation is stopped, return false.
	if ( !this.status )
	  return false;

	// Update the current time.
	this.current_time = new Date() - this.start_time;

	// Only start incrementing if we have passed the delay time.
	if ( this.current_time > this.controller.delay )
	{
		
		// Animating width?
		if ( this.desired_width != null )
			this.advanceWidth();

		// Animating height?
		if ( this.desired_height != null )
			this.advanceHeight();

		// Animating position?
		if ( this.desired_pos.X != null || this.desired_pos.Y != null )
			this.advancePosition();

		// Animating opacity?
		if ( this.desired_opacity != null )
			this.advanceOpacity();

		// Animating background color?
		if ( this.desired_back_color != null )
			this.advanceBackgroundColor();

		// Animating border color?
		if ( this.desired_border_color != null )
			this.advanceBorderColor();

		// Animating text color?
		if ( this.desired_text_color != null )
			this.advanceTextColor();
			
		// Loop through each event frame.
		var event_triggered = false;
		for ( var i = 0; i < this.event_frames.length; i++ )
		{

			// If it is time (or passed time) to trigger the event, do so.
			if ( !this.event_frames[i].triggered && this.current_time >= this.controller.delay + this.event_frames[i].time_offset )
			{

			  this.event_frames[i].event_func( this );
				this.event_frames[i].triggered = true;
				event_triggered = true;

			}

		}  // Next event frame.

		// If an event was triggered and there's an onEventFrame function, call it.
		if ( event_triggered && this.onEventFrame != null )
		  this.onEventFrame( this );

		// Call the onAdvance function if there is any.
		if ( this.onAdvance != null )
			this.onAdvance( this );

	}  // End if delay is over.

	// Call the onAdvance function if there is any.
	if ( this.onInterval != null )
		this.onInterval( this );

	// Should we continue processing?
	if ( this.current_time < this.run_time + this.controller.delay )
		return true;
	else
		return false;

}


//------------------------------------------------------------------------
// Name: stop()
// Desc: Stops the animation where it currently is. Does not finish it.
//------------------------------------------------------------------------
LW_Animation.prototype.stop = function()
{

	// Set the animation's status to not playing.
	this.status = false;

	// Call the onStop function if there is any.
	if ( this.onStop != null )
	  this.onStop( this );

}


//------------------------------------------------------------------------
// Name: finish()
// Desc: Does the required clean up of the animation.
//------------------------------------------------------------------------
LW_Animation.prototype.finish = function()
{

	// Set the animation's status to not playing.
	this.status = false;
	
	// Get rid of any animation errors. Set the desired values on the elements.
	if ( this.desired_width        ) LW_DOM_Library.setStyle( this.element, "width", this.desired_width + "px" );
	if ( this.desired_height       ) LW_DOM_Library.setStyle( this.element, "height", this.desired_height + "px" );

	if ( this.desired_pos.X        ) LW_DOM_Library.setX( this.element, this.desired_pos.X );
	if ( this.desired_pos.Y        ) LW_DOM_Library.setY( this.element, this.desired_pos.Y );

	if ( this.desired_opacity      ) LW_DOM_Library.setStyle( this.element, "opacity", this.desired_opacity );

	if ( this.desired_back_color   ) LW_DOM_Library.setStyle( this.element, "backgroundColor", "#" + this.desired_back_color.toHexString() );

	if ( this.desired_border_color ) LW_DOM_Library.setStyle( this.element, "borderColor", "#" + this.desired_border_color.toHexString() );

	if ( this.desired_text_color   ) LW_DOM_Library.setStyle( this.element, "color", "#" + this.desired_text_color.toHexString() );

	// Call the onFinish function if there is any.
	if ( this.onFinish != null )
	  this.onFinish( this );

}


//------------------------------------------------------------------------
// Public Static Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: tweenValue()
// Desc: Tweens the value according to user input.
//------------------------------------------------------------------------
LW_Animation.tweenValue = function( ease_type, current_time, duration, begin_val, change_val )
{

	// Linear tween.
	if ( ease_type == LW_Animation.EASE_NONE )
	{

	  return change_val * (current_time / duration) + begin_val;

	}
	// Ease in.
	else if ( ease_type == LW_Animation.EASE_IN  )
	{

	  return change_val * (current_time /= duration) * current_time + begin_val;

	}
	// Ease out.
	else if ( ease_type == LW_Animation.EASE_OUT )
	{

	  return -change_val * (current_time /= duration) * (current_time - 2) + begin_val;

	}
	// Ease in and out.
	else if ( ease_type == LW_Animation.EASE_BOTH )
	{

	  if ( (current_time /= duration / 2) < 1 ) return change_val / 2 * current_time * current_time + begin_val;

		return -change_val / 2 * ((--current_time) * (current_time - 2) - 1) + begin_val;

	}
	// Strong ease in.
	else if ( ease_type == LW_Animation.STRONG_EASE_IN )
	{

	  return change_val * (current_time /= duration) * current_time * current_time * current_time + begin_val;

	}
	// Strong ease out.
	else if ( ease_type == LW_Animation.STRONG_EASE_OUT )
	{

	  return -change_val * ((current_time = current_time / duration - 1) * current_time * current_time * current_time - 1) + begin_val;

	}
	// Strong ease in and out.
	else if ( ease_type == LW_Animation.STRONG_EASE_BOTH )
	{

	  if ((current_time /= duration / 2) < 1) return change_val / 2 * current_time * current_time * current_time * current_time + begin_val;

		return -change_val / 2 * ((current_time -= 2) * current_time * current_time * current_time - 2) + begin_val;

	}
	// Back ease in.
	else if ( ease_type == LW_Animation.BACK_EASE_IN )
	{

	  return change_val * (current_time /= duration) * current_time * (2.70158 * current_time - 1.70158) + begin_val;

	}
	// Back ease out.
	else if ( ease_type == LW_Animation.BACK_EASE_OUT )
	{

	  return change_val * ((current_time = current_time / duration - 1) * current_time * (2.70158 * current_time + 1.70158) + 1) + begin_val;

	}
	// Back ease in and out.
	else if ( ease_type == LW_Animation.BACK_EASE_BOTH )
	{

	  if ((current_time /= duration / 2) < 1) return change_val / 2 * (current_time * current_time * (3.5949095 * current_time - 2.5949095)) + begin_val;

		return change_val / 2 * ((current_time -= 2) * current_time * (3.5949095 * current_time + 2.5949095) + 2) + begin_val;

	}
	// Bounce ease in.
	else if ( ease_type == LW_Animation.BOUNCE_EASE_IN )
	{

		current_time = duration - current_time;

	  if ( (current_time /= duration) < (1 / 2.75) )
		  return change_val - (change_val * (7.5625 * current_time * current_time)) + begin_val;
		else if ( current_time < (2 / 2.75 ) )
		  return change_val - (change_val * (7.5625 * (current_time -= (1.5 / 2.75)) * current_time + 0.75)) + begin_val;
		else if ( current_time < (2.5 / 2.75) )
		  return change_val - (change_val * (7.5625 * (current_time -= (2.25 / 2.75)) * current_time + 0.9375)) + begin_val;
		else
		  return change_val - (change_val * (7.5625 * (current_time -= (2.625 / 2.75)) * current_time + 0.984375)) + begin_val;

	}
	// Bounce ease out.
	else if ( ease_type == LW_Animation.BOUNCE_EASE_OUT )
	{

	  if ( (current_time /= duration) < (1 / 2.75) )
		  return change_val * (7.5625 * current_time * current_time) + begin_val;
		else if ( current_time < (2 / 2.75 ) )
		  return change_val * (7.5625 * (current_time -= (1.5 / 2.75)) * current_time + 0.75) + begin_val;
		else if ( current_time < (2.5 / 2.75) )
		  return change_val * (7.5625 * (current_time -= (2.25 / 2.75)) * current_time + 0.9375) + begin_val;
		else
		  return change_val * (7.5625 * (current_time -= (2.625 / 2.75)) * current_time + 0.984375) + begin_val;

	}
	// Bounce ease in and out.
	else if ( ease_type == LW_Animation.BOUNCE_EASE_BOTH )
	{

		if ( current_time < duration / 2 )
		{

			current_time = duration - (current_time * 2);

			if ( (current_time /= duration) < (1 / 2.75) )
				return (change_val - (change_val * (7.5625 * current_time * current_time))) * 0.5 + begin_val;
			else if ( current_time < (2 / 2.75 ) )
				return (change_val - (change_val * (7.5625 * (current_time -= (1.5 / 2.75)) * current_time + 0.75))) * 0.5 + begin_val;
			else if ( current_time < (2.5 / 2.75) )
				return (change_val - (change_val * (7.5625 * (current_time -= (2.25 / 2.75)) * current_time + 0.9375))) * 0.5 + begin_val;
			else
				return (change_val - (change_val * (7.5625 * (current_time -= (2.625 / 2.75)) * current_time + 0.984375))) * 0.5 + begin_val;

		}

		current_time = current_time * 2 - duration;

		if ( (current_time /= duration) < (1 / 2.75) )
		  return change_val * (7.5625 * current_time * current_time) * 0.5 + change_val * 0.5 + begin_val;
		else if ( current_time < (2 / 2.75 ) )
		  return change_val * (7.5625 * (current_time -= (1.5 / 2.75)) * current_time + 0.75) * 0.5 + change_val * 0.5 + begin_val;
		else if ( current_time < (2.5 / 2.75) )
		  return change_val * (7.5625 * (current_time -= (2.25 / 2.75)) * current_time + 0.9375) * 0.5 + change_val * 0.5 + begin_val;
		else
		  return change_val * (7.5625 * (current_time -= (2.625 / 2.75)) * current_time + 0.984375) * 0.5 + change_val * 0.5 + begin_val;

	}
	// Elastic ease in.
	else if ( ease_type == LW_Animation.ELASTIC_EASE_IN )
	{

	  if ( current_time == 0 ) return begin_val;
		if ( (current_time /= duration) == 1 ) return begin_val + change_val;

		var p = duration * 0.3;
		var a = change_val;
		var s = p / 4;

		return -(a * Math.pow( 2, 10 * (current_time -= 1) ) * Math.sin( (current_time * duration - s) * (2 * Math.PI) / p )) + begin_val;

	}
	// Elastic ease out.
	else if ( ease_type == LW_Animation.ELASTIC_EASE_OUT )
	{

	  if ( current_time == 0 ) return begin_val;
		if ( (current_time /= duration) == 1 ) return begin_val + change_val;

		var p = duration * 0.3;
		var a = change_val;
		var s = p / 4;

		return a * Math.pow( 2, -10 * current_time ) * Math.sin( (current_time * duration - s) * (2 * Math.PI) / p ) + change_val + begin_val;

	}
	// Elastic ease in and out.
	else if ( ease_type == LW_Animation.ELASTIC_EASE_BOTH )
	{

	  if ( current_time == 0 ) return begin_val;
		if ( (current_time /= duration / 2) == 2 ) return begin_val + change_val;

		var p = duration * (0.3 * 1.5);
		var a = change_val;
		var s = p / 4;

		if ( current_time < 1 ) return -0.5 * (a * Math.pow( 2, 10 * (current_time -= 1) ) * Math.sin( (current_time * duration - s) * (2 * Math.PI) / p )) + begin_val;

		return a * Math.pow( 2, -10 * (current_time -= 1) ) * Math.sin( (current_time * duration - s) * (2 * Math.PI) / p ) * 0.5 + change_val + begin_val;

	}

}


//------------------------------------------------------------------------
// Name: LW_Animation_Sequence
// Desc: Stores a sequence of animations.
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Static Variables
//------------------------------------------------------------------------
LW_Animation_Sequence.sequences = new Array();


//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: LW_Animation_Sequence()
// Desc: Class constructor.
//------------------------------------------------------------------------
function LW_Animation_Sequence( options )
{

	// Store the default values.
	this.animations               = new Array();
	this.current_animation_index  = 0;
	this.sequence_index           = LW_Animation_Sequence.sequences.length;
	this.status                   = false;
	this.options                  = options;
	
	// Callbacks.
	this.onStart               = null;
	this.onAdvance             = null;
	this.onLoop                = null;
	this.onFinish              = null;

	// Store this animation sequence in the global sequences array.
	LW_Animation_Sequence.sequences[this.sequence_index] = this;

}


//------------------------------------------------------------------------
// Name: addAnimation()
// Desc: Adds an animation object to the animation sequence.
//------------------------------------------------------------------------
LW_Animation_Sequence.prototype.addAnimation = function( animation )
{

	// Store the animation in the sequence.
	this.animations.push( animation );

	// Add the onFinish and onStop functions.
	LW_Events_Handler.addEvent( animation, "onFinish", LW_Animation_Sequence.nextAnimation );
	LW_Events_Handler.addEvent( animation, "onStop", LW_Animation_Sequence.nextAnimation );

	// Store the sequence index in the animation.
	animation.sequence_index = this.sequence_index;

}


//------------------------------------------------------------------------
// Name: start()
// Desc: Starts the animation sequence.
//------------------------------------------------------------------------
LW_Animation_Sequence.prototype.start = function()
{

	// Only start if there is at least one animation in the sequence
	// and we are not already playing.
	if ( this.animations.length == 0 || this.status ) return;

	// Set as playing.
	this.status = true;
	
	// On start callback.
	if ( this.onStart != null && this.onStart( this ) == false )
		return;

	// Start the first animation in the sequence.
	this.animations[0].start();

}


//------------------------------------------------------------------------
// Name: reset()
// Desc: Cleans up the animation sequence.
//------------------------------------------------------------------------
LW_Animation_Sequence.prototype.reset = function()
{

	// Reset the values.
	this.status                   = false;
	this.current_animation_index  = 0;

}


//------------------------------------------------------------------------
// Public Static Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: nextAnimation()
// Desc: Plays the next animation in the sequence. This is set as the
//       animation's onFinish function.
//------------------------------------------------------------------------
LW_Animation_Sequence.nextAnimation = function( animation )
{

	// Get the animation sequence.
	var animation_sequence = LW_Animation_Sequence.sequences[animation.sequence_index];

	// If the animation sequence is stopped, return false.
	if ( !animation_sequence.status )
	  return false;

	// Increment the current animation index.
	animation_sequence.current_animation_index++;

	// Play the next animation if there is one.
	if ( animation_sequence.animations[animation_sequence.current_animation_index] != null )
	{
		
		// On advance callback.
		if ( animation_sequence.onAdvance != null && animation_sequence.onAdvance( animation_sequence ) == false )
			return;

		// Start the next animation.
		animation_sequence.animations[animation_sequence.current_animation_index].start();

	}  // End if next animation.
	else
	{
		
		// Should we loop?
		if ( animation_sequence.options && animation_sequence.options.loop == true )
		{
			
			// On loop callback.
			if ( animation_sequence.onLoop != null && animation_sequence.onLoop( animation_sequence ) == false )
				return;
				
			// Loop.
			animation_sequence.reset();
			animation_sequence.start();
			
		}  // End if looping.
		else
		{

			// Finish the animation sequence.
			animation_sequence.reset();
			
			// On finish callback.
			if ( animation_sequence.onFinish != null )
				animation_sequence.onFinish( animation_sequence );
			
		}

	}  // End if no more animations.

}


//------------------------------------------------------------------------
// Name: LW_Animation_Manager
// Desc: Manages each animation. All the animation is incremented through
//       the manager.
//------------------------------------------------------------------------
LW_Animation_Manager =
{

	//----------------------------------------------------------------------
  // Public Variables
  //----------------------------------------------------------------------
	increment_speed:     20,           // The speed at which the animation manager will increment each animation.
	playing_animations:  new Array(),  // An array of all the currently playing animations.
	interval_handle:     null,         // The handle that the setInterval() function returns.


	//----------------------------------------------------------------------
  // Public Member Functions
  //----------------------------------------------------------------------
	//------------------------------------------------------------------------
  // Name: addAnimation()
  // Desc: This function is used to add the animation to the animation
	//       manager for playing.
  //------------------------------------------------------------------------
	addAnimation: function( animation )
	{

		// Loop through each animation being played.
		for ( var i = 0; i < this.playing_animations.length; i++ )
		{

			// Get the animation.
		  var playing_animation = this.playing_animations[i];

			// Is the animation we're adding animate the
			// same element than this animation's element?
			if ( animation.element == playing_animation.element )
			{

				// Remove the animation from the playing animations array.
				this.playing_animations.splice( i, 1 );

				// Stop the currently playing animation so that we can play this one.
				playing_animation.stop();

			}  // End if managing the same element.

		}  // Next playing animation.

		// Add the animation to the playing animations array.
		this.playing_animations.push( animation );

		// If we don't have any animations playing,
		// we have to set up the timeout.
		if ( this.interval_handle == null )
		{

			// Set to advanced all animations.
			this.interval_handle = setInterval( LW_Animation_Manager.advanceAnimations, this.increment_speed, null );

		}  // End if no animations currently playing.

	},


	//------------------------------------------------------------------------
  // Name: advanceAnimations()
  // Desc: Advances each animation being played.
  //------------------------------------------------------------------------
	advanceAnimations: function()
	{

		// Loop through each animation being played.
		for ( var i = 0; i < LW_Animation_Manager.playing_animations.length; i++ )
		{

			// Get the animation from the array.
			var animation = LW_Animation_Manager.playing_animations[i];

			// Advance the animation.
			var continue_playing = animation.advanceFrame();

			// Is the animation done playing?
			if ( !continue_playing )
			{
				
				// Remove the animation from the playing animations array.
				LW_Animation_Manager.playing_animations.splice( i, 1 );
				
				// Finish up the animation.
				animation.finish();
				
				// If we don't have any more animations to play, stop
				// JavaScript from calling this function again.
				if ( LW_Animation_Manager.playing_animations.length == 0 )
				{
					clearInterval( LW_Animation_Manager.interval_handle );
					LW_Animation_Manager.interval_handle = null;
				}

			}  // End if stop playing this animation.

		}  // Next playing animation.

	}

}



