﻿/* An InfoBox is like an info window, but it displays
* under the marker, opens quicker, and has flexible styling.
* @param {GLatLng} latlng Point to place bar at
* @param {Map} map The map on which to display this InfoBox.
* @param {Object} opts Passes configuration options - content,
*   offsetVertical, offsetHorizontal, className, height, width
*/
function InfoBox(opts) {
    google.maps.OverlayView.call(this);
    this.latlng_ = opts.latlng;
    this.map_ = opts.map;
    this.padding_ = [20, 80, 20, 20];
    this.offsetVertical_ = -35;
    this.offsetHorizontal_ = -190;
    this.height_ = 145;
    this.width_ = 166;
    this.content_ = opts.content;
    this.title_ = opts.title;
    var me = this;
    this.onDraw = opts.onDraw;
    this.boundsChangedListener_;
    //google.maps.event.addListener(this.map_, "bounds_changed", function() {
        //return me.panMap.apply(me);
    //});    
    // Once the properties of this OverlayView are initialized, set its map so
    // that we can display it.  This will trigger calls to panes_changed and
    // draw.
    this.setMap(this.map_);
}

/* InfoBox extends GOverlay class from the Google Maps API
*/
InfoBox.prototype = new google.maps.OverlayView();

/* Creates the DIV representing this InfoBox
*/
InfoBox.prototype.remove = function() {
    if (this.div_) {
        this.div_.parentNode.removeChild(this.div_);
        this.div_ = null;
    }
};

/* Redraw the Bar based on the current projection and zoom level
*/
InfoBox.prototype.draw = function() {
    // Creates the element if it doesn't exist already.
    this.createElement();
    if (!this.div_) return;

    // Calculate the DIV coordinates of two opposite corners of our bounds to
    // get the size and position of our Bar
    var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
    if (!pixPosition) return;

    // Now position our DIV based on the DIV coordinates of our bounds
    this.div_.style.width = this.width_ + "px";
    this.div_.style.left = (pixPosition.x + this.offsetHorizontal_) + "px";
    //this.div_.style.height = this.height_ + "px";
    this.div_.style.top = (pixPosition.y + this.offsetVertical_) + "px";
    //this.div_.style.display = 'block';
};

/* Creates the DIV representing this InfoBox in the floatPane.  If the panes
* object, retrieved by calling get_panes, is null, remove the element from the
* DOM.  If the div exists, but its parent is not the floatPane, move the div
* to the new pane.
* Called from within draw.  Alternatively, this can be called specifically on
* a panes_changed event.
*/
InfoBox.prototype.createElement = function() {
    var panes = this.getPanes();
    var div = this.div_;
    if (!div) {
        // This does not handle changing panes.  You can set the map to be null and
        // then reset the map to move the div.
        //alert('create element');
        div = this.div_ = document.createElement("div");
        div.className = "InfoBox";
        div.style.width = this.width_ + "px";
        //div.style.height = this.height_ + "px";

        var contentDiv = document.createElement("div");
        contentDiv.className = "InfoBoxContent";
        contentDiv.innerHTML = this.content_ || "";

        var topDiv = document.createElement("div");
        topDiv.className = "InfoBoxHeader";

        var closeBtn = document.createElement("div");
        closeBtn.className = "InfoBoxClose";
        closeBtn.innerHTML = "X";

        var topTitle = document.createElement("div");
        topTitle.className = "InfoBoxTitle";
        topTitle.innerHTML = this.title_ || "&nbsp;";

        var arrowDiv = document.createElement("div");
        arrowDiv.className = "InfoBoxArrow";

        topDiv.appendChild(topTitle);
        topDiv.appendChild(closeBtn);

        function removeInfoBox(ib) {
            return function() {
                ib.set_map(null);
            };
        };

        google.maps.event.addDomListener(closeBtn, 'click', this.close.bind(this));

        div.appendChild(topDiv);
        div.appendChild(contentDiv);
        div.appendChild(arrowDiv);
        div.style.display = 'none';
        panes.floatPane.appendChild(div);
        //this.panMap();
        if (this.onDraw) this.onDraw();
    } else if (div.parentNode != panes.floatPane) {
        // The panes have changed.  Move the div.
        div.parentNode.removeChild(div);
        panes.floatPane.appendChild(div);
    } else {
        // The panes have not changed, so no need to create or move the div.
    }
}
InfoBox.prototype.open = function() {
    if (this.div_) {
        this.div_.style.display = 'block';
        this.panMap();
    }
}
InfoBox.prototype.close = function() {
    if ( this.div_ ) this.div_.style.display = 'none';
}
InfoBox.prototype.removeInfoBox = function() {
    this.set_map(null);
}

/* Pan the map to fit the InfoBox.
*/
InfoBox.prototype.panMap = function() {
    // if we go beyond map, pan map
    if (this.div_ && this.div_.style.display == 'none') {
        return;
    }
    var map = this.map_;
    var bounds = map.getBounds();
    if (!bounds) return;
    // The position of the infowindow
    var position = this.latlng_;

    // The dimension of the infowindow
    var iwWidth = this.width_;
    //var iwHeight = this.height_;
    var iwHeight = this.div_.clientHeight || 100;

    // The offset position of the infowindow
    var iwOffsetX = this.offsetHorizontal_;
    var iwOffsetY = this.offsetVertical_;

    // Padding on the infowindow
    var padding = this.padding_;

    // The degrees per pixel
    var mapDiv = map.getDiv();
    var mapWidth = mapDiv.offsetWidth;
    var mapHeight = mapDiv.offsetHeight;
    var boundsSpan = bounds.toSpan();
    var longSpan = boundsSpan.lng();
    var latSpan = boundsSpan.lat();
    var degPixelX = longSpan / mapWidth;
    var degPixelY = latSpan / mapHeight;

    // The bounds of the map
    var mapWestLng = bounds.getSouthWest().lng();
    var mapEastLng = bounds.getNorthEast().lng();
    var mapNorthLat = bounds.getNorthEast().lat();
    var mapSouthLat = bounds.getSouthWest().lat();

    // The bounds of the infowindow
    var iwWestLng = parseFloat(position.lng()) + (iwOffsetX - padding[3]) * degPixelX;
    var iwEastLng = parseFloat(position.lng()) + (iwOffsetX + iwWidth + padding[1]) * degPixelX;
    var iwNorthLat = parseFloat(position.lat()) - (iwOffsetY - padding[0]) * degPixelY;
    var iwSouthLat = parseFloat(position.lat()) - (iwOffsetY + iwHeight + padding[2]) * degPixelY;

    // calculate center shift
    var shiftLng =
      (iwWestLng < mapWestLng ? mapWestLng - iwWestLng : 0) +
      (iwEastLng > mapEastLng ? mapEastLng - iwEastLng : 0);
    var shiftLat =
      (iwNorthLat > mapNorthLat ? mapNorthLat - iwNorthLat : 0) +
      (iwSouthLat < mapSouthLat ? mapSouthLat - iwSouthLat : 0);

    // The center of the map
    var center = map.getCenter();

    // The new map center
    var centerX = center.lng() - shiftLng;
    var centerY = center.lat() - shiftLat;

    // center the map to the new shifted center
    map.setCenter(new google.maps.LatLng(centerY, centerX));

    // Remove the listener after panning is complete.
    if (this.boundsChangedListener_ != undefined) {
        google.maps.event.removeListener(this.boundsChangedListener_);
        this.boundsChangedListener_ = null;
    }
};
