




var E_ANIMATION_COMPLETE = 0;
var E_ANIMATION_CANCEL = 0;

var CSS_HEIGHT = "height";
var CSS_WIDTH = "width";
var CSS_TOP = "top";
var CSS_LEFT = "left";
var CSS_OPACITY = "opacity";

var TRANSITION_ELEM = "elem";
var TRANSITION_ATTR = "attr";
var TRANSITION_TRANSFORMER = "transformer";
var TRANSITION_UPDATER = "updater";
var TRANSITION_INIT_VAL = "initVal";
var TRANSITION_TARGET_VAL = "targetVal";



function nlGetHeightAnimation(elem, initVal, targetVal, duration, callBack, transformer, updater)
{
    var animation = new NLAnimation (elem, duration, callBack);
    var transition = new nlGetSizePosTransition(elem, CSS_HEIGHT, initVal, targetVal, transformer, updater);
    animation.addTransition(transition);
    return animation;
}


function nlGetWidthAnimation(elem, initVal, targetVal, duration, callBack, transformer, updater)
{
    var animation = new NLAnimation (elem, duration, callBack);
    var transition = new nlGetSizePosTransition(elem, CSS_WIDTH, initVal, targetVal, transformer, updater);
    animation.addTransition(transition);
    return animation;
}


function nlGetPositionAnimation(elem, initX, initY, targetX, targetY, duration, transformer, updater, callBack)
{
    var animation = new NLAnimation (elem, duration, onComplete);
    var transition = new nlGetSizePosTransition(elem, CSS_TOP, initX, targetX, transformer, updater);
    animation.addTransition(transition);
    var transition = new nlGetSizePosTransition(elem, CSS_LEFT, initY, targetY, transformer, updater);
    animation.addTransition(transition);
    return animation;
}


function nlGetSizeAnimation(elem, initW, initH, targetW, targetH, duration, transformer, updater, callBack)
{
    var animation = new NLAnimation (elem, duration, onComplete);
    var transition = new nlGetSizePosTransition(elem, CSS_TOP, initW, targetW, transformer, updater);
    animation.addTransition(transition);
    var transition = new nlGetSizePosTransition(elem, CSS_LEFT, initH, targetH, transformer, updater);
    animation.addTransition(transition);
    return animation;
}


function nlGetOpacityAnimation(elem, initVal, targetVal, duration, transformer, updater, callBack)
{
    var animation = new NLAnimation (obj, duration, onComplete);
    var transition = new nlGetSizePosTransition(elem, CSS_OPACITY, initVal, targetVal, transformer, updater);
    animation.addTransition(transition);
    return animation;
}


function nlGetSizePosTransition(elem, attrName, initVal, targetVal, transformer, updater)
{
    if (!transformer)
        transformer = NLAnimationTransformer.linear;
    if (!updater)
        updater = NLAnimationUpdater.height;

    var properties = {};
    properties[TRANSITION_ELEM] = elem;
    properties[TRANSITION_ATTR] = attrName;
    properties[TRANSITION_TRANSFORMER] = transformer;
    properties[TRANSITION_UPDATER] = updater;
    properties[TRANSITION_INIT_VAL] = parseInt(initVal);
    properties[TRANSITION_TARGET_VAL] = parseInt(targetVal);
    return new NLAnimationTransition(properties);
}


function nlGetOpacityTransition(elem, attrName, initVal, targetVal, transformer, updater)
{
    if (!transformer)
        transformer = NLAnimationTransformer.linear;
    if (!updater)
        updater = NLAnimationUpdater.height;

    var properties = {};
    properties[TRANSITION_ELEM] = elem;
    properties[TRANSITION_ATTR] = attrName;
    properties[TRANSITION_TRANSFORMER] = transformer;
    properties[TRANSITION_UPDATER] = updater;
    properties[TRANSITION_INIT_VAL] = initVal;
    properties[TRANSITION_TARGET_VAL] = targetVal;
    return new NLAnimationTransition(properties);
}


function NLAnimation (elem, duration, callback)
{
    this.id = new Date().getTime();

    if (!window.nlAnimations)
        window.nlAnimations = new Array();
    window.nlAnimations[this.id] = this;

    this.elem = elem;
    this.duration = duration;
    this.callback = callback;
    this.timeElapsed = 0;
    this.updateInterval = 20; // time interval to refresh the state of the object
    this.transitions = new Array();
    this.timer = null;

    return this;
}

NLAnimation.prototype.addTransition = function addTransition(properties)
{
    if (!properties || !properties[TRANSITION_ELEM] || !properties[TRANSITION_TARGET_VAL] || !properties[TRANSITION_UPDATER])
        return null;

    var transition = new NLAnimationTransition(properties)

    if (transition)
        this.transitions[this.transitions.length] = transition ;
    return transition;
}


NLAnimation.prototype.start = function NLAnimation_start()
{
    this.timeElapsed = 0;
    this.timer = setTimeout(function (){this.run();}.bind(this),0);
}

NLAnimation.prototype.run = function NLAnimation_run()
{
    var progress = 0;
    if (this.duration <= 0 || this.updateInterval ==0)
        progress = 1;
    else
    {
        progress = this.timeElapsed * 1.0 / this.duration;
        this.timeElapsed += this.updateInterval;
    }

    for(var i = 0; i < this.transitions.length; i++)
    {
        this.transitions[i].update(progress);
    }

    if (progress >= 1 )
    {
        this.timer = null;
        this.onComplete();
    }
    else
        this.timer = setTimeout(function (){this.run();}.bind(this),this.updateInterval);

    return;
}

NLAnimation.prototype.stop = function NLAnimation_stop()
{
    if (!this.timer)
        return;
    clearTimeout(this.timer);
    this.timer = null;
    if (this.callback)
        this.callback(E_ANIMATION_CANCEL, this.elem, this);

}

NLAnimation.prototype.onComplete = function NLAnimation_onComplete()
{
    if (this.callback)
        this.callback(E_ANIMATION_COMPLETE, this.elem, this);
}



function NLAnimationTransition(properties)
{
    this.elem = properties[TRANSITION_ELEM];

    this.updater = properties[TRANSITION_UPDATER];

    if (properties[TRANSITION_TRANSFORMER] == null)
        this.transformer = properties[TRANSITION_TRANSFORMER] ;
    else
        this.transformer = NLAnimationTransformer.linear;

    if (properties[TRANSITION_INIT_VAL])
        this.initVal = properties[TRANSITION_INIT_VAL];
    else
        this.initVal = this.updater.getVal(this.elem);

    this.targetVal = properties[TRANSITION_TARGET_VAL];

    return this;
}



NLAnimationTransition.prototype.update = function NLAnimationTransition_update(progress)
{
    var valuePct = this.transformer.transform(progress);
    this.updater.updateVal(this.elem, this.initVal, this.targetVal, valuePct);
}

function NLAnimationUpdaterBase (attrName, unitStr)
{
    this.attrName = attrName;
    this.unitStr = unitStr;
}

NLAnimationUpdaterBase.prototype.getVal = function NLAnimationUpdaterBase_getVal (obj)
{
    if (!this.attrName)
        return null;
    return parseFloat(getRuntimeStyle(obj, this.attrName));
}

NLAnimationUpdaterBase.prototype.calculateVal = function NLAnimationUpdaterBase_calculateVal(initVal, targetVal, percent)
{
    return initVal + (targetVal - initVal)*percent;
}

NLAnimationUpdaterBase.prototype.updateVal = function NLAnimationUpdaterBase_updateVal(obj, initVal, targetVal, percent)
{
    if(!this.attrName)
        return;
    obj.style[this.attrName] = this.calculateVal(initVal, targetVal, percent) + (this.unitStr ? this.unitStr : "");
}


function NLAnimationSizePosUpdater (attrName)
{
    this.attrName = attrName;
    this.unitStr = "px";
}
NLAnimationSizePosUpdater.prototype = new NLAnimationUpdaterBase();

NLAnimationSizePosUpdater.prototype.calculateVal = function NLAnimationSizePosUpdater_calculateVal(initVal, targetVal, percent)
{
    return Math.round(initVal + (targetVal - initVal)*percent);
}

function NLAnimationOpacityUpdater ()
{
    this.attrName = CSS_OPACITY;
    this.unitStr = "";
}

NLAnimationOpacityUpdater.prototype = new NLAnimationUpdaterBase();

NLAnimationOpacityUpdater.prototype.getVal = function NLAnimationOpacityUpdater_getVal (obj)
{
    return parseFloat(getRuntimeStyle(obj, this.attrName));
}

NLAnimationOpacityUpdater.prototype.updateVal = function NLAnimationOpacityUpdater_updateVal(obj, initVal, targetVal, percent)
{
    if(!this.attrName)
        return;
    var val = this.calculateVal(initVal*100, targetVal*100, percent);
    setObjectOpacity(val);

}

var NLAnimationUpdater =
{
    height : new NLAnimationSizePosUpdater(CSS_HEIGHT),
    width : new NLAnimationSizePosUpdater(CSS_WIDTH),
    top : new NLAnimationSizePosUpdater(CSS_TOP),
    left : new NLAnimationSizePosUpdater(CSS_LEFT),
    opacity: new NLAnimationOpacityUpdater ()
};

var NLAnimationTransformer =
{
    
    linear :
    {
        transform : function(x)
        {
            return x;
        }
    },

    
    easeIn :
    {
        transform : function(x)
        {
            return x * x;
        }
    },

    
    easeOut :
    {
        transform : function(x)
        {
            return 1 - ( x - 1) * (x - 1);
        }
    },

    
    easeInEaseOut :
    {
        transform : function(x)
        {
            return 3*x*x - 2*x*x*x;
            
        }
    }
};
