/*
	Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
	Available via Academic Free License >= 2.1 OR the modified BSD license.
	see: http://dojotoolkit.org/license for details
*/


if(!dojo._hasResource["dojox.fx.Timeline"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.fx.Timeline"] = true;
dojo.provide("dojox.fx.Timeline");
dojo.require("dojo.fx.easing");

dojox.fx.animateTimeline = function(/* Object */options, /* DomNode|String */node){
	// options: Object
	// 		The paramters passed to the timeline animation. Includes:
	// 			keys: Array
	// 				An array of objects, with style properties and values.
	// 			duration:
	// 				Duration of the animation in milliseconds.
	// 				Defaults to 1000.
	// node: DomNode
	// 		The DomNode or id to be animated.
	//
	// summary:
	//		An add-on to dojo.fx that provides the ability to create
	//		a complex property animation based on an array of "keyframes".
	// description:
	//		The Timeline is a replacement for the default dojo._Line.
	//		Instead of _Line.getValue returning a float between 0-1,
	//		_Timeline.getValue returns an object with all properties and
	//		their current values.
	//		A property does not have to appear in every keyframe.
	//		As in the example below, "height" is transitioned from the first
	//		keyframe to the third. "width" is transitioned from the first
	//		to the second to the third.
	//		Each keyframe can accept the following custom properties:
	//	step: String
	//		The start, finish or percentage that this keyframe represents.
	//		Allowed parameters are:
	//			0%-100%
	//			from (same as 0%, used to conform with the Webkit animation spec)
	//			to (same as 100%, used to conform with the Webkit animation spec)
	//	ease: String
	//		The string name of a dojo.fx.easing ease. Defaults to "linear". Use
	//		the suffix name of the ease, like: "quadIn", not: "dojo.fx.quadIn".
	//
	// example:
	// 		|	var keys = [
	// 		|	{
	// 		|		step:"0px",
	// 		|		ease:"quadInOut",
	// 		|		width:"50px",
	// 		|		height:"50px",
	// 		|	},{
	// 		|		step:"25%",
	// 		|		width:"190px"
	// 		|	},{
	// 		|		step:"100%",
	// 		|		width:"10px",
	// 		|		height:"200px",
	// 		|	}
	// 		|	];
	// 		|	ani = dojox.fx.animateTimeline({keys:keys, duration:2000}, "myDiv").play();
	//
	var _curve = new dojox.fx._Timeline(options.keys);
	var ani = dojo.animateProperty({
		node:dojo.byId(node || options.node),
		duration:options.duration || 1000,
		properties:_curve._properties,
		// don't change! This easing is for the timeline,
		// not individual properties
		easing:dojo.fx.easing.linear,
		onAnimate: function(v){
			//console.log("   ani:", v);
		}
	});
	dojo.connect(ani, "onEnd", function(node){
		// Setting the final style. Hiccups in the browser
		// can cause the animation to lose track. This ensures
		// that it finishes in the proper location.
		var sty = ani.curve.getValue(ani.reversed ? 0 : 1);
		dojo.style(node, sty);
	});
	dojo.connect(ani, "beforeBegin", function(){
		// remove default curve and replace it with Timeline
		if(ani.curve){ delete ani.curve; }
		ani.curve = _curve;
		_curve.ani = ani;
	})
	return ani; // dojo.Animation
}

dojox.fx._Timeline = function(/* Array */keys){
	// summary:
	//		The dojox.fx._Timeline object from which an instance
	//		is created
	// tags:
	//		private
	this.keys = dojo.isArray(keys) ? this.flatten(keys) : keys;
}

dojox.fx._Timeline.prototype.flatten = function(keys){
	// summary:
	//		An internally used function that converts the keyframes
	//		as used in the example above into a series of key values
	//		which is what is used in the animation parsing.
	var getPercent = function(str, idx){
		if(str == "from"){ return 0; }
		if(str == "to"){ return 1; }
		if(str === undefined){
			return idx==0 ? 0 : idx / (keys.length - 1)
		}
		return parseInt(str, 10) * .01
	}
	var p = {}, o = {};
	dojo.forEach(keys, function(k, i){
		var step = getPercent(k.step, i);
		var ease = dojo.fx.easing[k.ease] || dojo.fx.easing.linear;
		
		for(var nm in k){
			if(nm == "step" || nm == "ease" || nm == "from" || nm == "to"){ continue; }
			if(!o[nm]){
				o[nm] = {
					steps:[],
					values:[],
					eases:[],
					ease:ease
				};
				p[nm] = {};
				if(!/#/.test(k[nm])){
					p[nm].units = o[nm].units = /\D{1,}/.exec(k[nm]).join("");
				}else{
					p[nm].units = o[nm].units = "isColor";
				}
			}
			
			o[nm].eases.push(dojo.fx.easing[k.ease || "linear"]);
			
			o[nm].steps.push(step);
			if(p[nm].units == "isColor"){
				o[nm].values.push(new dojo.Color(k[nm]));
			}else{
				o[nm].values.push(parseInt(/\d{1,}/.exec(k[nm]).join("")));
			}
			
			if(p[nm].start === undefined){
				p[nm].start = o[nm].values[o[nm].values.length-1];
			}else{
				p[nm].end = o[nm].values[o[nm].values.length-1]
			}
		}
	});
	
	
	this._properties = p;
	return o; // Object
	
}

dojox.fx._Timeline.prototype.getValue = function(/*float*/ p){
	// summary:
	//		Replaces the native getValue in dojo.fx.Animation.
	//		Returns an object with all propeties used in the animation
	//		and the property's current value
	p = this.ani._reversed ? 1-p : p;
	var o = {}, self = this;
	
	var getProp = function(nm, i){
		return self._properties[nm].units!="isColor" ?
			self.keys[nm].values[i] + self._properties[nm].units :
			self.keys[nm].values[i].toCss();
	}
	
	for(var nm in this.keys){
		var k = this.keys[nm];
		for(var i=0; i<k.steps.length; i++){
			
			var step = k.steps[i];
			var ns = k.steps[i+1];
			var next = i < k.steps.length ? true : false;
			var ease = k.eases[i] || function(n){return n;};
			
			if(p == step){
				// first or last
				o[nm] = getProp(nm, i);
				if(!next || (next &&  this.ani._reversed)) break;
			
			}else if(p > step){
				
				if(next && p < k.steps[i+1]){
					// inbetween steps
					var end = k.values[i+1];
					var beg = k.values[i];
					
					var seg = (1 / (ns - step)) * (p - step);
					seg = ease(seg);
					
					if(beg instanceof dojo.Color){
						o[nm] = dojo.blendColors(beg, end, seg).toCss(false);
					}else{
						var df = end - beg;
						o[nm] = beg + seg * df + this._properties[nm].units;
					}
					break;
				
				}else{
					// completed keys before 100%
					o[nm] = getProp(nm, i);
				}
				
			}else if((next && !this.ani._reversed) || (!next && this.ani._reversed)){
				o[nm] = getProp(nm, i);
			}
		}
	}
	return o; // Object
};



}
