/*
Script: Fx.js
Contains the basic animation logic to be extended by all other Fx Classes.

License:
MIT-style license.
*/

var Fx = new Class({

Implements: [Chain, Events, Options],

options: {
/*
onStart: $empty,
onCancel: $empty,
onComplete: $empty,
*/
fps: 50,
unit: false,
duration: 500,
link: 'ignore',
transition: function(p){
return -(Math.cos(Math.PI * p) - 1) / 2;
}
},

initialize: function(options){
this.subject = this.subject || this;
this.setOptions(options);
this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
var wait = this.options.wait;
if (wait === false) this.options.link = 'cancel';
},

step: function(){
var time = $time();
if (time < this.time + this.options.duration){
var delta = this.options.transition((time - this.time) / this.options.duration);
this.set(this.compute(this.from, this.to, delta));
} else {
this.set(this.compute(this.from, this.to, 1));
this.complete();
}
},

set: function(now){
return now;
},

compute: function(from, to, delta){
return Fx.compute(from, to, delta);
},

check: function(caller){
if (!this.timer) return true;
switch (this.options.link){
case 'cancel': this.cancel(); return true;
case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
}
return false;
},

start: function(from, to){
if (!this.check(arguments.callee, from, to)) return this;
this.from = from;
this.to = to;
this.time = 0;
this.startTimer();
this.onStart();
return this;
},

complete: function(){
if (this.stopTimer()) this.onComplete();
return this;
},

cancel: function(){
if (this.stopTimer()) this.onCancel();
return this;
},

onStart: function(){
this.fireEvent('start', this.subject);
},

onComplete: function(){
this.fireEvent('complete', this.subject);
if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
},

onCancel: function(){
this.fireEvent('cancel', this.subject).clearChain();
},

pause: function(){
this.stopTimer();
return this;
},

resume: function(){
this.startTimer();
return this;
},

stopTimer: function(){
if (!this.timer) return false;
this.time = $time() - this.time;
this.timer = $clear(this.timer);
return true;
},

startTimer: function(){
if (this.timer) return false;
this.time = $time() - this.time;
this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
return true;
}

});

Fx.compute = function(from, to, delta){
return (to - from) * delta + from;
};

Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};