1 // ======= ANIMATION ========== //
  2 // Regexp used in this file
  3 var REGEX_PIXEL = /px\s?$/,
  4 	ALLOW_TYPES = /padding|margin|height|width|top|left|right|bottom|fontSize/,
  5 	TIMER_ID;
  6 /**
  7  * @namespace Simples.Animation
  8  * @description Animation controller if provide a standard animation object to this functionality it will execute the animation 
  9  */
 10 Simples.Animation = {
 11 	/* animations: currently active animations being run */
 12 	animations : {},
 13 	/* frameRate: global frame rate for animations */
 14 	frameRate : 24,
 15 	/* length: count of current active animations */
 16 	length : 0,
 17 	/**
 18 	 * @namespace Simples.Animation.tweens 
 19 	 * @description default tweens for animation 
 20 	 */
 21 	tweens : {
 22 		/**
 23 		 * @param frame: current frame
 24 		 * @param frameCount: total frames for animations
 25 		 * @param start: start value for tween
 26 		 * @param delta: difference to end value
 27 		*/
 28 		easing : function( frame, frameCount, start, delta ) {
 29 			return ((frame /= frameCount / 2) < 1) ? delta / 2 * frame * frame + start : -delta / 2 * ((--frame) * (frame - 2) - 1) + start;
 30 		},
 31 		/** @see Simples.Animation.tweens.easing */
 32 		linear : function( frame, frameCount, start, delta ){
 33 			return start + ( delta * ( frame/frameCount ));
 34 		},
 35 		/** @see Simples.Animation.tweens.easing */
 36 		quadratic : function( frame, frameCount, start, delta ){
 37 			return start + (((Math.cos((frame/frameCount)*Math.PI) )/2) * delta );
 38 		}
 39 	},
 40 	interval : Math.round( 1000 / this.frameRate ),
 41 	/**
 42 	 * Simples.Animation.create: used to to create an animation object which can be used by the animation queue runner
 43 	 * @param elem {Element} DOM Element to animate
 44 	 * @param setStyle {Object} CSS to use in animation, final position 
 45 	 * @param opts {Object}
 46 	 * @param opts.callback {Function} when animation complete
 47 	 * @param opts.tween {Function} tween to use when animating
 48 	 * @param opts.duration {Object} the time to elapse during animation
 49 	 */
 50 	create : function( elem, setStyle, opts ){
 51 		opts = opts || {};
 52 		if ( !( elem && elem.nodeType ) || Simples.isEmptyObject( setStyle ) ) {
 53 			if (typeof opts.callback === "function") {
 54 				opts.callback.call(elem);
 55 			}
 56 			return null;
 57 		}
 58 
 59 		var anim = {
 60 			0 : elem,
 61 			id : Simples.guid++,
 62 			callback : ( typeof opts.callback === "function" ) ? opts.callback : Simples.noop,
 63 			duration : ( typeof opts.duration === "number" && opts.duration > -1 ) ? opts.duration : 600,
 64 			tween : ( typeof opts.tween === "function" ) ? opts.tween : ( Simples.Animation.tweens[ opts.tween ] || Simples.Animation.tweens.easing ),
 65 			start : {},
 66 			finish : {}
 67 		};
 68 
 69 		// check for supported css animated features and prep for animation
 70 		for( var key in setStyle ){
 71 			var cKey = key.replace( RDASH_ALPHA, fcamelCase ),
 72 				opacity = ( cKey === OPACITY && setStyle[ key ] >= 0 && setStyle[ key ] <= 1 );
 73 
 74 			if( opacity || ALLOW_TYPES.test( cKey ) ){
 75 				anim.start[ cKey ] = ( Simples.getStyle( elem, cKey ) + "" ).replace(REGEX_PIXEL,"") * 1;
 76 				anim.finish[ cKey ] = ( setStyle[ key ] + "" ).replace(REGEX_PIXEL,"") * 1;
 77 			}                                        
 78 		}
 79 
 80 		var data = Simples.data(elem);
 81 		data.animations = data.animations || {};
 82 		data.animations[ anim.id ] = anim;
 83 
 84 		if( opts.manualStart !== true ){
 85 			Simples.Animation.start( anim );
 86 		}
 87 		return anim;
 88 	},
 89 	/**
 90 	 * Simples.Animation.start: used to add the animation to the animation runner queue
 91 	 * @param animation {Object} animation to perform action on
 92 	 */
 93 	start : function( animation ){
 94 
 95 		if( animation && animation.id ){
 96 			if( !hasOwn.call( this.animations, animation.id ) ){
 97 				this.length++;
 98 				this.animations[ animation.id ] = animation;
 99 				if( animation.duration === 0 ){
100 					this.stop( animation );
101 				} else if( !animation.startTime ){
102 					animation.startTime = new Date().getTime();
103 				}
104 			}
105 			
106 			if( !TIMER_ID ){
107 				this.interval = Math.round( 1000/ this.frameRate );
108 				TIMER_ID = WIN.setInterval(function(){ Simples.Animation._step(); }, this.interval );
109 			}
110 		}
111 	},
112 	/**
113 	 * Simples.Animation.reverse: used to take an animation in its current position and reverse and run
114 	 * @param animation {Object} animation to perform action on
115 	 */
116 	reverse : function( animation ){
117 		var start = animation.start, finish = animation.finish;
118 
119 		animation.start = finish;
120 		animation.finish = start;
121 		
122 		if( this.animations[ animation.id ] && animation.startTime ){
123 			var now = new Date().getTime(),
124 				diff = now - animation.startTime;
125 
126 			animation.startTime = now - ( animation.duration - diff );
127 		} else {
128 			if( this.animations[ animation.id ] ){
129 				delete this.animations[ animation.id ];
130 				this.length--;
131 			}
132 			this.start( animation );
133 		}
134 	}, 
135 	/**
136 	 * Simples.Animation.reset: used to reset an animation to either the start or finish position
137 	 * @param animation {Object} animation to perform action on
138 	 * @param resetToEnd {Boolean} whether to reset to finish (true) or start (false||undefined) state
139 	 */
140 	reset : function( animation, resetToEnd ){
141 
142 		var cssObj = resetToEnd ? animation.finish : animation.start,
143 			elem = animation[0];
144 			
145 		for( var name in cssObj ){
146 			Simples.setStyle( elem, name, cssObj[ name ] );
147 		}
148 		
149 		if( animation.startTime ){
150 			this.stop( animation );
151 		}
152 	},
153 	/**
154 	 * @private used by the queue runner to iterate over queued animations and update each postion
155 	 */
156 	_step : function(){
157 		if( this.length ){ 
158 			var now = new Date().getTime();    
159 			for( var id in this.animations ){
160 				var anim = this.animations[ id ],
161 					diff = now - anim.startTime,
162 					elem = anim[0],
163 					duration = anim.duration;
164 					
165 				if ( diff > duration ) {
166 					this.stop( anim, true );
167 				} else {
168 					for( var name in anim.start ){
169 						var start = anim.start[ name ];
170 						Simples.setStyle( elem, name, anim.tween( diff, duration, start, anim.finish[ name ] - start ) );
171 					
172 					}
173 				}
174 			}
175 		} else if( TIMER_ID ){
176 			WIN.clearInterval( TIMER_ID );
177 			TIMER_ID = null;
178 		}
179 	},
180 	/**
181 	 * Simples.Animation.stop: used to stop a supplied animation and cleanup after itsef
182 	 * @param animation {Object} the animation object to use and work on.
183 	 * @param jumpToEnd {Boolean} whether to leave in current position or set css to finish position
184 	 */
185 	stop : function( animation, jumpToEnd ){
186 		if( animation && hasOwn.call( this.animations, animation.id ) ){
187 
188 			animation.startTime = null;
189 
190 			if ( jumpToEnd ){
191 				this.reset( animation, true );
192 			}
193 
194 			var data = Simples.data( animation[0] );
195 			data.animations = data.animations || {};
196 			delete data.animations[ animation.id ];
197 
198 			animation.callback.call(animation[0], animation);
199 			delete this.animations[ animation.id ];
200 			this.length--;
201 		}
202 	}
203 };
204 
205 Simples.extend( /** @lends Simples.fn */ {
206 	/**
207 	 * @description From the instance of the Simples object used to bridge to the Simples.Animation functionality
208 	 * @param action {String} the name of the action to be performed - stop, start, reset, reverse; this is excluding create && _step
209 	 */
210 	animations: function(action) {
211 		if( action != ("create" || "_step") && Simples.Animation[ action ] ){
212 			var i = this.length,
213 				action = Simples.Animation[ action ];
214 			if( typeof action === "function" ){
215 				while (i) {
216 					var anims = Simples.data( this[--i], "animation" );
217 					Simples.Animation[ action ]( anim, arguments[2] );
218 				}
219 			}
220 		}
221 		return this;
222 	},
223 	/**
224 	 * @description Used to create animations off the elements in the instance of the Simples object
225 	 * @param action {String} the name of the action to be performed, excluding create && _step
226 	 * @param css {Object} CSS to use in animation, final position 
227 	 * @param opts {Object}
228 	 * @param opts.callback {Function} when animation complete
229 	 * @param opts.tween {Function} tween to use when animating
230 	 * @param opts.duration {Object} the time to elapse during animation
231 	 */
232 	animate: function(css, opts) {
233 		var i = this.length;
234 		while (i) {
235 			Simples.Animation.create( this[--i], css, opts );
236 		}
237 		return this;
238 	}
239 });