var Tween = Class.create();
Object.extend(Tween.prototype, {
	duration: 0,
	startValue: 0,
	endValue: 0,
	method: 'linear',
	update: function(newValue) {},
	onInit: function() {},
	onComplete: function() {},
	continueWith: function() {},
	autorun: false,
	runTimes: 1,
	counter: 0,
	loop: false,
	initialize: function(props) {
		Object.extend(this, props || {});
		if (this.autorun) this.start();
	},
	start: function() {
		this.startTime = 0;
		this.paused = 0;
		if (this.counter >= this.runTimes) {
			this.counter = 0;
			if (!this.loop) {
				this.continueWith();
				return;
			}
		}
		this.startTime = this.now();
		this.onInit();
		this.run();
	},
	run: function() {
		var that = this;
		if (this.paused) return;
		var time = this.now() - this.startTime;
		if (time >= this.duration || this.duration <= 0) {
			this.onTimerComplete();
			return;
		}
		var newValue = this.methods[this.method](time, this.startValue, this.endValue - this.startValue, this.duration);
		this.update(newValue);
		window.setTimeout(function() {that.run()}, 0);
	},
	onTimerComplete: function() {
		this.update(this.endValue);
		this.onComplete();
		this.counter++;
		this.start();
	},
	pause: function() {
		if (this.startTime) this.paused = this.now();
	},
	playAgain: function() {
		if (!this.paused) return;
		this.startTime += this.now() - this.paused;
		this.paused = 0;
		this.run();
	},
	now: function() {
		return new Date().getTime();
	},
	methods: {
		linear: function (t, b, c, d) { return c*t/d + b; },
		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;
		}
	}
});

var Sequencer = Class.create();
Object.extend(Sequencer.prototype, {
	tweens: [],
	autorun: false,
	initialize: function(props) {
		Object.extend(this, props || {});
		if (!this.tweens) return;
		for(var i = 0; i < this.tweens.length - 2; i++) {
			this.tweens[i].continueWith = this.tweens[i + 1].start;
		}
		if (this.loop) this.tweens[i].continueWith = this.tweens[0].start;
		if (this.autorun) this.start();
	},
	start: function() {
		if (this.tweens) this.tweens[0].start();
	}
});
