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

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

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

    //Disables automatic tooltip on marked images
    DisableImages: false,
    //Disables browser tooltips on images
    DisableBrowserTooltip: true,
    //Uses the Title-Attribute of images for Tooltip-Title
    UseImageTitle: true,
    //Uses the Alt-Attribute of images for Tooltip-Description
    UseImageAlt: true
};

/* Advanced Value Notation
 * 
 * sometime the AVN is allowed.
 * AVN allowes advanced values, like "78%" or "50% - 10"
 * Percentage data measures the percentage of a reference size
 * Absolute data measures absolute size like px
 * Allowed are also combinitions with "+" or "-"
 */

//The default config data. Can be changed for each tooltip which parameters
//(e.g.: to change the title of one tooltip you could pass the parameter "title=my own title")
CYBERTIP.CONFIG = {};

//Default text of the title bar of the effect
CYBERTIP.CONFIG.Title = ""

//Default text of the description line of the effect
CYBERTIP.CONFIG.Description = ""

//Default style attribute of the tooltip
CYBERTIP.CONFIG.ToolStyle = ""

//Default style attribute of the tooltip-text
CYBERTIP.CONFIG.TextStyle = ""

//Default style attribute of the tooltip-title
CYBERTIP.CONFIG.TitleStyle = ""

//Default style attribute of the tooltip-description
CYBERTIP.CONFIG.DescriptionStyle = ""

//Default setting wether to close the tip onmouseout or not
//(No effect when used with 'popup')
CYBERTIP.CONFIG.CloseOnMouseOut = true

//Default setting wether to close the tip when clicking on the tip
//(No effect when used with 'popup' and CloseButton is false)
CYBERTIP.CONFIG.CloseOnClick = true

//Default setting for stopping the closing when hovering over the tooltip
//That means the tooltip is selectable
CYBERTIP.CONFIG.StopClosingOnHover = false

//Default setting for showing a little closing Button
//(ps: Button has to be styled to position it correctly, class: tooltip_closebutton)
CYBERTIP.CONFIG.CloseButton = true

//Default reference Container - expects id of an element (e.g. "MyID") - false means window
//(some values (like MinX) uses the width or height of the container as reference)
CYBERTIP.CONFIG.ReferenceContainer = false

//Default width of a tooltip (specify "auto" to adapt to the content)
//(Advanced Value Notation allowed, ref: width of Container)
CYBERTIP.CONFIG.Width = "auto"

//Default height of a tooltip (specify "auto" to adapt to the content)
//(Advanced Value Notation allowed, ref: height of Container)
CYBERTIP.CONFIG.Height = "auto"

//Default minimal width of a tooltip (0 means ignored)
//(Advanced Value Notation allowed, ref: width of Container)
CYBERTIP.CONFIG.MinWidth = 0

//Default maximal width of a tooltip (0 means ignored)
//(Advanced Value Notation allowed, ref: width of Container)
CYBERTIP.CONFIG.MaxWidth = 0

//Default minimal height of a tooltip (0 means ignored)
//(Advanced Value Notation allowed, ref: height of Container)
CYBERTIP.CONFIG.MinHeight = 0

//Default maximal height of a tooltip (0 means ignored)
//(Advanced Value Notation allowed, ref: height of Container)
CYBERTIP.CONFIG.MaxHeight = 0

//Default minimal most left coordinate of the tooltip (false means no checking)
//(Advanced Value Notation allowed, ref: width of Container)
CYBERTIP.CONFIG.MinX = 0

//Default maximal most right coordinate of the tooltip (false means no checking)
//(Advanced Value Notation allowed, ref: width of Container)
CYBERTIP.CONFIG.MaxX = false

//Default minimal most top coordinate of the tooltip (false means no checking)
//(Advanced Value Notation allowed, ref: height of Container)
CYBERTIP.CONFIG.MinY = 0

//Default maximal most bottom coordinate of the tooltip (false means no checking)
//(Advanced Value Notation allowed, ref: height of Container)
CYBERTIP.CONFIG.MaxY = false

//Default value of autosize //Autosize is experimental !!!
CYBERTIP.CONFIG.Autosize = false

//Default value of the autosize ration 
//(e.g. 1.5 means, that the width shall be 1.5 times greater than the height)
CYBERTIP.CONFIG.AutosizeRatio = 1.5

//Default minimal width of a tooltip sized by autosize (0 means ignored)
CYBERTIP.CONFIG.AutosizeMinWidth = 100

//Default maximal width of a tooltip sized by autosize (0 means ignored)
CYBERTIP.CONFIG.AutosizeMaxWidth = 1000

//Default timeout (in ms) before the effect shows
CYBERTIP.CONFIG.ShowTimeout = 150

//Default timeout (in ms) before the effect hides
CYBERTIP.CONFIG.HideTimeout = 400

//Default horizontal distance from the tooltip to the mouse
//(Advanced Value Notation allowed (ref. width))
CYBERTIP.CONFIG.TipDistanceX = 10

//Default vertical distance from the tooltip to the mouse
//(Advanced Value Notation allowed (ref. height))
CYBERTIP.CONFIG.TipDistanceY = 10

//Default value for "popup" of a tooltip
//Popup means, that the tooltip does not follow the mouse but is centered on the screen
CYBERTIP.CONFIG.Popup = false;

//Default value for "fixed" of a tooltip
//Fixed means, that the tooltip does not follow the mouse but is fixed
//either relative to an element, or a given value or the position of mouse when the tip is created
CYBERTIP.CONFIG.Fixed = true;

//Default x coordinate for a fixed tooltip
//false, null or undefined means, that other variants are used for calcing the position
//(Advanced Value Notation allowed, ref. container width)
CYBERTIP.CONFIG.FixedX = false;

//Default y coordinate for a fixed tooltip
//false, null or undefined means, that other variants are used for calcing the position
//(Advanced Value Notation allowed, ref. container height)
CYBERTIP.CONFIG.FixedY = false;

//Default vertical align (reference point of the element) for fixed tooltips 
//(T or Top = Top, M or Middle or C or Center = Center, B or Bottom = Bottom)
//(T+ or B+ adds the TipDistance to the reference point)
//(false means mouse y is used)
//(Advanced Value Notation allowed (ref. height))
CYBERTIP.CONFIG.FixedAlignVertical = "C"

//Default vertical anchor (reference point of the tooltip) for fixed tooltips 
//(T or Top = Top, M or Middle or C or Center = Center, B or Bottom = Bottom)
//(T+ or B+ adds the TipDistance to the reference point)
//(false means mouse y is used)
//(Advanced Value Notation allowed (ref. height))
CYBERTIP.CONFIG.FixedAnchorVertical = "C"

//Default horizontal align (reference point of the element) for fixed tooltips 
//(L or Left = Left, M or Middle or C or Center = Center, R or Right = Right)
//(L+ or R+ adds the TipDistance to the reference point)
//(false means mouse x is used)
//(Advanced Value Notation allowed (ref. width))
CYBERTIP.CONFIG.FixedAlignHorizontal = "R"

//Default horizontal anchor (reference point of the tooltip) for fixed tooltips 
//(L or Left = Left, M or Middle or C or Center = Center, R or Right = Right)
//(L+ or R+ adds the TipDistance to the reference point)
//(false means mouse x is used)
//(Advanced Value Notation allowed (ref. width))
CYBERTIP.CONFIG.FixedAnchorHorizontal = "L+"

//Set to true if the Show Effect can be inverted by the Hide Effect
CYBERTIP.CONFIG.EffectInvertable = true;

//Default effect on showing (ONLY WHEN CYBEREFFECTS INSTALLED)
CYBERTIP.CONFIG.ShowEffect = "CyberEffects.Common.SimplePositioning.combineWith(CyberEffects.FadeIn.Alpha)"
+ ".setOptions({StartY: -15, Duration: 500})"

//Default effect on hiding (ONLY WHEN CYBEREFFECTS INSTALLED)
CYBERTIP.CONFIG.HideEffect = "CyberEffects.Common.SimplePositioning.combineWith(CyberEffects.FadeOut.Alpha)"
+ ".setOptions({EndY: 15, Duration: 500})"

//Default percentage of darkening the screen when a popup opens (0 is nothing, 1 is total darkness)
CYBERTIP.CONFIG.PopupDarkness = 0.5

//Default z-Index of the popup (needed for some layouts)
CYBERTIP.CONFIG.ZIndex = 1337

//Default Template of a tooltip
CYBERTIP.CONFIG.Template = "CyberTemplates.SimpleRoundShadow";


// ========== PUBLIC =========

/* This function creates a new cybertip for the element from which it is called
 */
function cybertip(){
    CyberTip.tip.apply(this, arguments);
}

/* This function creates a popup, darkening the screen, focussing the view on the popup
 */
function cyberpopup(){
    CyberTip.popup.apply(this, arguments);
}



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

CyberTip.inited = false;
CyberTip.activeTips = Array();
CyberTip.popupped = false;

/* Constructor for a new CyberTip
 * @param args Argument Array - first element is the content, the following are options
 * @param element The parent element for which this tip is created
 */
function CyberTip(args, element) {  
    CyberTip.activeTips.push(this);
    this.status = "init";

    var config = CYBERTIP.CONFIG;
    
    this.toolDiv = null;
    this.contentDiv = document.createElement("div");
    this.titleDiv = document.createElement("div");
    this.descDiv = document.createElement("div");
    this.element = element;

    //CONFIG
    this._initConfigs(args, config, element);

    // ==== TOOL INIT ====

    this.contentDiv.className = "cybertip_tooltip_content";
    this.titleDiv.className = "cybertip_tooltip_title";
    this.descDiv.className = "cybertip_tooltip_description";

    this.titleDiv.style["display"] = "none";
    this.descDiv.style["display"] = "none";

    //ARGUMENT CONFIG
    if (args.length > 1) {
        this._parseArguments(args, config, element);
    }
    
    //if (typeof args[0] == "object" && args[0].prototype == HTMLElement){
    //    this.contentDiv.appendChild(args[0]);
    //} else {
    this.contentDiv.innerHTML = this.unescape ? unescape(args[0] + "") : (args[0] + "");
    //}
    
    //END OF INIT
    this._initTip(args, config, element);

    this._startFirsttime(args, config, element);
    
    document.getElementsByTagName("body")[0].appendChild(this.toolDiv);
}
/* Initialises config data
 */
CyberTip.prototype._initConfigs = function(args, config, element){
    this.tipdisx = config.TipDistanceX;
    this.tipdisy = config.TipDistanceY;

    this.template = eval(config.Template);

    this.titleDiv.innerHTML = config.Title;
    this.descDiv.innerHTML = config.Description;
    
    CyberDll.DOM.applyStyle(this.contentDiv, config.TextStyle);
    CyberDll.DOM.applyStyle(this.titleDiv, config.TitleStyle);
    CyberDll.DOM.applyStyle(this.descDiv, config.DescriptionStyle);

    this.showtimeout = config.ShowTimeout;
    this.hidetimeout = config.HideTimeout;
    this.timeoutid = -1;

    this.closeonmouseout = config.CloseOnMouseOut;
    this.stopclosingonhover = config.StopClosingOnHover;
    this.closebutton = config.CloseButton;

    this.unescape = true;
    
    this.fixedx = (CyberDll.isNumber(config.FixedX) || CyberDll.isString(config.FixedX)) ? config.FixedX : null;
    this.fixedy = (CyberDll.isNumber(config.FixedY) || CyberDll.isString(config.FixedY)) ? config.FixedY : null;
    this.fixedAlignH = config.FixedAlignHorizontal;
    this.fixedAnchorH = config.FixedAnchorHorizontal;
    this.fixedAlignV = config.FixedAlignVertical;
    this.fixedAnchorV = config.FixedAnchorVertical;
    
    this.refcontainer = null;

    this.width = config.Width;
    this.minwidth = config.MinWidth;
    this.maxwidth = config.MaxWidth;
    this.height = config.Height;
    this.minheight = config.MinHeight;
    this.maxheight = config.MaxHeight;

    this.minx = config.MinX;
    this.maxx = config.MaxX;
    this.miny = config.MinY;
    this.maxy = config.MaxY;
    
    this.showeffect = config.ShowEffect;
    this.hideeffect = config.HideEffect;    
    
    this.autosize = config.Autosize;
    this.autosizeratio = config.AutosizeRatio;
    this.autosizeminwidth = config.AutosizeMinWidth;
    this.autosizemaxwidth = config.AutosizeMaxWidth;

    this.popup = config.Popup;
    this.fixed = config.Fixed;

    this.effectinvertable = config.EffectInvertable;
    this.stopclosingonhover = config.StopClosingOnHover;
}
/* parses all Arguments
 */
CyberTip.prototype._parseArguments = function(args, config, element){    
    for (var i = 1; i < args.length; i++){
        var aktarg = unescape(args[i]);
        var aktarglow = aktarg.toLowerCase();
            
        if (aktarglow == "popup"){
            this.fixed = false;
            this.popup = true;
        } else if (aktarglow == "floating"){
            this.fixed = false;
            this.popup = false;
        } else if (aktarglow == "fixed"){
            this.fixed = true;
            this.popup = false;

        } else if (aktarglow.indexOf("title=") == 0){
            this.titleDiv.innerHTML = aktarg.substr(6);
        } else if (aktarglow.indexOf("desc=") == 0){
            this.descDiv.innerHTML = aktarg.substr(5);
        } else if (aktarglow.indexOf("description=") == 0){
            this.descDiv.innerHTML = aktarg.substr(12);
        } else if (aktarglow.indexOf("textstyle=") == 0){
            CyberDll.DOM.applyStyle(this.contentDiv, aktarg.substr(10));
        } else if (aktarglow.indexOf("titlestyle=") == 0){
            CyberDll.DOM.applyStyle(this.titleDiv, aktarg.substr(11));
        } else if (aktarglow.indexOf("descstyle=") == 0){
            CyberDll.DOM.applyStyle(this.descDiv, aktarg.substr(10));

        } else if (aktarglow.indexOf("template=") == 0){
            this.template = eval(aktarg.substr(9));

        } else if (aktarglow.indexOf("tipdisx=") == 0){
            this.tipdisx = aktarg.substr(8) * 1;
        } else if (aktarglow.indexOf("tipdisy=") == 0){
            this.tipdisy = aktarg.substr(8) * 1;
        } else if (aktarglow.indexOf("tipdistancex=") == 0){
            this.tipdisx = aktarg.substr(13) * 1;
        } else if (aktarglow.indexOf("tipdistancey=") == 0){
            this.tipdisy = aktarg.substr(13) * 1;

        } else if (aktarglow.indexOf("zindex=") == 0){
            this.toolDiv.style["zIndex"] = aktarg.substr(7);

        } else if (aktarglow.indexOf("fixedx=") == 0){
            this.fixedx = aktarg.substr(7);
            if (this.fixedx == "null") this.fixedx = null;
        } else if (aktarglow.indexOf("fixedy=") == 0){
            this.fixedy = aktarg.substr(7);
            if (this.fixedy == "null") this.fixedy = null;
        } else if (aktarglow.indexOf("fixedalignh=") == 0){
            this.fixedAlignH = aktarg.substr(12);
        } else if (aktarglow.indexOf("fixedalignv=") == 0){
            this.fixedAlignV = aktarg.substr(12);
        } else if (aktarglow.indexOf("fixedanchorh=") == 0){
            this.fixedAnchorH = aktarg.substr(13);
        } else if (aktarglow.indexOf("fixedanchorv=") == 0){
            this.fixedAnchorV = aktarg.substr(13);

        } else if (aktarglow.indexOf("width=") == 0){
            this.width = aktarg.substr(6);
        } else if (aktarglow.indexOf("minwidth=") == 0){
            this.minwidth = aktarg.substr(9) * 1;
        } else if (aktarglow.indexOf("maxwidth=") == 0){
            this.maxwidth = aktarg.substr(9) * 1;
        } else if (aktarglow.indexOf("height=") == 0){
            this.height = aktarg.substr(7);
        } else if (aktarglow.indexOf("minheight=") == 0){
            this.minheight = aktarg.substr(10) * 1;
        } else if (aktarglow.indexOf("maxheight=") == 0){
            this.maxheight = aktarg.substr(10) * 1;
        } else if (aktarglow.indexOf("minx=") == 0){
            this.minx = aktarg.substr(4);
            if (this.minx == "false") this.minx = false;
        } else if (aktarglow.indexOf("maxx=") == 0){
            this.maxx = aktarg.substr(4);
            if (this.maxx == "false") this.maxx = false;
        } else if (aktarglow.indexOf("miny=") == 0){
            this.miny = aktarg.substr(4);
            if (this.miny == "false") this.miny = false;
        } else if (aktarglow.indexOf("maxy=") == 0){
            this.maxx = aktarg.substr(4);
            if (this.maxy == "false") this.maxy = false;

        } else if (aktarglow.indexOf("showtimeout=") == 0){
            this.showtimeout = 1 * aktarg.substr(12);
            if (this.showtimeout < 1) this.showtimeout = 1;
        } else if (aktarglow.indexOf("hidetimeout=") == 0){
            this.hidetimeout = 1 * aktarg.substr(12);
            if (this.hidetimeout < 1) this.hidetimeout = 1;
        } else if (aktarglow.indexOf("showeffect=") == 0){
            try {
                this.showeffect = eval(aktarg.substr(11));
            } catch (e) {
                this.showeffect = undefined;
            }
        } else if (aktarglow.indexOf("hideeffect=") == 0){
            try {
                this.hideeffect = eval(aktarg.substr(11));
            } catch (e) {
                this.hideeffect = undefined;
            }

        } else if (aktarglow == "autosize"){
            this.autosize = true;
        } else if (aktarglow.indexOf("autosizeratio=") == 0){
            this.autosizeratio = aktarg.substr(14) * 1;
        } else if (aktarglow.indexOf("autosizeminwidth=") == 0){
            this.autosizeminwidth = aktarg.substr(17) * 1;
        } else if (aktarglow.indexOf("autosizemaxwidth=") == 0){
            this.autosizemaxwidth = aktarg.substr(17) * 1;

        } else if (aktarglow.indexOf("refcontainer=") == 0){
            this.refcontainer = document.getElementById(aktarg.substr(13));
        } else if (aktarglow.indexOf("referencecontainer=") == 0){
            this.refcontainer = document.getElementById(aktarg.substr(20));
        } else if (aktarglow == "nounescape"){
            this.unescape = false;
        }
    }
}
/* initialises Settings after Config is inited and Arguments are parsed
 */
CyberTip.prototype._initTip = function(args, config, element){     
    var mouse = CyberDll.getMouse();
    
    this.tip_offsetx = this.tipdisx;
    this.tip_offsety = this.tipdisy;

    if (CyberDll.isString(config.ReferenceContainer))
        this.refcontainer = document.getElementById(config.ReferenceContainer);

    if (CyberDll.isString(this.showeffect)) this.showeffect = eval(this.showeffect);
    if (CyberDll.isString(this.hideeffect)) this.hideeffect = eval(this.hideeffect);
    this.invertshoweffect = this.hideeffect;
    this.inverthideeffect = this.showeffect;
    if (!this.effectinvertable){
        if (this.showeffect && this.showeffect.invert)
            this.invertshoweffect = this.showeffect.invert();
        if (this.hideeffect && this.hideeffect.invert)
            this.inverthideeffect = this.hideeffect.invert();
    }

    this.refwidth = this.refcontainer ? this.refcontainer.offsetWidth : CyberDll.screenw;
    this.refheight = this.refcontainer ? this.refcontainer.offsetHeight : CyberDll.screenh;

    this.fixedx = CyberDll.Number.fromAVN(this.fixedx);
    if (this.fixedx == undefined) this.fixedx = null;
    this.fixedy = CyberDll.Number.fromAVN(this.fixedy);
    if (this.fixedy == undefined) this.fixedy = null;

    this.width = CyberDll.Number.fromAVN(this.width, this.refwidth);
    this.minwidth = CyberDll.Number.fromAVN(this.minwidth, this.refwidth);
    this.maxwidth = CyberDll.Number.fromAVN(this.maxwidth, this.refwidth);
    this.height = CyberDll.Number.fromAVN(this.height, this.refheight);
    this.minheight = CyberDll.Number.fromAVN(this.minheight, this.refheight);
    this.maxheight = CyberDll.Number.fromAVN(this.maxheight, this.refheight);

    if (this.minx !== false)
        this.minx = CyberDll.Number.fromAVN(this.minx, this.refwidth);
    if (this.maxx !== false)
        this.maxx = CyberDll.Number.fromAVN(this.maxx, this.refwidth);
    if (this.miny !== false)
        this.miny = CyberDll.Number.fromAVN(this.miny, this.refheight);
    if (this.maxy !== false)
        this.maxy = CyberDll.Number.fromAVN(this.maxy, this.refheight);

    if (this.showtimeout <= 0) this.showtimeout = 1;
    if (this.hidetimeout <= 0) this.hidetimeout = 1;

    this.toolDiv = this.template.render({
        Box:{
            Ground:true
        }
    },[this.titleDiv,this.contentDiv,this.descDiv]).Element;

    this.baseClassName = (this.toolDiv.className ? this.toolDiv.className + " " : "") + "cybertip_tooltip";

    this.toolDiv.onclick = this.onclick;
    this.toolDiv.style["position"] = "absolute";
    this.toolDiv.style["visibility"] = "hidden";
    this.toolDiv.style["left"] = "0";
    this.toolDiv.style["top"] = "0";
    this.toolDiv.style["zIndex"] = "" + config.ZIndex;
    CyberDll.DOM.applyStyle(this.toolDiv, config.ToolStyle);

    if (this.popup){
        if (CyberTip.popupped) return;
        CyberTip.popupped = true;
        var thisTip = this;
        var clickFunc = function() {
            thisTip.onclick.apply(thisTip);
        }
        this.toolDiv.onclick = clickFunc;
        this.baseClassName += " cybertip_popup";
        CyberDll.DOM.eclipsing(this.toolDiv.style["zIndex"] * 1 - 1, config.PopupDarkness, clickFunc);
    }

    if (this.fixed){
        if (typeof this.element != "object"){
            this.fixedAlignH = null;
            this.fixedAnchorH = null;
            this.fixedAlignV = null;
            this.fixedAnchorV = null;
        } else {
            if (this.fixedx != null){
                this.fixedAlignH = null;
                this.fixedAnchorH = null;
            }
            if (this.fixedy != null){
                this.fixedAlignV = null;
                this.fixedAnchorV = null;
            }
        }
        this.calcFixedPosition();
    }

    if (this.titleDiv.innerHTML != "") this.titleDiv.style["display"] = "block";
    else this.titleDiv.style["display"] = "none";
    if (this.descDiv.innerHTML != "") this.descDiv.style["display"] = "block";
    else this.descDiv.style["display"] = "none";

    this.check_tooltip_position(mouse);
    this.setPosition(mouse);

    this.toolDiv.style["width"] = "auto";
    this.toolDiv.style["height"] = "auto";
    this.toolDiv.style["visibility"] = "hidden";
    this.toolDiv.className = this.baseClassName;

    if (CyberDll.isNumber(this.width))
        this.toolDiv.style["width"] = this.width + "px";
    if (CyberDll.isNumber(this.height))
        this.toolDiv.style["height"] = this.height + "px";

    var savethis = this;
    if (this.closeonmouseout && !element.cybertip_save_mouseout){
        element.cybertip_save_mouseout = element.onmouseout;
        if (!element.cybertip_save_mouseout) element.cybertip_save_mouseout = function(){};
        element.onmouseout = function(){
            savethis.onmouseout.call(savethis, arguments);
            if (CyberDll.isFunction(element.cybertip_save_mouseout))
                element.cybertip_save_mouseout.call(element, arguments);
        }
    }
    if (this.stopclosingonhover && !element.cybertip_save_mouseover){
        element.cybertip_save_mouseover = element.onmouseover;
        if (!element.cybertip_save_mouseover) element.cybertip_save_mouseover = function(){};
        element.onmouseover = function(){
            savethis.onmouseover.call(savethis, arguments);
            if (CyberDll.isFunction(element.cybertip_save_mouseover))
                element.cybertip_save_mouseover.call(element, arguments);
        }
    }

    element.cybertip_tooltip = this;
}
/* starts the tip the first time
 */
CyberTip.prototype._startFirsttime = function(args, config, element){ 
    var caller = this;
    this.timeoutid = window.setTimeout(function(){
        caller.timeoutid = -1;

        //AUTOSIZE handling
        if (caller.autosize){
            var width = caller.toolDiv.offsetWidth;
            var height = caller.toolDiv.offsetHeight;
            var area = width * height;
            if (height > 0 && width > 0){
                width = Math.round(Math.sqrt(area * caller.autosizeratio));
                if (CyberDll.isNumber(caller.autosizeminheight) && caller.autosizeminheight > 0)
                    width = Math.max(width, caller.autosizeminheight);
                if (CyberDll.isNumber(caller.autosizemaxheight) && caller.autosizemaxheight > caller.autosizeminheight)
                    width = Math.min(width, caller.autosizemaxheight);
                caller.toolDiv.style["width"] = width + "px";
            }
        }

        //Size limitation
        if (CyberDll.isNumber(caller.minwidth) && caller.minwidth > caller.toolDiv.offsetWidth)
            caller.toolDiv.style["minWidth"] = caller.minwidth + "px";
        if (CyberDll.isNumber(caller.maxwidth) && caller.maxwidth > caller.minwidth && caller.maxwidth < caller.toolDiv.offsetWidth)
            caller.toolDiv.style["maxWidth"] = caller.maxwidth + "px";

        if (CyberDll.isNumber(caller.minheight) && caller.minheight > caller.toolDiv.offsetHeight)
            caller.toolDiv.style["minHeight"] = caller.minheight + "px";
        if (CyberDll.isNumber(caller.maxheight) && caller.maxheight > caller.minheight && caller.maxheight < caller.toolDiv.offsetWidth)
            caller.toolDiv.style["maxHeight"] = caller.maxheight + "px";

        //Position
        if (caller.fixed){
            caller.calcFixedPosition();
        }

        //Effect and opening process
        var effect = CyberDll.extension("CyberEffects");
        if (effect && typeof caller.showeffect != "object") effect = false;
        
        caller.setPosition(CyberDll.getMouse());
        caller.status = "opening";
        if (!effect){
            if (caller.status == "opening"){
                caller.status = "open";
                caller.toolDiv.style["visibility"] = "visible";
                caller.update();
            }
        } else {
            caller.fadingid = caller.showeffect.start(caller.toolDiv, function(){
                caller.update();
            },function(){
                if (caller.status == "opening"){
                    caller.status = "open";
                    caller.toolDiv.style["visibility"] = "visible";
                    caller.update();
                }
            });
        }
    }, this.showtimeout);
} 
/* Calculates the Position for fixed CyberTips according to their align
 */
CyberTip.prototype.calcFixedPosition = function(){
    if (!this.fixed) return;
    
    var bounds = CyberDll.DOM.getBounds(this.element);
    if (this.fixedAlignH != null && this.fixedAnchorH != null){
        this.fixedx = Math.round(bounds.left
            + CyberDll.Number.fromAVN(CyberTip.horizontalNotation2AVN(this.fixedAlignH, this.tipdisx), bounds.width)
            - CyberDll.Number.fromAVN(CyberTip.horizontalNotation2AVN(this.fixedAnchorH, this.tipdisx), this.toolDiv.offsetWidth));
        
    }
    if (this.fixedAlignV != null && this.fixedAnchorV != null){
        this.fixedy = Math.round(bounds.top
            + CyberDll.Number.fromAVN(CyberTip.verticalNotation2AVN(this.fixedAlignV, this.tipdisy), bounds.height)
            - CyberDll.Number.fromAVN(CyberTip.verticalNotation2AVN(this.fixedAnchorV, this.tipdisy), this.toolDiv.offsetHeight));
    }
};
/* Sets the Position whether its a mouse-follow, fixed or popup CyberTip
 * (on opening or closing the targeting position is set for compatibility with CyberEffects)
 */
CyberTip.prototype.setPosition = function(mouse){
    var left, top;
    if (!this.popup){
        if (!this.fixed){
            left = mouse.x + CyberDll.Number.fromAVN(this.tip_offsetx, this.toolDiv.offsetWidth);
            top = mouse.y + CyberDll.Number.fromAVN(this.tip_offsety, this.toolDiv.offsetHeight);
        } else {
            left = this.fixedx;
            top = this.fixedy;
        }
    } else {
        left = mouse.w / 2 - this.toolDiv.offsetWidth / 2;
        top = mouse.h / 2 - this.toolDiv.offsetHeight / 2;
    }

    if (this.minx !== false && left < this.minx) left = this.minx;
    if (this.maxx !== false && left + this.toolDiv.offsetWidth > this.maxx) left = this.minx - this.toolDiv.offsetWidth;
    if (this.miny !== false && top < this.miny) top = this.miny;
    if (this.maxy !== false && top + this.toolDiv.offsetHeight > this.maxy) top = this.miny - this.toolDiv.offsetHeight;

    if (this.status == "opening" || this.status == "closing"){
        this.toolDiv.targetLeft = left;
        this.toolDiv.targetTop = top;
    } else {
        this.toolDiv.style["left"] = left + "px";
        this.toolDiv.style["top"] = top + "px";
    }
}
/* updates the CyberTip (Position)
 */
CyberTip.prototype.update = function(){
    if (this.status == "closing") return;
    if (this.status == "closed") return;
    var mouse;
    if (this.status == "opening") {
        mouse = CyberDll.getMouse();
        this.check_tooltip_position(mouse);
        this.setPosition(mouse);
        return;
    }

    mouse = CyberDll.getMouse();
    this.check_tooltip_position(mouse);
    this.setPosition(mouse);
}
/* Onclick method of the tip (used for popup-closing)
 */
CyberTip.prototype.onclick = function(){
    if (this.popup && (this.status == "opening" || this.status == "open")){
        this.close.apply(this);
    }
}
/* Closes the CyberTip
 */
CyberTip.prototype.close = function(){
    if (this.status != "opening" && this.status != "open" && this.status != "init") return;    
    
    if (this.popup){
        CyberTip.popupped = false;
        CyberDll.DOM.uneclipsing();
    }
    
    if (this.timeoutid >= 0){
        window.clearTimeout(this.timeoutid);
        if (this.status == "init"){
            this.toolDiv.style["display"] = "none";
            this.destroy();
            this.status = "closed";
            return;
        }
    }

    this.status = "closing";
    
    var savethis = this;
    this.timeoutid = window.setTimeout(function(){
        var effect = CyberDll.extension("CyberEffects");
        if (effect && typeof savethis.hideeffect != "object") effect = false;
    
        if (!effect){
            if (CyberDll.Fading.isFading(savethis.fadingid))
                CyberDll.Fading.stop(savethis.fadingid);
    
            if (savethis.status == "closing"){
                savethis.toolDiv.style["visibility"] = "hidden";
                CyberTip.remove(savethis);
                savethis.status = "closed";
            }
        } else {
            var prog = 0;
            var eff = savethis.hideeffect;
            if (CyberDll.Fading.isFading(savethis.fadingid)){
                prog = 1 - CyberDll.Fading.progressOf(savethis.fadingid);
                CyberDll.Fading.stop(savethis.fadingid);
                eff = savethis.invertshoweffect;
            }

            savethis.toolDiv.style["visibility"] = "hidden";
            this.fadingid = eff.start(savethis.toolDiv, null,function(){
                if (savethis.status == "closing"){
                    savethis.toolDiv.style["visibility"] = "hidden";
                    savethis.toolDiv.style["display"] = "none";
                    savethis.destroy();
                    savethis.status = "closed";
                }
            },prog);
        }
    }, this.hidetimeout);
}
CyberTip.prototype.open = function(){
    if (this.status != "closing" && this.status != "init") return;

    if (this.timeoutid >= 0){
        window.clearTimeout(this.timeoutid);
    }

    var savethis = this;
    this.timeoutid = window.setTimeout(function(){
        savethis.timeoutid = -1;
        if (savethis.fixed){
            savethis.calcFixedPosition();
        }

        var effect = CyberDll.extension("CyberEffects");
        if (effect && typeof savethis.showeffect != "object") effect = false;

        savethis.setPosition(CyberDll.getMouse());
        savethis.status = "opening";
        if (!effect){
            if (savethis.status == "opening"){
                savethis.status = "open";
                savethis.toolDiv.style["visibility"] = "visible";
                savethis.update();
            }
        } else {
            var prog = 0;
            var eff = savethis.showeffect;
            if (CyberDll.Fading.isFading(savethis.fadingid)){
                prog = 1 - CyberDll.Fading.progressOf(savethis.fadingid);
                CyberDll.Fading.stop(savethis.fadingid);
                eff = savethis.inverthideeffect;
            }

            savethis.fadingid = eff.start(savethis.toolDiv, function(){
                savethis.update();
            },function(){
                if (savethis.status == "opening"){
                    savethis.status = "open";
                    savethis.toolDiv.style["visibility"] = "visible";
                    savethis.update();
                }
            }, prog);
        }
    }, this.showtimeout);
}
/* Destructor of the CyberTip
 */
CyberTip.prototype.destroy = function(){
    CyberTip.remove(this);
    CyberTip.untip(this);
}
/* Checks the position of floating tips (adjust for maximal view)
 */
CyberTip.prototype.check_tooltip_position = function(mouse){
    if (this.popup) return;
    if (this.fixed) return;
    
    var imgw = this.toolDiv.offsetWidth;
    var imgh = this.toolDiv.offsetHeight;
    
    this.tip_offsetx = CyberDll.Number.fromAVN(this.tipdisx, this.toolDiv.offsetWidth);
    this.tip_offsety = CyberDll.Number.fromAVN(this.tipdisy, this.toolDiv.offsetHeight);
    
    var leftdis = mouse.x - mouse.scrollx - this.tipdisx;
    var rightdis = mouse.w - (mouse.x - mouse.scrollx) - this.tipdisx;
    var topdis = mouse.y - mouse.scrolly - this.tipdisy;
    var bottomdis = mouse.h - (mouse.y - mouse.scrolly) - this.tipdisy;
    
    if (imgw > rightdis && leftdis > rightdis)
        this.tip_offsetx = -this.tipdisx - imgw;
    if (imgh > bottomdis && topdis > bottomdis)
        this.tip_offsety = -this.tipdisy - imgh;
    
    if (this.tip_offsetx > 0){
        if (this.tip_offsety > 0){
            this.toolDiv.className = this.baseClassName + " cybertip_bottom_right";
        } else {
            this.toolDiv.className = this.baseClassName + " cybertip_top_right";
        }
    } else {
        if (this.tip_offsety > 0){
            this.toolDiv.className = this.baseClassName + " cybertip_bottom_left";
        } else {
            this.toolDiv.className = this.baseClassName + " cybertip_top_left";
        }
    }
}
/* The on mouse out event of the element
 */
CyberTip.prototype.onmouseout = function(){
    if (this.closeonmouseout){
        if (this.status == "open" || this.status == "opening" || this.status == "init"){
            this.close();
        }
    }
}
/* The on mouse out event of the element
 */
CyberTip.prototype.onmouseover = function(){
    if (this.stopclosingonhover){
        if (this.status == "closing"){
            this.open();
        }
    }
}

// == STATIC ==

/* Creates a CyberTip-Popup with screen darkening
 */
CyberTip.popup = function(){
    if (arguments.length == 0) return;
    arguments[arguments.length] = "POPUP";
    arguments.length++;
    CyberTip.tip.apply(this, arguments);
};
/* Creates a new CyberTip 
 */
CyberTip.tip = function(){    
    if (arguments.length == 0) return;
    //if (this.cybertip_tooltip){ CyberDll.Console.write("moin"); return;}

    var tip;
    var real_this = this;
    var args = arguments;
    if (typeof arguments[0] != "string"){
        args = Array();
        for (var i = 1; i < arguments.length; i++)
            args.push(arguments[i]);
        real_this = arguments[0];
    }
	
	for ( var index = 0; index < CyberTip.activeTips.length; ++index )
		if ( CyberTip.activeTips[index].element == real_this ) return;
	
    //if (typeof real_this.cybertip_tooltip != "undefined"){
    //    CyberTip.untip.call(real_this);
    //}
    //tip =
    new CyberTip(args, real_this);
//real_this.cybertip_tooltip = tip;
//real_this.cybertip_save_mouseout = this.onmouseout;
//real_this.onmouseout = CyberTip.untip;
};
/* Removes a CyberTip from activeTips-Array and from the DOM 
 */
CyberTip.remove = function(tip){
    var index = CyberTip.activeTips.indexOf(tip);
    if (index >= 0){
        CyberTip.activeTips[index] = CyberTip.activeTips[CyberTip.activeTips.length - 1];
        CyberTip.activeTips.pop();
    }
    document.getElementsByTagName("body")[0].removeChild(tip.toolDiv);
};
/* Cleans up the element after the tooltip disappears
 */
CyberTip.untip = function(tip){
    if (tip.element.cybertip_tooltip){
        if (CyberDll.Browser.IE) tip.element.cybertip_tooltip = undefined;
        else delete tip.element.cybertip_tooltip;

        if (tip.element.cybertip_save_mouseout){
            tip.element.onmouseout = tip.element.cybertip_save_mouseout;
            //this.cybertip_save_mouseout.apply(this, arguments);

            if (CyberDll.Browser.IE) tip.element.cybertip_save_mouseout = undefined;
            else delete tip.element.cybertip_save_mouseout;
        }
        if (tip.element.cybertip_save_mouseover){
            tip.element.onmouseover = tip.element.cybertip_save_mouseover;
            //this.cybertip_save_mouseover.apply(this, arguments);

            if (CyberDll.Browser.IE) tip.element.cybertip_save_mouseover = undefined;
            else delete tip.element.cybertip_save_mouseover;
        }
    }
};
/* searches through an element for images and make them tooltippable
 * @param ele Element to search through
 */
CyberTip.makeElementImagable = function(ele){
    if (ele.tagName && ele.tagName.toLowerCase() == "img"
        && ele.className.indexOf("cybertip", 0) < 0){
        ele.onmouseover = CyberDll.addFunction(CyberTip.image_mouseover, ele);
        ele.onmouseout = CyberDll.addFunction(CyberTip.image_mouseout, ele);
    }
    if (ele.hasChildNodes()){
        for (var i = 0; i < ele.childNodes.length; i++){
            CyberTip.makeElementImagable(ele.childNodes[i]);
        }
    }
};
/* Mousemove-Function for global mousemove
 * Updates each active Tip
 * @param e Event
 */
CyberTip.mousemove = function(e){
    for (var i = 0; i < CyberTip.activeTips.length; i++){
        var tip = CyberTip.activeTips[i];
        tip.update();
    }
};
/* checks if a src-string links to an image
 * @param src Source-String to check
 */
CyberTip.isimage = function(src){
    if (src.lastIndexOf(".png") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".jpg") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".jpeg") == src.length - 5)
        return true;
    else if (src.lastIndexOf(".gif") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".bmp") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".tga") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".tif") == src.length - 4)
        return true;
    else if (src.lastIndexOf(".tiff") == src.length - 5)
        return true;
    return false;
};
/* checks if an element is a link to an image
 * @param element Element to check
 */
CyberTip.islinktoimg = function(element){
    if (element && element.href &&
        element.tagName.toLowerCase() == "a"){
        return CyberTip.isimage(element.href);
    }
    return false;
};
/* mouseover-function for images
 * creates a CyberTip with Title-Hack
 * @param e Event
 */
CyberTip.image_mouseover = function(e){
    if (CYBERTIP.DisableImages) return;
    CyberDll.onmousemove(e);
    
    var imgsrc = this.src;
    if (this.cybersrc){
        imgsrc = this.cybersrc;
    } else {
        if (CyberTip.islinktoimg(this.parentNode)){
            imgsrc = this.parentNode.href;
        } else if (CyberTip.islinktoimg(this.parentNode.parentNode)){
            imgsrc = this.parentNode.parentNode.href;
        }
    }

    var title = "";
    if (this.title){
        title = this.title;
        if (CYBERTIP.DisableBrowserTooltip){
            this.ct_title = title;
            this.title = "";
        }
    }
    if (title == "" && this.ct_title && this.ct_title != ""){
        title = this.ct_title;
    }
    var alt = "";
    if (this.alt) alt = this.alt;

    cybertip.call(this, "<img src='"+imgsrc+"' alt='"+alt+"' title='"+title+"' />",
        CYBERTIP.UseImageTitle ? "TITLE=" + title : "",
        CYBERTIP.UseImageAlt ? "DESCRIPTION=" + alt : "");
};
/* additional mouseout event for images which rollbacks the Title-Hack
 */
CyberTip.image_mouseout = function(){
    if (CYBERTIP.DisableImages) return;

    if (this.ct_title && this.ct_title != ""){
        this.title = this.ct_title;
        this.ct_title = "";
    }
};
/* converts the vertical notation of align and anchor to an AVN representation
 * @param str String to convert
 * @param tipdisy Tip Distance Y for T+ and B+
 */
CyberTip.verticalNotation2AVN = function(str, tipdisy){
    if (CyberDll.isNumber(str)) return str;
    if (typeof str != "string") return undefined;
    if (str.length == 0) return undefined;
    str = str.toLowerCase();
    if (str.charAt(0) == "t") {
        if (str.charAt(str.length-1) == "+") return -tipdisy;
        else return 0;
    }
    if (str.charAt(0) == "b") {
        if (str.charAt(str.length-1) == "+") return "100% + " + tipdisy;
        else return "100%";
    }
    if (str.charAt(0) == "c" || str.charAt(0) == "m") return "50%";
    return str;
};
/* converts the horizontal notation of align and anchor to an AVN representation
 * @param str String to convert
 * @param tipdisx Tip Distance X for L+ and R+
 */
CyberTip.horizontalNotation2AVN = function(str, tipdisx){
    if (CyberDll.isNumber(str)) return str;
    if (typeof str != "string") return undefined;
    if (str.length == 0) return undefined;
    str = str.toLowerCase();
    if (str.charAt(0) == "l") {
        if (str.charAt(str.length-1) == "+") return -tipdisx;
        else return 0;
    }
    if (str.charAt(0) == "r") {
        if (str.charAt(str.length-1) == "+") return "100% + " + tipdisx;
        else return "100%";
    }
    if (str.charAt(0) == "c" || str.charAt(0) == "m") return "50%";
    return str;
};

/* Initialization of cybertips
 */
CyberDll.addOnload(function(){
    if (!CyberTip.inited){
        CyberTip.inited = true;

        if (!CYBERTIP.DisableImages){
            var images = document.getElementsByTagName("img");
            for (var i = 0; i < images.length; i++){
                if (images[i].className.indexOf("cybertip", 0) >= 0
                    && images[i].className.indexOf("notip", 0) < 0){
                    images[i].onmouseover = CyberDll.addFunction(CyberTip.image_mouseover, images[i].onmouseover);
                    //images[i].onmousemove = CyberDll.addFunction(cybertip_image_mousemove, images[i].onmousemove);
                    images[i].onmouseout = CyberDll.addFunction(CyberTip.image_mouseout, images[i].onmouseout);
                }
            }
        }
    
        var cybertip_divs = document.getElementsByTagName("div");
        for (var i2 = 0; i2 < cybertip_divs.length; i2++){
            if (cybertip_divs[i2].className.indexOf("cybertip_images", 0) >= 0
                && cybertip_divs[i2].className.indexOf("notip", 0) < 0){
                CyberTip.makeElementImagable(cybertip_divs[i2]);
            }
        }
    
        CyberDll.addGlobalMousemove(CyberTip.mousemove);
    }
});
