/** Object oriented Javascript for Animation
An Animation object models the transition of certain attributes to certain target values over a certain duration of time.
*/

function Interval (initialValue, finalValue) {
    Interval_constructor(this, initialValue, finalValue);
}

function Interval_constructor (obj, initialValue, finalValue) {
    obj.initialValue = initialValue;
    obj.finalValue = finalValue;
    obj.finished = false;
    obj.currentValue  = initialValue;
}

new Interval();
Interval.prototype.increment = function () {
    this.finished = true;
    return this.currentValue;
}

Interval.prototype.isFinished = function () {
    return this.finished;
}

Interval.prototype.getValue = function() {
    return this.currentValue;
}

Interval.prototype.getInitialValue = function () {
    return this.initialValue;
}

Interval.prototype.getFinalValue = function() {
    return this.finalValue;
}
function Interval_reverse (obj) {
    var temp= obj.initialValue;
    obj.initialValue= obj.finalValue;
    obj.finalValue = temp;
    obj.currentValue = obj.initialValue;
    obj.finished = false;
}

function Interval_reset (obj) {
    obj.currentValue = obj.initialValue;
    obj.finished = false;
}

Interval.prototype.reset = function () {
    Interval_reset(this);
}

Interval.prototype.reverse = function() {
    Interval_reverse(this);
}

function LinearInterval (initialValue, finalValue, steps) {
    Interval_constructor(this, initialValue, finalValue);
    this.steps = steps;
    this.incrementValue  = (this.finalValue - this.initialValue)/this.steps;
}
new LinearInterval();
LinearInterval.prototype = new Interval();

LinearInterval.prototype.reverse = function () {
    Interval_reverse(this);
    this.incrementValue = (this.finalValue - this.initialValue)/this.steps;
}

LinearInterval.prototype.increment = function () {
    this.currentValue += this.incrementValue;
    if (((this.initialValue>this.finalValue)&&(this.currentValue<=this.finalValue)) ||
        ((this.initialValue<this.finalValue)&&(this.currentValue>=this.finalValue))) {
        this.currentValue = this.finalValue;
        this.finished = true;
    }
    return this.currentValue;
}

function FactoredInterval_constructor (obj, initialValue, finalValue, factor, tolerance) {
    Interval_constructor(obj, initialValue, finalValue);
    obj.factor = factor;
    if (obj.factor > 1) {
        obj.factor = 1;
    }
    if (tolerance == undefined) {
        obj.tolerance = obj.factor*(0.05)*(obj.finalValue - obj.initialValue);
    } else {
        obj.tolerance = tolerance;
    }
}   
function FactoredInterval (initialValue, finalValue, factor, tolerance) {
    FactoredInterval_constructor(this, initialValue, finalValue, factor, tolerance);
}
new FactoredInterval();
FactoredInterval.prototype = new Interval();

function FactoredInterval_increment(obj) {
    obj.currentValue += (obj.finalValue - obj.currentValue)*(obj.factor);
    if (Math.abs(obj.finalValue-obj.currentValue) <= Math.abs(obj.tolerance)) {
        obj.currentValue = obj.finalValue;
        obj.finished = true;
    }
    return obj.currentValue;
}
FactoredInterval.prototype.increment = function () {
    return FactoredInterval_increment(this);
}

function ReverseFactoredInterval (initialValue, finalValue, factor, tolerance) {
    FactoredInterval_constructor(this,initialValue, finalValue, factor, tolerance);
}
new ReverseFactoredInterval();
ReverseFactoredInterval.prototype = new FactoredInterval();

function ReverseFactoredInterval_increment(obj) {
    if (obj.initialValue == obj.currentValue) {
        if (obj.finalValue>obj.initialValue) {
            obj.currentValue += obj.tolerance;
        } else {
            obj.currentValue -= obj.tolerance;
        }
    } else {
        obj.currentValue += (obj.currentValue - obj.initialValue)*(obj.factor);
        if (((obj.initialValue>obj.finalValue)&&(obj.currentValue<obj.finalValue)) ||
            ((obj.initialValue<obj.finalValue)&&(obj.currentValue>obj.finalValue))) {
            obj.currentValue = obj.finalValue;
            obj.finished = true;
        }
    }
    return obj.currentValue;
}
ReverseFactoredInterval.prototype.increment = function () {
    return ReverseFactoredInterval_increment(this);
}

function DoubleFactoredInterval (initialValue, finalValue, factor, tolerance) {
    FactoredInterval_constructor(this,initialValue, finalValue, factor, tolerance);
}
new DoubleFactoredInterval();
DoubleFactoredInterval.prototype= new FactoredInterval();

DoubleFactoredInterval.prototype.increment = function () {
    // are we before mid point?.
    if (((this.initialValue>this.finalValue)&&(this.currentValue>(this.initialValue+this.finalValue)/2))||
        ((this.initialValue<this.finalValue)&&(this.currentValue<(this.initialValue+this.finalValue)/2))) {
        // speed up
        return ReverseFactoredInterval_increment(this);
    } else {
        // in the later half slow down
        return FactoredInterval_increment(this);
    }
}

function Animation_constructor (obj, htmlObjectID, attribute, intervalObject,unitPostfix, dataPrefix, clockTick) {
        obj.id = Animation.all.length;
        Animation.all[obj.id]=obj;
        obj.htmlObjectID = htmlObjectID;
        obj.attribute = attribute;
        if (unitPostfix == undefined) {
            obj.unitPostfix = "";
        } else {
            obj.unitPostfix = unitPostfix;
        }
        if (dataPrefix == undefined) {
            obj.dataPrefix = "";
        } else {
            obj.dataPrefix = dataPrefix;
        }
        if (clockTick == undefined) {
            obj.clockTick = Animation.clockTick;
        } else {
            obj.clockTick = clockTick;
        }
        obj.started = false;
        obj.finished = false;
        obj.intervalObject = intervalObject;
        obj.timeout = null;
        obj.callback = "";
}

function Animation (htmlObjectID, attribute, intervalObject, unitPostfix, dataPrefix, clockTick) {
    if (htmlObjectID != undefined) {
        Animation_constructor(this, htmlObjectID, attribute, intervalObject, unitPostfix, dataPrefix, clockTick);
        Animation.all[Animation.all.length]=this;       
    }
}
new Animation();
Animation.all = new Array();
Animation.clockTick = 50;
Animation.run = function (animationID) {
    Animation.all[animationID].run();
}

Animation.prototype.start = function () {
    this.started = true;
    this.finished = false;
    this.setAttribute(this.intervalObject.getInitialValue());
    this.setTimeout();
}

Animation.prototype.setTimeout = function () {
    this.timeout = window.setTimeout("Animation.run("+this.id+")",this.clockTick);
//  this.timeout = window.setTimeout("alert(Animation.all["+this.id+"].run)", Animation.clockTick);
}

Animation.prototype.run = function () {
    if (this.isRunning()) {
        if (!this.intervalObject.isFinished()) {
            this.setAttribute(this.intervalObject.increment());
            this.setTimeout();
        } else {
            this.finish();
        }
    }
}

Animation.prototype.setAttribute = function (newValue) {
    eval("document.getElementById('"+this.htmlObjectID+"')."+this.attribute+"='"+this.dataPrefix+newValue+this.unitPostfix+"';");
}

Animation.prototype.getAttribute = function() {
    return eval("document.getElementById('"+this.htmlObjectID+"')."+this.attribute);
}

Animation.prototype.finish = function () {
    if (this.isRunning()) {
        this.kill();
        this.setAttribute(this.intervalObject.getFinalValue());
    }
    eval(this.callback);
}

Animation.prototype.kill = function () {
    if (this.isRunning()) {
        if (this.timeout !=null) {
            window.clearTimeout(this.timeout);
        }
        this.timeout = null;
        this.finished = true;
    }
}

Animation.prototype.setCallback = function (callstr) {
    this.callback = callstr;
}

Animation.prototype.reverse = function () {
    if (this.isRunning()) {
        this.finish();
    }
    this.intervalObject.reverse();
}

Animation.prototype.reset = function () {
    if (this.isRunning()) {
        this.kill();
    }
    this.finished = false;
    this.started = false;
    this.intervalObject.reset();
    this.setAttribute(this.intervalObject.getInitialValue());
}

Animation.prototype.isRunning = function () {
    return ((this.started)&&(!this.finished));
}

function OpenCloseAnimation(htmlObjectID,maxHeight, currentState, rate) {
    if (htmlObjectID!=undefined) {
        this.id = OpenCloseAnimation.all.length;
        OpenCloseAnimation.all[this.id]=this;
        var r = 10;
        if (rate!=undefined) {
            r = rate;
        }
        if (currentState !=undefined) {
            this.open = currentState;
        } else {
            this.open = true;
        }
        this.objId = htmlObjectID;
        this.maxHeight = maxHeight;
        this.rate = r;
        this.opening = false;
        this.closing = false;
        this.openAnimation = null;
        this.closeAnimation = null;
        //this.openAnimation = new Animation(htmlObjectId, "style.heigh", new LinearInterval(0,maxHeight,rate) , "px"));
        //this.openAnimation.setCallback("OpenCloseAnimation.openCallback('"+this.id+"')");
        //this.closeAnimation = new Animation(htmlObjectId, "style.heigh", new LinearInterval(maxHeight,0,rate) , "px"));
        //this.closeAnimation.setCallback("OpenCloseAnimation.closeCallback('"+this.id+"')");
    }
}
new OpenCloseAnimation();
OpenCloseAnimation.all = new Array();
OpenCloseAnimation.openCallback =function (id) {
    OpenCloseAnimation.all[id].openCallback();
}
OpenCloseAnimation.closeCallback = function (id) {
    OpenCloseAnimation.all[id].closeCallback();
}

OpenCloseAnimation.prototype.setValue = function (newValue) {
    document.getElementById(this.objId).style.height=""+newValue+"px";
}

OpenCloseAnimation.prototype.getValue = function () {
    var cv = document.getElementById(this.objId).style.height;
    var r= parseInt(cv);
    if (isNaN(r)) {
        return this.maxHeight;
    } else {
        return r;
    }
}

OpenCloseAnimation.prototype.startOpen = function(force) {
    if ((force!=undefined)&&(force)) {
        this.killAll();
        this.setValue(this.maxHeight);
        this.open = true;
    } else if (!this.opening) {
        this.killClosing();
        if (this.getValue()<this.maxHeight) {
            this.openAnimation = new Animation(this.objId,"style.height",new LinearInterval(this.getValue(),this.maxHeight,this.rate), "px");
            this.openAnimation.setCallback("OpenCloseAnimation.openCallback("+this.id+")");
            this.openAnimation.start();
            this.opening = true;
        } else {
            this.openAnimation = new Animation(this.objId,"style.height",new LinearInterval(this.getValue(),this.maxHeight,this.rate), "px");
            this.openAnimation.setCallback("OpenCloseAnimation.openCallback("+this.id+")");
            this.openAnimation.start();
            this.opening = true;
            //this.setValue(this.maxHeight);
            //this.open = true;
        }
    }
}

OpenCloseAnimation.prototype.openCallback = function () {
    this.opening = false;
    this.open = true;
}

OpenCloseAnimation.prototype.closeCallback = function() {
    this.closing = false;
    this.open = false;
}

OpenCloseAnimation.prototype.startClose = function(force) {
    if ((force!=undefined)&&(force)) {
        this.killAll();
        this.setValue(0);
        this.open = false;
    } else if (!this.closing) {
        this.killOpening();
        if (this.getValue()>0) {
            this.closeAnimation = new Animation(this.objId,"style.height",new LinearInterval(this.getValue(),0,this.rate),"px");
            this.closeAnimation.setCallback("OpenCloseAnimation.closeCallback("+this.id+")");
            this.closeAnimation.start();
            this.closing = true;
        } else {
            this.setValue(0);
            this.open = false;
        }
    }
}

OpenCloseAnimation.prototype.toggle = function (force) {
    if (this.open) {
        this.startClose(force);
    } else {
        this.startOpen(force);
    }
}

OpenCloseAnimation.prototype.isRunning = function () {
    return this.opening || this.closing;
}

OpenCloseAnimation.prototype.killOpening = function() {
    if (this.opening) {
        this.openAnimation.kill();
        this.opening = false;
    }
}

OpenCloseAnimation.prototype.killClosing =function() {
    if (this.closing) {
        this.closeAnimation.kill();
        this.closing = false;
    }
}

OpenCloseAnimation.prototype.killAll = function() {
    this.killClosing();
    this.killOpening();
}

function CompoundAnimation () {
    if (typeof(CompoundAnimation.all)!="undefined") {
        this.started=false;
        this.finished=false;
        this.animations = new Array();
        this.callback == "";
        this.id = CompoundAnimation.all.length;
        CompoundAnimation.all[this.id] = this;
    }
}
new CompoundAnimation();
CompoundAnimation.all = new Array();
CompoundAnimation.checkinCallback = function (id,animID) {
    for (var x=0; x<CompoundAnimation.all[id].animations.length; x++) {
        if (CompoundAnimation.all[id].animations[x].isRunning()) {
            return;
        }
    }
    CompoundAnimation.all[id].finished = true;
    CompoundAnimation.all[id].finishAll(); // for callback
}

CompoundAnimation.prototype.addAnimation = function (animObj) {
    var animId = this.animations.length;
    this.animations[animId] = animObj;
    animObj.compoundAnimId = this.id;
    animObj.compoundAnimSerial = animId;
    if (animObj.isRunning()) {
        animObj.reset();
    }
    animObj.setCallback("CompoundAnimation.checkinCallback("+this.id+","+animId+")");
}

CompoundAnimation.prototype.startAll = function () {
    for (var x=0;x<this.animations.length;x++) {
        this.animations[x].start();
    }
    this.started = true;
    this.finished = false;
}

CompoundAnimation.prototype.isRunning = function () {
    return ((this.started)&&(!this.finished));
}

CompoundAnimation.prototype.killAll = function () {
    if (this.isRunning()) {
        for (var x=0; x<this.animations.length; x++) {
            this.animations[x].kill();
        }
    }
    this.finished = true;
}

CompoundAnimation.prototype.finishAll = function () {
    if (this.isRunning()) {
        for (var x=0; x<this.animations.length; x++) {
            this.animations[x].finish();
        }
    }
    this.finished = true;
    eval(this.callback);
}

CompoundAnimation.prototype.resetAll = function () {
    for (var x=0; x<this.animations.length; x++) {
        this.animations[x].reset();
    }   
}

CompoundAnimation.prototype.reverseAll = function () {
    for (var x=0; x<this.animations.length; x++) {
        this.animations[x].reverse();
    }   
}

CompoundAnimation.prototype.setCallback = function (callstr) {
    this.callback = callstr;
}
