function Transition(curve, milliseconds, callback) {
    this.curve_ = curve;
    this.milliseconds_ = milliseconds;
    this.callback_ = callback;
    this.start_ = new Date().getTime();
    var me = this;
    this.runCallback_ = function() {
        me.run();
    };
}

Transition.prototype.run = function() {
    if (!this.hasNext()) return;
    this.callback_(this.next());
    setTimeout(this.runCallback_, 10);
}

Transition.prototype.hasNext = function() {
    if (this.done_) return this.oneLeft_;
    var now = new Date().getTime();
    if ((now - this.start_) > this.milliseconds_) {
        this.done_ = true;
        this.oneLeft_ = true;
    }
    return true;
}

Transition.prototype.next = function() {
    this.oneLeft_ = false;
    var now = new Date().getTime();
    var percentage = Math.min(1, (now - this.start_) / this.milliseconds_);
    return this.curve_(percentage);
}




/* some pre-defined animation curves */

function SineCurve(percentage) {
    return (1 - Math.cos(percentage * Math.PI)) / 2;
}

function SineStrongIn(percentage) {
    return (1 - Math.cos((Math.pow(percentage, 2)) * Math.PI)) / 2;
}

function BounceOut(percentage) {
    return (1 - Math.cos(percentage * Math.PI)) / (2 * percentage);
}


function StrongOut(percentage) {
    return Math.sin(.5 * Math.PI * percentage);
}

function VeryStrongOut(percentage) {
    return Math.pow(Math.sin(.5 * Math.PI * (percentage - 4)),(-.05 * (percentage - 7)));
}

function Linear(percentage) {
    return percentage;
}
