/**
 * $Id$
 * @version $Rev$
 * @class Tooltip
 * @constructor
 * @extends Rico
 * @author Mike de Boer (mdeboer AT ebuddy.com)
 */
var Tooltip = Class.create();
Tooltip.prototype = {
    /**
     * Initialize the Tooltip class.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {String} id
     * @param {HTMLDOMElement} container The container element of this checkbox instance.
     * @param {Object} options Optional.
     * @type Tooltip
     */
    initialize :
    function(id, container, options) {
        this.id = id;
        this.container = (container) ?  $(container) :  _proxy_jslib_handle(document, 'body', '', 0, 0);
        //set the initial state to building (not that we do anything with it :)
        this.state = Tooltip.BUILDING;
        this.lastTarget = null;
        this.noShow = [];

        this.setOptions(options); //default options will be overridden by the ones provided
        this._draw(); // tooltip nodes will be drawn to the grid
        this._attachBehaviors(); // behaviours like animations will be set (nodes are available now)
    },
    /**
     * Set the optional global settings for the Tooltip. If no options are provided,
     * the defaults are used.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {Object} options Generic object, containing all the custom options.
     * @type void
     */
    setOptions :
    function(options) {
        this.options = {
            disabled : false,
            classBody : 'tooltip',
            classTopLeft : 'tooltip_topleft',
            classBottomLeft : 'tooltip_bottomleft',
            classContent : 'tooltip_content',
            classTopRight : 'tooltip_topright',
            classBottomRight : 'tooltip_bottomright',
            classBottomMiddle: 'tooltip_bottommiddle',
            classDisabled : 'tooltip_disabled',
            classArrow : 'tooltip_arrow',
            classButton : 'close_button',
            classButtonHover : 'close_button_hover',
            classButtonDown : 'close_button_down',
            arrowOrientation : 'top', //may be top, right, bottom, left
            pathToContent : 'images/ui/tooltip/',
            icon : 'tooltip_icon_alert.gif',
            group : null,
            offsetX : 0,
            offsetY : 0,
            width : 320,
            height : 46,
            animate : true,
            animation : null,
            animateFade : true,
            fadeDuration : 0.5,
            bodyOpacity : 1.0,
            animateWidth : false,
            animateHeight : false,
            showCloseButton : true,
            onBeforeShow : null,
            onShow : null,
            onAfterShow : null,
            onBeforeHide : null,
            onHide : null,
            onDisable : null,
            onEnable : null,
            onLocaleChange : null,
            localeKey : null
 }
        Object.extend(this.options, options || {});
    },
    /**
     * Retrieve the (unique) identifier of this Tooltip instance.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type String
     */
    getId :
    function() {
        return this.id;
    },
    /**
     * Draw this Tooltip instance to the HTML grid.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    _draw :
    function() {
        this.domNode = document.createElement('div');
         _proxy_jslib_handle(this.domNode, 'setAttribute', '', 1, 0)('id', this.id);
        Element.addClassName(this.domNode, this.options.classBody);
        if (!isNaN(this.options.width))
            this.domNode.style.width = this.options.width + "px";
        else
            this.options.animateWidth = false;
        if (!isNaN(this.options.height))
            this.domNode.style.height = this.options.height + "px";
        else
            this.options.animateheight = false;
        this.container.appendChild(this.domNode);

        var struct = ['<table height="100%" cellpadding="0" cellspacing="0">\
                <tr>\
                     <td id="', this.id, '_cell1" class="', this.options.classTopLeft, '">\
                        <img src="" id="', this.id, '_icon" border="0"/>\
                     </td>\
                     <td id="', this.id, '_cell2" class="', this.options.classContent, '">&nbsp;</td>\
                     <td id="', this.id, '_cell3" class="', this.options.classTopRight, '"></td>\
                </tr>\
                <tr>\
                    <td class="', this.options.classBottomLeft, '"></td>\
                    <td class="', this.options.classBottomMiddle, '"></td>\
                    <td class="', this.options.classBottomRight, '"></td>\
                </tr>\
            </table>\
            <div id="', this.id, '_arrow" class="', this.options.classArrow, '">&nbsp;</div>'];
         _proxy_jslib_assign('', this.domNode, 'innerHTML', '=', ( struct.join('')));
        if (this.options.showCloseButton) {
            // create close button
            this.closeButton = document.createElement('div');
             _proxy_jslib_handle(this.closeButton, 'setAttribute', '', 1, 0)('id', this.id + '_close');
            Element.addClassName(this.closeButton, this.options.classButton);
            this.domNode.appendChild(this.closeButton);
        }
        this.domIcon = $(this.id + '_icon');
        this.domContent = $(this.id + '_cell2');
        this.domArrow = $(this.id + '_arrow');
        
        Element.addClassName(this.domArrow, this.options.arrowOrientation);
        
        if (!isNaN(this.options.offsetX))
            this.domNode.style.left = this.options.offsetX + "px";
        if (!isNaN(this.options.offsetY))
             _proxy_jslib_assign('', this.domNode.style, 'top', '=', ( this.options.offsetY + "px"));
        
        Element.hide(this.domNode);
        this.state = Tooltip.HIDDEN;
    },
    /**
     * Remove the Tooltip DOM node from the document tree.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    remove :
    function() {
        var container = this.container;
        var node = this.domNode;
        this.container = this.domNode = this.domNode.tooltip = null;
        container.removeChild(node);
    },
    /**
     * Show the Tooltip widget.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {mixed} relNode The DOM node against which the Tooltip must be positioned
     * @param {String} content The contents (body) for the Tooltip. May contain HTML fragments
     * @param {String} icon Icon filename to display next to the Tooltips' body. Optional.
     * @type void
     */
    show :
    function(relNode, content, icon) {
        if (this.noShow.indexOf(relNode) > -1) return false;
        if (this.options.showCloseButton) {
            // reset close button
            if (Element.hasClassName(this.closeButton, this.options.classButtonHover))
                Element.removeClassName(this.closeButton, this.options.classButtonHover);
            if (Element.hasClassName(this.closeButton, this.options.classButtonDown))
                Element.removeClassName(this.closeButton, this.options.classButtonDown);
        }

        relNode = $(relNode);
        this.lastTarget = relNode;
        relPos = (relNode) ? Position.positionedOffset(relNode) : [0, 0];

        if (this.options.onBeforeShow != null)
            this.options.onBeforeShow(this);
            
        var elDims = (relNode) ? Element.getDimensions(relNode) : {width: 0, height: 0};
        // Direct the main element to the correct position:
        this.domNode.style.left = (relPos[0] + this.options.offsetX) + "px";
         _proxy_jslib_assign('', this.domNode.style, 'top', '=', ( (relPos[1] + this.options.offsetY + elDims.height) + "px"));
        // Set the content for this specific tooltip:
         _proxy_jslib_assign('', this.domIcon, 'src', '=', ( this.options.pathToContent + (icon || this.options.icon)));
         _proxy_jslib_assign('', this.domContent, 'innerHTML', '=', ( ( _proxy_jslib_handle(null, 'content', content, 0, 0)) ?  _proxy_jslib_handle( _proxy_jslib_handle(null, 'content', content, 0, 0).stripScripts(), 'replace', '', 1, 0)(/\n/g, '<br/>') : ""));
            
        if (this.options.onShow != null)
            this.options.onShow(this);
            
        if (!this.isVisible()) {
            Element.show(this.domNode); //display the element before animating anything (avoids IE6 bug(s)) 
            this.state = Tooltip.VISIBLE; //see: Tooltip#isVisible()
            if (this.options.animate)
                this.options.animation.reverse();
            else
                this.correctPosition();
            
            if (this.options.onAfterShow != null)
                this.options.onAfterShow(this);
        } else this.correctPosition();
    },
    /**
     * Hide the Tooltip widget.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    hide :
    function() {
        if (this.isVisible()) {
            var force = (arguments.length > 0) ? arguments[0] : false;
        
            if (this.options.onBeforeHide != null)
                this.options.onBeforeHide(this);
            
            if (this.options.animate && !force) {
                this.state = Tooltip.ANIMATING;
                this.options.animation.play();
            } else {
                Element.hide(this.domNode);
                this.state = Tooltip.HIDDEN;
            }
            
            if (this.options.onHide != null)
                this.options.onHide(this);
        }
    },
    /**
     * Check if the Tooltip is visible on the grid, or hidden
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type Boolean
     */
    isVisible :
    function() {
        return (this.state == Tooltip.VISIBLE);
    },
    /**
     * Set this Tooltip to 'disabled', ie. not clickable/ changeable.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    onDisable :
    function() {
        if (!this.options.disabled) {
            Element.addClassName(this.domNode, this.options.classDisabled);
            this.options.disabled = true;
            if (this.options.onDisable != null)
                this.options.onDisable(this);
        }
    },
    /**
     * Set this Tooltip to 'enabled', ie. clickable AND changeable.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    onEnable :
    function() {
        if (this.options.disabled) {
            Element.removeClassName(this.domNode, this.options.classDisabled);
            this.options.disabled = false;
            if (this.options.onEnable != null)
                this.options.onEnable(this);
        }
    },
    /**
     * Correct the position of this Tooltip when it appeared outside the window.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    correctPosition :
    function() {
        var pos      = Position.positionedOffset(this.domNode);
        var contDims = Element.getDimensions(this.domNode);
        var winDims  = window.getSize();
        
        if ((pos[0] + contDims.width) > winDims.width)
            pos[0] = winDims.width - contDims.width;
            
        if ((pos[1] + contDims.height) > winDims.height) pos[1] = winDims.height - contDims.height;
        this.domNode.style.left = pos[0] + "px";
         _proxy_jslib_assign('', this.domNode.style, 'top', '=', ( pos[1] + "px"));
    },
    /**
     * Change arrow position based on orientation
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param (Integer) offset
     * @type void
     */
    changeArrowPosition :
    function(offset) {
    	switch (this.options.arrowOrientation) {
    		case 'top':
    		    this.domArrow.style.left = offset + 'px';
    		    break;
            case 'left':
            case 'right':
                 _proxy_jslib_assign('', this.domArrow.style, 'top', '=', ( offset + 'px'));
                break;
            case 'bottom':
                this.domArrow.style.left = (this.options.width - offset) + 'px';
                break;
    	}
    },
    /**
     * Change tooltip offset
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param (Integer) offsetX
     * @param (Integer) offsetY
     * @type void
     */
    changeOffset :
    function(offsetX, offsetY) {
        if (offsetX != null)
            this.options.offsetX = offsetX;
        if (offsetY != null)
            this.options.offsetY = offsetY;
    },
    /**
     * Get tooltip horizontal offset (X)
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @type (Integer)
     */
    getOffsetX :
    function() {
        return this.options.offsetX;
    },
    /**
     * Set tooltip horizontal offset (X)
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param (Integer) offsetX
     * @type void
     */
    setOffsetX :
    function(offsetX) {
        this.options.offsetX = offsetX;
    },
    /**
     * Get tooltip vertical offset (Y)
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @type (Integer)
     */
    getOffsetY :
    function() {
        return this.options.offsetY;
    },
    /**
     * Set tooltip vertical offset (Y)
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param (Integer) offsetY
     * @type void
     */
    setOffsetY :
    function(offsetY) {
        this.options.offsetY = offsetY;
    },
    /**
     * Reset arrow position
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @type void
     */
    resetArrowPosition :
    function() {
        Element.removeClassName(this.domArrow, this.options.arrowOrientation);
        this.domArrow.style.left = null;
         _proxy_jslib_assign('', this.domArrow.style, 'top', '=', ( null));
        Element.addClassName(this.domArrow, this.options.arrowOrientation);
    },
    /**
     * Mimic an Event, to avoid errors under exceptional circumstances.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type Object
     */
    mimicEvent :
    function() {
       var type = (arguments.length == 1) ? arguments[0] : "click";
       return {type: type, _bogus: true};
    },
    /**
     * Fire a custom event (API functionality).
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {String} type
     * @type Object
     */
    fireEvent :
    function(type) {
       var fullType = new (String)("on-" + type).camelize();
       if (typeof  _proxy_jslib_handle(this, (fullType), 0, 0) == "function")
            _proxy_jslib_handle(this, (fullType), 1, 0)(this.mimicEvent());
       else if (typeof  _proxy_jslib_handle(this.options, (fullType), 0, 0) != "undefined" &&  _proxy_jslib_handle(this.options, (fullType), 0, 0) != null)
            _proxy_jslib_handle(this.options, (fullType), 1, 0)(this, this.mimicEvent());
    },
    /**
     * Change the visual appearance of the tooltip close button as the mouse hovers over it.
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param {Event} e
     * @type void
     */
   onCloseEnter :
   function(e) {
        if (!Element.hasClassName(this.closeButton, this.options.classButtonHover))
            Element.addClassName(this.closeButton, this.options.classButtonHover);
   },
   /**
     * Change the visual appearance of tooltip close button after the mouse left.
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param {Event} e
     * @type void
     */
   onCloseLeave :
   function(e) {
        if (Element.hasClassName(this.closeButton, this.options.classButtonHover))
            Element.removeClassName(this.closeButton, this.options.classButtonHover);
   },
    /**
     * Change the visual appearance of the tooltip close button after it has been clicked.
     * @author Alex Popescu (apopescu AT ebuddy.com)
     * @param {Event} e
     * @type void
     */
    onCloseClick :
    function(e) {
        if (!Element.hasClassName(this.closeButton, this.options.classButtonDown))
            Element.addClassName(this.closeButton, this.options.classButtonDown);
        this.noShow.push(this.lastTarget);
        this.hide();
    },
    /**
     * Add various event handlers to a <i>Tooltip</i> object.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    _attachBehaviors :
    function() {
         _proxy_jslib_assign('', Tooltip.cache, (this.id), '=', ( this));
        if (this.options.group != null) {
            if (! _proxy_jslib_handle(Tooltip.groupCache, (this.options.group), 0, 0))
                 _proxy_jslib_assign('', Tooltip.groupCache, (this.options.group), '=', ( new (Array)()));
             _proxy_jslib_handle(Tooltip.groupCache, (this.options.group), 0, 0).push(this);
        }
        
        if (this.options.animate && this.options.animation == null) {
            // Instantiate the core animator:
            this.options.animation = new (Animator)({
                duration: parseInt(this.options.fadeDuration * 1000),
                transition: Animator.makeEaseIn(2),
                actionData: this,
                onComplete: function (anim) {
                    var tooltip = anim.options.actionData;
                    if (anim.state == 1) {
                        Element.hide(anim.subjects[0].els[0]);
                        tooltip.state = Tooltip.HIDDEN;
                    } else {
                        tooltip.state = Tooltip.VISIBLE;
                        tooltip.correctPosition();
                    }
                }
            });
            // Define the various ways the Tooltip will be animated:
            if (this.options.animateFade)
                this.options.animation.addSubject(new (NumericalStyleSubject)(this.domNode, 'opacity', this.options.bodyOpacity, 0))
 if (this.options.animateWidth)
                this.options.animation.addSubject(new (NumericalStyleSubject)(this.domNode, 'width',   this.options.width, 0));
            if (this.options.animateHeight)
                this.options.animation.addSubject(new (NumericalStyleSubject)(this.domNode, 'height',  this.options.height, 0));
            
            if ((this.options.animation.subjects.length == 0) | (is_ie))
                this.options.animate = false;
        }
        // Disable selections in IE
        this.domNode.onselectstart = function() { return false; };
        
        if (this.options.showCloseButton) {
            // attach behaviors to the close button
            this.closeButton.onmouseover = this.onCloseEnter.bindAsEventListener(this);
            this.closeButton.onmouseout = this.onCloseLeave.bindAsEventListener(this);
            this.closeButton.onclick = this.onCloseClick.bindAsEventListener(this);
        }
    }
};
/**
 * @final Tooltip#BUILDING
 */
Tooltip.BUILDING = 0;
/**
 * @final Tooltip#HIDDEN
 */
Tooltip.HIDDEN = 1;
/**
 * @final Tooltip#VISIBLE
 */
Tooltip.VISIBLE = 2;
/**
 * @final Tooltip#ANIMATING
 */
Tooltip.ANIMATING = 3;

Tooltip.cache = [];
Tooltip.groupCache = []; ;
_proxy_jslib_flush_write_buffers() ;