﻿/************************************************************************
 *
 *	Author: Bill Seddon (www.lyquidity.com)
 *
 *	Script: EffectSequence.js
 *
 *	Wraps code needed to perform composite animations of an element
 *	allowing a complete animation definition to be passed to the 
 *	constructor in a form that makes the animation sequence more 
 *	readable than trying to read the code.
 *
 *	Its designed to be used in a chain of animation sequences.
 *
 * License:
 *	 No license  - use as you like 
 *	 No warranty - use at your own risk
 *
 * Notes:
 *	
 *	The example below assumes there is an element, for example a <div>, 
 *	containing something to be animated.  The content could be anthing
 *	that can appear in html.  The most obvious examples are some text 
 *	or an image.
 *
 *	var ch = new Chain();
 *	ch.chain(function()
 *	{
 *      var sequence = null;
 * 		sequence = new EffectSequence( { element: 'ipSomeWords', transitionCallChain: true,
 *										 chainDelay: 0, startCSS: 'ipSomeWordsStart', 
 *										 effectsSequence:
 *		[ { duration:  500, transition: Fx.Transitions.Quad.easeInOut,	delay:	  0,	css: 'opaque' }, 
 *		  { duration: 1000, transition: Fx.Transitions.Quad.easeIn,		delay:    0,	css: 'ipSomeWordsSlideEnd' }, 
 *		  { duration: 1500, transition: Fx.Transitions.Quad.easeOut,	delay:	  0,	css: 'ipSomeWordsEnd' },
 *		  { duration:  500, transition: Fx.Transitions.Quad.easeIn,		delay:	  0 ,	css: 'transparent' }
 *		]}).start(ch);
 *
 *	});
 *
 *  For a single sequence a chain is not needed
 *
 * ----------------------------------------------------------------------
 *
 * This file exposes two "static" method used to manage effect transitions:
 *
 * addCurrentTransition()
 * KillTransitions()
 *
 ************************************************************************/

// Used by the "static" functions
var currentTransition = [];

// Class defintion
EffectSequence = new Class({

	/********************************************************************
	 *	Function: initialize
	 *
	 *	  Called automatically when a sequence instance is created.
	 *
	 *	Arguments:
	 *
	 *	  definition			- An associative array containing
	 *
	 *    The associative array has properties of these names:
	 *
	 *    element				- The mootool-ized element to be animated.
	 *							  If the name of an element is passed it
	 *							  will be converted to a mootools element
	 *							  [MANDATORY]
	 *	  transitionCallChain	- Allows the caller to control when the the 
	 *							  next link should executed immediate (false) 
	 *							  or in a transition chain
	 *							  [OPTIONAL]
	 *							  [DEFAULT: true]
	 *	  chainDelay			- Allows the caller to control how quickly 
	 *							  the next link is executed
	 *							  [OPTIONAL]
	 *							  [DEFAULT: 0]
	 *    startCSS				- CSS drescribing the initial position for 
	 *							  the element to be animated
	 *							  [OPTIONAL]
	 *							  [DEFAULT: {}]
	 *	  effectsSequence		- An array of effect transitions to perform
	 *							  Each effect transition is an associative 
	 *							  array
	 *							  [OPTIONAL]
	 *							  [DEFAULT: []]
	 *							  A valid associative array he following 
	 *							  possible members:
	 *
	 *    unit					- The unit of measure
	 *    fps					- The frames per second 
	 *    duration				- how long the effect will take to complete
	 *    transition			- The transition to use when animating
	 *	  css					- The css or id of a style describing the
	 *							  end point of an animation
	 *	  delay					- The number of milliseconds to delay before
	 *							- starting the animation
	 *
	 **********************************************************************/

	initialize: function(definition)
	{
		// Set the defaults
		$extend(this, { startTransparent: true, 
						endTransparent: false, 
						transitionCallChain: true, 
						chainDelay: 0,
						startCSS: {},
						effectsSequence: []
					  });

		$extend(this, definition);
		
		// Make sure that element is a mootools element not an element name
		if (this.element != null && $type(this.element == 'string'))
		{
			this.element = $(this.element);
		}
	},

	/********************************************************************
	 *	Function: start
	 *
	 *	  Starts the animation sequence.
	 *
	 *	Arguments:
	 *
	 *	  chain					- The parent chain in which this sequence 
	 *							  will execute
	 *							  [OPTIONAL]
	 *  Returns:
	 *
	 *	  The inner chain generated by the animation transitions
	 *
	 *  Remarks:
	 *
	 *	  After the sequence has been setup (transitionCallChain==false) or 
	 *	  after the sequence has concluded (transitionCallChain==true) 
	 *    if there is a next link in the chain it will be called.
	 *
	 *    To run two sequences simultaneously set:
	 *		transitionCallChain=false and
	 *		chainDelay=0
	 *
	 **********************************************************************/

	startChained: function(ch) 
	{
		if (ch == null) return null;

		var seq = this;

		ch.chain(function()
		{
			var transition = seq.start();
		
			if (seq.transitionCallChain)
				transition = transition.chain(function()
				{
					if (ch.chains.length > 0)
						ch.callChain.delay(seq.chainDelay, ch);
				}); else
			{
				if (ch.chains.length > 0)
					ch.callChain.delay(seq.chainDelay, ch);
			}

			return transition;
		});
		
	},

	start: function() 
	{
		// Set transparency and the initial conditions as requested
		this.startCSS = $merge({ visibility: 'visible' }, this.startCSS );
		this.element.setStyles(this.startCSS);

		// this.dump('CSS', this.startCSS);

		// Create an effects object
		var elementEffects = this.element.effects({ wait: true, duration: 0, transition: Fx.Transitions.Quad.easeInOut} );

		// This is called with a set of "null" change parameters so that a transition 
		// chain can be accessed.  effects.start.delay() returns an integer not a 
		// transition so if a delay is requested on the first effect is not possible
		// to get the transition.
		var transition = elementEffects.start( {} );
		var seq = this;

		this.effectsSequence.each(function(effect, i)
		{
			transition = transition.chain(function()
			{
				$extend(elementEffects.options, effect);
				this.start.delay(effect.delay, elementEffects, seq.changeTo(effect.css) );
			});
			
			addCurrentTransition(transition);
		});

		return transition;
	},
	
	/********************************************************************
	 *	Private Function: changeTo
	 *
	 *	  Computes the style elements that are changing
	 *
	 *	Arguments:
	 *
	 *	  style					- The name of the style to be used as the
	 *							  target definition for an animation
	 *
	 *  Returns:
	 *
	 *	  a css definition of the changing attributes
	 *
	 **********************************************************************/

	changeTo: function(style)
	{
		var to = null;
		if ($type(style) == 'object')
			to = style;
		else if(style == 'transparent')
			to = { opacity: 0 };
		else if (style == 'opaque')
			to = { opacity: 1};
		else
			to = changes(style);
		// this.dump("To", to);
		return to;
	},
	
	/********************************************************************
	 *	Private Function: dump
	 *
	 *	  Dumps the content of an associative array
	 *
	 *	Arguments:
	 *
	 *	  prefix	- A name so the user can identify the source content
	 *	  toDump	- The object to be dumped
	 *
	 *  Returns:
	 *
	 *	  a css definition of the changing attributes
	 *
	 **********************************************************************/

	dump: function(prefix, todump)
	{
		writeDebug(prefix + ': ');
		for(var key in todump)
		{
			writeDebug(key + ': ' + todump[key] + "; ");
		}
		writeDebug('</br>');		
	}
});

// currentTransition is a list of the currently (or potentially)
// executing transitions.  This function adds a transition to 
// the list.
function addCurrentTransition(effect)
{
	currentTransition.push(effect);
	return effect;
};

function KillTransitions()
{
	if(currentTransition != null) 
	{
		// alert(currentTransition.length);
		currentTransition.each(function(transition)
		{
			try
			{
				transition.stop();
			}
			catch(err)
			{
			}
		});
		currentTransition = [];
	}
};
