
    //var framerate = 20; is not set in funktionen.php
    var framedelay = 1000 / framerate;
    var firstdelay = 500;
    var framenr = 0;
    var currentAnimators = new AnimatorList();
    var saveElemStateCache = new Object();
   
    var frametimer = window.setTimeout("renderFrame();", firstdelay);    
       
    try {
        // damit der mauszeiger und hintergrundbilder im ie6 nicht flackern
        document.execCommand("BackgroundImageCache", false, true);
    } catch(err) {
    }
           
    function debugMsg(msg) {
        document.getElementById("debugInfo").innerHTML += msg + "<br>\n";
    }
       
    function renderFrame() {
        framenr++;
        //debugMsg("Frame "+framenr+" --- "+currentAnimators.size()+" animators running.");
        for (var i=0; i<currentAnimators.size(); i++) {
            var animator = currentAnimators.get(i);
            if (!animator.finished) {
                animator.animate();
            }
        }
        // set timer for next frame
        frametimer = window.setTimeout("renderFrame();", framedelay);
    }
    
    function startAnimator(elem, animator, params) {
        var id = elem.id;
        if (!id) {
            alert("Elements without id cannot be animated.");
            return false;
        }
        currentAnimators.removeById( id ); // remove existing animator for the same element
        currentAnimators.add( eval("new "+animator+"(elem, id, params)") );
    }
    
    function stopAllAnimators() {
        currentAnimators.removeAll();
    }
    
    function saveElemState(id) {
        var elem = document.getElementById(id);
        saveElemStateCache[id] = new Object();
        saveElemStateCache[id]["x"] = elem.style.marginLeft;
        saveElemStateCache[id]["y"] = elem.style.marginTop;
        saveElemStateCache[id]["w"] = elem.style.width;
        saveElemStateCache[id]["h"] = elem.style.height;
        saveElemStateCache[id]["opacity"] = getOpacity(elem);
    }

    function restoreElemState(id) {
        var elem = document.getElementById(id);
        elem.style.marginLeft = saveElemStateCache[id]["x"];
        elem.style.marginTop = saveElemStateCache[id]["y"];
        elem.style.width = saveElemStateCache[id]["w"];
        elem.style.height = saveElemStateCache[id]["h"];
        setOpacity(elem, saveElemStateCache[id]["opacity"]);
    }
    
    function AnimatorList() {
        this.animators = new Array();
        
        this.size = function() {
            return this.animators.length;
        }
        
        this.add = function(animator) {
            this.animators.push(animator);
        }
        
        this.get = function(i) {
            return this.animators[i];
        }
        
        this.getById = function(id) {
            var i = this.find(id);
            if (i != -1) {
                return this.get(i);
            } else {
                return null;
            }
        }

        this.find = function(id) {
            for (var i=0; i<this.animators.length; i++) {
                var animator = this.animators[i];
                if (animator.id == id) {
                    return i;
                }
            }
            return -1;
        }
                
        this.remove = function(i) {
            var animatorsNew = new Array();
            for (var j=0; j<this.animators.length; j++) {
                if (j != i) {
                    animatorsNew.push(this.animators[j]);
                }
            }
            this.animators = animatorsNew;
        }

        this.removeById = function(id) {
            var i = this.find(id);
            if (i != -1) {
                this.remove(i);
            }
        }
        
        this.removeAll = function() {
            while(this.animators.length > 0) {
                this.remove(this.animators.length-1);
            }
        }
    }
    
    
    ////
    //// CSS helper functions
    ////
    
    function getOpacity(elem) {
        var value = elem.style.opacity;
        if (!value || value == "") {
            return 1.0;
        } else {
            return value;
        }
    }
    
    function setOpacity(elem, value) {
        if (value == 0.0) {
            elem.style.visibility = 'hidden';
        } else {
            elem.style.visibility = 'visible';
        }
        elem.style.opacity = value;
        elem.style.filter = 'alpha(opacity=' + value*100 + ')';
    }
    
    ////
    //// Animators
    ////
    
    /**
     * Abstract Animator.
     */
    function AbstractAnimator(elem, id, params) {
        this.elem = elem;
        this.id = id;
        this.params = params;
        // getParam funtion
        this.getParam = function(name) {
            var paramsArray = this.params.split(",");
            for (var i=0; i<paramsArray.length; i++) {
                var parArray = paramsArray[i].split("=");
                var parName = parArray[0];
                if (parName == name) {
                    var parValue = parArray[1];
                    return parValue;
                }
            }
            return null;
        }
        // read parameters
        this.x = this.getParam("x");
        this.y = this.getParam("y");
        this.w = this.getParam("w");
        this.h = this.getParam("h");
        this.opacity = this.getParam("opacity");
        this.t = this.getParam("t");
        this.onfinish = this.getParam("onfinish");
        this.alert = this.getParam("alert");
        this.onalert = this.getParam("onalert");
        // set other values
        this.finished = false;
        this.startX = parseInt(this.elem.style.marginLeft);
        this.startY = parseInt(this.elem.style.marginTop);
        this.startW = parseInt(this.elem.style.width);
        this.startH = parseInt(this.elem.style.height);
        this.startOpacity = getOpacity(this.elem);
        this.currentX = this.startX;
        this.currentY = this.startY;
        this.currentW = this.startW;
        this.currentH = this.startH;
        this.currentOpacity = this.startOpacity;
        this.xChange = this.x - this.startX;
        this.yChange = this.y - this.startY;
        this.wChange = this.w - this.startW;
        this.hChange = this.h - this.startH;
        this.opacityChange = this.opacity - this.startOpacity;
        this.currentFrame = 0;
        this.numberOfFrames = Math.round(this.t / framedelay);

        /**
         * Render one frame.
         */
        this.animate = function() {
            this.currentFrame++;
            if (this.currentFrame >= this.numberOfFrames) {
                this.finish();
            } else {
                // calculate coordinates
                this.currentX = this.ease(this.currentFrame, this.startX, this.xChange, this.numberOfFrames);
                this.currentY = this.ease(this.currentFrame, this.startY, this.yChange, this.numberOfFrames);
                if (this.w != null) {
                    this.currentW = this.ease(this.currentFrame, this.startW, this.wChange, this.numberOfFrames);
                }
                if (this.h != null) {
                    this.currentH = this.ease(this.currentFrame, this.startH, this.hChange, this.numberOfFrames);
                }
                if (this.opacity != null) {
                    this.currentOpacity = this.ease(this.currentFrame, this.startOpacity, this.opacityChange, this.numberOfFrames);
                }
                // update element
                this.elem.style.marginLeft = this.currentX + "px";
                this.elem.style.marginTop = this.currentY + "px";
                if (this.w != null) {
                    this.elem.style.width = this.currentW + "px";
                }
                if (this.h != null) {
                    this.elem.style.height = this.currentH + "px";
                }
                if (this.opacity != null) {
                    setOpacity(this.elem, this.currentOpacity);
                }
                // call alert?
                if (this.alert != null) {
                    if ((1.0*this.currentFrame)/(1.0*this.numberOfFrames) >= this.alert) {
                        this.alert = null;
                        eval(this.onalert);
                    }
                }
            }
        }
        
        /**
         * Render last frame.
         */
        this.finish = function() {
            this.currentFrame++;
            // calculate coordinates
            this.currentX = this.x;
            this.currentY = this.y;
            // update element
            this.elem.style.marginLeft = this.currentX;
            this.elem.style.marginTop = this.currentY;
            // set finished state
            this.finished = true;
            // remove animator
            currentAnimators.removeById(this.id);
            // execute onfinish
            if(this.onfinish != null) {
                eval(this.onfinish);
            }
        }
        
    }
    
    /**
     * LinearAnimator, extends AbstractAnimator.
     */
    function LinearAnimator(elem, id, params) {

        this.base = AbstractAnimator;
        this.base(elem, id, params);
                        
        /**
         * Easing calculation.
         * @param t     current time
         * @param b     beginning value
         * @param c     change in value
         * @param d     duration, change in time
         * @returns     current value
         */
        this.ease = function(t,b,c,d) {
            var r, changePerTime;
            changePerTime = c / d;
            r = b + (t * changePerTime);
            return r;
        }

    }

    /**
     * CubicAnimator, extends AbstractAnimator.
     */
    function CubicAnimator(elem, id, params) {

        this.base = AbstractAnimator;
        this.base(elem, id, params);
                
        /**
         * Easing points.
         * {Mx:0,My:0,Nx:44,Ny:-132,Px:8,Py:13}
         * {Mx:52,My:-119,Nx:54,Ny:-92,Px:10,Py:26}
         * {Mx:116,My:-185,Nx:58,Ny:-32,Px:26,Py:17}
         * {Mx:200, My:-200}
         */
        this.easingPoints = new Array();
        this.easingPoints[0] = new Object();
        this.easingPoints[0]["Mx"] = 0;
        this.easingPoints[0]["My"] = 0;
        this.easingPoints[0]["Nx"] = 44;
        this.easingPoints[0]["Ny"] = -132;
        this.easingPoints[0]["Px"] = 8;
        this.easingPoints[0]["Py"] = 13;
        this.easingPoints[1] = new Object();
        this.easingPoints[1]["Mx"] = 52;
        this.easingPoints[1]["My"] = -119;
        this.easingPoints[1]["Nx"] = 54;
        this.easingPoints[1]["Ny"] = -92;
        this.easingPoints[1]["Px"] = 10;
        this.easingPoints[1]["Py"] = 26;
        this.easingPoints[2] = new Object();
        this.easingPoints[2]["Mx"] = 116;
        this.easingPoints[2]["My"] = -185;
        this.easingPoints[2]["Nx"] = 58;
        this.easingPoints[2]["Ny"] = -32;
        this.easingPoints[2]["Px"] = 26;
        this.easingPoints[2]["Py"] = 17;
        this.easingPoints[3] = new Object();
        this.easingPoints[3]["Mx"] = 200;
        this.easingPoints[3]["My"] = -200;
        this.easingPoints[3]["Nx"] = 0;
        this.easingPoints[3]["Ny"] = 0;
        this.easingPoints[3]["Px"] = 0;
        this.easingPoints[3]["Py"] = 0;
        
        /**
         * Easing calculation.
         * @param t     current time
         * @param b     beginning value
         * @param c     change in value
         * @param d     duration, change in time
         * @returns     current value
         */
        this.ease = function(t,b,c,d) {
            var i,r;

            r = 200 * t/d;
            
            for(i=0; r>this.easingPoints[i+1]["Mx"]; i++) {
            }
            
            i = this.easingPoints[i];

            if (i["Px"] != 0) {
                r = (-i["Nx"]+Math.sqrt(i["Nx"]*i["Nx"]-4*i["Px"]*(i["Mx"]-r)))/(2*i["Px"]);
            } else {
                r=-(i["Mx"]-r)/i["Nx"];
            }

            r = b-c*((i["My"]+i["Ny"]*r+i["Py"]*r*r)/200);
            
            return r;
        }

    }
    
    
