// 
// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.com
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 

/**
* HelpBalloon.js
* Prototype/Scriptaculous based help balloons / dialog balloons
* @version 2.0.1
* @requires prototype.js <http://www.prototypejs.org/>
* @author Beau D. Scott <beau_scott@hotmail.com>
* 4/10/2008
*/
var HelpBalloon = Object.extend(Class.create(), {
    /**
    * Enumerated value for dynamic rendering position.
    * @static
    */
    POS_DYNAMIC: -1,
    /**
    * Enumerated value for the top-left rendering position.
    * @static
    */
    POS_TOP_LEFT: 0,
    /**
    * Enumerated value for the top-right rendering position.
    * @static
    */
    POS_TOP_RIGHT: 1,
    /**
    * Enumerated value for the bottom-left rendering position.
    * @static
    */
    POS_BOTTOM_LEFT: 2,
    /**
    * Enumerated value for the bottom-right rendering position.
    * @static
    */
    POS_BOTTOM_RIGHT: 3,
    /**
    * CSS Classname to look for when doing auto link associations.
    * @static
    */
    ELEMENT_CLASS_NAME: 'HelpBalloon',
    /**
    * Global array of all HelpBalloon instances.
    * (Cheaper than document.getElementByClassName with a property check)
    * @static
    * @private
    */
    _balloons: [],
    /**
    * Event listener that auto-associates anchors with a dynamic HelpBalloon.
    * Also begins mouse movement registration
    * @static
    */
    registerClassLinks: function (e) {
        $A(document.getElementsByClassName(HelpBalloon.ELEMENT_CLASS_NAME))
			.each(function (obj) {
			    // Only apply any element with an href tag
			    if (obj && obj.tagName && obj.href && obj.href != '') {
			        new HelpBalloon({
			            icon: obj,
			            method: 'get'
			        });
			    }
			});

        Event.observe(document, 'mousemove', HelpBalloon._trackMousePosition);

    },

    /**
    * Private cache of the client's mouseX position
    */
    _mouseX: 0,

    /**
    * Private cache of the client's mouseY position
    */
    _mouseY: 0,

    /**
    * @param {Event} e
    */
    _trackMousePosition: function (e) {
        if (!e) e = window.event;
        HelpBalloon._mouseX = e.clientX;
        HelpBalloon._mouseY = e.clientY;
    }
});

//
// Event for activating HelpBalloon classed links
//
Event.observe(window, 'load', HelpBalloon.registerClassLinks);

HelpBalloon.prototype = {

    //
    // Properties
    // 	
    /**
    * Configuration options
    * @var {HelpBalloon.Options}
    */
    options: null,

    /**
    * Containing element of the balloon
    * @var {Element}
    */
    container: null,
    /**
    * Inner content container
    * @var {Element}
    */
    inner: null,
    /**
    * A reference to the anchoring element/icon
    * @var {Element}
    */
    icon: null,
    /**
    * Content container
    * @var {Element}
    */
    content: null,
    /**
    * Closing button element
    * @var {Element}
    */
    button: null,
    /**
    * The closer object. This can be the same as button, but could 
    * also be a div with a png loaded as the back ground, browser dependent.
    * @var {Element}
    */
    closer: null,
    /**
    * Title container
    * @var {Element}
    */
    titleContainer: null,
    /**
    * Background container (houses the balloon images
    * @var {Element}
    */
    bgContainer: null,
    /**
    * Array of balloon image references
    * @var {Array}
    */
    balloons: null,

    /**
    * The local store of 'title'. Will change if the balloon is making a remote call
    * unless options.title is specified
    * @var {String}
    */
    _titleString: null,

    /**
    * The balloons visibility state.
    * @var {Boolean}
    */
    visible: false,

    /**
    * Rendering status
    * @var {Boolean}
    */
    drawn: false,

    /**
    * Stores the balloon coordinates
    * @var {Object}
    */
    balloonCoords: null,

    /**
    * Width,height of the balloons
    * @var {Array}
    */
    balloonDimensions: null,

    /**
    * ID for HelpBalloon
    * @var {String}
    */
    id: null,

    /**
    * Used at render time to measure the dimensions of the loaded balloon
    * @private
    */
    _lastBalloon: null,

    //
    // Methods
    //

    /**
    * @param {Object} options
    * @see HelpBalloon.Options
    * @constructor
    */
    initialize: function (options) {

        this.options = new HelpBalloon.Options();
        Object.extend(this.options, options || {});

        this._titleString = this.options.title;
        this.balloonDimensions = [0, 0];

        //
        // Preload the balloon and button images so they're ready
        // at render time
        //
        // 0 1
        //  X
        // 2 3
        //
        this.balloons = [];
        for (var i = 0; i < 4; i++) {
            var balloon = new Element('img', {
                src: this.options.balloonPrefix + i + this.options.balloonSuffix
            });
            this.balloons.push(balloon.src);
        }

        this._lastBalloon = balloon;

        this.button = new Element('img', {
            src: this.options.button
        });

        //
        // Create the anchoring icon, or attach the balloon to the given icon element
        // If a string is passed in, assume it's a URL, if it's an object, assume it's
        // a DOM member.
        //
        if (typeof this.options.icon == 'string') {
            this.icon = new Element('img', {
                src: this.options.icon,
                id: this.id + "_icon"
            });
            Element.setStyle(this.icon, this.options.iconStyle);
        }
        else {
            //
            // Not a string given (most likely an object. Do not append the element
            // Kind of a hack for now, but I'll fix it in the next version.
            //
            this.icon = this.options.icon;
            this.options.returnElement = true;
        }

        this.icon._HelpBalloon = this;

        //
        // Attach rendering events
        //

        for (i = 0; i < this.options.useEvent.length; i++)
            Event.observe(this.icon, this.options.useEvent[i], this.toggle.bindAsEventListener(this));

        this.container = new Element('div');
        this.container._HelpBalloon = this;

        this.id = 'HelpBalloon_' + Element.identify(this.container);

        HelpBalloon._balloons.push(this);

        //
        // If we are not relying on other javascript to attach the anchoring icon
        // to the DOM, we'll just do where the script is called from. Default behavior.
        //
        // If you want to use external JavaScript to attach it to the DOM, attach this.icon
        //
        if (!this.options.returnElement) {
            document.write('<span id="' + this.id + '"></span>');
            var te = $(this.id);
            var p = te.parentNode;
            p.insertBefore(this.icon, te);
            p.removeChild(te);
        }
    },

    /**
    * Toggles the help balloon
    * @param {Object} e Event
    */
    toggle: function (event) {
        if (!event) event = window.event || { type: this.options.useEvent, target: this.icon };
        var icon = Event.element(event);
        Event.stop(event);
        if (event.type == this.options.useEvent && !this.visible && icon == this.icon) {
            this.show(event);
        }
        else
            this.hide();
    },

    /**
    * Triggers the balloon to appear
    */
    show: function (event) {
        if (!this.visible) {
            if (!event) event = window.event;
            if (!this.drawn || !this.options.cacheRemoteContent) this._draw();
            this._reposition(event);
            this._hideOtherHelps();
            if (this.options.showEffect && !this.options.skipEffectsForIE) {
                this.options.showEffect(this.container, Object.extend(this.options.showEffectOptions, {
                    afterFinish: this._afterShow.bindAsEventListener(this)
                }));
            }
            else {
                this._afterShow();
            }
            Event.observe(window, 'resize', this._reposition.bindAsEventListener(this));
        }
    },

    /**
    * Sets the container to block styling and hides the elements below the
    * container (if in IE)
    * @private
    */
    _afterShow: function () {
        Element.setStyle(this.container, {
            'display': 'block'
        });
        this._hideLowerElements();
        this.visible = true;
        if (this.options.autoHideTimeout) {
            setTimeout(this._hideQueue.bind(this), this.options.autoHideTimeout);
        }
    },

    /**
    * Checks the mouse position and triggers a hide after the time specified in autoHideTimeout
    * if the mouse is not currently over the balloon, otherwise it requeue's a hide for later.
    */
    _hideQueue: function () {
        if (Position.within(this.container, HelpBalloon._mouseX, HelpBalloon._mouseY))
            setTimeout(this._hideQueue.bind(this), this.options.autoHideTimeout);
        else
            this.hide();
    },

    /**
    * Hides the balloon
    */
    hide: function () {
        if (this.visible) {
            this._showLowerElements();
            if (this.options.hideEffect && !this.options.skipEffectsForIE) {
                this.options.hideEffect(this.container, Object.extend(this.options.hideEffectOptions, {
                    afterFinish: this._afterHide.bindAsEventListener(this)
                }));
            }
            else {
                this._afterHide();
            }
            Event.stopObserving(window, 'resize', this._reposition.bindAsEventListener(this));
        }
    },

    /**
    * Sets the container's display to block
    * @private
    */
    _afterHide: function () {
        Element.setStyle(this.container, {
            'display': 'none'
        });
        this.visible = false;
    },

    /**
    * Redraws the balloon based on the current coordinates of the icon.
    * @private
    */
    _reposition: function (event) {
        if (this.icon.tagName.toLowerCase() == 'area' || !!this.icon.isMap) {
            this.balloonCoords = Event.pointer(event);
        }
        else {
            this.balloonCoords = this._getXY(this.icon);
            //Horizontal and vertical offsets in relation to the icon's 0,0 position.
            // Default is the middle of the object
            var ho = this.icon.offsetWidth / 2;
            var vo = this.icon.offsetHeight / 2;

            var offsets = this.options.anchorPosition.split(/\s+/gi);
            // Only use the first two specified values
            if (offsets.length > 2)
                offsets.length = 2;

            for (var i = 0; i < offsets.length; i++) {
                switch (offsets[i].toLowerCase()) {
                    case 'left':
                        ho = 0;
                        break;
                    case 'right':
                        ho = this.icon.offsetWidth;
                        break;
                    case 'center':
                        ho = this.icon.offsetWidth / 2;
                        break;
                    case 'top':
                        vo = 0;
                        break;
                    case 'middle':
                        vo = this.icon.offsetHeight / 2;
                        break;
                    case 'bottom':
                        vo = this.icon.offsetHeight;
                        break;
                    default:
                        var numVal = parseInt(offsets[i]);
                        if (!isNaN(numVal)) {
                            // 0 = width, 1 = height (WxH)
                            if (i == 0) {
                                if (numVal < 0) {
                                    ho = 0;
                                }
                                else {
                                    if (numVal > this.icon.offsetWidth)
                                        ho = this.icon.offsetWidth;
                                    else
                                        ho = numVal
                                }
                            }
                            else {
                                if (numVal < 0) {
                                    vo = 0;
                                }
                                else {
                                    if (numVal > this.icon.offsetHeight)
                                        vo = this.icon.offsetHeight;
                                    else
                                        vo = numVal
                                }
                            }
                        }
                        break;
                }
            }
            this.balloonCoords.x += ho;
            this.balloonCoords.y += vo;
        }

        //
        // Figure out what position to show based on available realestate
        // unless 
        // 0 1
        //  X
        // 2 3
        // Number indicates position of corner opposite anchor
        //
        var pos = 1;
        if (this.options.fixedPosition == HelpBalloon.POS_DYNAMIC) {
            var offsetHeight = this.balloonCoords.y - this.balloonDimensions[1];
            if (offsetHeight < 0)
                pos += 2;

            var offsetWidth = this.balloonCoords.x + this.balloonDimensions[0];
            var ww = Prototype.Browser.IE ? document.body.clientWidth : window.outerWidth;
            if (offsetWidth > ww)
                pos--;
        }
        else
            pos = this.options.fixedPosition;

        var zx = 0;
        var zy = 0;

        //
        // 0 1
        //  X
        // 2 3
        //
        switch (pos) {
            case 0:
                zx = this.balloonCoords.x - this.balloonDimensions[0];
                zy = this.balloonCoords.y - this.balloonDimensions[1];
                break;

            case 1:
                zx = this.balloonCoords.x;
                zy = this.balloonCoords.y - this.balloonDimensions[1];
                break;

            case 2:
                zx = this.balloonCoords.x - this.balloonDimensions[0];
                zy = this.balloonCoords.y;
                break;

            case 3:
                zx = this.balloonCoords.x;
                zy = this.balloonCoords.y;
                break;
        }
        var containerStyle = {
            /*'backgroundRepeat': 'no-repeat',
            'backgroundColor': 'transparent',
            'backgroundPosition': 'top left',*/
            'left': zx + "px",
            'top': zy + "px",
            'width': this.balloonDimensions[0] + 'px',
            'height': this.balloonDimensions[1] + 'px'
        }
        if (Prototype.Browser.IE) {
            //
            // Fix for IE alpha transparencies
            //
            if (this.balloons[pos].toLowerCase().indexOf('.png') > -1) {
                Element.setStyle(this.bgContainer, {
                    'left': '0px',
                    'top': '0px',
                    'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.balloons[pos] + "', sizingMethod='scale')",
                    'width': this.balloonDimensions[0] + 'px',
                    'height': this.balloonDimensions[1] + 'px',
                    'position': 'absolute'
                });
            }
            else
                containerStyle['background'] = 'transparent url(' + this.balloons[pos] + ') top left no-repeat';
        }
        else {
            containerStyle['background'] = 'transparent url(' + this.balloons[pos] + ') top left no-repeat';
        }
        Element.setStyle(this.container, containerStyle);
    },

    /**
    * Renders the Balloon
    * @private
    */
    _draw: function () {
        Element.setStyle(
			this.container,
			Object.extend(this.options.balloonStyle, {
			    'position': 'absolute',
			    'display': 'none'
			})
		);

        var url = this.options.dataURL;

        //
        // Play nicely with anchor tags being used as the icon. Use it's specified href as our
        // data URL unless one has already been used specified in this.options.dataURL. 
        // We'll also force a new request with this as it may be an image map.
        //
        if (this.icon.className == 'a') {
            if (!this.options.dataURL && this.icon.href != '') {
                url = this.icon.href;
                this.options.cacheRemoteContent = false;
            }
        }

        if (url && (!this.drawn || !this.options.cacheRemoteContent)) {
            var cont = new Ajax.Request(this.options.dataURL, { asynchronous: false, method: this.options.method });
            //
            // Expects the following XML format:
            // <HelpBalloon>
            // 		<title>My Title</title>
            // 		<content>My content</content>
            // </HelpBaloon>
            //
            var doHTML = false;
            if (cont.transport.responseXML) {
                var xml = cont.transport.responseXML.getElementsByTagName('HelpBalloon')[0];

                if (xml) {
                    if (!this.options.title) {
                        xmlTitle = xml.getElementsByTagName('title')[0];
                        if (xmlTitle) this._titleString = xmlTitle.firstChild.nodeValue;
                    }

                    xmlContent = xml.getElementsByTagName('content')[0];
                    if (xmlContent) this.options.content = xmlContent.firstChild.nodeValue;
                }
                else
                    doHTML = true;
            }
            else
                doHTML = true;

            if (doHTML) {
                // Attempt to get the title from a <title/> HTML tag, unless the title option has been set. If so, use that.
                if (!this.options.title) {
                    var htmlTitle = cont.transport.responseText.match(/\<title\>([^\<]+)\<\/title\>/gi);
                    if (htmlTitle) {
                        htmlTitle = htmlTitle.toString().replace(/\<title\>|\<\/title\>/gi, '');
                        this._titleString = htmlTitle;
                    }
                }
                this.options.content = cont.transport.responseText;
            }
        }

        this.balloonDimensions[0] = this._lastBalloon.width;
        this.balloonDimensions[1] = this._lastBalloon.height;

        var contentDimensions = [
			this.balloonDimensions[0] - (2 * this.options.contentMargin),
			this.balloonDimensions[1] - (2 * this.options.contentMargin)
		];

        var buttonDimensions = [
			this.button.width,
			this.button.height
		];

        //
        // Create all the elements on demand if they haven't been created yet
        //
        if (!this.drawn) {
            this.inner = new Element('div');

            this.titleContainer = new Element('div');
            this.inner.appendChild(this.titleContainer);

            // PNG fix for IE
            if (Prototype.Browser.IE && this.options.button.toLowerCase().indexOf('.png') > -1) {
                this.bgContainer = new Element('div');

                // Have to create yet-another-child of container to house the background for IE... when it was set in
                // the main container, it for some odd reason prevents child components from being clickable.
                this.container.appendChild(this.bgContainer);

                this.closer = new Element('div');
                Element.setStyle(this.closer, {
                    'filter':
						"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.options.button + "', sizingMethod='scale')"
                });
            }
            else {
                this.closer = this.button;
            }

            Event.observe(this.closer, 'click', this.toggle.bindAsEventListener(this));
            this.inner.appendChild(this.closer);

            this.content = new Element('div');
            this.inner.appendChild(this.content);

            this.container.appendChild(this.inner);

            document.getElementsByTagName('body')[0].appendChild(this.container);

            this.drawn = true;
        }

        // Reset the title element and reappend the title value (could have changed with a new URL)
        this.titleContainer.innerHTML = '';
        this.titleContainer.appendChild(document.createTextNode(this._titleString));

        // Reset content value:
        this.content.innerHTML = this.options.content;

        //
        // Reapply styling to components as values might have changed
        //

        Element.setStyle(this.inner, {
            'position': 'absolute',
            'top': this.options.contentMargin + 'px',
            'left': this.options.contentMargin + 'px',
            'width': contentDimensions[0] + 'px',
            'height': contentDimensions[1] + 'px'
        });

        Element.setStyle(this.titleContainer, {
            'width': (contentDimensions[0] - buttonDimensions[0]) + 'px',
            'height': buttonDimensions[1] + 8 + 'px',
            'position': 'absolute',
            'overflow': 'hidden',
            'top': '-15px',
            'left': '0px'
        });

        Element.setStyle(this.titleContainer, this.options.titleStyle);

        Element.setStyle(this.closer, {
            'width': buttonDimensions[0] + 'px',
            'height': buttonDimensions[1] + 'px',
            'cursor': 'pointer',
            'position': 'absolute',
            'top': '0px',
            'right': '0px'
        });

        Element.setStyle(this.content, {
            'width': contentDimensions[0] + 'px',
            'height': (contentDimensions[1] - this.button.height) + 'px',
            'overflow': 'auto',
            'position': 'absolute',
            'top': buttonDimensions[1] + 'px',
            'left': '0px'            
        });
        
        Element.setStyle(this.content, this.options.contentStyle);

    },

    /**
    * Gets the current position of the obj
    * @param {Element} element to get position of
    * @return Object of (x, y, x2, y2)
    */
    _getXY: function (obj) {
        var pos = Position.cumulativeOffset(obj)
        var y = pos[1];
        var x = pos[0];
        var x2 = x + parseInt(obj.offsetWidth);
        var y2 = y + parseInt(obj.offsetHeight);
        return { 'x': x, 'y': y, 'x2': x2, 'y2': y2 };

    },

    /**
    * Determins if the object is a child of the balloon element
    * @param {Element} Element to check parentage
    * @return {Boolean}
    * @private
    */
    _isChild: function (obj) {
        var i = 15;
        do {
            if (obj == this.container)
                return true;
            obj = obj.parentNode;
        } while (obj && i--);
        return false
    },

    /**
    * Determines if the balloon is over this_obj object
    * @param {Element} Object to look under
    * @return {Boolean}
    * @private
    */
    _isOver: function (this_obj) {
        if (!this.visible) return false;
        if (this_obj == this.container || this._isChild(this_obj)) return false;
        var this_coords = this._getXY(this_obj);
        var that_coords = this._getXY(this.container);
        if (
			(
			 (
			  (this_coords.x >= that_coords.x && this_coords.x <= that_coords.x2)
			   ||
			  (this_coords.x2 >= that_coords.x && this_coords.x2 <= that_coords.x2)
			 )
			 &&
			 (
			  (this_coords.y >= that_coords.y && this_coords.y <= that_coords.y2)
			   ||
			  (this_coords.y2 >= that_coords.y && this_coords.y2 <= that_coords.y2)
			 )
			)

		  ) {
            return true;
        }
        else
            return false;
    },

    /**
    * Restores visibility of elements under the balloon
    * (For IE)
    * @private
    */
    _showLowerElements: function () {
        if (this.options.hideUnderElementsInIE) {
            var elements = this._getWeirdAPIElements();
            for (var i = 0; i < elements.length; i++) {
                if (this._isOver(elements[i])) {
                    if (elements[i].style.visibility != 'visible' && elements[i].hiddenBy == this) {
                        elements[i].style.visibility = 'visible';
                        elements[i].hiddenBy = null;
                    }
                }
            }
        }
    },

    /**
    * Hides elements below the balloon
    * (For IE)
    * @private
    */
    _hideLowerElements: function () {
        if (this.options.hideUnderElementsInIE) {
            var elements = this._getWeirdAPIElements();
            for (var i = 0; i < elements.length; i++) {
                if (this._isOver(elements[i])) {
                    if (elements[i].style.visibility != 'hidden') {
                        elements[i].style.visibility = 'hidden';
                        elements[i].hiddenBy = this;
                    }
                }
            }
        }
    },

    /**
    * Determines which elements need to be hidden
    * (For IE)
    * @return {Array} array of elements
    */
    _getWeirdAPIElements: function () {
        if (!Prototype.Browser.IE) return [];
        var objs = ['select', 'input', 'object'];
        var elements = [];
        for (var i = 0; i < objs.length; i++) {
            var e = document.getElementsByTagName(objs[i]);
            for (var j = 0; j < e.length; j++) {
                elements.push(e[j]);
            }
        }
        return elements;
    },

    /**
    * Hides the other visible help balloons
    * @param {Event} e
    */
    _hideOtherHelps: function (e) {
        if (this.options.hideOtherBalloonsOnDisplay) {
            $A(HelpBalloon._balloons).each(function (obj) {
                if (obj != this) {
                    obj.hide();
                }
            } .bind(this));
        }
    }
};

/**
* HelpBalloon.Options
* Helper class for defining options for the HelpBalloon object
*/
HelpBalloon.Options = Class.create();
HelpBalloon.Options.prototype = {

    /**
    * @constructor
    * @param {Object} overriding options
    */
    initialize: function (values) {
        // Apply the overriding values to this
        Object.extend(this, values || {});
    },

    /**
    * Show Effect
    * The Scriptaculous (or compatible) showing effect function
    * @var Function
    */
    showEffect: window.Scriptaculous ? Effect.Appear : null,

    /**
    * Show Effect options
    */
    showEffectOptions: { duration: 0.5 },

    /**
    * Apply Effects for IE.
    * since it doesn't render well... skip effects for IE.
    */ 
    skipEffectsForIE: Prototype.Browser.IE ? true : false,

    /**
    * Hide Effect
    * The Scriptaculous (or compatible) hiding effect function
    * @var Function
    */
    hideEffect: window.Scriptaculous ? Effect.Fade : null,

    /**
    * Show Effect options
    */
    hideEffectOptions: { duration: 0.5 },

    /**
    * For use with embedding this object into another. If true, the icon is not created
    * and not appeneded to the DOM at construction.
    * Default is false
    * @var {Boolean}
    */
    returnElement: false,

    /**
    * URL to the anchoring icon image file to use. This can also be a direct reference 
    * to an existing element if you're using that as your anchoring icon. ex. 'Images/info.gif' 
    * @var {Object}
    */
    icon: null,

    /**
    * Alt text of the help icon
    * @var {String}
    */
    altText: '',

    /**
    * URL to pull the title/content XML
    * @var {String}
    */
    dataURL: null,

    /**
    * Static title of the balloon
    * @var {String}
    */
    title: 'Help',

    /**
    * Static content of the balloon
    * @var {String}
    */
    content: null,

    /**
    * The event type to listen for on the icon to show the balloon.
    * Default 'click'
    * @var {String}
    */
    useEvent: ['click'],

    /**
    * Request method for dynamic content. (get, post)
    * Default 'get'
    * @var {String}
    */
    method: 'get',

    /**
    * Flag indicating cache the request result. If this is false, every
    * time the balloon is shown, it will retrieve the remote url and parse it
    * before the balloon appears, updating the content. Otherwise, it will make
    * the call once and use the same content with each subsequent showing.
    * Default true
    * @var {Boolean}
    */
    cacheRemoteContent: true,

    /**
    * Vertical and horizontal margin of the content pane
    * @var {Number}
    */
    contentMargin: 35,

    /**
    * X coordinate of the closing button
    * @var {Number}
    */
    buttonX: 246,

    /**
    * Y coordinate of the closing button
    * @var {Number}
    */
    buttonY: 35,

    /**
    * Closing button image path. ex. 'Images/button.png'
    * @var {String}
    */
    button: null,

    /**
    * Balloon image path prefix. There are 4 button images, numerically named, starting with 0.
    * 0 1
    *  X
    * 2 3
    * X indicates the anchor corner. ex 'Images/redBG-'
    * @var {String}
    */
    balloonPrefix: null,

    /**
    * The image filename suffix, including the file extension
    * @var {String}
    */
    balloonSuffix: '.png',

    /**
    * Position of the balloon's anchor relative to the icon element.
    * Combine one horizontal indicator (left, center, right) and one vertical indicator (top, middle, bottom).
    * Numeric values can also be used in an X Y order. So a value of 9 13 would place the anchor 9 pixels from
    * the left and 13 pixels below the top. (0,0 is top left). If values are greater than the width or height
    * the width or height of the anchor are used instead. If less than 0, 0 is used.
    * Default is 'center middle'
    * @var {String}
    */
    anchorPosition: 'center middle',

    /**
    * Flag indicating whether to hide the elements under the balloon in IE.
    * Setting this to false can cause rendering issues in Internet Explorer
    * as some elements appear on top of the balloon if they're not hidden.
    * Default is true.
    * @var {Boolean}
    */
    hideUnderElementsInIE: true,

    /**
    * Default Balloon styling
    * @var {Object}
    */
    balloonStyle: {},

    /**
    * Default Title Bar style   PAY ATTENTION... USES DOM STYLING.
    * @var {Object}
    */
    titleStyle: {
        'color': 'black',
        'fontSize': '16px',
        'fontWeight': 'bold',
        'fontFamily': 'Verdana',
        'borderBottom': '#ccc 3px solid'
    },

    /**
    * Default Content Bar style   PAY ATTENTION... USES DOM STYLING.
    * @var {Object}
    */
    contentStyle: {
        'fontFamily': 'verdana',
        'fontSize': '11px',
        'fontWeight': 'normal',
        'color': 'black', 
	'paddingTop': '5px'	               
    },

    /**
    * Icon custom styling
    * @var {Object}
    */
    iconStyle: {
        'cursor': 'pointer',
	'verticalAlign': 'text-bottom',
	'paddingBottom': '2px'
    },

    /**
    * Flag indication whether to automatically hide any other visible HelpBalloon on the page before showing the current one.
    * @var {Boolean}
    */
    hideOtherBalloonsOnDisplay: true,

    /**
    * If you want the balloon to always display in a particular location, set this 
    */
    fixedPosition: HelpBalloon.POS_DYNAMIC,

    /**
    * Number of milliseconds to hide the balloon after showing and after the mouse is not over the balloon.
    * A value of 0 means it will not auto-hide
    * @var {Number}
    */
    autoHideTimeout: 0

};
