/* Author:        Philip Trettner
 * Copyright:    (C) by Philip Trettner
 * Creation Date: 14/10/19
 * Last Update:   14/10/19
 */

// ========== CONFIG ========

var CYBEREFFECTS = {
    Author: "Philip Trettner",
    Version: "1.0.0",

    //Update Interval in ms of each Effect (must be > 0, 10 equates 100 fps, 20 equates 50 fps)
    Interval: 20
};

// ========== LIBRARY ========

var CyberEffects = {
    FadeIn: {
        Alpha: "Fadein from full transparent to full opaque",
        ClipWidthLeftRight: "Fadein from 0% to 100% width and from left to right",
        ClipWidthRightLeft: "Fadein from 0% to 100% width and from right to left",
        ClipWidthFromCenter: "Fadein from center to the left and right border",
        ClipHeightTopBottom: "Fadein from 0% to 100% height and from top to bottom",
        ClipHeightBottomTop: "Fadein from 0% to 100% height and from bottom to top",
        ClipHeightFromCenter: "Fadein from center to the top and bottom border",
        ClipFromCenter: "Fadein from center to all borders",
        ClipEdgeTopLeft: "Fadein from the top left edge to the bottom right edge",
        ClipEdgeTopRight: "Fadein from the top right edge to the bottom left edge",
        ClipEdgeBottomLeft: "Fadein from the bottom left edge to the top right edge",
        ClipEdgeBottomRight: "Fadein from the bottom right edge to the top left edge"
    },
    FadeOut: {
        Alpha: "Fadeout from full opaque to full transparent",
        ClipWidthLeftRight: "Fadeout from 100% to 0% width and from left to right",
        ClipWidthRightLeft: "Fadeout from 100% to 0% width and from right to left",
        ClipWidthToCenter: "Fadein from the left and right border to center",
        ClipHeightTopBottom: "Fadeout from 100% to 0% height and from top to bottom",
        ClipHeightBottomTop: "Fadeout from 100% to 0% height and from bottom to top",
        ClipHeightToCenter: "Fadein from the top and bottom border to center",
        ClipToCenter: "Fadein from all borders to center",
        ClipEdgeTopLeft: "Fadeout from the top left edge to the bottom right edge",
        ClipEdgeTopRight: "Fadeout from the top right edge to the bottom left edge",
        ClipEdgeBottomLeft: "Fadeout from the bottom left edge to the top right edge",
        ClipEdgeBottomRight: "Fadeout from the bottom right edge to the top left edge"
    },
    Common: {
        Wait: "Just waits the duration doing nothing",
        SimplePositioning: "Effect which changes (additive) Position from a start value to an end value (for adaption use e.g. .targetLeft instead of .style.left)",
        Coloring: "Effect which changes text- and background-color from start values to end values",
        SimpleSizing: "Effect which changes Size from a start value to an end value (AVN allowed, ref: target-Values (targetWidth, targetHeight))"
    }
};

// ========== PRIVATE ========

/* Creates a new CyberEffect
 * @param onstart called before the timer starts, function(element, options, progress) with context of the CyberEffect
 * @param ontick called each tick of the timer, function(element, options) with context of the config of the fading (tag = {element, effect, options})
 * @param onfinish called when the effect is over, function(element, options) with context of the config of the fading (tag = {element, effect, options})
 * @param defoptions object of options
 * @param description description of the effect
 */
function CyberEffect(onstart, ontick, onfinish, defoptions, description){
    this.onstart = onstart;
    this.ontick = ontick;
    this.onfinish = onfinish;
    this.options = defoptions;
    this.options.get = function(name, effect){
        return this[effect.aliasname + name];
    };
    this.description = description;
    this.aliasname = "";
    this.getDuration = function(options){
        //cyberinspector_add("options", this.options);
        return options[this.aliasname + "Duration"];
    };
}

/* Starts a new effect on an element
 * @param element Element which will be effected
 * @param ontick additional ontick function() with context of the fading config (optional)
 * @param onfinish additional onfinish function() with context of the fading config (optional)
 * @param progress how much the effect is already progressed (optional)
 * @return returns the fading ID
 */
CyberEffect.prototype.start = function(element, ontick, onfinish, progress){
    if (typeof progress != "number") progress = 0;

    var options = CyberDll.cloneObject(this.options);
    options.Save = {};
    if (typeof this.onstart == "function")
        this.onstart(element, options, progress);

    var f_ontick = ontick;
    var f_onfinish = onfinish;
    //    cyberinspector();
    //    cyberinspector_add("test", this);
    var fid = CyberDll.Fading.start(
        this.getDuration(options),
        CYBEREFFECTS.Interval,
        function(){
            if (typeof f_ontick == "function")
                f_ontick.call(this);
            if (typeof this.tag.effect.ontick == "function")
                this.tag.effect.ontick.call(this, this.tag.element, this.tag.options);
        },
        function(){
            if (typeof this.tag.effect.onfinish == "function")
                this.tag.effect.onfinish.call(this, this.tag.element, this.tag.options);
            if (typeof f_onfinish == "function")
                f_onfinish.call(this);
        },
        {
            effect: this,
            element: element,
            options: options
        },
        progress
        );

    return fid;
};
/* Sets the alias of an effect (and all of its subeffects)
 * The alias is a prefix for the otions (e.g. alias "test" will give you test_Duration for Duration)
 * @param name Prefix which shall be used
 * @return Returns a new Effect with this alias (old effect unchanged)
 */
CyberEffect.prototype.alias = function(name){
    var newopt = {};
    for (var attr in this.options){
        if (typeof this.options[attr] != "function"){
            var parts = attr.split('_');
            newopt[name + "_" + parts[parts.length-1]] = this.options[attr];
        }
    }

    var neweff = new CyberEffect(
        this.onstart,
        this.ontick,
        this.onfinish,
        newopt,
        this.description
        );
    neweff.aliasname = name + "_";
    neweff.getDuration = this.getDuration;
    //cyberinspector();
    //cyberinspector_add("test", neweff);
    return neweff;
};
/* Sets the Options of an effect
 * @param options Object with the Options (alias has to be as prefix)
 * @return Returns a new Object with the given Options overriding the old
 */
CyberEffect.prototype.setOptions = function(options){
    var newopt = CyberDll.cloneObject(this.options);
    for (var attr in options){
        newopt[attr] = options[attr];
    }
    var neweff = new CyberEffect(
        this.onstart,
        this.ontick,
        this.onfinish,
        newopt,
        this.description
        );
    neweff.aliasname = this.aliasname;
    neweff.getDuration = this.getDuration;
    return neweff;
};
CyberEffect.prototype.invert = function(){
    var options = CyberDll.cloneObject(this.options);
    var savethis = this;
    var neweff = new CyberEffect(
        function(ele, opt, prog){
            if (typeof savethis.onstart == "function")
                savethis.onstart.call(savethis, ele, opt, 1 - prog);
        },
        function(ele, opt){
            if (typeof savethis.ontick == "function"){
                var savetageff = this.tag.effect;
                this.tag.effect = savethis;
                this.progress = 1 - this.progress;
                this.runtime = this.duration - this.runtime;
                savethis.ontick.call(this, ele, opt);
                this.progress = 1 - this.progress;
                this.runtime = this.duration - this.runtime;
                this.tag.effect = savetageff;
            }
        },
        function(ele, opt){
            if (typeof savethis.ontick == "function"){
                this.tag.effect = savethis;
                this.progress = 0;
                this.runtime = 0;
                savethis.ontick.call(this, ele, opt);
            }
        //savethis.onfinish.call(this, ele, opt);
        /*var savetageff = this.tag.effect;
            if (typeof savethis.onfinish == "function"){
                this.tag.effect = savethis;
                savethis.onfinish.call(this, ele, opt);
            }*/
        },
        options, "(" + savethis.description + ") vice versa");

    neweff.aliasname = this.aliasname;

    neweff.getDuration = function(options){
        return savethis.getDuration(options);
    };

    neweff.alias = function(name){
        return savethis.alias(name);
    };

    return neweff;
};
/* Combines two effects which running simultaneously
 * The Duration is the Duration of this effect not of the parameter
 * @param effect Effect which shall be the partner
 * @return Returns the combined effect
 */
CyberEffect.prototype.combineWith = function(effect){
    var options = CyberDll.cloneObject(this.options);
    for (var attr in effect.options)
        if (typeof options[attr] == "undefined")
            options[attr] = effect.options[attr];
    
    var savethis = this;
    var saveeff = effect;
    var neweff = new CyberEffect(
        function(ele, opt, prog){
            if (typeof savethis.onstart == "function")
                savethis.onstart.call(savethis, ele, opt, prog);
            if (typeof saveeff.onstart == "function")
                saveeff.onstart.call(saveeff, ele, opt, prog);
        },
        function(ele, opt){
            var savetageff = this.tag.effect;
            if (typeof savethis.ontick == "function"){
                this.tag.effect = savethis;
                savethis.ontick.call(this, ele, opt);
            }
            if (typeof saveeff.ontick == "function"){
                this.tag.effect = saveeff;
                saveeff.ontick.call(this, ele, opt);
            }
            this.tag.effect = savetageff;
        },
        function(ele, opt){
            var savetageff = this.tag.effect;
            if (typeof savethis.onfinish == "function"){
                this.tag.effect = savethis;
                savethis.onfinish.call(this, ele, opt);
            }
            if (typeof saveeff.onfinish == "function"){
                this.tag.effect = saveeff;
                saveeff.onfinish.call(this, ele, opt);
            }
            this.tag.effect = savetageff;
        },
        options,
        "(" + this.description + ") combined with (" + effect.description + ")"
        );
    neweff.aliasname = this.aliasname;
    neweff.getDuration = function(options){
        return savethis.getDuration(options);
    };
    
    neweff.alias = function(name){
        return savethis.alias(name).combineWith(saveeff.alias(name));
    };
    
    return neweff;
};
/* Combines two effects where this effect runs after the parameter effect
 * @param effect Effect which runs first
 * @return Returns the combined effect
 */
CyberEffect.prototype.combineAfter = function(effect){
    var options = CyberDll.cloneObject(this.options);
    for (var attr in effect.options)
        if (typeof options[attr] == "undefined")
            options[attr] = effect.options[attr];
    
    var newthis = this;
    var argeff = effect;
    var argfinished = false;
    var progprop = effect.getDuration(effect.options) / (effect.getDuration(effect.options) + this.getDuration(this.options));
    var neweff = new CyberEffect(
        function(ele, opt, prog){
            //CyberDll.Console.write(prog + " - " + progprop);
            if (prog > progprop){
                if (typeof newthis.onstart == "function")
                    newthis.onstart.call(newthis, ele, opt, prog < progprop ? 0 : (prog - progprop) / (1 - progprop));
            } else {
                if (typeof argeff.onstart == "function")
                    argeff.onstart.call(argeff, ele, opt, prog > progprop ? 1 : prog / progprop);
            }
        },
        function(ele, options){
            if (this.runtime < argeff.getDuration(options)){
                if (typeof argeff.ontick == "function"){
                    var newconf = CyberDll.cloneObject(this);
                    newconf.duration = argeff.getDuration(options);
                    newconf.progress = this.runtime / argeff.getDuration(options);
                    newconf.tag = CyberDll.cloneObject(newconf.tag);
                    newconf.tag.effect = argeff;
                    argeff.ontick.call(newconf, ele, options);
                }
            } else {
                if (!argfinished){
                    if (typeof argeff.onfinish == "function"){
                        var newconf = CyberDll.cloneObject(this);
                        newconf.duration = argeff.getDuration(options);
                        newconf.runtime = argeff.getDuration(options);
                        newconf.progress = 1;
                        newconf.tag = CyberDll.cloneObject(newconf.tag);
                        newconf.tag.effect = argeff;
                        argeff.onfinish.call(newconf, ele, options);
                    }
                    argfinished = true;
                }
                if (typeof newthis.ontick == "function"){
                    var newconf = CyberDll.cloneObject(this);
                    newconf.duration = newthis.getDuration(options);
                    newconf.progress = CyberDll.Number.clip((this.runtime - argeff.getDuration(options)) / newthis.getDuration(options), 0, 1);
                    newconf.starttime = this.starttime + argeff.getDuration(options);
                    newconf.tag = CyberDll.cloneObject(newconf.tag);
                    newconf.tag.effect = newthis;
                    newthis.ontick.call(newconf, ele, options);
                }
            }
        },
        function(ele, options){
            if (!argfinished){
                if (typeof argeff.onfinish == "function"){
                    var newconf = CyberDll.cloneObject(this);
                    newconf.duration = argeff.getDuration(options);
                    newconf.runtime = argeff.getDuration(options);
                    newconf.progress = 1;
                    newconf.tag = CyberDll.cloneObject(newconf.tag);
                    newconf.tag.effect = argeff;
                    argeff.onfinish.call(newconf, ele, options);
                }
                argfinished = true;
            }
            if (typeof newthis.onfinish == "function"){
                var newconf = CyberDll.cloneObject(this);
                newconf.duration = newthis.getDuration(options);
                newconf.progress = 1;
                newconf.starttime = this.starttime + argeff.getDuration(options);
                newconf.tag = CyberDll.cloneObject(newconf.tag);
                newconf.tag.effect = newthis;
                newthis.onfinish.call(newconf, ele, options);
            }
            argfinished = false;
        },
        options,
        "(" + this.description + ") after (" + effect.description + ")"
        );
    neweff.getDuration = function(options){
        return newthis.getDuration(options) + argeff.getDuration(options);
    };
    neweff.alias = function(name){
        return newthis.alias(name).combineAfter(argeff.alias(name));
    };
    //neweff.aliasname = this.aliasname;
    return neweff;
};
/* Combines two effects where this effect runs before the parameter effect
 * @param effect Effect which runs last
 * @return Returns the combined effect
 */
CyberEffect.prototype.combineBefore = function(effect){
    return effect.combineAfter(this);
};
/* Combines an array of effects which starts at the given time
 * @param effs Array of Array with [[Startpoint, Effect], [Startpoint, Effect], ...]
 * @return Returns the combined mighty effect
 */
CyberEffect.combine = function(effs){
    var effects = effs.sort(function(a,b){
        return a[0] - b[0]
    });
    var options = {};
    for (var i = 0; i < effects.length; i++){
        if (effects[i].length == 2) effects[i].push(false);
        else effects[i][2] = false;

        for (var attr in effects[i][1].options)
            if (typeof options[attr] == "undefined")
                options[attr] = effects[i][1].options[attr];
    }
    
    var neweff = new CyberEffect(
        function(ele, opt, prog){
            for (var i = effects.length - 1; i >= 0; i--){
                if (typeof effects[i][1].onstart == "function")
                    effects[i][1].onstart.call(effects[i][1], ele, opt, prog);
                effects[i][2] = false;
            }
        },
        function(ele, options){
            var newconf = CyberDll.cloneObject(this);
            newconf.tag = CyberDll.cloneObject(newconf.tag);
            
            for (var i = 0; i < effects.length; i++){
                var dur = effects[i][1].getDuration(options);
                if (!effects[i][2] && this.runtime >= effects[i][0] && this.runtime <= effects[i][0] + dur){
                    if (typeof effects[i][1].ontick == "function"){
                        newconf.duration = dur;
                        newconf.progress = CyberDll.Number.clip((this.runtime - effects[i][0]) / dur, 0, 1);
                        newconf.runtime = this.runtime - effects[i][0];
                        newconf.starttime = this.starttime + effects[i][0];
                        newconf.tag.effect = effects[i][1];
                        effects[i][1].ontick.call(newconf, ele, options);
                    }
                }
                if (this.runtime >= effects[i][0] + dur && !effects[i][2]){
                    effects[i][2] = true;
                    if (typeof effects[i][1].onfinish == "function"){
                        newconf.duration = dur;
                        newconf.progress = 1;
                        newconf.runtime = dur;
                        newconf.starttime = this.starttime + effects[i][0];
                        newconf.tag.effect = effects[i][1];
                        effects[i][1].onfinish.call(newconf, ele, options);
                    }
                }
            }
        },
        function(ele, options){
            var newconf = CyberDll.cloneObject(this);
            newconf.tag = CyberDll.cloneObject(newconf.tag);
            
            for (var i = 0; i < effects.length; i++){
                if (!effects[i][2]){
                    if (typeof effects[i][1].onfinish == "function"){
                        var dur = effects[i][1].getDuration(options);
                        newconf.duration = dur;
                        newconf.progress = 1;
                        newconf.runtime = dur;
                        newconf.starttime = this.starttime + effects[i][0];
                        newconf.tag.effect = effects[i][1];
                        effects[i][1].onfinish.call(newconf, ele, options);
                    }
                }
                
                effects[i][2] = false;
            }
        },
        options
        );
    neweff.aliasname = effects[0][1].aliasname;
    neweff.getDuration = function(opts){
        var duration = 0;
        for (var i = 0; i < effects.length; i++){
            duration = Math.max(duration,
                effects[i][0] + effects[i][1].getDuration(opts));
        }
        return duration;
    };
    neweff.alias = function(name){
        var neweffs = [];
        for (var i = 0; i < effects.length; i++)
            neweffs.push([effects[i][0], effects[i][1].alias(name)]);
        return CyberEffect.combine(neweffs);
    };
    
    return neweff;
};

CyberEffects.FadeIn.Alpha = new CyberEffect(
    function(ele, opt, prog){
        if (prog == 0) ele.style["visibility"] = "hidden";
        CyberDll.DOM.setOpacity(ele, CyberDll.Number.extrapolate(prog, opt.get("MinOpacity", this), opt.get("MaxOpacity", this)));
    },
    function(ele, opt){
        ele.style["visibility"] = "visible";
        CyberDll.DOM.setOpacity(ele, CyberDll.Number.extrapolate(this.progress, opt.get("MinOpacity", this.tag.effect), opt.get("MaxOpacity", this.tag.effect)));
    },
    function(ele, opt){
        CyberDll.DOM.setOpacity(ele, opt.get("MaxOpacity", this.tag.effect));
    },
    {
        Duration: 500        ,
        MaxOpacity: 1,
        MinOpacity: 0
    },
    CyberEffects.FadeIn.Alpha
    );
        
CyberEffects.FadeOut.Alpha = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setOpacity(ele, CyberDll.Number.extrapolate(1 - prog, opt.get("MinOpacity", this), opt.get("MaxOpacity", this)));
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        CyberDll.DOM.setOpacity(ele, CyberDll.Number.extrapolate(1 - this.progress, opt.get("MinOpacity", this.tag.effect), opt.get("MaxOpacity", this.tag.effect)));
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setOpacity(ele, opt.get("MinOpacity", this.tag.effect));
    },
    {
        Duration: 500        ,
        MaxOpacity: 1,
        MinOpacity: 0
    },
    CyberEffects.FadeOut.Alpha
    );
        
CyberEffects.FadeIn.ClipWidthLeftRight = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, 0, Math.round(ele.offsetWidth * prog), "keep", "keep");
        ele.style["visibility"] = "hidden";
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, 0, Math.round(opt.Save.Width * this.progress), "keep", "keep");
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, null, null, "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipWidthLeftRight
    );
        
CyberEffects.FadeOut.ClipWidthLeftRight = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(ele.offsetWidth * prog), null, "keep", "keep");
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
        }
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(opt.Save.Width * this.progress), null, "keep", "keep");
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, opt.Save.Width, opt.Save.Width, "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipWidthLeftRight
    );
        
CyberEffects.FadeIn.ClipWidthRightLeft = new CyberEffect(
    function(ele, opt, prog){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(ele.offsetWidth * (1 - prog)), null, "keep", "keep");
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(opt.Save.Width * (1 - this.progress)), null, "keep", "keep");
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, null, null, "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipWidthRightLeft
    );
        
CyberEffects.FadeOut.ClipWidthRightLeft = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, null, Math.round(ele.offsetWidth * (1 - prog)), "keep", "keep");
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
        }
        CyberDll.DOM.setClippingRectFaster(ele, 0, Math.round(opt.Save.Width * (1 - this.progress)), "keep", "keep");
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, 0, 0, "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipWidthRightLeft
    );
        
CyberEffects.FadeIn.ClipHeightTopBottom = new CyberEffect(
    function(ele, opt, prog){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", 0, Math.round(ele.offsetHeight * prog));
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", 0, Math.round(opt.Save.Height * this.progress));
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", null, null);
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipHeightTopBottom
    );
        
CyberEffects.FadeOut.ClipHeightTopBottom = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(ele.offsetHeight * prog), null);
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(opt.Save.Height * this.progress), null);
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", opt.Save.Height, null);
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipHeightTopBottom
    );
        
CyberEffects.FadeIn.ClipHeightBottomTop = new CyberEffect(
    function(ele, opt, prog){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(ele.offsetHeight * (1 - prog)), null);
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(opt.Save.Height * (1 - this.progress)), null);
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", null, null);
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipHeightBottomTop
    );
        
CyberEffects.FadeOut.ClipHeightBottomTop = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", null, Math.round(ele.offsetHeight * (1 - prog)));
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", 0, Math.round(opt.Save.Height * (1 - this.progress)));
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", 0, 0);
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipHeightBottomTop
    );
        
CyberEffects.FadeIn.EdgeTopLeft = CyberEffects.FadeIn.ClipWidthLeftRight.combineWith(CyberEffects.FadeIn.ClipHeightTopBottom);
CyberEffects.FadeIn.EdgeTopRight = CyberEffects.FadeIn.ClipWidthRightLeft.combineWith(CyberEffects.FadeIn.ClipHeightTopBottom);
CyberEffects.FadeIn.EdgeBottomLeft = CyberEffects.FadeIn.ClipWidthLeftRight.combineWith(CyberEffects.FadeIn.ClipHeightBottomTop);
CyberEffects.FadeIn.EdgeBottomRight = CyberEffects.FadeIn.ClipWidthRightLeft.combineWith(CyberEffects.FadeIn.ClipHeightBottomTop);
CyberEffects.FadeOut.EdgeTopLeft = CyberEffects.FadeOut.ClipWidthLeftRight.combineWith(CyberEffects.FadeOut.ClipHeightTopBottom);
CyberEffects.FadeOut.EdgeTopRight = CyberEffects.FadeOut.ClipWidthRightLeft.combineWith(CyberEffects.FadeOut.ClipHeightTopBottom);
CyberEffects.FadeOut.EdgeBottomLeft = CyberEffects.FadeOut.ClipWidthLeftRight.combineWith(CyberEffects.FadeOut.ClipHeightBottomTop);
CyberEffects.FadeOut.EdgeBottomRight = CyberEffects.FadeOut.ClipWidthRightLeft.combineWith(CyberEffects.FadeOut.ClipHeightBottomTop);

CyberEffects.FadeIn.ClipWidthFromCenter = new CyberEffect(
    function(ele, opt, prog){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(ele.offsetWidth * (1 - prog) * 0.5), Math.round(ele.offsetWidth / 2 + ele.offsetWidth * prog * 0.5), "keep", "keep");;
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(opt.Save.Width * (1 - this.progress) * 0.5), Math.round(opt.Save.Width / 2 + opt.Save.Width * this.progress * 0.5), "keep", "keep");
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, null, null, "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipWidthFromCenter
    );
CyberEffects.FadeIn.ClipHeightFromCenter = new CyberEffect(
    function(ele, opt, prog){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(ele.offsetHeight * (1 - prog) * 0.5), Math.round(ele.offsetHeight / 2 + ele.offsetHeight * prog * 0.5));
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
            ele.style["visibility"] = "visible";
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(opt.Save.Height * (1 - this.progress) * 0.5), Math.round(opt.Save.Height / 2 + opt.Save.Height * this.progress * 0.5));
    },
    function(ele, opt){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", null, null);
    },
    {
        Duration: 500
    },
    CyberEffects.FadeIn.ClipHeightFromCenter
    );
CyberEffects.FadeIn.ClipFromCenter = CyberEffects.FadeIn.ClipWidthFromCenter.combineWith(CyberEffects.FadeIn.ClipHeightFromCenter);
CyberEffects.FadeOut.ClipWidthToCenter = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(ele.offsetWidth * prog * 0.5), ele.offsetWidth / 2 + Math.round(ele.offsetWidth * (1 - prog) * 0.5), "keep", "keep");
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Width) {
            opt.Save.Width = ele.offsetWidth;
        }
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(opt.Save.Width * this.progress * 0.5), opt.Save.Width / 2 + Math.round(opt.Save.Width * (1 - this.progress) * 0.5), "keep", "keep");
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, Math.round(opt.Save.Width * 0.5), Math.round(opt.Save.Width * 0.5), "keep", "keep");
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipWidthToCenter
    );
CyberEffects.FadeOut.ClipHeightToCenter = new CyberEffect(
    function(ele, opt, prog){
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(ele.offsetHeight * prog * 0.5), Math.round(ele.offsetHeight / 2 + ele.offsetHeight * (1 - prog) * 0.5));
        ele.style["visibility"] = "visible";
    },
    function(ele, opt){
        if (!opt.Save.Height) {
            opt.Save.Height = ele.offsetHeight;
        }
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(opt.Save.Height * this.progress * 0.5), Math.round(opt.Save.Height / 2 + opt.Save.Height * (1 - this.progress) * 0.5));
    },
    function(ele, opt){
        ele.style["visibility"] = "hidden";
        CyberDll.DOM.setClippingRectFaster(ele, "keep", "keep", Math.round(opt.Save.Height * 0.5), Math.round(opt.Save.Height * 0.5));
    },
    {
        Duration: 500
    },
    CyberEffects.FadeOut.ClipHeightToCenter
    );
CyberEffects.FadeOut.ClipToCenter = CyberEffects.FadeOut.ClipWidthToCenter.combineWith(CyberEffects.FadeOut.ClipHeightToCenter);


CyberEffects.Common.Wait = new CyberEffect(
    null,null,null,
    {
        Duration: 500
    },
    CyberEffects.Common.Wait
    );

CyberEffects.Common.SimplePositioning = new CyberEffect(
    function(ele, opt, prog){
        if (!CyberDll.CSS.elementHasStyleValue(ele, "left")){
            if (!CyberDll.CSS.elementHasStyleValue(ele, "right")){
                opt.Save.PositionMode = "L";
                ele.targetLeft = 0;
            } else {
                opt.Save.PositionMode = "R";
                ele.targetRight = CyberDll.Number.fromStyleValue(ele.style.right);
            }
        } else {
            if (!CyberDll.CSS.elementHasStyleValue(ele, "right")){
                opt.Save.PositionMode = "L";
                ele.targetLeft = CyberDll.Number.fromStyleValue(ele.style.left);
            } else {
                opt.Save.PositionMode = "LR";
                ele.targetLeft = CyberDll.Number.fromStyleValue(ele.style.left);
                ele.targetRight = CyberDll.Number.fromStyleValue(ele.style.right);
            }
        }
        if (!CyberDll.CSS.elementHasStyleValue(ele, "top")){
            if (!CyberDll.CSS.elementHasStyleValue(ele, "bottom")){
                opt.Save.PositionMode += "T";
                ele.targetTop = 0;
            } else {
                opt.Save.PositionMode += "B";
                ele.targetBottom = CyberDll.Number.fromStyleValue(ele.style.bottom);
            }
        } else {
            if (!CyberDll.CSS.elementHasStyleValue(ele, "bottom")){
                opt.Save.PositionMode += "T";
                ele.targetTop = CyberDll.Number.fromStyleValue(ele.style.top);
            } else {
                opt.Save.PositionMode += "TB";
                ele.targetTop = CyberDll.Number.fromStyleValue(ele.style.top);
                ele.targetBottom = CyberDll.Number.fromStyleValue(ele.style.bottom);
            }
        }
        
        if (opt.Save.PositionMode.indexOf("L") >= 0)
            ele.style.left = Math.round(ele.targetLeft + CyberDll.Number.extrapolate(prog, opt.get("StartX", this), opt.get("EndX", this))) + "px";
        if (opt.Save.PositionMode.indexOf("R") >= 0)
            ele.style.right = Math.round(ele.targetRight - CyberDll.Number.extrapolate(prog, opt.get("StartX", this), opt.get("EndX", this))) + "px";
        if (opt.Save.PositionMode.indexOf("T") >= 0)
            ele.style.top = Math.round(ele.targetTop + CyberDll.Number.extrapolate(prog, opt.get("StartY", this), opt.get("EndY", this))) + "px";
        if (opt.Save.PositionMode.indexOf("B") >= 0)
            ele.style.bottom = Math.round(ele.targetBottom - CyberDll.Number.extrapolate(prog, opt.get("StartY", this), opt.get("EndY", this))) + "px";
    },
    function(ele, opt){
        if (opt.Save.PositionMode.indexOf("L") >= 0)
            ele.style.left = Math.round(ele.targetLeft + CyberDll.Number.extrapolate(this.progress, opt.get("StartX", this.tag.effect), opt.get("EndX", this.tag.effect))) + "px";
        if (opt.Save.PositionMode.indexOf("R") >= 0)
            ele.style.right = Math.round(ele.targetRight - CyberDll.Number.extrapolate(this.progress, opt.get("StartX", this.tag.effect), opt.get("EndX", this.tag.effect))) + "px";
        if (opt.Save.PositionMode.indexOf("T") >= 0)
            ele.style.top = Math.round(ele.targetTop + CyberDll.Number.extrapolate(this.progress, opt.get("StartY", this.tag.effect), opt.get("EndY", this.tag.effect))) + "px";
        if (opt.Save.PositionMode.indexOf("B") >= 0)
            ele.style.bottom = Math.round(ele.targetBottom - CyberDll.Number.extrapolate(this.progress, opt.get("StartY", this.tag.effect), opt.get("EndY", this.tag.effect))) + "px";
    },
    function(ele, opt){
        if (opt.Save.PositionMode.indexOf("L") >= 0)
            ele.style.left = (ele.targetLeft + opt.get("EndX", this.tag.effect)) + "px";
        if (opt.Save.PositionMode.indexOf("R") >= 0)
            ele.style.right = (ele.targetRight - opt.get("EndX", this.tag.effect)) + "px";
        if (opt.Save.PositionMode.indexOf("T") >= 0)
            ele.style.top = (ele.targetTop + opt.get("EndY", this.tag.effect)) + "px";
        if (opt.Save.PositionMode.indexOf("B") >= 0)
            ele.style.bottom = (ele.targetBottom - opt.get("EndY", this.tag.effect)) + "px";
    },
    {
        StartX: 0,
        StartY: 0,
        EndX: 0,
        EndY: 0,
        Duration: 500
    },
    CyberEffects.Common.SimplePositioning
    );
CyberEffects.Common.Coloring = new CyberEffect(
    function(ele, opt, prog){
        if (opt.get("Color", this)){
            ele.style["color"] = opt.get("StartColor", this)
            .interpolate(opt.get("EndColor", this), 1 - prog).toCSSString();
        }
        if (opt.get("BGColor", this)){
            ele.style["backgroundColor"] = opt.get("StartBGColor", this)
            .interpolate(opt.get("EndBGColor", this), 1 - prog).toCSSString();
        }
    },
    function(ele, opt){
        if (opt.get("Color", this.tag.effect)){
            ele.style["color"] = opt.get("StartColor", this.tag.effect)
            .interpolate(opt.get("EndColor", this.tag.effect), 1 - this.progress).toCSSString();
        }
        if (opt.get("BGColor", this.tag.effect)){
            ele.style["backgroundColor"] = opt.get("StartBGColor", this.tag.effect)
            .interpolate(opt.get("EndBGColor", this.tag.effect), 1 - this.progress).toCSSString();
        }
    },
    function(ele, opt){
        if (opt.get("Color", this.tag.effect)){
            ele.style["color"] = opt.get("EndColor", this.tag.effect).toCSSString();
        }
        if (opt.get("BGColor", this.tag.effect)){
            ele.style["backgroundColor"] = opt.get("EndBGColor", this.tag.effect).toCSSString();
        }
    },
    {
        Color: true,
        StartColor: new CyberColor(0,0,0),
        EndColor: new CyberColor(0,0,0),
        BGColor: true,
        StartBGColor: new CyberColor(1,1,1),
        EndBGColor: new CyberColor(1,1,1),
        Duration: 500
    },
    CyberEffects.Common.Coloring
    );
CyberEffects.Common.SimpleSizing = new CyberEffect(
    function(ele, opt, prog){
        //CyberDll.Console.write(this.aliasname + ": " + prog + " - " + opt.get("StartHeight", this))
        if (opt.get("Width", this)){
            if (!ele.targetWidth) {
                ele.targetWidth = ele.offsetWidth;
            }
            ele.style["width"] = CyberDll.Number.extrapolate(prog,
                CyberDll.Number.fromAVN(opt.get("StartWidth", this), ele.targetWidth),
                CyberDll.Number.fromAVN(opt.get("EndWidth", this), ele.targetWidth)) + "px";
        }
        if (opt.get("Height", this)){
            if (!ele.targetHeight) {
                ele.targetHeight = ele.offsetHeight;
            }
            ele.style["height"] = CyberDll.Number.extrapolate(prog,
                CyberDll.Number.fromAVN(opt.get("StartHeight", this), ele.targetHeight),
                CyberDll.Number.fromAVN(opt.get("EndHeight", this), ele.targetHeight)) + "px";
        }
    },
    function(ele, opt){
        if (opt.get("Width", this.tag.effect)){
            ele.style["width"] = CyberDll.Number.extrapolate(this.progress,
                CyberDll.Number.fromAVN(opt.get("StartWidth", this.tag.effect), ele.targetWidth),
                CyberDll.Number.fromAVN(opt.get("EndWidth", this.tag.effect), ele.targetWidth)) + "px";
        }
        if (opt.get("Height", this.tag.effect)){
            ele.style["height"] = CyberDll.Number.extrapolate(this.progress,
                CyberDll.Number.fromAVN(opt.get("StartHeight", this.tag.effect), ele.targetHeight),
                CyberDll.Number.fromAVN(opt.get("EndHeight", this.tag.effect), ele.targetHeight)) + "px";
        }
    },
    function(ele, opt){
        if (opt.get("Width", this.tag.effect)){
            ele.style["width"] = CyberDll.Number.fromAVN(opt.get("EndWidth", this.tag.effect), ele.targetWidth) + "px";
        }
        if (opt.get("Height", this.tag.effect)){
            ele.style["height"] = CyberDll.Number.fromAVN(opt.get("EndHeight", this.tag.effect), ele.targetHeight) + "px";
        }
    },
    {
        Width: true,
        StartWidth: 0,
        EndWidth: "100%",
        Height: true,
        StartHeight: 0,
        EndHeight: "100%",
        Duration: 500
    },
    CyberEffects.Common.SimpleSizing
    );

