
/**
 * 
 *  fTween - tweening animation
 *  
 *  @copyright Copyright (c) 2009 fCMS Development Team
 *  @author Arne Blankerts <theseer@fcms.de>
 *  
 *  Based on "Tween.js" from http://jstween.blogspot.com/
 *  
 *  Original license and copyright as follows:
 * 
 *  TERMS OF USE - EASING EQUATIONS
 *  Open source under the BSD License.
 *  Copyright (c) 2001 Robert Penner
 *  
 *  JavaScript version copyright (C) 2006 by Philippe Maegerman
 *  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 the author nor the names of 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 fTween(obj, prop, func, begin, finish, duration, suffixe){
   this.init(obj, prop, func, begin, finish, duration, suffixe)
}

fTween.prototype = {
   
   obj: {},
   prop: '',
   begin: 0,
   change: 0,
   prevTime: 0,
   prevPos: 0,
   looping: false,
   name: 'x',
   suffixe: '',
   
   _duration: 0,
   _time: 0,
   _pos: 0,
   _position: 0,
   _startTime: 0,
   _finish: 0,
   _listeners: [],
   
   create: function(o,f) {
      var a = new Array() ;
      var l = arguments.length ;
      for(var i = 2 ; i < l ; i++) a[i - 2] = arguments[i] ;
      return function() {
         var aP = [].concat(arguments, a) ;
         f.apply(o, aP);
      }
   },
   
   init: function(obj, prop, func, begin, finish, duration, suffixe) {
      if (!arguments.length) return;
      this._listeners = new Array();
      this.addListener(this);
      if(suffixe) this.suffixe = suffixe;
      this.obj = obj;
      this.prop = prop;
      this.begin = begin;
      this._pos = begin;
      this.setDuration(duration);
      if (func!=null && func!='') {
         this.func = this[func];
      }
      this.setFinish(finish);
   },
   
   func: function (t, b, c, d) { 
      return c*t/d + b;     
   },
   
   setTime: function(t) {
   	this.prevTime = this._time;
   	if (t > this.getDuration()) {
   		if (this.looping) {
   			this.rewind (t - this._duration);
   			this.update();
   			this.broadcastMessage('onMotionLooped',{target:this,type:'onMotionLooped'});
   		} else {
   			this._time = this._duration;
   			this.update();
   			this.stop();
   			this.broadcastMessage('onMotionFinished',{target:this,type:'onMotionFinished'});
   		}
   	} else if (t < 0) {
   		this.rewind();
   		this.update();
   	} else {
   		this._time = t;
   		this.update();
   	}
   },
   
   getTime: function() {
	   return this._time;
   },
   
   setDuration: function(d) { 
	   this._duration = (d == null || d <= 0) ? 100000 : d;
   },
   
   getDuration: function() {
	   return this._duration;
   },
   
   setPosition: function(p) {
   	this.prevPos = this._pos;
   	var a = this.suffixe != '' ? this.suffixe : '';
   	this.obj[this.prop] = Math.round(p) + a;
   	this._pos = p;
   	this.broadcastMessage('onMotionChanged',{target:this,type:'onMotionChanged'});
   },
   
   getPosition: function(t) {
	   if (t == undefined) t = this._time;
	   return this.func(t, this.begin, this.change, this._duration);
   },
   
   setFinish: function(f) {
	   this.change = f - this.begin;
   },
   
   getFinish: function() {
	   return this.begin + this.change;
   },
   
   start: function() {
   	this.rewind();
   	this.startEnterFrame();
   	this.broadcastMessage('onMotionStarted',{target:this,type:'onMotionStarted'});
   	//alert('in');
   },
   
   rewind: function(t) {
   	this.stop();
   	this._time = (t == undefined) ? 0 : t;
   	this.fixTime();
   	this.update();
   },
   
   fforward: function() {
   	this._time = this._duration;
   	this.fixTime();
   	this.update();
   },
   
   update: function() {
	   this.setPosition(this.getPosition(this._time));
	},
   
   startEnterFrame: function() {
   	this.stopEnterFrame();
   	this.isPlaying = true;
   	this.onEnterFrame();
   },
   
   onEnterFrame: function() {
	   if (this.isPlaying) {
		   this.nextFrame();
		   setTimeout(this.create(this, this.onEnterFrame), 0);
	   }
   },
   
   nextFrame: function() {
	   this.setTime((this.getTimer() - this._startTime) / 1000);
	},
   
   stop: function(){
	   this.stopEnterFrame();
	   this.broadcastMessage('onMotionStopped',{target:this,type:'onMotionStopped'});
   },
   
   stopEnterFrame: function(){
   	this.isPlaying = false;
   },

   continueTo: function(finish, duration){
   	this.begin = this._pos;
   	this.setFinish(finish);
   	if (this._duration != undefined)
   		this.setDuration(duration);
   	this.start();
   },
   
   resume: function(){
   	this.fixTime();
   	this.startEnterFrame();
   	this.broadcastMessage('onMotionResumed',{target:this,type:'onMotionResumed'});
   },
   
   yoyo: function (){
   	this.continueTo(this.begin,this._time);
   },

   addListener: function(o){
   	this.removeListener (o);
   	return this._listeners.push(o);
   },
   
   removeListener: function(o){
   	var a = this._listeners;	
   	var i = a.length;
   	while (i--) {
   		if (a[i] == o) {
   			a.splice (i, 1);
   			return true;
   		}
   	}
   	return false;
   },
   
   broadcastMessage: function(){
   	var arr = new Array();
   	for(var i = 0; i < arguments.length; i++){
   		arr.push(arguments[i])
   	}
   	var e = arr.shift();
   	var a = this._listeners;
   	var l = a.length;
   	for (var i=0; i<l; i++){
   		if(a[i][e])
   		a[i][e].apply(a[i], arr);
   	}
   },
   
   fixTime: function(){
   	this._startTime = this.getTimer() - this._time * 1000;
   },
   
   getTimer: function(){
	  return new Date().getTime() - this._time;
   },
   
   backEaseIn: function(t,b,c,d,a,p){
   	if (s == undefined) var s = 1.70158;
   	return c*(t/=d)*t*((s+1)*t - s) + b;
   },
   
   backEaseOut: function(t,b,c,d,a,p){
   	if (s == undefined) var s = 1.70158;
   	return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
   },
   
   backEaseInOut: function(t,b,c,d,a,p){
   	if (s == undefined) var s = 1.70158; 
   	if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
   	return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
   },
   
   elasticEaseIn: function(t,b,c,d,a,p){
		if (t==0) return b;  
		if ((t/=d)==1) return b+c;  
		if (!p) p=d*.3;
		if (!a || a < Math.abs(c)) {
			a=c; var s=p/4;
		} else { 
			var s = p/(2*Math.PI) * Math.asin (c/a);
      }
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;	
   },
   
   elasticEaseOut: function (t,b,c,d,a,p){
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
	},
   
   elasticEaseInOut: function (t,b,c,d,a,p){
   	if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) var p=d*(.3*1.5);
   	if (!a || a < Math.abs(c)) {var a=c; var s=p/4; }
   	else var s = p/(2*Math.PI) * Math.asin (c/a);
   	if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
   	return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
   },

   bounceEaseOut: function(t,b,c,d){
   	if ((t/=d) < (1/2.75)) {
   		return c*(7.5625*t*t) + b;
   	} else if (t < (2/2.75)) {
   		return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
   	} else if (t < (2.5/2.75)) {
   		return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
   	} else {
   		return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
   	}
   },
   
   bounceEaseIn: function(t,b,c,d){
   	return c - this.bounceEaseOut (d-t, 0, c, d) + b;
   },
   
   bounceEaseInOut: function(t,b,c,d){
   	if (t < d/2) return this.bounceEaseIn (t*2, 0, c, d) * .5 + b;
   	else return this.bounceEaseOut (t*2-d, 0, c, d) * .5 + c*.5 + b;
   },
   
   strongEaseInOut: function(t,b,c,d){
   	return c*(t/=d)*t*t*t*t + b;
   },
   
   regularEaseIn: function(t,b,c,d){
   	return c*(t/=d)*t + b;
   },
   
   regularEaseOut: function(t,b,c,d){
   	return -c *(t/=d)*(t-2) + b;
   },
   
   regularEaseInOut: function(t,b,c,d){
   	if ((t/=d/2) < 1) return c/2*t*t + b;
   	return -c/2 * ((--t)*(t-2) - 1) + b;
   },
   
   strongEaseIn: function(t,b,c,d){
   	return c*(t/=d)*t*t*t*t + b;
   },
   
   strongEaseOut: function(t,b,c,d){
   	return c*((t=t/d-1)*t*t*t*t + 1) + b;
   },
   
   strongEaseInOut: function(t,b,c,d){
   	if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
   	return c/2*((t-=2)*t*t*t*t + 2) + b;
   }
   
}

