
//=============================================================================

//
// constants
//

//
// The HOTSPOT_HOTZONE_INFLATE constant defines the zone, around a hotspot,
// where, should the mouse move into, will not prompt the hotspots callout to
// close.
//var HOTSPOT_HOTZONE_INFLATE = 20;
var HOTSPOT_HOTZONE_INFLATE = 20;

//=============================================================================

Rectangle = Class.create ();

Rectangle.prototype = 
{
    initialize: function(options) 
    {
        var opts = options || {};

        this.x = opts.x || 0;
        this.y = opts.y || 0;
        this.width  = opts.width  || 0;
        this.height = opts.height || 0;
    },

    inside: function(x, y) 
    {
        return ( x >= this.x && x <= this.x + this.width ) &&
               ( y >= this.y && y <= this.y + this.height );
    },
    
    inflate: function(by)
    {
        this.x -= by;
        this.y -= by;
        this.width += by;
        this.height+= by;
    }
};

//-----------------------------------------------------------------------------

Workspace = Class.create ();

Workspace.prototype = 
{
    initialize: function(workspace_container_element) 
    {
        
        this.element = workspace_container_element;
        
        Event.observe(this.element, 'mousemove',
                      this._onMouseMove.bind(this));
        
        Event.observe(this.element, 'click',
                      this._onClick.bind(this));
    },
    
    //-------------------------------------------------------------------------
    // == event handlers
    
    _onMouseMove: function ( event )
    //-----------------------------
    {
        if ( HotSpot.active )
        {
            if ( !HotSpot.active.locked )
            {
                //var viewportOffset = Window.getScroll();
                
                var hotSpotBounds = HotSpot.active.getBounds();
                var calloutBounds = HotSpot.active.getCalloutBounds();
                                
                hotSpotBounds.inflate ( HOTSPOT_HOTZONE_INFLATE );
                                
                //if ( !Prototype.Browser.WebKit )
                //{
                //    mouseX = event.clientX + viewportOffset.left;
                 //   mouseY = event.clientY + viewportOffset.top;
    
                //var mouseX = event.clientX
                //var mouseY = event.clientY
                
                var mouseX = Event.pointerX(event);
                var mouseY = Event.pointerY(event);

                if (!hotSpotBounds.inside(mouseX, mouseY) && !calloutBounds.inside(mouseX, mouseY)) {
                    HotSpot.active.hideCallout();
                }

            }
        }
    },
    
    _onClick: function(event) {
        var hotSpot = HotSpot.active;
        if (hotSpot) {
            if (hotSpot.locked) {
                hotSpot.locked = false;
                hotSpot.hideCallout();
            }
        }
    }
};

//-----------------------------------------------------------------------------

HotSpot = Class.create();

HotSpot.active = null;

HotSpot.find = function ( id )
//----------------------------
{
    var element = $( 'hotspot_' + id );
    
    if ( element )
        return element._instanceOfHotSpot;
    
    return null;
};

HotSpot.prototype =
{
    initialize: function( element, url )
    //**********************************
    {       
        this.element = $( element );
        this.id = this.element.id.replace( 'hotspot_', '' );
        this.element._instanceOfHotSpot = this;

        this.callout = $( 'callout_' + this.id );
        this.callout.hotSpot = this;
        
        this.url = url;
            
        Event.observe ( this.element,
                       "mouseover",
                       this._onMouseOver.bind ( this ) );

        Event.observe ( window,
                        'scroll',
                        function ( event )
                        {
                            if ( HotSpot.active )
                            {
                                HotSpot.active._positionCallout ();
                            }
                        }
                      );

        Event.observe ( window,
                        'resize',
                        function ( event )
                        {
                            if ( HotSpot.active )
                            {
                                HotSpot.active._positionCallout ();
                            }
                        }
                      );
        
        // For hotspot callout child anchor tags with class name of 'popup'
        var popup_links = this.callout.select(".popup");
        
        popup_links.push($(this.element.select(".popup")[0]));

        // If container itself is an anchor tag
        if ($(this.element).hasClassName('popup')) {
            popup_links.push(this.element);
        }
        
        // Also match any anchor tags that may be wrapping the hotspot
        if ($(this.element.parentNode).hasClassName('popup')) {
            popup_links.push(this.element.parentNode);
        }
        
        // bind a click event that opens a popup window and closes the callout
        popup_links.each(function(anc) {
            if (anc) {
                anc.observe('click', function(evt) {
                    new PopupWindow(anc.href, 'location_page', { width: 500 });
                    evt.stop();
                    this.hideCallout();
                    return false;
                }.bind(this));                
            }
        }.bind(this));

        this.hideCallout();
    },
    
    //-------------------------------------------------------------------------
    // == queries 

    getBounds : function ()
    //---------------------
    {
        var hotSpot = this.element;
        
        var offset = hotSpot.cumulativeOffset();
        
        options =
            { x: offset[0], //parseInt ( hotSpot.style.left.replace ( 'px', '' ) ),
              y: offset[1], //parseInt ( hotSpot.style.top.replace ( 'px', '' ) ),
              width: hotSpot.getWidth (),
              height: hotSpot.getHeight () };

        return new Rectangle ( options );                         
    },
    
    getCalloutBounds : function ()
    //----------------------------
    {
        var callout = $( this.callout );
        var offset = callout.cumulativeOffset();

        options =
            { x: offset[0], //parseInt ( callout.style.left.replace ( 'px', '' ) ),
              y: offset[1], //parseInt ( callout.style.top.replace ( 'px', '' ) ),
              width: callout.getWidth(),
              height: callout.getHeight() };

        return new Rectangle ( options );                         
    },

    //-------------------------------------------------------------------------
    // == operations

    showCallout: function ( lock )
    //----------------------------
    {
        if ( !HotSpot.active || !HotSpot.active.locked )
        {
            if ( HotSpot.active && HotSpot.active != this )
            {
                HotSpot.active.hideCallout();
            }
               
            this.locked = lock;

            //this.element.style.border = '1px solid gray';

            this._positionCallout ();
            //this.callout.style.visibility = 'visible';
            this.callout.show();//.style.visibility = 'visible';
            
            HotSpot.active = this;
        }
    },

    hideCallout: function ()
    //----------------------
    {
        //this.element.style.border = '0';

        //this.callout.style.visibility = 'hidden';
        this.callout.hide();
        
        if ( HotSpot.active == this )
        {
            HotSpot.active = null;
        }
    },

    //-------------------------------------------------------------------------
    // == implementation
    
    _positionCallout: function()
    //--------------------------
    {
        //
        var CALLOUT_VERTICAL_ADJUSTMENT = 22;
        var CALLOUT_HORIZONTAL_ADJUSTMENT = 0;
        
        var hotSpot = this.element;
        var callout = $( this.callout );
        
        // the hotspot dimentions
        var hotSpotTop = parseInt ( hotSpot.style.top.replace ( 'px', '' ), 10 );
        var hotSpotLeft = parseInt ( hotSpot.style.left.replace ( 'px', '' ), 10 );
        var hotSpotHeight = hotSpot.getHeight ();
        var hotSpotWidth = hotSpot.getWidth ();
        
        // the callout dimensions
        var calloutHeight = this.callout.getHeight ();
        var calloutWidth = this.callout.getWidth ();
        
        // the window client area dimensions
        var clientDimensions = Window.getClientDimensions ();
        var clientHeight = clientDimensions.height;
        var clientWidth = clientDimensions.width;
        
        // the window client offset
        var clientOffset = Window.getScroll ();
        var clientTopOffset = clientOffset.top;
        var clientLeftOffset = clientOffset.left;
        
        // Map Container offset
        // TODO refactor so this hack is unnecessary 
        var map_container = $("map-container");
        var containerOffset = map_container.cumulativeOffset();
        var containerOffsetLeft = containerOffset[0];
        var containerOffsetTop = containerOffset[1];
                
        // the availlable space
        var spaceAbove = (hotSpotTop - (hotSpotHeight / 2) - clientTopOffset) - containerOffsetTop;
        var spaceBelow = clientHeight - (hotSpotTop - hotSpotHeight / 2) + clientTopOffset;
        var spaceLeft  = hotSpotLeft - clientLeftOffset;
        var spaceRight = (clientWidth - (hotSpotLeft + hotSpotWidth) + clientLeftOffset) - containerOffsetLeft;
        
        // does the callout fit below and to the right of the hotspot?
        if ( spaceBelow > calloutHeight + CALLOUT_VERTICAL_ADJUSTMENT &&
             spaceRight > calloutWidth )
        {
            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                CALLOUT_VERTICAL_ADJUSTMENT +
                'px';
                         
            callout.style.left =
                hotSpotLeft +
                hotSpot.getWidth () -
                CALLOUT_HORIZONTAL_ADJUSTMENT +
                'px';

            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopRight' );
            callout.removeClassName ( 'CalloutBottomLeft' );
            callout.removeClassName ( 'CalloutBottomRight' );

            // add the current possitioining class name
            callout.addClassName ( 'CalloutTopLeft' );
        }
        // otherwise, does the callout fit above and to the right of the
        // hotspot?
        else if ( spaceAbove > calloutHeight - CALLOUT_VERTICAL_ADJUSTMENT &&
                  spaceRight > calloutWidth )
        {
            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                calloutHeight +
                'px';
                         
            callout.style.left =
                hotSpotLeft +
                hotSpot.getWidth () -
                CALLOUT_HORIZONTAL_ADJUSTMENT +
                'px';

            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopLeft' );
            callout.removeClassName ( 'CalloutTopRight' );
            callout.removeClassName ( 'CalloutBottomRight' );
    
            // add the current possitioining class name
            callout.addClassName ( 'CalloutBottomLeft' );
        }
        // otherwise, does the callout fit below and to the left of the
        // hotspot?
        else if ( spaceBelow > calloutHeight + CALLOUT_VERTICAL_ADJUSTMENT &&
                  spaceLeft > calloutWidth )
        {
            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                CALLOUT_VERTICAL_ADJUSTMENT +
                'px';
                         
            callout.style.left =
                hotSpotLeft -
                calloutWidth +
                CALLOUT_HORIZONTAL_ADJUSTMENT +
                'px';
            
            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopLeft' );
            callout.removeClassName ( 'CalloutBottomLeft' );
            callout.removeClassName ( 'CalloutBottomRight' );

            // add the current possitioining class name
            callout.addClassName ( 'CalloutTopRight' );
        }
        // otherwise, does the callout fit above and to the left of the
        // hotspot?
        else if ( spaceAbove > calloutHeight - CALLOUT_VERTICAL_ADJUSTMENT &&
                  spaceLeft > calloutWidth )
        {
            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                calloutHeight +
                'px';

            callout.style.left =
                hotSpotLeft -
                calloutWidth +
                CALLOUT_HORIZONTAL_ADJUSTMENT +
                'px';

            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopLeft' );
            callout.removeClassName ( 'CalloutTopRight' );
            callout.removeClassName ( 'CalloutBottomLeft' );

            // add the current possitioining class name
            callout.addClassName ( 'CalloutBottomRight' );
        }
        // otherwise, does the callout fit to the right of the hotspot?
        else if ( ( spaceAbove + spaceBelow  >
                    calloutHeight - CALLOUT_VERTICAL_ADJUSTMENT ) &&
                  ( spaceRight > calloutWidth ) )
        {
            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopLeft' );
            callout.removeClassName ( 'CalloutTopRight' );
            callout.removeClassName ( 'CalloutBottomLeft' );
            callout.removeClassName ( 'CalloutBottomRight' );
            
            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                ( calloutHeight / 2 ) +
                'px';

            callout.style.left =
                hotSpotLeft +
                hotSpotWidth +
                'px';
        }
        // otherwise, does the callout fit to the left of the hotspot?
        else if ( ( spaceAbove + spaceBelow  >
                    calloutHeight - CALLOUT_VERTICAL_ADJUSTMENT ) &&
                  ( spaceLeft > calloutWidth ) )
        {
            // remove any previous positioning classes
            callout.removeClassName ( 'CalloutTopLeft' );
            callout.removeClassName ( 'CalloutTopRight' );
            callout.removeClassName ( 'CalloutBottomLeft' );
            callout.removeClassName ( 'CalloutBottomRight' );

            callout.style.top =
                hotSpotTop +
                ( hotSpot.getHeight () / 2 ) -
                ( calloutHeight / 2 ) +
                'px';

            callout.style.left =
                hotSpotLeft -
                calloutWidth +
                'px';
        }
            
    },

    _onMouseOver: function(event) {
        this.showCallout();
    }
        
};

//=============================================================================

PopupWindow = Class.create ();
// Creates or updates a new popup window by name/id
PopupWindow.prototype = {
    
    initialize: function(url, name_id, options) {
        var default_options = {
            scrollbars: 1
        };
        var new_window = window.open(url, name_id,
                                     this._stringifyOptions(
                                        Object.extend(default_options, options)
                                     ));

        new_window.focus();
        return this;
    },
    
    // Convert options object into a string formatting for window.open
    _stringifyOptions: function(options_object) {
        var str = "";
        for (attrib in options_object) {
            if (attrib != null) {
              str += attrib + "=" + options_object[attrib] + ",";
            }
        }
        return str.replace(/,$/, ""); // chop vestigial comma
    }

};

//=============================================================================

Event.observe(window, 'load', function() {
    var workspace = new Workspace($("content"));
});
