/* Author:        Philip Trettner
 * Copyright:    (C) by Philip Trettner
 * Creation Date: 08/09/19
 * Last Update:   23/09/19
 */

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

    // ============= MEMBERS ============ \\

    //Mouse values
    mousex: 0,
    mousey: 0,
    //Screen values
    screenw: 0,
    screenh: 0,
    scrollx: 0,
    scrolly: 0,
    //Flags
    Browser: {
        Opera: false,
        IE: false,
        Mozilla: false,
        Netscape: false,
        Safari: false,
        Konquerer: false,
        Name: ""
    },
    //Internal
    onmousemoves: Array(),

    // ============ FUNCTIONS =========== \\

    /* Adds a function to an existing function
     * (new function is executed after existing)
     * @param oldfunc Old Function
     * @param newfunc Function to add
     */
    addFunction: function(newfunc, oldfunc){
        if (typeof oldfunc != 'function')
            return newfunc;
        else if (typeof newfunc != 'function')
            return oldfunc;
        else return function() {
            oldfunc.apply(this, arguments);
            newfunc.apply(this, arguments);
        };
    },
    /* Adds a function to an existing function
     * (new function is executed before existing)
     * @param oldfunc Old Function
     * @param newfunc Function to add
     */
    addFunctionBefore: function(newfunc, oldfunc){
        if (typeof oldfunc != 'function')
            return newfunc;
        else if (typeof newfunc != 'function')
            return oldfunc;
        else return function() {
            newfunc.apply(this, arguments);
            oldfunc.apply(this, arguments);
        };
    },
    
    /* Clones an object
     * @param obj Object to clone
     */
    cloneObject: function(obj){
        var newobj = {};
        for (var attr in obj)
            newobj[attr] = obj[attr];
        return newobj;
    },
    
    /* Clones an object recursive (clones objects inside an object)
     * @param obj Object to clone
     */
    cloneObjectRecursive: function(obj){
        var newobj = {};
        for (var attr in obj){
            if (typeof obj[attr] == "object")
                newobj[attr] = CyberDll.cloneObjectRecursive(obj[attr]);
            else newobj[attr] = obj[attr];
        }
        return newobj;
    },

    /* Adds a global mouse move function
     * @param func Function to add
     */
    addGlobalMousemove: function(func){
        CyberDll.onmousemoves.push(func);
    },
    /* Removes a global mouse move function
     * @param func Function to remove
     */
    removeGlobalMousemove: function(func){
        var ind = CyberDll.onmousemoves.indexOf(func);
        if (ind >= 0){
            CyberDll.onmousemoves[ind] = CyberDll.onmousemoves[CyberDll.onmousemoves.length-1];
            CyberDll.onmousemoves.pop();
        }
    },

    /* Checks if an extension is installed
     */
    extension: function(ext_name){
        return eval("typeof " + ext_name.toUpperCase()) != "undefined" ;
    },

    /* Adds a new function to the onload
     * @param func Function to add
     */
    addOnload: function(func){
        window.onload = CyberDll.addFunction(func, window.onload);
    },

    /*
     * Returns a mouse object
     */
    getMouse: function(){
        CyberDll.updateVars();
    
        return {
            x:CyberDll.mousex,
            y:CyberDll.mousey,
            w:CyberDll.screenw,
            h:CyberDll.screenh,
            width:CyberDll.screenw,
            height:CyberDll.screenh,
            scrollx:CyberDll.scrollx,
            scrolly:CyberDll.scrolly
        };
    },

    /* Sets Mouse X and Y
     * @param e The eventobject of the event
     */
    onmousemove: function(e){
        if(!e) e = window.event;
        var body = (window.document.compatMode && window.document.compatMode == "CSS1Compat") ?
        window.document.documentElement : window.document.body || null;
    
        CyberDll.mousex = e.pageX ? e.pageX : e.clientX + body.scrollLeft;
        CyberDll.mousey = e.pageY ? e.pageY : e.clientY + body.scrollTop;

        CyberDll.scrollx = window.scrollX;
        CyberDll.scrolly = window.scrollY;
        if (undefined == window.scrollX){
            CyberDll.scrollx = body.scrollLeft;
            CyberDll.scrolly = body.scrollTop;
        }

        for (var i = 0; i < CyberDll.onmousemoves.length; i++){
            if (typeof CyberDll.onmousemoves[i] == "function"){
                CyberDll.onmousemoves[i](e);
            }
        }
    },

    /* Tests if a given object is an array
     * @param obj Object to test
     */
    isArray: function(obj){
        var index = obj.constructor.toString().indexOf("function Array(");
        if (index == -1) return false;
        return index < 10;
    },

    /* Tests if a given object is a string
     * @param obj Object to test
     */
    isString: function(obj){
        return typeof obj == "string";
    },

    /* Tests if a given object is a number
     * @param obj Object to test
     */
    isNumber: function(obj){
        return typeof obj == "number" && !isNaN(obj);
    },

    /* Tests if a given object is a function
     * @param obj Object to test
     */
    isFunction: function(obj){
        return typeof obj == "function";
    },
    
    /*
     * Provides function related to Strings
     */
    String:{

        /* Capitalizes a given String (e.g.: hElLo => Hello)
         */
        capitalize: function(str){
            var output = str.toLowerCase();
            output[0] = output[0].toUpperCase();
            return output;
        },

        /* Removes surrounding spaces of a string
         * @param str String to edit
         */
        trim: function(str){
            return CyberDll.String.trimBefore(CyberDll.String.trimAfter(str));
        },
        /* Removes all spaces of a string
         * @param str String to edit
         */
        trimAll: function(str){
            return str.replace(" ", "");
        },
        /* Removes leading spaces of a string
         * @param str String to edit
         */
        trimBefore: function(str){
            while(str.length > 0 && str.charAt(0) == " ")
                str = str.substr(1);
            return str;
        },
        /* Removes trailing spaces of a string
         * @param str String to edit
         */
        trimAfter: function(str){
            while(str.length > 0 && str.charAt(str.length - 1) == " ")
                str = str.substr(0, str.length-1);
            return str;
        },

        /* Converts a normal css style (e.g.: font-family) to a js css style (e.g.: fontFamily)
         */
        converCSStoJSCSS: function(style){
            while(style.indexOf('-') > 0){
                var i = style.indexOf('-');
                if (i == 0) return style;
                style = style.substring(0, i) + style.charAt(i+1).toUpperCase() + style.substring(i + 2);
            }
            return style;
        },
        
        /* Gets a string represantation of an object in a readable object form
         * @param obj Object to write
         * @param lvl Depth of the Object to dump (0 for only this one)
         * @param prefix Prefix of the output lines
         */
        dump: function(obj, lvl, prefix){
            if (typeof lvl != "number") lvl = 0;
            if (typeof prefix != "string") prefix = "";
            var s = prefix + "{";
            for (var attr in obj){
                s += "<br />"+prefix+"&nbsp;&nbsp;" + attr + ": " + typeof obj[attr];
                if (CyberDll.isNumber(obj[attr]) || typeof obj[attr] == "boolean" || CyberDll.isString(obj[attr]))
                    s += " ("+obj[attr]+")";
                if (lvl > 0 && typeof obj[attr] == "object")
                    s += "<br />" + CyberDll.String.dump(obj[attr], lvl - 1, prefix + "&nbsp;&nbsp;");
            }
            s += "<br />"+prefix+"}";
            return s;
        }
    },

    /* Provides numberrelated functions
     */
    Number:{
        /* clips the number to the range [min,max]
         * @param value Value to clip
         * @param min Minimal Value
         * @param max Maximal Value
         */
        clip: function(value, min, max){
            if (value < min) return min;
            if (value > max) return max;
            return value;
        },
          
        /* interpolates the number in the range [min,max] to [0,1]
         * @param value Value to clip
         * @param min Lower End
         * @param max Upper End
         */
        interpolate: function(value, min, max){
            if (min == max) return min;
            value = CyberDll.Number.clip(value, min, max);
            return (value - min) / (max - min);
        },
          
        /* extrapolates the number in the range [0,1] to [min,max]
         * @param value Value to clip
         * @param min Lower End
         * @param max Upper End
         */
        extrapolate: function(value, min, max){
            value = CyberDll.Number.clip(value, 0, 1);
            return min + (max - min) * value;
        },
        
        /* takes a style string of positioning and makes a number out of it
         * @param valstring String
         */
        fromPixelPointValue: function(valstring){
            return 1 * valstring.replace("pt", "").replace("px", "");
        },
        
        /* takes a style string of positioning and makes a number out of it (securer)
         * @param valstring String
         */
        fromStyleValue: function(valstring){
            if (typeof valstring != "string") return 0;
            if (valstring == "inherit") return 0;
            if (valstring == "auto") return 0;
            return 1 * valstring.replace("pt", "").replace("px", "").replace("em", "");
        },
        
        /* Reference Value Notation
         * 
         * RVN allows absolute and percentage data, referenced by a refsize
         * "" means 0
         */
        /* gets the value of a RVN-String
         * @param rvn RVN-Expression
         * @param refsize Reference size
         */
        fromRVN: function(rvn, refsize){
            if (CyberDll.isNumber(rvn)) return rvn;
            if (typeof rvn != "string") return undefined;
            if (rvn == "") return 0;
            if (rvn.indexOf("%") < 0) return rvn * 1;
            return rvn.replace("%", "") * refsize / 100;
        },
        
        /* Advanced Value Notation
         * 
         * AVN allowes advanced values, like "78%" or "50% - 10"
         * Percentage data measures the percentage of a reference size
         * Absolute data measures absolute size
         * Allowed are also combinitions with "+" or "-"
         */
        /* gets the value of an AVN
         * @param avn AVN string or number
         * @param refsize Reference size
         */
        fromAVN: function(avn, refsize){
            if (CyberDll.isNumber(avn)) return avn;
            if (typeof avn != "string") return undefined;
            var value = 0;
            var parts = CyberDll.String.trimAll(avn).split("+");
            for (var i = 0; i < parts.length; i++){
                var parts2 = parts[i].split("-");
                value += CyberDll.Number.fromRVN(parts2[0], refsize);
                for (var j = 2; j < parts2.length; j++)
                    value -= CyberDll.Number.fromRVN(parts2[j], refsize);
            }
            return value;
        }
    },
    
    /* Engine providing Fadings
     */
    Fading:{
        //Array with all the config data needed
        configs: Array(),

        /* Starts a new Fading
         * @param duration Total duration in msecs
         * @param interval Interval delay in msecs
         * @param ontick function executed each tick
         * @param onfinish function executed on finish
         * @param tag a Tag object (optional)
         * @param progress progress which already progressed
         */
        start: function(duration, interval, ontick, onfinish, tag, progress){
            if (typeof ontick != "function" && typeof onfinish != "function") return -1;
            if (typeof progress != "number") progress = 0;
            progress = CyberDll.Number.clip(progress, 0, 1);
            
            var fadeid = CyberDll.Fading.configs.length;
            var config = {
                progress: progress,
                starttime: new Date().getTime() - duration * progress,
                runtime: duration * progress,
                duration: duration,
                interval: interval,
                ontick: ontick,
                onfinish: onfinish,
                id: fadeid,
                tag: tag,
                finished: false
            };
            if (typeof config.ontick != "function"){
                config.tid = window.setInterval(function(){
                    window.clearInterval(config.tid);
                    config.runtime = config.duration;
                    config.progress = 1;
                    config.finished = true;
                    config.onfinish.apply(config);
                },duration);
            } else {
                config.tid = window.setInterval(function(){
                    var akttime = new Date().getTime();
                    config.runtime = akttime - config.starttime;
                    if (config.runtime > config.duration) config.runtime = config.duration;
                    config.progress = config.runtime / config.duration;

                    config.ontick.apply(config);

                    if (config.progress >= 1){
                        window.clearInterval(config.tid);
                        CyberDll.Fading.configs[config.id] = undefined;
                        config.progress = 1;
                        config.runtime = config.duration;
                        config.finished = true;

                        if (typeof config.onfinish == "function")
                            config.onfinish.apply(config);
                    }
                },interval);
            }
            CyberDll.Fading.configs.push(config);
            return fadeid;
        },

        /* Cancels a running fading
         * @param fadeid Fading ID
         * @return Config Data
         */
        stop: function(fadeid){
            if (!CyberDll.Fading.isFading(fadeid)) return undefined;

            var config = CyberDll.Fading.configs[fadeid];
            window.clearInterval(config.tid);
            CyberDll.Fading.configs[fadeid] = undefined;
            return config;
        },

        /* Checks if a Fading is running
         * @param fadeid Id to check
         */
        isFading: function(fadeid){
            if (fadeid < 0 || fadeid >= CyberDll.Fading.configs.length) return false;
            return CyberDll.Fading.configs[fadeid] != undefined && !CyberDll.Fading.configs[fadeid].finished;
        },

        /* Reads progress of a Fading
         * @param fadeid ID of the Fading
         */
        progressOf: function(fadeid){
            if (!CyberDll.Fading.isFading(fadeid)) return 0;
            return CyberDll.Fading.configs[fadeid].progress;
        }
    },
    
    /*
     * Provides function related to the DOM
     */
    DOM: {
        eclipsing_div: false,
        eclipsing_fid: -1,
        images: Array(),
      
        /* Applies a style in string form to an element
         * @param ele Element to apply to
         * @param stylestring Style in string format
         */
        applyStyle: function(ele, stylestring){
            if (typeof stylestring != "string") return;
            if (stylestring == "") return;
            var styles = stylestring.split(';');
            for (var i = 0; i < styles.length; i++) {
                var styleargs = styles[i].split(':');
                if (styleargs.length == 2){
                    styleargs[0] = CyberDll.String.converCSStoJSCSS(styleargs[0]);
                    ele.style[CyberDll.String.trim(styleargs[0])] =
                    CyberDll.String.trim(styleargs[1]);
                }
            }
        },

        /* adds a class to an element
         * @param ele Element to change
         * @param className name of the class to add
         */
        addClass: function(ele, className){
            if (!ele.className) ele.className = className;
            else ele.className += " " + className;
        },

        /* Applies a function to each element which matches
         * @param tag Tag of the matching elements (null for ignoring tag)
         * @param classname Class of the matching elements (null for ignoring class)
         * @param func function(ele) to call
         */
        foreachElement: function(tag, classname, func){
            if (typeof tag != "string") tag = "*";
            var eles = document.getElementsByTagName(tag);
            for (var i = 0; i < eles.length; i++){
                if (typeof classname != "string" || eles[i].className == classname)
                    func(eles[i]);
            }
        },
        
        /* preloads an image for later use
         * @param src image src (string) or array of string
         */
        preloadImage: function(src){
            if (CyberDll.isString(src)){
                var img = new Image();
                img.src = src;
                CyberDll.DOM.images.push(img);
            } else if (typeof src == "object" && src.prototype == Array){
                for (var i = 0; i < src.length; i++){
                    var img = new Image();
                    img.src = src[i];
                    CyberDll.DOM.images.push(img);
                }
            }
        },
        
        /* gets the absolute left value of an element
         * @param ele Element
         */
        getLeft: function(ele){
            var lPos = 0;
            if (ele.offsetParent){
                while (ele.offsetParent){
                    lPos += ele.offsetLeft;
                    ele = ele.offsetParent;
                }
            }
            else if (ele.x)
                lPos += ele.x;
            return lPos;
        },
        
        /* gets the absolute top value of an element
         * @param ele Element
         */
        getTop: function(ele){
            var tPos = 0;
            if (ele.offsetParent){
                while (ele.offsetParent){
                    tPos += ele.offsetTop;
                    ele = ele.offsetParent;
                }
            }
            else if (ele.y)
                tPos += ele.y;
            return tPos;
        },

        /* gets the bounding measurements of an element
         * @param ele element to measure
         * @return object {left, right, top, bottom, width, height}
         */
        getBounds: function(ele){
            var output ={
                left: CyberDll.DOM.getLeft(ele),
                top: CyberDll.DOM.getTop(ele),
                width: ele.offsetWidth,
                height: ele.offsetHeight
            };
            output.right = CyberDll.screenw - output.left;
            output.bottom = CyberDll.screenh - output.top;
            return output;
        },
        
        /* Makes the screen darkening
         * @param zIndex the z-Indexo of the div
         * @param opa the opacity of the div of [0,1]
         * @param onclick the onclick handler
         */
        eclipsing: function(zIndex, opa, onclick){
            if (CyberDll.DOM.eclipsing_div === false){
                CyberDll.DOM.eclipsing_div = document.createElement("div");
                document.getElementsByTagName("body")[0].appendChild(CyberDll.DOM.eclipsing_div);
            }
            
            CyberDll.DOM.eclipsing_div.maxOpa = opa;
            CyberDll.DOM.setOpacity(CyberDll.DOM.eclipsing_div, opa);
            CyberDll.DOM.eclipsing_div.style["position"] = "fixed";
            CyberDll.DOM.eclipsing_div.style["display"] = "block";
            CyberDll.DOM.eclipsing_div.style["zIndex"] = zIndex * 1;
            CyberDll.DOM.eclipsing_div.style["left"] = "0";
            CyberDll.DOM.eclipsing_div.style["right"] = "0";
            CyberDll.DOM.eclipsing_div.style["top"] = "0";
            CyberDll.DOM.eclipsing_div.style["bottom"] = "0";
            CyberDll.DOM.eclipsing_div.style["width"] = "100%";
            CyberDll.DOM.eclipsing_div.style["height"] = "100%";
            CyberDll.DOM.eclipsing_div.style["background"] = "#000";
            if (typeof onclick == "function"){
                CyberDll.DOM.eclipsing_div.onclick = onclick;
            }
                
            if (CyberDll.extension("CyberEffects")){
                var prog = 0;
                if (CyberDll.Fading.isFading(CyberDll.DOM.eclipsing_fid)){
                    prog = 1 - CyberDll.Fading.stop(CyberDll.DOM.eclipsing_fid).progress;
                }
                CyberDll.DOM.eclipsing_fid = CyberEffects.FadeIn.Alpha.setOptions({
                    MaxOpacity: opa
                }).start(CyberDll.DOM.eclipsing_div, null, null, prog);
            }
        },

        /* Removes a darkening of the screen
         */
        uneclipsing: function(){
            if (CyberDll.DOM.eclipsing_div !== false){
                if (CyberDll.extension("CyberEffects")){
                    var prog = 0;
                    if (CyberDll.Fading.isFading(CyberDll.DOM.eclipsing_fid)){
                        prog = 1 - CyberDll.Fading.stop(CyberDll.DOM.eclipsing_fid).progress;
                    }
                    CyberDll.DOM.eclipsing_fid = CyberEffects.FadeOut.Alpha.setOptions({
                        MaxOpacity: CyberDll.DOM.eclipsing_div.maxOpa
                    }).start(CyberDll.DOM.eclipsing_div, null,
                        function(){
                            CyberDll.DOM.eclipsing_div.style["display"] = "none";
                        }, prog);
                } else {
                    CyberDll.DOM.eclipsing_div.style["display"] = "none";
                }
            }
        },
        
        /* Sets the Transparency of an object
         * @param ele Element
         * @param opacity Opacity in [0,1]
         */
        setOpacity: function(ele, opacity){
            if (typeof opacity != "number") opacity = 0;
            CyberDll.Number.clip(opacity, 0, 1);
            if (!CyberDll.Browser.IE){
                ele.style["opacity"] = opacity + "";
            } else {
                ele.style["filter"] = "Alpha(opacity=" + Math.round(opacity * 100) +")";
            }
        },
        
        /* Sets a clipping rect for an element
         * @param ele Element to clip
         * @param left Left (in px, NaN for auto, "keep" for keeping old value)
         * @param top Top (in px, NaN for auto, "keep" for keeping old value)
         * @param right Right from left (in px, NaN for auto, "keep" for keeping old value)
         * @param bottom Bottom from top (in px, NaN for auto, "keep" for keeping old value)
         */
        setClippingRect: function(ele, left, right, top, bottom){
            if (left == "keep" || right == "keep" || top == "keep" || bottom == "keep"){
                if (typeof ele.style.clip != "undefined" && ele.style.clip != ""){
                    var oldclip = ele.style.clip;
                    //oldclip = "";
                    //var match = oldclip.match(/rect\([^0-9]*(\d)[^0-9]*(\d)[^0-9]*(\d)[^0-9]*(\d)[^0-9]*\)/i);
                    var match = oldclip.match(/rect\((.*)\)/i);
                    if (match != null){
                        var vals = match[1].split(',');
                        if (vals.length == 4){
                            if (top == "keep" && vals[0].indexOf("auto") < 0) top = CyberDll.Number.fromPixelPointValue(vals[0]);
                            if (right == "keep" && vals[1].indexOf("auto") < 0) right = CyberDll.Number.fromPixelPointValue(vals[1]);
                            if (bottom == "keep" && vals[2].indexOf("auto") < 0) bottom = CyberDll.Number.fromPixelPointValue(vals[2]);
                            if (left == "keep" && vals[3].indexOf("auto") < 0) left = CyberDll.Number.fromPixelPointValue(vals[3]);
                        }
                    }
                }
            }
            var wert1 = CyberDll.isNumber(top) ? top : "auto";
            var wert2 = CyberDll.isNumber(right) ? right : "auto";
            var wert3 = CyberDll.isNumber(bottom) ? bottom : "auto";
            var wert4 = CyberDll.isNumber(left) ? left : "auto";
            ele.style["clip"] = "rect(" + wert1 + "," + wert2 + "," + wert3 + "," + wert4 + ")";
        //alert(clip);//if (clip.indexOf("NaN") >= 0) alert(clip);
        },
        
        /* Sets a clipping rect for an element (higher performance)
         * @param ele Element to clip
         * @param left Left (in px, NaN for auto, "keep" for keeping old value)
         * @param top Top (in px, NaN for auto, "keep" for keeping old value)
         * @param right Right from left (in px, NaN for auto, "keep" for keeping old value)
         * @param bottom Bottom from top (in px, NaN for auto, "keep" for keeping old value)
         */
        setClippingRectFaster: function(ele, left, right, top, bottom){
            if (typeof ele.CyberDll == "undefined") ele.CyberDll = {};
            if (typeof ele.CyberDll.ClippingLeft == "undefined") ele.CyberDll.ClippingLeft = "auto";
            if (typeof ele.CyberDll.ClippingRight == "undefined") ele.CyberDll.ClippingRight = "auto";
            if (typeof ele.CyberDll.ClippingTop == "undefined") ele.CyberDll.ClippingTop = "auto";
            if (typeof ele.CyberDll.ClippingBottom == "undefined") ele.CyberDll.ClippingBottom = "auto";
          
            if (left === "keep") left = ele.CyberDll.ClippingLeft;
            else ele.CyberDll.ClippingLeft = left;
            if (right === "keep") right = ele.CyberDll.ClippingRight;
            else ele.CyberDll.ClippingRight = right;
            if (top === "keep") top = ele.CyberDll.ClippingTop;
            else ele.CyberDll.ClippingTop = top;
            if (bottom === "keep") bottom = ele.CyberDll.ClippingBottom;
            else ele.CyberDll.ClippingBottom = bottom;
            
            var wert1 = CyberDll.isNumber(top) ? top + "px" : "auto";
            var wert2 = CyberDll.isNumber(right) ? right + "px" : "auto";
            var wert3 = CyberDll.isNumber(bottom) ? bottom + "px" : "auto";
            var wert4 = CyberDll.isNumber(left) ? left + "px" : "auto";
            ele.style["clip"] = "rect(" + wert1 + "," + wert2 + "," + wert3 + "," + wert4 + ")";
        }
    },

    /*
     * Provides functions related to CSS
     */
    CSS:{

        /* Checks if this element has a usable style value (like 100px)
         * @param ele Element
         * @param style Style as string
         */
        elementHasStyleValue: function(ele, style){
            if (typeof ele.style[style] != "string") return false;
            if (ele.style[style] == "") return false;
            if (ele.style[style] == "auto") return false;
            if (ele.style[style] == "inherit") return false;
            return true;
        }
    },

    /* Provides functions dealing with points {x,y}
     */
    Points: {
        /* Calcs the Distance between two points
         * @param p1 point 1
         * @param p2 point 2
         */
        distance: function(p1, p2){
            var sqr = function(a){
                return a*a;
            };
            return Math.sqrt(sqr(p1.x-p2.x) + sqr(p1.y-p2.y));
        }
    },

    /* provides functions related to color handling
     */
    Color: {
        /* Interpolates between two colors
         * @param color1 first color
         * @param color2 second color
         * @param p p in [0,1] (p == 1 => color1, p == 0 => color2)
         */
        interpolate: function(color1, color2, p){
            return new CyberColor(color1.r * p + color2.r * (1 - p),color1.g * p + color2.g * (1 - p),color1.b * p + color2.b * (1 - p),color1.a * p + color2.a * (1 - p));
        }
    },

    /* Provides functions for canvassing
     */
    Canvas: {
        /* Creates a new canvas HTMLElement
         * @param parent
         */
        create: function(parent){
            var canvas = document.createElement("canvas");
            parent.appendChild(canvas);
            if (typeof G_vmlCanvasManager == "object") G_vmlCanvasManager.initElement(canvas);
            return canvas;
        }
    },

    /*
     * Provides function related to the Console
     */
    Console: {
        console_obj: false,
        auto_display_onwrite: true,

        /* Writes a new line to the console
         * @param msg Message to write
         */
        write: function(msg){
            if (!CyberDll.Console.console_obj) return;
            if (CyberDll.Console.auto_display_onwrite)
                CyberDll.Console.display();
            CyberDll.Console.console_obj.ContentElement.innerHTML = escape((msg + "").replace("/\n/", "<br />", "gmi")) + "<br />" + CyberDll.Console.console_obj.Element.innerHTML;
        },
        /* Sets the console test
         * @param msg Message to write
         */
        set: function(msg){
            if (!CyberDll.Console.console_obj) return;
            if (CyberDll.Console.auto_display_onwrite)
                CyberDll.Console.display();
            CyberDll.Console.console_obj.ContentElement.innerHTML = escape((msg + "").replace("/\n/", "<br />", "gmi"));
        },

        /* Writes a debug nr to the console
         * @param nr Writes this Nr
         */
        debug: function(nr){
            if (!CyberDll.Console.console_obj) return;
            if (CyberDll.Console.auto_display_onwrite)
                CyberDll.Console.display();
            CyberDll.Console.write("debug: "  + nr);
        },
        
        /* Writes an object in a readable object form to the console
         * @param obj Object to write
         * @param lvl Depth of the Object to dump (0 for only this one)
         * @param prefix Prefix of the output lines
         */
        dump: function(obj, lvl, prefix){
            if (!CyberDll.Console.console_obj) return;
            if (CyberDll.Console.auto_display_onwrite)
                CyberDll.Console.display();
            CyberDll.Console.write(CyberDll.String.dump(obj,lvl,prefix));
        },
        
        /* Watches a property of an object and write changes to the Console
         * @param obj Object to watch
         * @param prop property to watch
         */
        watch: function(obj, prop){
            obj.watch(prop, function(p, oldv, newv){
                CyberDll.Console.write(p + ": " + newv);
                return newv;
            });
        },

        /* Clears the console
         */
        clear: function(){
            if (!CyberDll.Console.console_obj) return;
            CyberDll.Console.console_obj.ContentElement.innerHTML = "";
        },

        /* Sets the Position of the Console
         * @param x X-Coord
         * @param y Y-Coord
         */
        setPosition: function(x,y){
            if (!CyberDll.Console.console_obj) return;
            CyberDll.Console.console_obj.Element.style["left"] = x + "px";
            CyberDll.Console.console_obj.Element.style["top"] = y + "px";
        },

        /*
         * Displays a console
         */
        display: function(){
            if (!CyberDll.Console.console_obj) return;
            CyberDll.Console.console_obj.Element.style["display"] = "block";
        },
        
        /*
         * Creates a console
         */
        create: function(){
            CyberDll.Console.console_obj = CyberTemplate.Default.render({
                Box:{
                    Ground:true
                }
            }, "");
            CyberDll.Console.console_obj.Element.style["position"] = "absolute";
            CyberDll.Console.console_obj.Element.style["left"] = "0";
            CyberDll.Console.console_obj.Element.style["top"] = "0";
            CyberDll.Console.console_obj.Element.style["width"] = "300px";
            CyberDll.Console.console_obj.Element.style["height"] = "250px";
            CyberDll.Console.console_obj.Element.style["padding"] = "5px";
            CyberDll.Console.console_obj.Element.style["fontFamily"] = "Courier New";
            CyberDll.Console.console_obj.Element.style["border"] = "solid 1px #0f0";
            CyberDll.Console.console_obj.Element.style["backgroundColor"] = "#000";
            CyberDll.Console.console_obj.Element.style["color"] = "#0f0";
            CyberDll.Console.console_obj.Element.style["display"] = "none";
            CyberDll.Console.console_obj.Element.style["zIndex"] = "999999";
            CyberDll.Console.console_obj.Element.style["overflow"] = "scroll";
            CyberDll.DOM.setOpacity(CyberDll.Console.console_obj.Element, 0.8);
            CyberDll.Console.console_obj.Element.onmouseover = function(){
                CyberDll.DOM.setOpacity(CyberDll.Console.console_obj.Element, 1);
            };
            CyberDll.Console.console_obj.Element.onmouseout = function(){
                CyberDll.DOM.setOpacity(CyberDll.Console.console_obj.Element, 0.8);
            };
            document.getElementsByTagName("body")[0].appendChild(CyberDll.Console.console_obj.Element);
        }
    },

    /*
     * Updates important variables
     */
    updateVars: function(){
        if (!CyberDll.Browser.IE){
            CyberDll.screenw = window.innerWidth;
            CyberDll.screenh = window.innerHeight;
        } else {
            if (document.body && document.body.clientHeight){
                CyberDll.screenw = document.body.clientWidth;
                CyberDll.screenh = document.body.clientHeight;
            } else {
                CyberDll.screenw = document.documentElement.clientWidth;
                CyberDll.screenh = document.documentElement.clientHeight;
            }
        }
    },

    createWorkarounds: function(){
        if (!Array.prototype.indexOf)
        {
            Array.prototype.indexOf = function(elt /*, from*/)
            {
                var len = this.length;

                var from = Number(arguments[1]) || 0;
                from = (from < 0)? Math.ceil(from): Math.floor(from);
                if (from < 0)
                    from += len;

                for (; from < len; from++)
                {
                    if (from in this &&this[from] === elt)
                        return from;
                }
                return -1;
            };
        }
    }
};

/* Creates a new Template
 * @param render Render is the ultimate Template function which provides
 * just everything the Template is capable of
 * Signature: function(options, content) where content is a string/HTMLObject/Array of HTMLObject and options is an object (e.g. for a box: options = { Box: {} })
 *
 * Standard options:
 * Box (e.g. Div) => { Box: {}}
 * Input          => { Input: { Type: "text", Size: 20 }}
 *
 * Other Options:
 * Specify e.g. Extension: "CyberTip"
 * Specify e.g. Class: "MyClass" for additional classes (should be class of ContentElement)
 * Specify e.g. Style: "color:#f00" for additional styles (style for ContentElement)
 * Specify e.g. Name: "MyName" for name of the ContentElement
 * Sepcify in a Box Ground: true for telling the template its a ground div
 * 
 * Known Options:
 * Box, Input, Extension, Class, Style, Rounded, Angled, Shadowed, Type, Size, Name, Value, Ground
 *
 * @return Returns an object { Element, ContentElement, OtherElements }, OtherElements is an array
 */
function CyberTemplate(render){
    this.render = render;
}

/* Object providing Templates
 */
CyberTemplates = { };

/* Default Template
 */
CyberTemplates.Default = new CyberTemplate(function(opts, content){
    var Element, ContentElement, OtherElements = Array();

    if (opts.Box){
        Element = ContentElement = document.createElement("div");
        Element.className = "cybertip_box";
        if (opts.Box.Ground){
            CyberDll.DOM.addClass(Element, "cybertip_ground");
        }
    } else if (opts.Input){
        Element = ContentElement = document.createElement("input");
        Element.className = "cybertip_input";
        if (opts.Input.Type) ContentElement.type = opts.Input.Type;
        if (opts.Input.Size) ContentElement.size = opts.Input.Size;
        if (opts.Input.Value) ContentElement.value = opts.Input.Value;
    } else {
        Element.className = "cybertip_span";
        Element = ContentElement = document.createElement("span");
    }

    if (content){
        if (typeof content != "object"){
            ContentElement.innerHTML = content + "";
        } else if (content.constructor == Array){
            for (var i = 0; i < content.length; i++){
                ContentElement.appendChild(content[i]);
            }
        } else {
            ContentElement.appendChild(content);
        }
    }
    
    if (opts.Class)
        ContentElement.className = opts.Class;
    if (opts.Name)
        ContentElement.name = opts.Name;
    if (opts.Style)
        CyberDll.DOM.applyStyle(ContentElement, opts.Style);
    
    return {
        Element: Element,
        ContentElement: ContentElement,
        OtherElements: OtherElements
    };
});
/* Round Template with shadow
 */
CyberTemplates.SimpleRoundShadow = new CyberTemplate(function(opts, content){
    var Element, ContentElement, OtherElements = Array();

    if (opts.Box){
        Element = document.createElement("div");
        ContentElement = document.createElement("div");
        ContentElement.className = "cyber_block";
        Element.appendChild(ContentElement);

        for (var i = 1; i < 9; i++){
            var edgeDiv = document.createElement("div");
            edgeDiv.className = "cyber_edge_" + i;
            Element.appendChild(edgeDiv);
        }
        
        Element.className = "cyber_box";
        if (opts.Box.Ground){
            CyberDll.DOM.addClass(Element, "cyber_ground");
        }
    } else if (opts.Input){
        Element = ContentElement = document.createElement("input");
        Element.className = "cyber_input";
        if (opts.Input.Type) ContentElement.type = opts.Input.Type;
        if (opts.Input.Size) ContentElement.size = opts.Input.Size;
        if (opts.Input.Value) ContentElement.value = opts.Input.Value;
    } else {
        Element.className = "cyber_span";
        Element = ContentElement = document.createElement("span");
    }

    if (content){
        if (typeof content != "object"){
            ContentElement.innerHTML = content + "";
        } else if (CyberDll.isArray(content)){
            for (var i = 0; i < content.length; i++){
                ContentElement.appendChild(content[i]);
            }
        } else {
            ContentElement.appendChild(content);
        }
    }
    
    if (opts.Class)
        ContentElement.className = opts.Class;
    if (opts.Name)
        ContentElement.name = opts.Name;
    if (opts.Style)
        CyberDll.DOM.applyStyle(ContentElement, opts.Style);
    
    return {
        Element: Element,
        ContentElement: ContentElement,
        OtherElements: OtherElements
    };
});

//Sets Default Template
CyberTemplate.Default = CyberTemplates.Default;

/* Class for holding Colors
 * @param r red value in [0,1]
 * @param g green value in [0,1]
 * @param b blue value in [0,1]
 * @param a alpha value in [0,1] (optional)
 */
function CyberColor(r, g, b, a){
    this.r = CyberDll.Number.clip(r, 0, 1);
    this.g = CyberDll.Number.clip(g, 0, 1);
    this.b = CyberDll.Number.clip(b, 0, 1);
    this.a = CyberDll.Number.clip(a, 0, 1);
    this.toHTMLString = function(){
        return "rgb(" + Math.round(255*this.r)+","+ Math.round(255*this.g)+","+ Math.round(255*this.b)+")";
    };
    this.toCSSString = function(){
        return "rgb(" + Math.round(255*this.r)+"," + Math.round(255*this.g)+"," + Math.round(255*this.b)+")";
    };
}
/* interpolates this color with another color
 * @param color second color
 * @param p p in [0,1], where p == 1 is this color and p == 0 the second
 */
CyberColor.prototype.interpolate = function(color, p){
    return CyberDll.Color.interpolate(this, color, p);
};

(function(){   
    //Browser Detection
    var brow = "Unknown";
    var n=navigator;
    var nua=n.userAgent;

    var op=(nua.indexOf('Opera')!=-1);
    var saf=(nua.indexOf('Safari')!=-1);
    var konq=(!saf && (nua.indexOf('Konqueror')!=-1) ) ? true : false;
    var moz=( (!saf && !konq ) && ( nua.indexOf('Gecko')!=-1 ) ) ? true : false;
    var ie=((nua.indexOf('MSIE')!=-1)&&!op);
    if (op)
    {
        brow = 'Opera';
    }
    else if (saf)
    {
        brow = 'Safari';
    }
    else if (konq)
    {
        brow = 'Konqueror';
    }
    else if (ie)
    {
        brow = 'Microsoft Internet Explorer';
    }
    else
    {
        brow = navigator.appName;
    }
    
    CyberDll.Browser.Opera = op;
    CyberDll.Browser.Safari = saf;
    CyberDll.Browser.Konquerer = konq;
    CyberDll.Browser.Mozilla = moz;
    CyberDll.Browser.Netscape = !!document.layers;
    CyberDll.Browser.IE = ie;
    CyberDll.Browser.name = brow;

    //Register Mouse move
    if(window.Event && document.captureEvents) {
        document.captureEvents(Event.MOUSEMOVE);
        document.onmousemove = CyberDll.addFunction(CyberDll.onmousemove, document.onmousemove);
    } else {
        document.attachEvent("onmousemove", CyberDll.onmousemove);
    }

    CyberDll.createWorkarounds();

    CyberDll.addOnload(function(){
        CyberDll.Console.create();
    });
})();


