// SpryDOMEffects.js - version 0.6 - Spry Pre-Release 1.7

//

// Copyright (c) 2007. Adobe Systems Incorporated.

// All rights reserved.

//

// Redistribution and use in source and binary forms, with or without

// modification, are permitted provided that the following conditions are met:

//

//   * Redistributions of source code must retain the above copyright notice,

//     this list of conditions and the following disclaimer.

//   * Redistributions in binary form must reproduce the above copyright notice,

//     this list of conditions and the following disclaimer in the documentation

//     and/or other materials provided with the distribution.

//   * Neither the name of Adobe Systems Incorporated nor the names of its

//     contributors may be used to endorse or promote products derived from this

//     software without specific prior written permission.

//

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE

// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

// POSSIBILITY OF SUCH DAMAGE.



(function() { // BeginSpryComponent

	

if (typeof Spry == "undefined" || !Spry.Utils || !Spry.$$)

{

	alert("SpryDOMEffects.js requires SpryDOMUtils.js");

	return;

}



if (!Spry.Effect) Spry.Effect = {};



Spry.Effect.Animator = function(opts)

{

	Spry.Effect.Animator.Notifier.call(this);



	this.animatorID = Spry.Effect.Animator.nextID++;

	this.dropFrames = true;

	this.fps = 60; // frames per-second

	this.duration = 500; // msecs

	this.timer = 0;

	this.startTime = 0; // Used only when dropFrames is true.

	this.currentFrame = 0;

	this.easeFunc = Spry.Effect.Animator.defaultEaseFunc;

	this.stopped = false;



	Spry.Effect.Animator.copyProps(this, opts);



	this.interval = 1000 / this.fps;

	this.numFrames = (this.duration / 1000) * this.fps;



	if (this.onComplete)

	{

		var self = this;

		this.addObserver({ onAnimationComplete: function(){ self.onComplete(); } });

	}

};



Spry.Effect.Animator.nextID = 1;



Spry.Effect.Animator.copyProps = function(dst, src)

{

	if (src)

	{

		for (prop in src)

			dst[prop] = src[prop];

	}

	return dst;

};



Spry.Effect.Animator.getElement = function(element)

{

	if (arguments.length > 1)

	{

		for (var i = 0, elements = [], length = arguments.length; i < length; i++)

			elements.push(Spry.Effect.Animator.getElement(arguments[i]));

		return elements;

	}

	if (typeof element == 'string')

		element = document.getElementById(element);

	return element;

};



Spry.Effect.Animator.defaultEaseFunc = function(time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); };



Spry.Effect.Animator.Notifier = function()

{

	this.observers = [];

	this.suppressNotifications = 0;

};



Spry.Effect.Animator.Notifier.prototype.addObserver = function(observer)

{

	if (!observer)

		return;



	// Make sure the observer isn't already on the list.



	var len = this.observers.length;

	for (var i = 0; i < len; i++)

	{

		if (this.observers[i] == observer)

			return;

	}

	this.observers[len] = observer;

};



Spry.Effect.Animator.Notifier.prototype.removeObserver = function(observer)

{

	if (!observer)

		return;



	for (var i = 0; i < this.observers.length; i++)

	{

		if (this.observers[i] == observer)

		{

			this.observers.splice(i, 1);

			break;

		}

	}

};



Spry.Effect.Animator.Notifier.prototype.notifyObservers = function(methodName, data)

{

	if (!methodName)

		return;



	if (!this.suppressNotifications)

	{

		var len = this.observers.length;

		for (var i = 0; i < len; i++)

		{

			var obs = this.observers[i];

			if (obs)

			{

				if (typeof obs == "function")

					obs(methodName, this, data);

				else if (obs[methodName])

					obs[methodName](this, data);

			}

		}

	}

};



Spry.Effect.Animator.Notifier.prototype.enableNotifications = function()

{

	if (--this.suppressNotifications < 0)

	{

		this.suppressNotifications = 0;

		Spry.Debug.reportError("Unbalanced enableNotifications() call!\n");

	}

};



Spry.Effect.Animator.Notifier.prototype.disableNotifications = function()

{

	++this.suppressNotifications;

};



Spry.Effect.Animator.prototype = new Spry.Effect.Animator.Notifier;

Spry.Effect.Animator.prototype.constructor = Spry.Effect.Animator;



Spry.Effect.Animator.prototype.start = function()

{

	this.stopped = false;

	this.currentFrame = 0;

	this.startTime = (new Date()).getTime();



	this.notifyObservers("onAnimationStart");



	var self = this;

	this.timer = setTimeout(function(){ self.onStepAnimation(); }, this.interval);

};



Spry.Effect.Animator.prototype.stop = function()

{

	if (this.timer)

		clearTimeout(this.timer);

	this.timer = 0;

	this.stopped = true;



	this.notifyObservers("onAnimationStopped");

};



Spry.Effect.Animator.prototype.onStepAnimation = function()

{

	var obj = {};



	if (this.dropFrames)

	{

		obj.duration = this.duration;

		obj.elapsed = ((new Date).getTime()) - this.startTime;

		if (obj.elapsed > obj.duration)

			obj.elapsed = obj.duration;

	}

	else

	{

		obj.duration = this.numFrames;

		obj.elapsed = ++this.currentFrame;

	}



	obj.easingConst = this.easeFunc(obj.elapsed, 0, 1, obj.duration)



	this.notifyObservers("onPreDraw", obj);

	this.draw(obj.elapsed, obj.duration, obj.easingConst);

	this.notifyObservers("onPostDraw", obj);



	if (!this.stopped)

	{

		if (obj.elapsed < obj.duration)

		{

			var self = this;

			this.timer = setTimeout(function(){ self.onStepAnimation(); }, this.interval);

		}

		else

		{

			this.stop();

			this.notifyObservers("onAnimationComplete");

		}

	}

};



Spry.Effect.Animator.prototype.draw = function(elapsed, duration, easingConst)

{

	// The default draw method does nothing. It is assumed that

	// derived classes will provide their own implementation of this

	// method.



	debug.log("elapsed: " + elapsed + " -- duration: " + duration + " -- easingConst: " + easingConst);

};





///////////////////////////////////////////////////////////////////////////////



Spry.Effect.CSSAnimator = function(elements, styleStr, opts)

{

	this.animationSets = [];



	Spry.Effect.Animator.call(this, opts);



	this.add(elements, styleStr);

};



Spry.Effect.CSSAnimator.prototype = new Spry.Effect.Animator();

Spry.Effect.CSSAnimator.prototype.constructor = Spry.Effect.CSSAnimator;



Spry.Effect.CSSAnimator.prototype.add = function(elements, styleStr)

{

	// The first argument for the CSSAnimator can be

	// the id of an element, an element node, or an array of

	// elements and/or ids.



	elements = Spry.$$(elements);



	if (elements.length < 1)

		return;



	var animSet = { elements: elements, cssProps: []};



	this.animationSets.push(animSet);



	// Convert the styleStr into an object.



	var toObj = Spry.Utils.styleStringToObject(styleStr);

	for (var p in toObj)

	{

		var obj = new Object;

		var v = toObj[p];

		obj.value = new Number(v.replace(/[^-\d\.]+/g, ""));

		obj.units = v.replace(/[-\d+\.]/g, "");

		toObj[p] = obj;

	}



	for (var i = 0; i < elements.length; i++)

	{

		var obj = animSet.cssProps[i] = new Object;

		for (var p in toObj)

		{

			var pFuncs = Spry.Effect.CSSAnimator.stylePropFuncs[p];

			if (!pFuncs)

				pFuncs = Spry.Effect.CSSAnimator.stylePropFuncs["default"];



			obj[p] = new Object;

			obj[p].from = new Number(pFuncs.get(elements[i], p).replace(/[^-\d\.]+/g, ""));

			obj[p].to = toObj[p].value;

			obj[p].distance = obj[p].to - obj[p].from;

			obj[p].units = toObj[p].units;

		}

	}

};



Spry.Effect.CSSAnimator.prototype.start = function()

{

	for (var s = 0; s < this.animationSets.length; s++)

	{

		var animSet = this.animationSets[s];

		var elements = animSet.elements;

		var cssProps = animSet.cssProps;



		for (var i = 0; i < elements.length; i++)

		{

			var ele = elements[i];

	

			var eleProps = ele.spryCSSAnimatorProps;

			if (!eleProps)

				eleProps = ele.spryCSSAnimatorProps = new Object;

	

			var obj = cssProps[i];

			for (var p in obj)

				eleProps[p] = this.animatorID;

		}

	}



	return Spry.Effect.Animator.prototype.start.call(this);

};



Spry.Effect.CSSAnimator.prototype.stop = function()

{

	for (var s = 0; s < this.animationSets.length; s++)

	{

		var animSet = this.animationSets[s];

		var elements = animSet.elements;

		var cssProps = animSet.cssProps;



		for (var i = 0; i < elements.length; i++)

		{

			var ele = elements[i];

			var obj = cssProps[i];

	

			var eleProps = ele.spryCSSAnimatorProps;

			for (var p in obj)

			{

				if (eleProps[p] == this.animatorID)

					delete eleProps[p];

			}

		}

	}



	return Spry.Effect.Animator.prototype.stop.call(this);

};



Spry.Effect.CSSAnimator.prototype.draw = function(elapsed, duration, easingConst)

{

	for (var s = 0; s < this.animationSets.length; s++)

	{

		var animSet = this.animationSets[s];

		var elements = animSet.elements;

		var cssProps = animSet.cssProps;



		for (var i = 0; i < elements.length; i++)

		{

			var ele = elements[i];

			var eleProps = ele.spryCSSAnimatorProps;

			var obj = cssProps[i];

			for (var p in obj)

			{

				if (eleProps[p] == this.animatorID)

				{

					var pFuncs = Spry.Effect.CSSAnimator.stylePropFuncs[p];

					if (!pFuncs)

						pFuncs = Spry.Effect.CSSAnimator.stylePropFuncs["default"];

	

					if (elapsed > duration)

						pFuncs.set(ele, p, obj[p].to + obj[p].units);

					else

						pFuncs.set(ele, p, obj[p].from + (obj[p].distance * easingConst) + obj[p].units);

				}

			}

		}

	}

};



Spry.Effect.CSSAnimator.stylePropFuncs = {};



Spry.Effect.CSSAnimator.stylePropFuncs["default"] = {

	get: function(ele, prop)

	{

		return ele.style[prop];

	},



	set: function(ele, prop, val)

	{

		ele.style[prop] = val;

	}

};



Spry.Effect.CSSAnimator.stylePropFuncs["opacity"] = {

	get: function(ele, prop)

	{

		var val = 1;

		

		if (ele.style.opacity)

			val = ele.style.opacity;

		else if (ele.style.filter)

		{

			var strVal = ele.style.filter.replace(/.*alpha\(opacity=(\d+)\).*/, "$1");

			if (strVal)

				val = parseInt(strVal) / 100;

		}

		return val + "";

	},



	set: function(ele, prop, val)

	{

		ele.style.opacity = "" + val;

		ele.style.filter = "alpha(opacity=" + (val * 100) + ")";

	}

};



///////////////////////////////////////////////////////////////////////////////



Spry.$$.Results.defaultEaseFunc = function(time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); };



Spry.$$.Results.animatePropertyTo = function(propName, to, options)

{

	var opts = { interval: 10, duration: 1000, onComplete: null, transition: Spry.$$.Results.defaultEaseFunc };

	Spry.Effect.Animator.copyProps(opts, options);



	var objs = [];

	for (var i = 0; i < this.length; i++)

	{

		var obj = objs[i] = new Object;

		obj.ele = this[i];

		obj.from = obj.ele[propName];

		obj.distance = to - obj.from;

	}



	var startTime = (new Date).getTime();



	var animateFunc = function()

	{

		var elapsedTime = ((new Date).getTime()) - startTime;



		if (elapsedTime > opts.duration)

		{

			for (var i = 0; i < objs.length; i++)

				objs[i].ele[propName] = to;

			if (opts.onComplete)

				opts.onComplete();

		}

		else

		{

			for (var i = 0; i < objs.length; i++)

			{

				var obj = objs[i];

				obj.ele[propName] = opts.transition(elapsedTime, obj.from, obj.distance, opts.duration);

			}

			setTimeout(animateFunc, opts.interval);

		}

	};



	setTimeout(animateFunc, opts.interval);

	return this;

};



Spry.$$.Results.animateStyleTo = function(styleStr, options)

{

	var a = new Spry.Effect.CSSAnimator(this, styleStr, options);

	a.start();

	return this;

};



})(); // EndSpryComponent
