﻿/*
ClusterMarker Version 1.3.1
A marker manager for the Google Maps API
http://googlemapsapi.martinpearman.co.uk/clustermarker	
Copyright Martin Pearman 2008
Last updated 1st September 2008
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

function ClusterMarker($map, $options) {
    this._map = $map;
    this._mapMarkers = [];
    this._iconBounds = [];
    this._clusterMarkers = [];
    this._eventListeners = [];
    if (typeof ($options) === 'undefined') {
        $options = {};
    }
    this.borderPadding = ($options.borderPadding) ? $options.borderPadding : 256;
    this.clusteringEnabled = ($options.clusteringEnabled === false) ? false : true;
    if ($options.clusterMarkerClick) {
        this.clusterMarkerClick = $options.clusterMarkerClick;
    }
    if ($options.clusterMarkerIcon) {
        this.clusterMarkerIcon = $options.clusterMarkerIcon;
    } else {
        this.clusterMarkerIcon = new GIcon();
        this.clusterMarkerIcon.image = '/TipsCommunity/Includes/Images/Google/Markers/marker_cluster_zoom.png';
        this.clusterMarkerIcon.iconSize = new GSize(45, 34);
        this.clusterMarkerIcon.iconAnchor = new GPoint(22, 14);
        //this.clusterMarkerIcon.infoWindowAnchor=new GPoint(9, 31);
        this.clusterMarkerIcon.shadow = '/TipsCommunity/Includes/Images/Google/Markers/marker_cluster_zoom_shadow.png';
        this.clusterMarkerIcon.shadowSize = new GSize(45, 34);
    }
    this.clusterMarkerTitle = ($options.clusterMarkerTitle) ? $options.clusterMarkerTitle : globalResourceTextInstance.tipsClickToZoom;
    if ($options.fitMapMaxZoom) {
        this.fitMapMaxZoom = $options.fitMapMaxZoom;
    }
    this.intersectPadding = ($options.intersectPadding) ? $options.intersectPadding : 0;
    if ($options.markers) {
        this.addMarkers($options.markers);
    }
    GEvent.bind(this._map, 'moveend', this, this._moveEnd);
    GEvent.bind(this._map, 'zoomend', this, this._zoomEnd);
    GEvent.bind(this._map, 'maptypechanged', this, this._mapTypeChanged);
}

ClusterMarker.prototype.addMarkers = function($markers) {
    var i;
    if (!$markers[0]) {
        //	assume $markers is an associative array and convert to a numerically indexed array
        var $numArray = [];
        for (i in $markers) {
            $numArray.push($markers[i]);
        }
        $markers = $numArray;
    }
    for (i = $markers.length - 1; i >= 0; i--) {
        $markers[i]._isVisible = false;
        $markers[i]._isActive = false;
        $markers[i]._makeVisible = false;
    }
    this._mapMarkers = this._mapMarkers.concat($markers);
};

ClusterMarker.prototype._clusterMarker = function($clusterGroupIndexes) {
    function $newClusterMarker($location, $icon, $title) {
        return new GMarker($location, { icon: $icon, title: $title });
    }
    var $clusterGroupBounds = new GLatLngBounds(), i, $clusterMarker, $clusteredMarkers = [], $marker, $this = this, $mapMarkers = this._mapMarkers;
    for (i = $clusterGroupIndexes.length - 1; i >= 0; i--) {
        $marker = $mapMarkers[$clusterGroupIndexes[i]];
        $marker.index = $clusterGroupIndexes[i];
        $clusterGroupBounds.extend($marker.getLatLng());
        $clusteredMarkers.push($marker);
    }
    $clusterMarker = $newClusterMarker($clusterGroupBounds.getCenter(), this.clusterMarkerIcon, this.clusterMarkerTitle.replace(/%count/gi, $clusterGroupIndexes.length));
    $clusterMarker.clusterGroupBounds = $clusterGroupBounds; //	only req'd for default cluster marker click action
    this._eventListeners.push(GEvent.addListener($clusterMarker, 'click', function() {
        $this.clusterMarkerClick({ clusterMarker: $clusterMarker, clusteredMarkers: $clusteredMarkers });
    }));
    $clusterMarker._childIndexes = $clusterGroupIndexes;
    for (i = $clusterGroupIndexes.length - 1; i >= 0; i--) {
        $mapMarkers[$clusterGroupIndexes[i]]._parentCluster = $clusterMarker;
    }
    return $clusterMarker;
};

ClusterMarker.prototype.clusterMarkerClick = function($args) {
    this._map.setCenter($args.clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($args.clusterMarker.clusterGroupBounds));
};

ClusterMarker.prototype._filterActiveMapMarkers = function() {
    var $borderPadding = this.borderPadding, $mapZoomLevel = this._map.getZoom(), $mapProjection = this._map.getCurrentMapType().getProjection(), $mapPointSw, $activeAreaPointSw, $activeAreaLatLngSw, $mapPointNe, $activeAreaPointNe, $activeAreaLatLngNe, $activeAreaBounds = this._map.getBounds(), i, $marker, $uncachedIconBoundsIndexes = [], $oldState, $mapMarkers = this._mapMarkers, $iconBounds = this._iconBounds;
    if ($borderPadding) {
        $mapPointSw = $mapProjection.fromLatLngToPixel($activeAreaBounds.getSouthWest(), $mapZoomLevel);
        $activeAreaPointSw = new GPoint($mapPointSw.x - $borderPadding, $mapPointSw.y + $borderPadding);
        $activeAreaLatLngSw = $mapProjection.fromPixelToLatLng($activeAreaPointSw, $mapZoomLevel);
        $mapPointNe = $mapProjection.fromLatLngToPixel($activeAreaBounds.getNorthEast(), $mapZoomLevel);
        $activeAreaPointNe = new GPoint($mapPointNe.x + $borderPadding, $mapPointNe.y - $borderPadding);
        $activeAreaLatLngNe = $mapProjection.fromPixelToLatLng($activeAreaPointNe, $mapZoomLevel);
        $activeAreaBounds.extend($activeAreaLatLngSw);
        $activeAreaBounds.extend($activeAreaLatLngNe);
    }
    this._activeMarkersChanged = false;
    if (typeof ($iconBounds[$mapZoomLevel]) === 'undefined') {
        //	no iconBounds cached for this zoom level
        //	no need to check for existence of individual iconBounds elements
        this._iconBounds[$mapZoomLevel] = [];
        this._activeMarkersChanged = true; //	force refresh(true) as zoomed to uncached zoom level
        for (i = $mapMarkers.length - 1; i >= 0; i--) {
            $marker = $mapMarkers[i];
            $marker._isActive = $activeAreaBounds.containsLatLng($marker.getLatLng()) ? true : false;
            $marker._makeVisible = $marker._isActive;
            if ($marker._isActive) {
                $uncachedIconBoundsIndexes.push(i);
            }
        }
    } else {
        //	icondBounds array exists for this zoom level
        //	check for existence of individual iconBounds elements
        for (i = $mapMarkers.length - 1; i >= 0; i--) {
            $marker = $mapMarkers[i];
            $oldState = $marker._isActive;
            $marker._isActive = $activeAreaBounds.containsLatLng($marker.getLatLng()) ? true : false;
            $marker._makeVisible = $marker._isActive;
            if (!this._activeMarkersChanged && $oldState !== $marker._isActive) {
                this._activeMarkersChanged = true;
            }
            if ($marker._isActive && typeof ($iconBounds[$mapZoomLevel][i]) === 'undefined') {
                $uncachedIconBoundsIndexes.push(i);
            }
        }
    }
    return $uncachedIconBoundsIndexes;
};

ClusterMarker.prototype._filterIntersectingMapMarkers = function() {
    var $clusterGroup, i, j, $mapZoomLevel = this._map.getZoom(), $mapMarkers = this._mapMarkers, $iconBounds = this._iconBounds;
    for (i = $mapMarkers.length - 1; i > 0; i--) {
        if ($mapMarkers[i]._makeVisible) {
            $clusterGroup = [];
            for (j = i - 1; j >= 0; j--) {
                if ($mapMarkers[j]._makeVisible && $iconBounds[$mapZoomLevel][i].intersects($iconBounds[$mapZoomLevel][j])) {
                    $clusterGroup.push(j);
                }
            }
            if ($clusterGroup.length !== 0) {
                $clusterGroup.push(i);
                for (j = $clusterGroup.length - 1; j >= 0; j--) {
                    $mapMarkers[$clusterGroup[j]]._makeVisible = false;
                }
                this._clusterMarkers.push(this._clusterMarker($clusterGroup));
            }
        }
    }
};

ClusterMarker.prototype.fitMapToMarkers = function() {
    var $mapMarkers = this._mapMarkers, $markersBounds = new GLatLngBounds(), i;
    for (i = $mapMarkers.length - 1; i >= 0; i--) {
        $markersBounds.extend($mapMarkers[i].getLatLng());
    }
    var $fitMapToMarkersZoom = this._map.getBoundsZoomLevel($markersBounds);

    if (this.fitMapMaxZoom && $fitMapToMarkersZoom > this.fitMapMaxZoom) {
        $fitMapToMarkersZoom = this.fitMapMaxZoom;
    }
    this._map.setCenter($markersBounds.getCenter(), $fitMapToMarkersZoom);
    this.refresh();
};

ClusterMarker.prototype._mapTypeChanged = function() {
    this.refresh(true);
};

ClusterMarker.prototype._moveEnd = function() {
    if (!this._cancelMoveEnd) {
        this.refresh();
    } else {
        this._cancelMoveEnd = false;
    }
};

ClusterMarker.prototype._preCacheIconBounds = function($indexes, $mapZoomLevel) {
    var $mapProjection = this._map.getCurrentMapType().getProjection(), i, $marker, $iconSize, $iconAnchorPoint, $iconAnchorPointOffset, $iconBoundsPointSw, $iconBoundsPointNe, $iconBoundsLatLngSw, $iconBoundsLatLngNe, $intersectPadding = this.intersectPadding, $mapMarkers = this._mapMarkers;
    for (i = $indexes.length - 1; i >= 0; i--) {
        $marker = $mapMarkers[$indexes[i]];
        $iconSize = $marker.getIcon().iconSize;
        $iconAnchorPoint = $mapProjection.fromLatLngToPixel($marker.getLatLng(), $mapZoomLevel);
        $iconAnchorPointOffset = $marker.getIcon().iconAnchor;
        $iconBoundsPointSw = new GPoint($iconAnchorPoint.x - $iconAnchorPointOffset.x - $intersectPadding, $iconAnchorPoint.y - $iconAnchorPointOffset.y + $iconSize.height + $intersectPadding);
        $iconBoundsPointNe = new GPoint($iconAnchorPoint.x - $iconAnchorPointOffset.x + $iconSize.width + $intersectPadding, $iconAnchorPoint.y - $iconAnchorPointOffset.y - $intersectPadding);
        $iconBoundsLatLngSw = $mapProjection.fromPixelToLatLng($iconBoundsPointSw, $mapZoomLevel);
        $iconBoundsLatLngNe = $mapProjection.fromPixelToLatLng($iconBoundsPointNe, $mapZoomLevel);
        this._iconBounds[$mapZoomLevel][$indexes[i]] = new GLatLngBounds($iconBoundsLatLngSw, $iconBoundsLatLngNe);
    }
};

ClusterMarker.prototype.refresh = function($forceFullRefresh) {
    var i, $marker, $zoomLevel = this._map.getZoom(), $uncachedIconBoundsIndexes = this._filterActiveMapMarkers();
    if (this._activeMarkersChanged || $forceFullRefresh) {
        this._removeClusterMarkers();
        if (this.clusteringEnabled && $zoomLevel < this._map.getCurrentMapType().getMaximumResolution()) {
            if ($uncachedIconBoundsIndexes.length > 0) {
                this._preCacheIconBounds($uncachedIconBoundsIndexes, $zoomLevel);
            }
            this._filterIntersectingMapMarkers();
        }
        for (i = this._clusterMarkers.length - 1; i >= 0; i--) {
            this._map.addOverlay(this._clusterMarkers[i]);
        }
        for (i = this._mapMarkers.length - 1; i >= 0; i--) {
            $marker = this._mapMarkers[i];
            if (!$marker._isVisible && $marker._makeVisible) {
                this._map.addOverlay($marker);
                $marker._isVisible = true;
            }
            if ($marker._isVisible && !$marker._makeVisible) {
                this._map.removeOverlay($marker);
                $marker._isVisible = false;
            }
        }
    }
};

ClusterMarker.prototype._removeClusterMarkers = function() {
    var i, j, $map = this._map, $eventListeners = this._eventListeners, $clusterMarkers = this._clusterMarkers, $childIndexes, $mapMarkers = this._mapMarkers;
    for (i = $clusterMarkers.length - 1; i >= 0; i--) {
        $childIndexes = $clusterMarkers[i]._childIndexes;
        for (j = $childIndexes.length - 1; j >= 0; j--) {
            delete $mapMarkers[$childIndexes[j]]._parentCluster;
        }
        $map.removeOverlay($clusterMarkers[i]);
    }
    for (i = $eventListeners.length - 1; i >= 0; i--) {
        GEvent.removeListener($eventListeners[i]);
    }
    this._clusterMarkers = [];
    this._eventListeners = [];
};

ClusterMarker.prototype.removeMarkers = function() {
    var i, $mapMarkers = this._mapMarkers, $map = this._map;
    for (i = $mapMarkers.length - 1; i >= 0; i--) {
        if ($mapMarkers[i]._isVisible) {
            $map.removeOverlay($mapMarkers[i]);
        }
        delete $mapMarkers[i]._isVisible;
        delete $mapMarkers[i]._isActive;
        delete $mapMarkers[i]._makeVisible;
    }
    this._removeClusterMarkers();
    this._mapMarkers = [];
    this._iconBounds = [];
};

ClusterMarker.prototype.triggerClick = function($index) {
    var $marker = this._mapMarkers[$index];
    if ($marker._isVisible) {
        //	$marker is visible
        GEvent.trigger($marker, 'click');
    }
    else if ($marker._isActive) {
        //	$marker is clustered
        var $intersectingMarkerIndexes = $marker._parentCluster._childIndexes, $intersectDetected, $uncachedIconBoundsIndexes, i, j, $mapZoomLevel = this._map.getZoom();
        do {
            $intersectDetected = false;
            $mapZoomLevel++;
            if (typeof (this._iconBounds[$mapZoomLevel]) === 'undefined') {
                //	no iconBounds cached for this zoom level
                //	no need to check for existence of individual iconBounds elements
                this._iconBounds[$mapZoomLevel] = [];
                // need to create cache for all clustered markers at $mapZoomLevel
                this._preCacheIconBounds($intersectingMarkerIndexes, $mapZoomLevel);
            } else {
                //	iconBounds array exists for this zoom level
                //	check for existence of individual iconBounds elements
                $uncachedIconBoundsIndexes = [];
                for (j = $intersectingMarkerIndexes.length - 1; j >= 0; j--) {
                    if (typeof (this._iconBounds[$mapZoomLevel][$intersectingMarkerIndexes[j]] === 'undefined')) {
                        $uncachedIconBoundsIndexes.push($intersectingMarkerIndexes[j]);
                    }
                }
                if ($uncachedIconBoundsIndexes.length >= 1) {
                    this._preCacheIconBounds($uncachedIconBoundsIndexes, $mapZoomLevel);
                }
            }
            for (i = $intersectingMarkerIndexes.length - 1; i >= 0; i--) {
                if ($intersectingMarkerIndexes[i] !== $index && this._iconBounds[$mapZoomLevel][$intersectingMarkerIndexes[i]].intersects(this._iconBounds[$mapZoomLevel][$index])) {	//	don't check that clustered marker intersects itself
                    $intersectDetected = true;
                    break;
                }
            }

        }
        while ($intersectDetected && $mapZoomLevel < this._map.getCurrentMapType().getMaximumResolution());
        this._map.setCenter($marker.getLatLng(), $mapZoomLevel);
        this.triggerClick($index);
    } else {
        // $marker is not within active area (map bounds + border padding)
        this._map.setCenter($marker.getLatLng());
        this.triggerClick($index);
    }
};

ClusterMarker.prototype._zoomEnd = function() {
    this._cancelMoveEnd = true;
    this.refresh(true);
};
//////Start DragAndZoom////////////////////
function DragZoomControl(opts_boxStyle, opts_other, opts_callbacks) { this.globals = { draggingOn: false, cornerTopDiv: null, cornerRightDiv: null, cornerBottomDiv: null, cornerLeftDiv: null, mapPosition: null, outlineDiv: null, mapWidth: 0, mapHeight: 0, mapRatio: 0, startX: 0, startY: 0, borderCorrection: 0 }; this.globals.style = { opacity: .2, fillColor: "#000", border: "2px solid blue" }; var style = this.globals.style; for (var s in opts_boxStyle) { style[s] = opts_boxStyle[s] } var borderStyleArray = style.border.split(' '); style.outlineWidth = parseInt(borderStyleArray[0].replace(/\D/g, '')); style.outlineColor = borderStyleArray[2]; style.alphaIE = 'alpha(opacity=' + (style.opacity * 100) + ')'; this.globals.backStack = []; this.globals.options = { buttonHTML: 'zoom ...', buttonStartingStyle: { width: '52px', border: '1px solid black', padding: '2px' }, buttonStyle: { background: '#FFF' }, backButtonHTML: 'zoom back', backButtonStyle: { background: '#FFF', display: 'none' }, buttonZoomingHTML: 'Drag a region on the map', buttonZoomingStyle: { background: '#FF0' }, overlayRemoveTime: 6000, backButtonEnabled: false, stickyZoomEnabled: false, rightMouseZoomOutEnabled: false, minDragSize: 0 }; for (var s in opts_other) { this.globals.options[s] = opts_other[s] } if (opts_callbacks == null) { opts_callbacks = {} } this.globals.callbacks = opts_callbacks }; DragZoomControl.prototype = new GControl(); DragZoomControl.prototype.saveMapContext = function(text) { if (this.globals.options.backButtonEnabled) { this.saveBackContext_(text, true); this.globals.backButtonDiv.style.display = 'block' } }; DragZoomControl.prototype.initiateZoom = function() { this.buttonclick_() }; DragZoomControl.prototype.initiateZoomBack = function() { if (this.globals.options.backButtonEnabled) this.backbuttonclick_() }; DragZoomControl.prototype.initButton_ = function(buttonContainerDiv) { var G = this.globals; var buttonDiv = document.createElement('div'); buttonDiv.innerHTML = G.options.buttonHTML; DragZoomUtil.style([buttonDiv], { cursor: 'pointer', zIndex: 200 }); DragZoomUtil.style([buttonDiv], G.options.buttonStartingStyle); DragZoomUtil.style([buttonDiv], G.options.buttonStyle); buttonContainerDiv.appendChild(buttonDiv); return buttonDiv }; DragZoomControl.prototype.initBackButton_ = function(buttonContainerDiv) { var G = this.globals; var backButtonDiv = document.createElement('div'); backButtonDiv.innerHTML = G.options.backButtonHTML; DragZoomUtil.style([backButtonDiv], { cursor: 'pointer', zIndex: 200 }); DragZoomUtil.style([backButtonDiv], G.options.buttonStartingStyle); DragZoomUtil.style([backButtonDiv], G.options.backButtonStyle); buttonContainerDiv.appendChild(backButtonDiv); return backButtonDiv }; DragZoomControl.prototype.setButtonMode_ = function(mode) { var G = this.globals; if (mode == 'zooming') { G.buttonDiv.innerHTML = G.options.buttonZoomingHTML; DragZoomUtil.style([G.buttonDiv], G.options.buttonStartingStyle); DragZoomUtil.style([G.buttonDiv], G.options.buttonZoomingStyle) } else { G.buttonDiv.innerHTML = G.options.buttonHTML; DragZoomUtil.style([G.buttonDiv], G.options.buttonStartingStyle); DragZoomUtil.style([G.buttonDiv], G.options.buttonStyle) } }; DragZoomControl.prototype.initialize = function(map) { var G = this.globals; var me = this; var mapDiv = map.getContainer(); var buttonContainerDiv = document.createElement("div"); DragZoomUtil.style([buttonContainerDiv], { cursor: 'pointer', zIndex: 150 }); var buttonDiv = this.initButton_(buttonContainerDiv); var backButtonDiv = this.initBackButton_(buttonContainerDiv); mapDiv.appendChild(buttonContainerDiv); var zoomDiv = document.createElement("div"); var DIVS_TO_CREATE = ['outlineDiv', 'cornerTopDiv', 'cornerLeftDiv', 'cornerRightDiv', 'cornerBottomDiv']; for (var i = 0; i < DIVS_TO_CREATE.length; i++) { var id = DIVS_TO_CREATE[i]; var div = document.createElement("div"); DragZoomUtil.style([div], { position: 'absolute', display: 'none' }); zoomDiv.appendChild(div); G[id] = div } DragZoomUtil.style([zoomDiv], { position: 'absolute', display: 'none', overflow: 'hidden', cursor: 'crosshair', zIndex: 101 }); mapDiv.appendChild(zoomDiv); GEvent.addDomListener(buttonDiv, 'click', function(e) { me.buttonclick_(e) }); GEvent.addDomListener(backButtonDiv, 'click', function(e) { me.backbuttonclick_(e) }); GEvent.addDomListener(zoomDiv, 'mousedown', function(e) { me.coverMousedown_(e) }); GEvent.addDomListener(document, 'mousemove', function(e) { me.drag_(e) }); GEvent.addDomListener(document, 'mouseup', function(e) { me.mouseup_(e) }); G.mapPosition = DragZoomUtil.getElementPosition(mapDiv); G.buttonDiv = buttonDiv; G.backButtonDiv = backButtonDiv; G.mapCover = zoomDiv; G.map = map; G.borderCorrection = G.style.outlineWidth * 2; this.setDimensions_(); this.initStyles_(); G.mapCover.onselectstart = function() { return false }; return buttonContainerDiv }; DragZoomControl.prototype.getDefaultPosition = function() { return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(3, 120)) }; DragZoomControl.prototype.coverMousedown_ = function(e) { var G = this.globals; var pos = this.getRelPos_(e); G.startX = pos.left; G.startY = pos.top; if (e.which) { var rightMouse = (e.which != 1) } else if (e.button) { var rightMouse = (e.button != 1) } G.draggingRightMouse = rightMouse; DragZoomUtil.style([G.mapCover], { background: 'transparent', opacity: 1, filter: 'alpha(opacity=100)' }); DragZoomUtil.style([G.outlineDiv], { left: G.startX + 'px', top: G.startY + 'px', display: 'block', width: '1px', height: '1px' }); G.draggingOn = true; G.cornerTopDiv.style.top = (G.startY - G.mapHeight) + 'px'; G.cornerTopDiv.style.display = 'block'; G.cornerLeftDiv.style.left = (G.startX - G.mapWidth) + 'px'; G.cornerLeftDiv.style.top = G.startY + 'px'; G.cornerLeftDiv.style.display = 'block'; G.cornerRightDiv.style.left = G.startX + 'px'; G.cornerRightDiv.style.top = G.startY + 'px'; G.cornerRightDiv.style.display = 'block'; G.cornerBottomDiv.style.left = G.startX + 'px'; G.cornerBottomDiv.style.top = G.startY + 'px'; G.cornerBottomDiv.style.width = '0px'; G.cornerBottomDiv.style.display = 'block'; if (G.callbacks.dragstart != null) { G.callbacks.dragstart(G.startX, G.startY) } return false }; DragZoomControl.prototype.drag_ = function(e) { var G = this.globals; if (G.draggingOn) { var pos = this.getRelPos_(e); var rect = this.getRectangle_(G.startX, G.startY, pos, G.mapRatio); if (rect.left) { addX = -rect.width } else { addX = 0 } if (rect.top) { addY = -rect.height } else { addY = 0 } DragZoomUtil.style([G.outlineDiv], { left: G.startX + addX + 'px', top: G.startY + addY + 'px', display: 'block', width: '1px', height: '1px' }); G.outlineDiv.style.width = rect.width + "px"; G.outlineDiv.style.height = rect.height + "px"; G.cornerTopDiv.style.height = ((G.startY + addY) - (G.startY - G.mapHeight)) + 'px'; G.cornerLeftDiv.style.top = (G.startY + addY) + 'px'; G.cornerLeftDiv.style.width = ((G.startX + addX) - (G.startX - G.mapWidth)) + 'px'; G.cornerRightDiv.style.top = G.cornerLeftDiv.style.top; G.cornerRightDiv.style.left = (G.startX + addX + rect.width + G.borderCorrection) + 'px'; G.cornerBottomDiv.style.top = (G.startY + addY + rect.height + G.borderCorrection) + 'px'; G.cornerBottomDiv.style.left = (G.startX - G.mapWidth + ((G.startX + addX) - (G.startX - G.mapWidth))) + 'px'; G.cornerBottomDiv.style.width = (rect.width + G.borderCorrection) + 'px'; if (G.callbacks.dragging != null) { G.callbacks.dragging(G.startX, G.startY, rect.endX, rect.endY) } return false } }; DragZoomControl.prototype.mouseup_ = function(e) { var G = this.globals; if (G.draggingOn) { var pos = this.getRelPos_(e); G.draggingOn = false; var rect = this.getRectangle_(G.startX, G.startY, pos, G.mapRatio); if (rect.left) rect.endX = rect.startX - rect.width; if (rect.top) rect.endY = rect.startY - rect.height; this.resetDragZoom_(); if (rect.width >= G.options.minDragSize && rect.height >= G.options.minDragSize) { var nwpx = new GPoint(rect.startX, rect.startY); var nepx = new GPoint(rect.endX, rect.startY); var sepx = new GPoint(rect.endX, rect.endY); var swpx = new GPoint(rect.startX, rect.endY); var nw = G.map.fromContainerPixelToLatLng(nwpx); var ne = G.map.fromContainerPixelToLatLng(nepx); var se = G.map.fromContainerPixelToLatLng(sepx); var sw = G.map.fromContainerPixelToLatLng(swpx); var zoomAreaPoly = new GPolyline([nw, ne, se, sw, nw], G.style.outlineColor, G.style.outlineWidth + 1, .4); try { G.map.addOverlay(zoomAreaPoly); setTimeout(function() { G.map.removeOverlay(zoomAreaPoly) }, G.options.overlayRemoveTime) } catch (e) { } var polyBounds = zoomAreaPoly.getBounds(); var ne = polyBounds.getNorthEast(); var sw = polyBounds.getSouthWest(); var se = new GLatLng(sw.lat(), ne.lng()); var nw = new GLatLng(ne.lat(), sw.lng()); if (G.options.rightMouseZoomOutEnabled && G.draggingRightMouse) { var mapSpan = G.map.getBounds().toSpan(); var polySpan = polyBounds.toSpan(); var dSize = Math.max(mapSpan.lat() / polySpan.lat(), mapSpan.lng() / polySpan.lng()); var zoomLevel = G.map.getZoom() - Math.ceil(Math.log(dSize, 2)) } else { var zoomLevel = G.map.getBoundsZoomLevel(polyBounds) } var center = polyBounds.getCenter(); G.map.setCenter(center, zoomLevel); if (G.callbacks.dragend != null) { G.callbacks.dragend(nw, ne, se, sw, nwpx, nepx, sepx, swpx) } } if (G.options.stickyZoomEnabled) { this.initCover_(); if (G.options.backButtonEnabled) this.saveBackContext_(G.options.backButtonHTML, false); G.backButtonDiv.style.display = 'none' } } }; DragZoomControl.prototype.setDimensions_ = function() { var G = this.globals; var mapSize = G.map.getSize(); G.mapWidth = mapSize.width; G.mapHeight = mapSize.height; G.mapRatio = G.mapHeight / G.mapWidth; DragZoomUtil.style([G.mapCover, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], { top: '0px', left: '0px', width: G.mapWidth + 'px', height: G.mapHeight + 'px' }) }; DragZoomControl.prototype.initStyles_ = function() { var G = this.globals; DragZoomUtil.style([G.mapCover, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], { filter: G.style.alphaIE, opacity: G.style.opacity, background: G.style.fillColor }); G.outlineDiv.style.border = G.style.border }; DragZoomControl.prototype.buttonclick_ = function() { var G = this.globals; G.backButtonDiv.style.display = 'none'; if (G.mapCover.style.display == 'block') { this.resetDragZoom_(); if (G.options.backButtonEnabled) { this.restoreBackContext_(); if (G.backStack.length == 0) G.backButtonDiv.style.display = 'none' } } else { this.initCover_(); if (G.options.backButtonEnabled) this.saveBackContext_(G.options.backButtonHTML, false) } }; DragZoomControl.prototype.backbuttonclick_ = function() { var G = this.globals; if (G.options.backButtonEnabled && G.backStack.length > 0) { this.restoreBackContext_(); if (G.callbacks['backbuttonclick'] != null) { G.callbacks.backbuttonclick(G.methodCall) } } }; DragZoomControl.prototype.saveBackContext_ = function(text, methodCall) { var G = this.globals; var backFrame = {}; backFrame["center"] = G.map.getCenter(); backFrame["zoom"] = G.map.getZoom(); backFrame["maptype"] = G.map.getCurrentMapType(); backFrame["text"] = G.backButtonDiv.innerHTML; backFrame["methodCall"] = methodCall; G.backStack.push(backFrame); G.backButtonDiv.innerHTML = text }; DragZoomControl.prototype.restoreBackContext_ = function() { var G = this.globals; var backFrame = G.backStack.pop(); G.map.setCenter(backFrame["center"], backFrame["zoom"], backFrame["maptype"]); G.backButtonDiv.innerHTML = backFrame["text"]; G.methodCall = backFrame["methodCall"]; if (G.backStack.length == 0) G.backButtonDiv.style.display = 'none' }; DragZoomControl.prototype.initCover_ = function() { var G = this.globals; G.mapPosition = DragZoomUtil.getElementPosition(G.map.getContainer()); this.setDimensions_(); this.setButtonMode_('zooming'); DragZoomUtil.style([G.mapCover], { display: 'block', background: G.style.fillColor }); DragZoomUtil.style([G.outlineDiv], { width: '0px', height: '0px' }); if (G.callbacks['buttonclick'] != null) { G.callbacks.buttonclick() } }; DragZoomControl.prototype.getRelPos_ = function(e) { var pos = DragZoomUtil.getMousePosition(e); var G = this.globals; return { top: (pos.top - G.mapPosition.top), left: (pos.left - G.mapPosition.left)} }; DragZoomControl.prototype.getRectangle_ = function(startX, startY, pos, ratio) { var left = false; var top = false; var dX = pos.left - startX; var dY = pos.top - startY; if (dX < 0) { dX = dX * -1; left = true } if (dY < 0) { dY = dY * -1; top = true } delta = dX > dY ? dX : dY; return { startX: startX, startY: startY, endX: startX + delta, endY: startY + parseInt(delta * ratio), width: delta, height: parseInt(delta * ratio), left: left, top: top} }; DragZoomControl.prototype.resetDragZoom_ = function() { var G = this.globals; DragZoomUtil.style([G.mapCover, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], { display: 'none', opacity: G.style.opacity, filter: G.style.alphaIE }); G.outlineDiv.style.display = 'none'; this.setButtonMode_('normal'); if (G.options.backButtonEnabled && (G.backStack.length > 0)) G.backButtonDiv.style.display = 'block' }; var DragZoomUtil = {}; DragZoomUtil.gE = function(sId) { return document.getElementById(sId) }; DragZoomUtil.getMousePosition = function(e) { var posX = 0; var posY = 0; if (!e) var e = window.event; if (e.pageX || e.pageY) { posX = e.pageX; posY = e.pageY } else if (e.clientX || e.clientY) { posX = e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); posY = e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) } return { left: posX, top: posY} }; DragZoomUtil.getElementPosition = function(element) { var leftPos = element.offsetLeft; var topPos = element.offsetTop; var parElement = element.offsetParent; while (parElement != null) { leftPos += parElement.offsetLeft; topPos += parElement.offsetTop; parElement = parElement.offsetParent } return { left: leftPos, top: topPos} }; DragZoomUtil.style = function(elements, styles) { if (typeof (elements) == 'string') { elements = DragZoomUtil.getManyElements(elements) } for (var i = 0; i < elements.length; i++) { for (var s in styles) { elements[i].style[s] = styles[s] } } }; DragZoomUtil.getManyElements = function(idsString) { var idsArray = idsString.split(','); var elements = []; for (var i = 0; i < idsArray.length; i++) { elements[elements.length] = DragZoomUtil.gE(idsArray[i]) }; return elements };
/////////////////////////////////////
/*
* ExtInfoWindow Class, v1.0 
*  Copyright (c) 2007, Joe Monahan (http://www.seejoecode.com)
* 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This class lets you add an info window to the map which mimics GInfoWindow
* and allows for users to skin it via CSS.  Additionally it has options to
* pull in HTML content from an ajax request, triggered when a user clicks on
* the associated marker.
*/
function ExtInfoWindow(marker, windowId, html, opt_opts) {
    this.html_ = html;
    this.marker_ = marker;
    this.infoWindowId_ = windowId;
    this.options_ = opt_opts == null ? {} : opt_opts;
    this.ajaxUrl_ = this.options_.ajaxUrl == null ? null : this.options_.ajaxUrl;
    this.callback_ = this.options_.ajaxCallback == null ? null : this.options_.ajaxCallback;
    this.maxContent_ = this.options_.maxContent == null ? null : this.options_.maxContent;
    this.maximizeEnabled_ = this.maxContent_ == null ? false : true;
    this.isMaximized_ = false;
    this.borderSize_ = this.options_.beakOffset == null ? 0 : this.options_.beakOffset;
    this.paddingX_ = this.options_.paddingX == null ? 0 + this.borderSize_ : this.options_.paddingX + this.borderSize_;
    this.paddingY_ = this.options_.paddingY == null ? 0 + this.borderSize_ : this.options_.paddingY + this.borderSize_;
    this.map_ = null;
    this.container_ = document.createElement('div');
    this.container_.style.position = 'relative';
    this.container_.style.display = 'none';
    this.contentDiv_ = document.createElement('div');
    this.contentDiv_.id = this.infoWindowId_ + '_contents';
    this.contentDiv_.innerHTML = this.html_;
    this.contentDiv_.style.display = 'block';
    this.contentDiv_.style.visibility = 'hidden';
    this.wrapperDiv_ = document.createElement('div');
};
ExtInfoWindow.prototype = new GOverlay();
ExtInfoWindow.prototype.initialize = function(map) {
    this.map_ = map;
    if (this.maximizeEnabled_) {
        this.maxWidth_ = this.map_.getSize().width * 0.9;
        this.maxHeight_ = this.map_.getSize().height * 0.9;
    }
    this.defaultStyles = {
        containerWidth: this.map_.getSize().width / 2,
        borderSize: 1
    };
    this.wrapperParts = {
        tl: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        t: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        tr: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        l: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        r: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        bl: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        b: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        br: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        beak: { t: 0, l: 0, w: 0, h: 0, domElement: null },
        close: { t: 0, l: 0, w: 0, h: 0, domElement: null }
    };
    if (this.maximizeEnabled_) {
        this.wrapperParts.max = { t: 0, l: 0, w: 0, h: 0, domElement: null }
        this.wrapperParts.min = { t: 0, l: 0, w: 0, h: 0, domElement: null }
    }

    for (var i in this.wrapperParts) {
        var tempElement = document.createElement('div');
        tempElement.id = this.infoWindowId_ + '_' + i;
        tempElement.style.visibility = 'hidden';
        document.body.appendChild(tempElement);
        tempElement = document.getElementById(this.infoWindowId_ + '_' + i);
        var tempWrapperPart = this.wrapperParts[i];
        tempWrapperPart.w = parseInt(this.getStyle_(tempElement, 'width'), 10);
        tempWrapperPart.h = parseInt(this.getStyle_(tempElement, 'height'), 10);
        document.body.removeChild(tempElement);
    }
    for (var i in this.wrapperParts) {
        if (i == 'close') {            
            this.wrapperDiv_.appendChild(this.contentDiv_);
        }
        var wrapperPartsDiv = null;
        if (this.wrapperParts[i].domElement == null) {
            wrapperPartsDiv = document.createElement('div');
            this.wrapperDiv_.appendChild(wrapperPartsDiv);
        } else {
            wrapperPartsDiv = this.wrapperParts[i].domElement;
        }
        wrapperPartsDiv.id = this.infoWindowId_ + '_' + i;
        wrapperPartsDiv.style.position = 'absolute';
        wrapperPartsDiv.style.width = this.wrapperParts[i].w + 'px';
        wrapperPartsDiv.style.height = this.wrapperParts[i].h + 'px';
        wrapperPartsDiv.style.top = this.wrapperParts[i].t + 'px';
        wrapperPartsDiv.style.left = this.wrapperParts[i].l + 'px';
        this.wrapperParts[i].domElement = wrapperPartsDiv;
    }

    this.map_.getPane(G_MAP_FLOAT_PANE).appendChild(this.container_);
    this.container_.id = this.infoWindowId_;
    var containerWidth = this.getStyle_(document.getElementById(this.infoWindowId_), 'width');
    this.container_.style.width = (containerWidth == null ? this.defaultStyles.containerWidth : containerWidth);
    this.map_.getContainer().appendChild(this.contentDiv_);
    this.contentWidth = this.getDimensions_(this.container_).width;
    this.contentDiv_.style.width = this.contentWidth + 'px';
    this.contentDiv_.style.position = 'absolute';
    this.container_.appendChild(this.wrapperDiv_);
    if (this.maximizeEnabled_) {
        this.minWidth_ = this.getDimensions_(this.container_).width;
        console.log(this.minWidth_);
    }

    if (this.maximizeEnabled_) {
        thisMap = this.map_;
        thisMaxWidth = this.maxWidth_;
        thisMaxHeight = this.maxHeight_;
        thisContainer = this.container_;
        thisMaxContent = this.maxContent_;
        thisMinWidth = this.container_.style.width;
        thisMinHeight = this.container_.style.height;
        GEvent.addDomListener(this.wrapperParts.max.domElement, 'click',
      function() {
          var infoWindow = thisMap.getExtInfoWindow();
          infoWindow.container_.style.width = thisMaxWidth + 'px';
          infoWindow.ajaxRequest_(thisMaxContent);
          infoWindow.isMaximized_ = true;
          infoWindow.redraw(true);         
          infoWindow.toggleMaxMin_();
      }
    );
        GEvent.addDomListener(this.wrapperParts.min.domElement, 'click',
      function() {
          var infoWindow = thisMap.getExtInfoWindow();
          infoWindow.container_.style.width = thisMinWidth;
          infoWindow.container_.style.height = thisMinHeight;
          if (infoWindow.ajaxUrl_ != null) {
              infoWindow.ajaxRequest_(this.ajaxUrl_);
          } else {
              infoWindow.contentDiv_.innerHTML = infoWindow.html_;
          }
          infoWindow.isMaximized_ = false;
          infoWindow.redraw(true);
          infoWindow.resize();
          infoWindow.toggleMaxMin_();
      }
    );
        this.toggleMaxMin_();
    }
    var stealEvents = ['mousedown', 'dblclick', 'DOMMouseScroll'];
    for (i = 0; i < stealEvents.length; i++) { GEvent.bindDom(this.container_, stealEvents[i], this, this.onClick_); }
    GEvent.trigger(this.map_, 'extinfowindowopen');
    if (this.ajaxUrl_ != null) {this.ajaxRequest_(this.ajaxUrl_); }
};
ExtInfoWindow.prototype.onClick_ = function(e) {
    if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) {
        window.event.cancelBubble = true;
        window.event.returnValue = false;
    } else {     
        e.stopPropagation();
    }};

ExtInfoWindow.prototype.remove = function() {
    if (this.map_.getExtInfoWindow() != null) {
        GEvent.trigger(this.map_, 'extinfowindowbeforeclose');

        GEvent.clearInstanceListeners(this.container_);
        if (this.container_.outerHTML) {
            this.container_.outerHTML = ''; //prevent pseudo-leak in IE
        }
        if (this.container_.parentNode) {
            this.container_.parentNode.removeChild(this.container_);
        }
        this.container_ = null;
        GEvent.trigger(this.map_, 'extinfowindowclose');
        this.map_.setExtInfoWindow_(null);
    }};
ExtInfoWindow.prototype.copy = function() {return new ExtInfoWindow(this.marker_, this.infoWindowId_, this.html_, this.options_);};
ExtInfoWindow.prototype.redraw = function(force) {
    if (!force || this.container_ == null) return;

    var contentHeight = this.contentDiv_.offsetHeight;
    this.contentDiv_.style.height = contentHeight + 'px';
    this.contentWidth = this.getDimensions_(this.container_).width;
    this.contentDiv_.style.width = this.container_.style.width;
    // EMIL kommenterade bort raden nedan
    // this.contentDiv_.style.left = this.wrapperParts.l.w + 'px';
    this.contentDiv_.style.top = this.wrapperParts.tl.h + 'px';
    this.contentDiv_.style.visibility = 'visible';
    this.wrapperParts.tl.t = 0;
    this.wrapperParts.tl.l = 0;
    this.wrapperParts.t.l = this.wrapperParts.tl.w;
    this.wrapperParts.t.w = (this.wrapperParts.l.w + this.contentWidth + this.wrapperParts.r.w) - this.wrapperParts.tl.w - this.wrapperParts.tr.w;
    this.wrapperParts.t.h = this.wrapperParts.tl.h;
    this.wrapperParts.tr.l = this.wrapperParts.t.w + this.wrapperParts.tl.w;
    this.wrapperParts.l.t = this.wrapperParts.tl.h;
    this.wrapperParts.l.h = contentHeight;
    this.wrapperParts.r.l = this.contentWidth + this.wrapperParts.l.w;
    this.wrapperParts.r.t = this.wrapperParts.tr.h;
    this.wrapperParts.r.h = contentHeight;
    this.wrapperParts.bl.t = contentHeight + this.wrapperParts.tl.h;
    this.wrapperParts.b.l = this.wrapperParts.bl.w;
    this.wrapperParts.b.t = contentHeight + this.wrapperParts.tl.h;
    this.wrapperParts.b.w = (this.wrapperParts.l.w + this.contentWidth + this.wrapperParts.r.w) - this.wrapperParts.bl.w - this.wrapperParts.br.w;
    this.wrapperParts.b.h = this.wrapperParts.bl.h;
    this.wrapperParts.br.l = this.wrapperParts.b.w + this.wrapperParts.bl.w;
    this.wrapperParts.br.t = contentHeight + this.wrapperParts.tr.h;
    //EMIL added + 6
    this.wrapperParts.beak.l = (this.borderSize_ + 7) + (this.contentWidth / 2) - (this.wrapperParts.beak.w / 2);
    this.wrapperParts.beak.t = this.wrapperParts.bl.t + this.wrapperParts.bl.h - this.borderSize_;
    this.wrapperParts.close.l = this.wrapperParts.tr.l + this.wrapperParts.tr.w - this.wrapperParts.close.w - this.borderSize_;
    this.wrapperParts.close.t = this.borderSize_;
    if (this.maximizeEnabled_) {
        this.wrapperParts.max.l = this.wrapperParts.close.l - this.wrapperParts.max.w - 5;
        this.wrapperParts.max.t = this.wrapperParts.close.t;
        this.wrapperParts.min.l = this.wrapperParts.max.l;
        this.wrapperParts.min.t = this.wrapperParts.max.t;
    }
    for (var i in this.wrapperParts) {
        if (i == 'close') {            
            this.wrapperDiv_.insertBefore(this.contentDiv_, this.wrapperParts[i].domElement);
        }
        var wrapperPartsDiv = null;
        if (this.wrapperParts[i].domElement == null) {
            wrapperPartsDiv = document.createElement('div');
            this.wrapperDiv_.appendChild(wrapperPartsDiv);
        } else {
            wrapperPartsDiv = this.wrapperParts[i].domElement;
        }
        wrapperPartsDiv.id = this.infoWindowId_ + '_' + i;
        wrapperPartsDiv.style.position = 'absolute';
        wrapperPartsDiv.style.width = this.wrapperParts[i].w + 'px';
        wrapperPartsDiv.style.height = this.wrapperParts[i].h + 'px';
        wrapperPartsDiv.style.top = this.wrapperParts[i].t + 'px';
        wrapperPartsDiv.style.left = this.wrapperParts[i].l + 'px';
        this.wrapperParts[i].domElement = wrapperPartsDiv;
    }
    var currentMarker = this.marker_;
    var thisMap = this.map_;
    GEvent.addDomListener(this.wrapperParts.close.domElement, 'click',
    function() {
        thisMap.closeExtInfoWindow();
    }
  );
    var pixelLocation = this.map_.fromLatLngToDivPixel(this.marker_.getPoint());
    this.container_.style.position = 'absolute';
    var markerIcon = this.marker_.getIcon();
    this.container_.style.left = (pixelLocation.x
    - (this.contentWidth / 2)
    - markerIcon.iconAnchor.x
    + markerIcon.infoWindowAnchor.x
  ) + 'px';

    this.container_.style.top = (pixelLocation.y
    - this.wrapperParts.bl.h
    - contentHeight
    - this.wrapperParts.tl.h
    - this.wrapperParts.beak.h
    - markerIcon.iconAnchor.y
    + markerIcon.infoWindowAnchor.y
    + this.borderSize_
  ) + 'px';

    this.container_.style.display = 'block';

    if (this.map_.getExtInfoWindow() != null) {
        this.repositionMap_();
    }
};
ExtInfoWindow.prototype.toggleMaxMin_ = function() {
    if (this.wrapperParts.max.domElement != null && this.wrapperParts.min.domElement != null) {
        if (this.isMaximized_) {
            this.wrapperParts.max.domElement.style.display = 'none';
            this.wrapperParts.min.domElement.style.display = 'block';
        } else {
            this.wrapperParts.max.domElement.style.display = 'block';
            this.wrapperParts.min.domElement.style.display = 'none';
        }}}
ExtInfoWindow.prototype.resize = function() {
    var tempElement = this.contentDiv_.cloneNode(true);
    tempElement.id = this.infoWindowId_ + '_tempContents';
    tempElement.style.visibility = 'hidden';
    tempElement.style.height = 'auto';
    document.body.appendChild(tempElement);
    tempElement = document.getElementById(this.infoWindowId_ + '_tempContents');
    var contentHeight = tempElement.offsetHeight;
    document.body.removeChild(tempElement);
    this.contentDiv_.style.height = contentHeight + 'px';
    var contentWidth = this.container_.offsetWidth;
    var pixelLocation = this.map_.fromLatLngToDivPixel(this.marker_.getPoint());
    var oldWindowHeight = this.wrapperParts.t.domElement.offsetHeight + this.wrapperParts.l.domElement.offsetHeight + this.wrapperParts.b.domElement.offsetHeight;
    var oldWindowPosTop = this.wrapperParts.t.domElement.offsetTop;
    this.wrapperParts.l.domElement.style.height = contentHeight + 'px';
    this.wrapperParts.r.domElement.style.height = contentHeight + 'px';
    var newPosTop = this.wrapperParts.b.domElement.offsetTop - contentHeight;
    this.wrapperParts.l.domElement.style.top = newPosTop + 'px';
    this.wrapperParts.r.domElement.style.top = newPosTop + 'px';
    this.contentDiv_.style.top = newPosTop + 'px';
    windowTHeight = parseInt(this.wrapperParts.t.domElement.style.height, 10);
    newPosTop -= windowTHeight;
    this.wrapperParts.close.domElement.style.top = newPosTop + this.borderSize_ + 'px';
    this.wrapperParts.tl.domElement.style.top = newPosTop + 'px';
    this.wrapperParts.t.domElement.style.top = newPosTop + 'px';
    this.wrapperParts.tr.domElement.style.top = newPosTop + 'px';

    this.repositionMap_();
};
ExtInfoWindow.prototype.repositionMap_ = function() {
    var mapNE = this.map_.fromLatLngToDivPixel(
    this.map_.getBounds().getNorthEast()
  );
    var mapSW = this.map_.fromLatLngToDivPixel(
    this.map_.getBounds().getSouthWest()
  );
    var markerPosition = this.map_.fromLatLngToDivPixel(
    this.marker_.getPoint()
  );

    var panX = 0;
    var panY = 0;
    var paddingX = this.paddingX_;
    var paddingY = this.paddingY_;
    var infoWindowAnchor = this.marker_.getIcon().infoWindowAnchor;
    var iconAnchor = this.marker_.getIcon().iconAnchor;
    var windowT = this.wrapperParts.t.domElement;
    var windowL = this.wrapperParts.l.domElement;
    var windowB = this.wrapperParts.b.domElement;
    var windowR = this.wrapperParts.r.domElement;
    var windowBeak = this.wrapperParts.beak.domElement;

    var offsetTop = markerPosition.y - (-infoWindowAnchor.y + iconAnchor.y + this.getDimensions_(windowBeak).height + this.getDimensions_(windowB).height + this.getDimensions_(windowL).height + this.getDimensions_(windowT).height + this.paddingY_);
    if (offsetTop < mapNE.y) {
        panY = mapNE.y - offsetTop;
    } else {
        var offsetBottom = markerPosition.y + this.paddingY_;
        if (offsetBottom >= mapSW.y) {
            panY = -(offsetBottom - mapSW.y);
        }
    }
    
    var offsetRight = Math.round(markerPosition.x + this.getDimensions_(this.container_).width / 2 + this.getDimensions_(windowR).width + this.paddingX_ + infoWindowAnchor.x - iconAnchor.x);
    if (offsetRight > mapNE.x) {
        panX = -(offsetRight - mapNE.x);
    } else {        
        var offsetLeft = -(Math.round((this.getDimensions_(this.container_).width / 2 - this.marker_.getIcon().iconSize.width / 2) + this.getDimensions_(windowL).width + this.borderSize_ + this.paddingX_) - markerPosition.x - infoWindowAnchor.x + iconAnchor.x);
        if (offsetLeft < mapSW.x) { panX = mapSW.x - offsetLeft;}
    }
    if (panX != 0 || panY != 0 && this.map_.getExtInfoWindow() != null) {
        this.map_.panBy(new GSize(panX, panY));
    }
};

ExtInfoWindow.prototype.ajaxRequest_ = function(url) {
    var thisMap = this.map_;
    var thisCallback = this.callback_;
    GDownloadUrl(url, function(response, status) {
        if (thisMap.getExtInfoWindow() !== null) {
            var infoWindow = document.getElementById(thisMap.getExtInfoWindow().infoWindowId_ + '_contents');
            if (response == null || status == -1) {
                infoWindow.innerHTML = '<span class="error">ERROR: The Ajax request failed to get HTML content from "' + url + '"</span>';
            } else {
                infoWindow.innerHTML = response;
            }
            if (thisCallback != null) {
                thisCallback();
            }
            thisMap.getExtInfoWindow().resize();
        }
        GEvent.trigger(thisMap, 'extinfowindowupdate');
    });
};

ExtInfoWindow.prototype.getDimensions_ = function(element) {
    var display = this.getStyle_(element, 'display');
    if (display != 'none' && display != null) { // Safari bug
        return { width: element.offsetWidth, height: element.offsetHeight };
    }
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return { width: originalWidth, height: originalHeight };
};
ExtInfoWindow.prototype.getStyle_ = function(element, style) {
    var found = false;
    style = this.camelize_(style);
    if (element.id == this.infoWindowId_ && style == 'width' && element.style.display == 'none') {
        element.style.visibility = 'hidden';
        element.style.display = '';
    }
    var value = element.style[style];
    if (!value) {
        if (document.defaultView && document.defaultView.getComputedStyle) {
            var css = document.defaultView.getComputedStyle(element, null);
            value = css ? css[style] : null;
        } else if (element.currentStyle) {
            value = element.currentStyle[style];
        }
    }
    if ((value == 'auto') && (style == 'width' || style == 'height') && (this.getStyle_(element, 'display') != 'none')) {
        if (style == 'width') {
            value = element.offsetWidth;
        } else {
            value = element.offsetHeight;
        }
    }
    if (element.id == this.infoWindowId_ && style == 'width' && element.style.display != 'none') {
        element.style.display = 'none';
        element.style.visibility = 'visible';
    }
    return (value == 'auto') ? null : value;
};

ExtInfoWindow.prototype.camelize_ = function(element) {
    var parts = element.split('-'), len = parts.length;
    if (len == 1) return parts[0];
    var camelized = element.charAt(0) == '-'
    ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
    : parts[0];

    for (var i = 1; i < len; i++) {
        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
    }
    return camelized;
};

GMap.prototype.ExtInfoWindowInstance_ = null;
GMap.prototype.ClickListener_ = null;
GMap.prototype.InfoWindowListener_ = null;

GMarker.prototype.openExtInfoWindow = function(map, cssId, html, opt_opts) {
    if (map == null) {
        throw 'Error in GMarker.openExtInfoWindow: map cannot be null';
        return false;
    }
    if (cssId == null || cssId == '') {
        throw 'Error in GMarker.openExtInfoWindow: must specify a cssId';
        return false;
    }

    map.closeInfoWindow();
    if (map.getExtInfoWindow() != null) {map.closeExtInfoWindow();}
    if (map.getExtInfoWindow() == null) {
        map.setExtInfoWindow_(new ExtInfoWindow(this,cssId,html,opt_opts));
        if (map.ClickListener_ == null) { map.ClickListener_ = GEvent.addListener(map, 'click',
      function(event) {
          if (!event && map.getExtInfoWindow() != null) { map.closeExtInfoWindow(); }});}
        if (map.InfoWindowListener_ == null) {            
            map.InfoWindowListener_ = GEvent.addListener(map, 'infowindowopen',
      function(event) { if (map.getExtInfoWindow() != null) { map.closeExtInfoWindow(); } } ); } map.addOverlay(map.getExtInfoWindow()); }};

/** Remove the ExtInfoWindow instance @param {GMap2} map The map where the GMarker and ExtInfoWindow exist*/
GMarker.prototype.closeExtInfoWindow = function(map) {if (map.getExtInfoWindow() != null) {map.closeExtInfoWindow();}};
/**Get the ExtInfoWindow instance from the map*/
GMap2.prototype.getExtInfoWindow = function() {return this.ExtInfoWindowInstance_;};
/** Set the ExtInfoWindow instance for the map @private */
GMap2.prototype.setExtInfoWindow_ = function(extInfoWindow) {this.ExtInfoWindowInstance_ = extInfoWindow;}
/** Remove the ExtInfoWindow from the map*/
GMap2.prototype.closeExtInfoWindow = function() { if (this.getExtInfoWindow() != null) {this.ExtInfoWindowInstance_.remove();}};
//////////////////////////////////
function LabeledMarker(latlng, opt_opts) { this.latlng_ = latlng; this.opts_ = opt_opts; this.labelText_ = opt_opts.labelText || ""; this.labelClass_ = opt_opts.labelClass || "LabeledMarker_markerLabel"; this.labelOffset_ = opt_opts.labelOffset || new GSize(0, 0); this.clickable_ = opt_opts.clickable || true; this.title_ = opt_opts.title || ""; this.labelVisibility_ = true; if (opt_opts.draggable) { opt_opts.draggable = false } GMarker.apply(this, arguments) }; LabeledMarker.prototype = new GMarker(new GLatLng(0, 0)); LabeledMarker.prototype.initialize = function(map) { GMarker.prototype.initialize.apply(this, arguments); this.map_ = map; this.div_ = document.createElement("div"); this.div_.className = this.labelClass_; this.div_.innerHTML = this.labelText_; this.div_.style.position = "absolute"; this.div_.style.cursor = "pointer"; this.div_.title = this.title_; map.getPane(G_MAP_MARKER_PANE).appendChild(this.div_); if (this.clickable_) { function newEventPassthru(obj, event) { return function() { GEvent.trigger(obj, event) } } var eventPassthrus = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout']; for (var i = 0; i < eventPassthrus.length; i++) { var name = eventPassthrus[i]; GEvent.addDomListener(this.div_, name, newEventPassthru(this, name)) } } }; LabeledMarker.prototype.redraw = function(force) { GMarker.prototype.redraw.apply(this, arguments); this.redrawLabel_() }; LabeledMarker.prototype.redrawLabel_ = function() { var p = this.map_.fromLatLngToDivPixel(this.latlng_); var z = GOverlay.getZIndex(this.latlng_.lat()); this.div_.style.left = (p.x + this.labelOffset_.width) + "px"; this.div_.style.top = (p.y + this.labelOffset_.height) + "px"; this.div_.style.zIndex = z }; LabeledMarker.prototype.remove = function() { GEvent.clearInstanceListeners(this.div_); if (this.div_.outerHTML) { this.div_.outerHTML = "" } if (this.div_.parentNode) { this.div_.parentNode.removeChild(this.div_) } this.div_ = null; GMarker.prototype.remove.apply(this, arguments) }; LabeledMarker.prototype.copy = function() { return new LabeledMarker(this.latlng_, this.opts_) }; LabeledMarker.prototype.show = function() { GMarker.prototype.show.apply(this, arguments); if (this.labelVisibility_) { this.showLabel() } else { this.hideLabel() } }; LabeledMarker.prototype.hide = function() { GMarker.prototype.hide.apply(this, arguments); this.hideLabel() }; LabeledMarker.prototype.setLatLng = function(latlng) { this.latlng_ = latlng; GMarker.prototype.setLatLng.apply(this, arguments); this.redrawLabel_() }; LabeledMarker.prototype.setLabelVisibility = function(visibility) { this.labelVisibility_ = visibility; if (!this.isHidden()) { if (this.labelVisibility_) { this.showLabel() } else { this.hideLabel() } } }; LabeledMarker.prototype.getLabelVisibility = function() { return this.labelVisibility_ }; LabeledMarker.prototype.hideLabel = function() { this.div_.style.visibility = 'hidden' }; LabeledMarker.prototype.showLabel = function() { this.div_.style.visibility = 'visible' };