/*************************************
 * GLOBAL VARIABLES
 */
PATH_TO_ICON_FOLDER="/running/images/";

CLOSE_MAP=function() {hideMap()};

/*************************************
 * DO NOT EDIT BELOW HERE
 */
if(typeof(ESS)=="undefined") var ESS={};
ESS.Utils={
	gel:function(_elemId){
		return document.getElementById(_elemId);
	},
	createElem:function(_nodeName){
		return document.createElement(_nodeName);
	},
	createTxtNode:function(_nodeValue){
		return document.createTextNode(_nodeValue);
	},
	gelstn:function(_tagName,_parentElem){
		if(!_parentElem)
			_parentElem=document;
		return _parentElem.getElementsByTagName(_tagName);
	},
	gelscn:function(_className,_parentElem){
		var _allElems=ESS.Utils.gelstn("*",_parentElem);
		var _returnArr=[];
		_className=_className.replace(/\-/g,"\\-");
		var _regExp=new RegExp("(^|\\s)"+_className+"(\\s|$)");
		for(var i=0; i<_allElems.length; i++){
			if(_regExp.test(_allElems[i].className))
				_returnArr.push(_allElems[i]);
		}
		return _returnArr;
	},
	inArray:function(_needle,_haystack){
		for(var i=0; i<_haystack.length; i++){
			if(_haystack[i]===_needle)
				return true;
		}
		return false;
	},
	truncate:function(_string,_len,_ending){
		var _newStr=_string.substr(0,_len);
		_newStr=_newStr.replace(/(\.*) $/,"$1");
		if(_newStr.length<_string.length)
			_newStr=_newStr+_ending
		return _newStr;
	}
};
var gel=ESS.Utils.gel;
var createElem=ESS.Utils.createElem;
var createTxtNode=ESS.Utils.createTxtNode;
var gelstn=ESS.Utils.gelstn;
var gelscn=ESS.Utils.gelscn;
var inArray=ESS.Utils.inArray;
var truncate=ESS.Utils.truncate;
ESS.Map={
	
	/* Important HTML element IDs used by multiple methods
	 */
	MapElemId:null,
	FilterElemId:null,
	
	/* Store for currently displayed Markers and Info Window
	 */
	Displayed:{
		Markers:{},
		InfoWindow:null
	},
	
    initMap: function() {
        // Check for browser compatibility
        if(!google.maps.BrowserIsCompatible()){
            alert("Google maps is not compatible with your browser.");
            return;
        }
        // Init map
        ESS.Map.MapObj=new google.maps.Map2(gel(ESS.Map.MapElemId));
        new google.maps.KeyboardHandler(ESS.Map.MapObj);
        //ESS.Map.MapObj.enableScrollWheelZoom();
        // Init controls
        ESS.Map.addNavigationControls();
        ESS.Map.addToggleActions();
        ESS.Map.addYouSearched();
        //ESS.Map.addInnerShadows();
        // Set center point
        ESS.Map.MapObj.setCenter(
            new google.maps.LatLng(ESS.Map.DefaultView.Center.Lat, ESS.Map.DefaultView.Center.Lng),
            ESS.Map.DefaultView.ZoomLevel
        );
        //
        ESS.Map.MapObj.addMapType(G_PHYSICAL_MAP);
        switch(ESS.Map.DefaultView.View){
            case "map":
                ESS.Map.MapObj.setMapType(G_NORMAL_MAP);
            break;
            case "satellite":
                ESS.Map.MapObj.setMapType(G_SATELLITE_MAP);
            break;
            case "terrain":
                ESS.Map.MapObj.setMapType(G_PHYSICAL_MAP);
            break;
        }
        // OnUnload to close memory leaks
        gel(ESS.Map.MapElemId).onunload=google.maps.Unload;
        // Init error pane
        ESS.Map.initErrorElem();
        // Create icons ready for placing
        ESS.Map.createIcons();
        // Extend GOverlay object for custom Info Windows
        ESS.Map.extendGOverlay();
        // Get data - calls placeMarkers as callback
        ESS.Map.Data.load();  
    },

	/* Load the map in specified HTML element
	 * @param {string} _elemId : HTML element to load map into
	 * @param {object} _defViewOpts : Optional object literal defining initial map view
	 */
	loadMap:function(_elemId, _MapUrl, _MapType){
		// Save destination HTML elem ID
		if(!gel(_elemId))
			throw new Error("Cannot find element with ID "+_elemId+". A DIV element with this ID must be present in the page for the map to load.");
		gel(_elemId).innerHTML = '';
        ESS.Map.MapElemId=_elemId;
        ESS.Map.Data.Url = _MapUrl;
		// Reset custom center, zoom, perm layers and limitToIds
        if(!ESS.Map.DefaultView.Center) {
            ESS.Map.DefaultView.Center={Lat:53.5,Lng:-2.7};
        }
        ESS.Map.DefaultView.ZoomLevel=6;
        ESS.Map.DefaultView.View = _MapType;
        ESS.Map.Layers.AlwaysOn=['Event', 'Home'];
        ESS.Map.Data.QueryParams=[];
			
		if(ESS.Map.DefaultView.Layers=="all")
			ESS.Map.Layers.Selected[ESS.Map.DefaultView.Type]=ESS.Map.Layers.Available[ESS.Map.DefaultView.Type];
		else
			ESS.Map.Layers.Selected[ESS.Map.DefaultView.Type]=ESS.Map.DefaultView.Layers;
		// If API has not been loaded (ie. first run)
        if(ESS.Map.MapObj) {
            ESS.Map.MapObj.clearOverlays();
            google.maps.Unload();
        }
        for(var _clustererId in ESS.Map.Clusterers){
            ESS.Map.Clusterers[_clustererId].removeMarkers();
            delete ESS.Map.Clusterers[_clustererId];
        }
        ESS.Map.Displayed.Markers={};
		ESS.Map.showMapLoading();
        // Load maps API - must be at least v2.92 for "callback" support
        google.load("maps","2.95",{callback: ESS.Map.initMap});
		
	},
	
	/* Data related methods
	 * 
	 */
	Data:{
		QueryParams:[],
		Url:null,
		XmlHttpObj:null,
		Xml:null,
		Json:null,
		load:function(){
			// Show loader
			ESS.Map.showDataLoading();
            //init json
            ESS.Map.Data.Json={};
			// Get data
			ESS.Map.Data.XmlHttpObj=google.maps.XmlHttp.create();
			ESS.Map.Data.XmlHttpObj.onreadystatechange=function(){
				if(ESS.Map.Data.XmlHttpObj.readyState==4){
					if(ESS.Map.Data.XmlHttpObj.status==200 || ESS.Map.Data.XmlHttpObj.status===0){
						ESS.Map.Data.Xml=ESS.Map.Data.XmlHttpObj.responseXML;
                        var _center=gelstn("center", ESS.Map.Data.Xml.documentElement);
                        ESS.Map.DefaultView.Center.isCustom = false;
                        for(var i=0; i<_center.length; i++) {
                            if(0 < gelstn("lat", _center[i]).length && 0 < gelstn("lng", _center[i]).length) {
                                ESS.Map.DefaultView.Center = {Lat:google.maps.Xml.value(gelstn("lat",_center[i])[0]),Lng:google.maps.Xml.value(gelstn("lng",_center[i])[0]), isCustom: true};
                            } 
                        }
                        // Reset center and zoom
                        //ESS.Map.MapObj.setCenter(
                        //    new google.maps.LatLng(ESS.Map.DefaultView.Center.Lat, ESS.Map.DefaultView.Center.Lng),
                        //    ESS.Map.DefaultView.ZoomLevel
                        //);
						var _partnerNodes=gelstn("marker",ESS.Map.Data.Xml.documentElement);
						for(i=0; i<_partnerNodes.length; i++){
							var _id=google.maps.Xml.value(gelstn("id",_partnerNodes[i])[0]);
							var _layer=google.maps.Xml.value(gelstn("type",_partnerNodes[i])[0]).replace("'","").replace(/ /g,"_")+"s";
							var _name=google.maps.Xml.value(gelstn("name",_partnerNodes[i])[0]);
							var _location={
								lat:google.maps.Xml.value(gelstn("lat",_partnerNodes[i])[0]),
								lng:google.maps.Xml.value(gelstn("lng",_partnerNodes[i])[0])
							};
							var _date=google.maps.Xml.value(gelstn("date",_partnerNodes[i])[0]);
							var _description=google.maps.Xml.value(gelstn("description",_partnerNodes[i])[0]);
							var _link=google.maps.Xml.value(gelstn("link",_partnerNodes[i])[0]);
							// If layer group does not exist then create it
							
							if(typeof(ESS.Map.Data.Json[_layer])!="object")
								ESS.Map.Data.Json[_layer]=new Array();
							// Save partner data grouped by layer
							ESS.Map.Data.Json[_layer].push({
								Id:_id,
								Name:_name,
								Link:_link,
								Layer:_layer,
								Location:{
									Lat:_location.lat,
									Lng:_location.lng
								},
								Date:_date,
								Description:_description
							});
						}
						// Data ready
						ESS.Map.Data.onReady();
					}
					else
						ESS.Map.showError("Error retrieving XML data");
				}
			};
			
			// Load data
			ESS.Map.Data.XmlHttpObj.open("GET", /*'http://running.zanete.dev'+*/ESS.Map.Data.Url,true);
			ESS.Map.Data.XmlHttpObj.send(null);
		},
		onReady:function(){
			// Hide loader
			ESS.Map.hideLoading();
			// Write name
			if(ESS.Map.Layers.AlwaysOn.length > 0){
				var _markerData = ESS.Map.Data.getJsonDataById(ESS.Map.Layers.AlwaysOn[0]);
                if(_markerData) {
                    var _elem = gel("permanentLayerItemName");
                    _elem.className=_markerData.Layer.toLowerCase();
                    _elem.innerHTML=truncate(_markerData.Name,10,"...");
                    _elem.title=_markerData.Name;
                }
			}
			// Place markers
			ESS.Map.placeMarkers(true);
			// Save position
			ESS.Map.MapObj.savePosition();
			// Subscribe to interaction events
			google.maps.Event.addListener(ESS.Map.MapObj,"moveend",function(){
                ESS.Map.placeMarkers();
			});
			google.maps.Event.addListener(ESS.Map.MapObj,"zoomend",function(){
				// Hide any Info Windows and show marker
				// TO DO: keep info window open!!
				var _infoWins=gelscn("info");
				for(var i=0; i<_infoWins.length; i++){
					if(_infoWins[i].style.display!="none"){
						_infoWins[i].style.display="none";
						if(navigator.userAgent.match(/MSIE 6\./))
							var _markerElems=gelstn("div",_infoWins[i].parentNode);
						else
							var _markerElems=gelstn("img",_infoWins[i].parentNode);
						for(var j=0; j<_markerElems.length; j++){
							_markerElems[j].style.visibility="visible";
						}
					}
				}
			});
		},
		getJsonDataById:function(_id){
			for(var _layer in ESS.Map.Data.Json){
				for(var i=0; i<ESS.Map.Data.Json[_layer].length; i++){
					if(ESS.Map.Data.Json[_layer][i].Id == _id)
						return ESS.Map.Data.Json[_layer][i];
				}
			}
			return null;
		}
	},
	
	/* Default map view
	 * NOTE: 53.5,-2.7 and zoom=6 shows most of GB
	 */
	DefaultView:{
		Center:{Lat:53.5,Lng:-2.7},
		ZoomLevel:2,
		Type:"global",
		Layers:"all",
		View:"map"
	},
	
	/* Layers specification
	 * Available : used to build filter interface
	 * Selected : used by placeMarkers() to decide which marker types to place
	 */
	Layers:{
		Available:{
			global:["Homes","Events"]
		},
		Selected:{
			global:["Homes","Events"]
		},
		AlwaysOn:[]
	},
	
	/* Icon specifications
	 */
	Icons:{
		// Normal Icons
		Homes:{
			IconWidth:37,
			IconHeight:43,
			IconAnchor:[19,40],
			IconFileName:"gmap_icon-home.png",
			InfoWindowAnchor:[19,40],
			GIcon:null
		},
		Events:{
			IconWidth:37,
			IconHeight:43,
			IconAnchor:[19,40],
			IconFileName:"gmap_icon.png",
			InfoWindowAnchor:[19,40],
			GIcon:null
		},
		
		// Cluster Icons
		HomesCluster:{
			IconWidth:37,
			IconHeight:43,
			IconAnchor:[19,40],
			IconFileName:"gmap_icon-home.png",
			InfoWindowAnchor:[19,40],
			GIcon:null
		},
		EventsCluster:{
			IconWidth:37,
			IconHeight:43,
			IconAnchor:[19,40],
			IconFileName:"gmap_icon-multi.png",
			InfoWindowAnchor:[19,40],
			GIcon:null
		}
	},
	
	/*
	 */
	Clusterers:{},
	
	/*
	 */
	addNavigationControls:function(){
		function CustomNavigationControls(){}
		CustomNavigationControls.prototype = new google.maps.Control();
		
		CustomNavigationControls.prototype.initialize = function(map) {
			
            var bottom = createElem("div");
            bottom.className = "gmap-bottom";
            bottom.innerHTML = '&nbsp;'
            ESS.Map.MapObj.getContainer().appendChild(bottom);

            var container = createElem("div");
			container.id="customNavigationControls";
			container.className = "gmap-controls";

            var bg = createElem('div');
            bg.className = "gmap-controls-bg";
            bg.innerHTML = '&nbsp;';
            container.appendChild(bg);

            var controls = createElem("div");
            controls.className = "controls";
            container.appendChild(controls);

            var panUp = createElem('a');
            panUp.className = "gcon-top";
            panUp.appendChild(createTxtNode("Pan Left"));
            controls.appendChild(panUp);
            GEvent.addDomListener(panUp, "click", function() {
				ESS.Map.MapObj.panDirection(0,1);
			});

            var panDown = createElem('a');
            panDown.className = "gcon-bottom";
            panDown.appendChild(createTxtNode("Pan Right"));
            controls.appendChild(panDown);
            GEvent.addDomListener(panDown, "click", function() {
				ESS.Map.MapObj.panDirection(0,-1);
			});

            var panRight = createElem('a');
            panRight.className = "gcon-right";
            panRight.appendChild(createTxtNode("Pan Right"));
            controls.appendChild(panRight);
            GEvent.addDomListener(panRight, "click", function() {
				ESS.Map.MapObj.panDirection(-1,0);
			});

            var panLeft = createElem('a');
            panLeft.className = "gcon-left";
            panLeft.appendChild(createTxtNode("Pan Left"));
            controls.appendChild(panLeft);
            GEvent.addDomListener(panLeft, "click", function() {
				ESS.Map.MapObj.panDirection(1,0);
			});

            var zoomIn = createElem('a');
            zoomIn.className = "gcon-plus";
            zoomIn.appendChild(createTxtNode("Zoom in"));
            controls.appendChild(zoomIn);
            GEvent.addDomListener(zoomIn, "click", function() {
				ESS.Map.MapObj.zoomIn();
			});

            var zoomOut = createElem('a');
            zoomOut.className = "gcon-minus";
            zoomOut.appendChild(createTxtNode("Zoom out"));
            controls.appendChild(zoomOut);
            GEvent.addDomListener(zoomOut, "click", function() {
				ESS.Map.MapObj.zoomOut();
			});

            var recenter = createElem('a');
            recenter.className = "gcon-center";
            recenter.appendChild(createTxtNode("Recenter"));
            controls.appendChild(recenter);
            GEvent.addDomListener(recenter, "click", function() {
				ESS.Map.MapObj.returnToSavedPosition();
			});
		
			ESS.Map.MapObj.getContainer().appendChild(container);
			return container;
		}
		
		CustomNavigationControls.prototype.getDefaultPosition = function() {
			return new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT, new google.maps.Size(16, 32));
		}
		
		ESS.Map.MapObj.addControl(new CustomNavigationControls());
	},
	
	/*
	 */
	addToggleActions:function(){
        GEvent.addDomListener(gel('show-terrain'), "click", function() {
                ESS.Map.MapObj.setMapType(G_PHYSICAL_MAP);
                Events.showMapType('show-terrain');
                return false;
            });
		GEvent.addDomListener(gel('show-normal'), "click", function() {ESS.Map.MapObj.setMapType(G_NORMAL_MAP); Events.showMapType('show-normal'); return false;});
        GEvent.addDomListener(gel('show-satellite'), "click", function() {ESS.Map.MapObj.setMapType(G_SATELLITE_MAP); Events.showMapType('show-satellite'); return false;});
	},

    addYouSearched: function() {
        if('' != MAP_BAR) {
            var container = createElem("div");
            container.className = "event-bar";
            container.innerHTML = MAP_BAR;
            ESS.Map.MapObj.getContainer().appendChild(container);
            //add shadow
            var shadow = createElem("div");
            shadow.className = "gmap-shadow";
            shadow.innerHTML = '&nbsp;'
            ESS.Map.MapObj.getContainer().appendChild(shadow);


        }
    },


	/* Create and show loading icon while map initially loads
	 * - map will overwrite loading elem when loaded
	 * addLoadingElem()
	 * 		@param {string} _id : ID to add to element when created
	 * 		@param {HtmlElement} _childNode : HtmlElement node to append to HTML element when created
	 * hideLoading()
	 * 		@param {string} _id : ID tof element to hide. Defaults to "loading-data-container"
	 */
	addLoadingElem:function(_id,_childNode){
		var _container=createElem("div");
		_container.id=_id;
		if(_childNode)
			_container.appendChild(_childNode);
		else
			_container.innerHTML="&nbsp;";
		gel(ESS.Map.MapElemId).appendChild(_container);
	},
	showDataLoading:function(){
		if(gel("loading-data-container"))
			gel("loading-data-container").parentNode.removeChild(gel("loading-data-container"));
		var _childNode=createElem("div");
		_childNode.id="loading-data";
		_childNode.innerHTML="Loading Events...";
		ESS.Map.addLoadingElem("loading-data-container",_childNode);
	},
	showMapLoading:function(){
		ESS.Map.addLoadingElem("loading-map");
	},
	hideLoading:function(_id){
		if(!_id) _id="loading-data-container";
		gel(_id).style.display="none";
	},
	
	/* Create, show and hide errors
	 * showError()
	 * 		@param {string} _message : Error message to show
	 */
	initErrorElem:function(){
		var _container=createElem("div");
		_container.id="error-container";
		var _messageContainer=_container.cloneNode(true);
		_messageContainer.id="error-message";
		_container.appendChild(_messageContainer);
		gel(ESS.Map.MapElemId).appendChild(_container);
	},
	showError:function(_message){
		gel("error-message").innerHTML=_message;
		gel("error-container").style.display="block";
		ESS.Map.hideLoading();
	},
	hideError:function(){
		gel("error-container").style.display="none";
	},
	
	/* Show/hide filter interface
	 * openFilter() and hideFilter() handle animation effect
	 */
	openFilter:function(){
		// Elem to slide
		var _filterElem=gel(ESS.Map.FilterElemId);
		
		// Maximise containing div
		var _filterContainer=_filterElem.parentNode;
		if(_filterContainer.className!="maximised")
			_filterContainer.className="maximised";
		
		//
		var _filterWidth = parseInt(_filterElem.offsetWidth);
		
		if(_filterElem.style.left)
			var _filterLeft = parseInt(_filterElem.style.left);
		else
			var _filterLeft = 0;
		
		
			
		if(_filterLeft < _filterWidth){
			_filterElem.style.left = _filterLeft + Math.ceil((_filterWidth - _filterLeft) / 2) + "px";
			if(_filterElem.className == "closed")
				_filterElem.className = "";
			setTimeout(ESS.Map.openFilter,100);
		}
		else {
			_filterElem.style.left=null;
			_filterElem.className="open";
		}
	},
	hideFilter:function(){
		// Elem to slide
		var _filterElem=gel(ESS.Map.FilterElemId);
		
		// Starting width and left position
		var _filterWidth = parseInt(_filterElem.offsetWidth);
		if(_filterElem.style.left)
			var _filterLeft = parseInt(_filterElem.style.left);
		else
			var _filterLeft = _filterWidth;
		
		if(_filterLeft > 1){
			_filterElem.style.left = Math.floor(_filterLeft / 2) + "px";
			setTimeout(ESS.Map.hideFilter,100);
		}
		else {
			// Remove inline style
			_filterElem.className="closed";
			_filterElem.style.left=null;
			// Minimise containing div
			_filterElem.parentNode.className="minimised";
		}
	},
	
	/* Handle layer selections and
	 * "show/hide all" buttons
	 */
	/*firstSelection:true,
	handleLayerSelection:function(){
		// Reset selected layers
		ESS.Map.Layers.Selected[ESS.Map.DefaultView.Type]=[];
		// Clear any markers and info windows
		ESS.Map.MapObj.clearOverlays();
		for(var _clustererId in ESS.Map.Clusterers){
			ESS.Map.Clusterers[_clustererId].removeMarkers();
			delete ESS.Map.Clusterers[_clustererId];
		}
		ESS.Map.Displayed.Markers={};
		// Add selected layers
		for(var i=0; i<ESS.Map.Layers.Available[ESS.Map.DefaultView.Type].length; i++){
			var _lyrLabel=ESS.Map.Layers.Available[ESS.Map.DefaultView.Type][i].replace(/ /g,"_");
			var _lyrId=_lyrLabel.replace(/_/g," ").split(" ")[0].toLowerCase();
			var _elem=gel("lbl-"+_lyrId);
			if(_elem.checked){
				ESS.Map.Layers.Selected[ESS.Map.DefaultView.Type].push(_lyrLabel.replace(/_/g," "));
				_elem.parentNode.className="";
			}
			else {
				_elem.parentNode.className="unselected";
			}
		}
		// Zoom out
		if(ESS.Map.firstSelection && ESS.Map.Layers.AlwaysOn.length > 0) {
			switch(ESS.Map.DefaultView.Type){
				case "item":
					ESS.Map.MapObj.zoomOut();
				break;
				case "global":
					ESS.Map.MapObj.setZoom(6);
				break;
			}
			ESS.Map.firstSelection = false;
		}
		ESS.Map.placeMarkers();
	},
	selectAllLayers:function(){
		ESS.Map.toggleAllLayers(true);
	},
	hideAllLayers:function(){
		ESS.Map.toggleAllLayers(false);
	},
	toggleAllLayers:function(_checked){
		for(var i=0; i<ESS.Map.Layers.Available[ESS.Map.DefaultView.Type].length; i++){
			var _lyrLabel=ESS.Map.Layers.Available[ESS.Map.DefaultView.Type][i].replace(/ /g,"_");
			var _lyrId=_lyrLabel.replace(/_/g," ").split(" ")[0].toLowerCase();
			gel("lbl-"+_lyrId).checked=_checked;
		}
		ESS.Map.handleLayerSelection();
	},*/
	
	/* Place markers on map
	 * NOTE: GIcons should be created first by ESS.Map.createIcons
	 */
	placeMarkers:function(firstLoad){
		// For permanent markers
		for(var i=0; i<ESS.Map.Layers.AlwaysOn.length; i++){

			var _id = ESS.Map.Layers.AlwaysOn[i];
			// Get Json object from ID
			var _markerData = ESS.Map.Data.getJsonDataById(_id);
			if(_markerData && !ESS.Map.Displayed.Markers[_markerData.Id]){
				// Use Clusterer to render
				ESS.Map.Clusterers["permLayer_"+_id] = new ClusterMarker(ESS.Map.MapObj,{
					markers:[ESS.Map.createMarker(_markerData)],
					clusteringEnabled:false,
					fitMapMaxZoom:13
				});
				ESS.Map.Clusterers["permLayer_"+_id].refresh(true);
			}
		}
		// For every marker in the loaded data
		for(var _layer in ESS.Map.Data.Json){
			// If layer is not selected then do not place
			if(!inArray(_layer.replace(/_/g," "),ESS.Map.Layers.Selected[ESS.Map.DefaultView.Type]))
				continue;
			// Temporarily collect markers to pass to clustering object
			var _markerCollection=[];
			// Get all markers in current viewport for this layer
			for(var i=0; i<ESS.Map.Data.Json[_layer].length; i++){
				var _markerData = ESS.Map.Data.Json[_layer][i];
				// If not already displayed
				if(_markerData && !ESS.Map.Displayed.Markers[_markerData.Id]){
					// Add marker to collection - will be added to map by clusterer
					_markerCollection.push(ESS.Map.createMarker(_markerData));
				}
			}
			if(!ESS.Map.Clusterers[_layer]){
				ESS.Map.Clusterers[_layer] = new ClusterMarker(ESS.Map.MapObj,{
					markers:_markerCollection,
					clusterMarkerTitle:"Click to zoom in and show %count "+_layer+"...",
					clusterMarkerIcon:ESS.Map.Icons[_layer+"Cluster"].GIcon,
					minClusterSize:4,
                    fitMapMaxZoom:13
				});
			}
			else if(_markerCollection.length > 0){
				ESS.Map.Clusterers[_layer].addMarkers(_markerCollection);
			}
			ESS.Map.Clusterers[_layer].refresh(true);
		}
		// Handle initial center and zoom of map
		if(firstLoad) {
                    // If center and zoom passed via loadMap
                    if(ESS.Map.DefaultView.Center.isCustom){
                        //find out zoom level
                        var ZoomLevel = ESS.Map.DefaultView.ZoomLevel;
                        if (ESS.Map.Clusterers.Events && ESS.Map.Clusterers.Events._mapMarkers) {
                            var markerBounds = new GLatLngBounds();
                            for(i=ESS.Map.Clusterers.Events._mapMarkers.length-1; i>=0; i--){
                                markerBounds.extend(ESS.Map.Clusterers.Events._mapMarkers[i].getLatLng());
                            }

                            /* Work out the distance of the furthest north east
                             * point and subtract it from the center point. This
                             * means that when it has worked out the boundary
                             * and correct zoom level it can still fits the
                             * markers in after centers.
                             */
                            var offsetLat = markerBounds.getNorthEast().lat() - ESS.Map.DefaultView.Center.Lat;
                            var offsetLng = markerBounds.getNorthEast().lng() - ESS.Map.DefaultView.Center.Lng;
                            markerBounds.extend(new google.maps.LatLng(ESS.Map.DefaultView.Center.Lat - offsetLat, ESS.Map.DefaultView.Center.Lng - offsetLng))

                            if(ZoomLevel > 3) {
                                ZoomLevel = ESS.Map.MapObj.getBoundsZoomLevel(markerBounds);
                            } else {
                                ZoomLevel = 3;
                            }                            
                        }
                        //ESS.Map.MapObj.getBoundsZoomLevel($markersBounds);
                                        ESS.Map.MapObj.setCenter(
                                                new google.maps.LatLng(ESS.Map.DefaultView.Center.Lat, ESS.Map.DefaultView.Center.Lng),
                                                ZoomLevel
                                        );
                                }
                                // ...or center on permanent marker
                    else {
                        if(ESS.Map.Clusterers.Events) {
                            ESS.Map.Clusterers.Events.fitMapToMarkers();
                        }
                    }
		}
	},
	createMarker:function(_markerSpec){
		// Create marker
		var _point = new google.maps.LatLng(_markerSpec.Location.Lat, _markerSpec.Location.Lng);
		var _marker = new google.maps.Marker(_point,{icon:ESS.Map.Icons[_markerSpec.Layer].GIcon});
		// Record marker
		ESS.Map.Displayed.Markers[_markerSpec.Id] = true;
		// Create custom info window
		var _infoWin = new ESS.Map.CustomInfoWindow(
			_markerSpec.Location.Lat,
			_markerSpec.Location.Lng,
			{
				content:ESS.Map.getInfoWindowHtml(_markerSpec),
				className:_markerSpec.Layer.toLowerCase(),
                title: _markerSpec.Description
			}
		);
		_marker.InfoWindow = _infoWin;
		google.maps.Event.addListener(_marker,"click",function(){this.InfoWindow.show(this)});
		return _marker;
	},
	markerWillBeWithinViewportBounds:function(_markerData){
		var _bounds=ESS.Map.MapObj.getBounds();
		var _mapSwBounds=_bounds.getSouthWest();
		var _mapNeBounds=_bounds.getNorthEast();
		return (
			_markerData.Location.Lat > _mapSwBounds.lat() &&
			_markerData.Location.Lat < _mapNeBounds.lat() &&
			_markerData.Location.Lng > _mapSwBounds.lng() &&
			_markerData.Location.Lng < _mapNeBounds.lng()
		);
	},
	
	/* Generate Info Window content from data object
	 * @param {object} _markerObj : Marker specification object
	 * @returns {string}
	 */
	getInfoWindowHtml: function(_markerObj) {
        var _html="";
        if(_markerObj.Date) {
            _html+=_markerObj.Date + '<br />';
        }
        _html+= "<strong><a href=\""+_markerObj.Link+"\" title=\""+_markerObj.Name+"\">"+truncate(_markerObj.Name,25,"...")+"</a></strong>";

		if(_markerObj.Description) {
			_html+='<span class="distance" title="'+_markerObj.Description+'">'+_markerObj.Description+'</span>';
		}
		return _html;
	},
	
	/* Create GIcons from icon specs
	 * createIcon()
	 * @param {object} _mkrConfigObj : Icon specification object (ESS.Map.Icons)
	 * @returns {GIcon}
	 */
	createIcons:function(){
		for(var _iconType in ESS.Map.Icons){
			ESS.Map.Icons[_iconType].GIcon=ESS.Map.createIcon(ESS.Map.Icons[_iconType]);
		}
	},
	createIcon:function(_mkrConfigObj){
		var GIcon=new google.maps.Icon();
		GIcon.image=PATH_TO_ICON_FOLDER+_mkrConfigObj.IconFileName;
		GIcon.iconSize=new google.maps.Size(_mkrConfigObj.IconWidth,_mkrConfigObj.IconHeight);
		GIcon.iconAnchor=new google.maps.Point(_mkrConfigObj.IconAnchor[0],_mkrConfigObj.IconAnchor[1]);
		GIcon.infoWindowAnchor=new google.maps.Point(_mkrConfigObj.InfoWindowAnchor[0],_mkrConfigObj.InfoWindowAnchor[1]);
		GIcon.shadow=PATH_TO_ICON_FOLDER+"gmap_icon-shadow.png";
		GIcon.shadowSize=new google.maps.Size(50,43);
        GIcon.shadowAnchor = new google.maps.Point(_mkrConfigObj.IconAnchor[0],_mkrConfigObj.IconAnchor[1]);
		return GIcon;
	},
	
	/* Get bounds of all markers on specified layer
	 * @param {string} _layer : Layer name as defined by "filter_category" for each ESS.Map.Data.Json item
	 * @returns {GLatLngBounds}
	 */
	getLayerBounds:function(_layer){
		var _markers=ESS.Map.Data.Json[_layer];
		var _w,_e,_n,_s;
		// Sort array of markers by Lat
		_markers.sort(function(_a,_b){
			var _x=parseFloat(_a.Location.Lat);
	  		var _y=parseFloat(_b.Location.Lat);
	    	return ((_x<_y)? -1:((_x>_y)? 1:0));
		});
		_w=_markers[0].Location.Lat;
		_e=_markers[_markers.length-1].Location.Lat;
		// Sort array of markers by Lng
		_markers.sort(function(_a,_b){
			var _x=parseFloat(_a.Location.Lng);
	  		var _y=parseFloat(_b.Location.Lng);
	    	return ((_x<_y)? -1:((_x>_y)? 1:0));
		});
		_s=_markers[0].Location.Lng;
		_n=_markers[_markers.length-1].Location.Lng;
		var _bounds=new google.maps.LatLngBounds(new google.maps.LatLng(_w,_s),new google.maps.LatLng(_e,_n));
		return _bounds;
	},
	
	/* Callback function to wrap methods extending GOverlay
	 * class with ESS.Map.CustomInfoWindow class.
	 * initialize() and redraw() methods replace GOverlay methods
	 * show()
	 * 		@param {GMarker} _marker : Marker whose Info Window we want to show
	 */
	extendGOverlay:function(){
		ESS.Map.CustomInfoWindow=function(_lat,_lng,_opts){
			// Variable options
			this.LatLng=new google.maps.LatLng(_lat,_lng);
			this.Content=_opts.content;
			if(_opts.className)
				this.ExtraClasses=_opts.className;
			// Hardcoded dims and coords :)
            if(_opts.title) {
                this.Title = _opts.title;
            }
			this.Width=235;
			this.Height=100;
			this.OffsetHorizontal=-86;
			this.OffsetVertical=(this.Height*-1)+9;
		}
		ESS.Map.CustomInfoWindow.prototype=new google.maps.Overlay();
		ESS.Map.CustomInfoWindow.prototype.initialize=function(){
			// Outer containing div
			var _divOuter=createElem("div");

			if(this.ExtraClasses)
				_divOuter.className="info "+this.ExtraClasses;
			else
				_divOuter.className="info";
			// Inner containing divs
			var _divInner1=_divOuter.cloneNode(false);
			_divInner1.className="info-shadow";
			_divOuter.appendChild(_divInner1);
            var _divInner2=_divOuter.cloneNode(false);
			_divInner2.className="info-bg";
			_divOuter.appendChild(_divInner2);
			var _divInner3=_divOuter.cloneNode(false);
			_divInner3.className="info-detail";
            if(this.Title) {
                _divInner3.title = this.Title;
            }
            _divInner3.innerHTML=this.Content;
			_divOuter.appendChild(_divInner3);
			// Close button
			var _closeBtn=createElem("a");
			_closeBtn.className="info-close";
			function createCloseInfoWindowFunc(_overlay){
				return function(){ 
					google.maps.Event.trigger(_overlay,"closeclick");
					ESS.Map.MapObj.removeOverlay(_overlay);
					_overlay.Marker.show();
				};
			}
			google.maps.Event.addDomListener(_closeBtn,'click',createCloseInfoWindowFunc(this));
			_divInner3.appendChild(_closeBtn);
			// Hide for now
			_divOuter.style.display="none";
			// Add to marker pane of map
			ESS.Map.MapObj.getPane(G_MAP_MARKER_PANE).appendChild(_divOuter);
			// Save properties
			this.Div=_divOuter;
			this.Map=ESS.Map.MapObj;
		};
		ESS.Map.CustomInfoWindow.prototype.remove=function(){
		  this.Div.parentNode.removeChild(this.Div);
		};
		ESS.Map.CustomInfoWindow.prototype.redraw=function(_force){
			// Redraw if coords have changed
			if(!_force) return;
			// Calculate the DIV coordinates of two opposite corners of our bounds to
			// get the size and position of our map
			var _pixPosition=this.Map.fromLatLngToDivPixel(this.LatLng);
			// 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";
			if(this.ZIndex)
				this.Div.style.zIndex=this.ZIndex;
		};
		ESS.Map.CustomInfoWindow.prototype.show=function(_marker){
			// If InfoWindow is displayed then remove it
			if(ESS.Map.Displayed.InfoWindow){
				ESS.Map.MapObj.removeOverlay(ESS.Map.Displayed.InfoWindow);
				ESS.Map.Displayed.InfoWindow.Marker.show();
				ESS.Map.Displayed.InfoWindow=null;
			}
			// Show it
			ESS.Map.Displayed.InfoWindow=_marker.InfoWindow;
			// Save z-index to apply later
			if(_marker.R && _marker.R[0])
				this.ZIndex=_marker.R[0].style.zIndex;
			ESS.Map.MapObj.addOverlay(_marker.InfoWindow);
			// Hide marker
			this.Marker=_marker;
			_marker.hide();
			// Center the map to accomodate the calling cards dimensions
			var _point=new google.maps.LatLng(this.LatLng.lat(),this.LatLng.lng());
			var _pixelPos=this.Map.fromLatLngToDivPixel(_point);
			//_pixelPos.y=_pixelPos.y;
			this.Map.panTo(this.Map.fromDivPixelToLatLng(_pixelPos));
		};
	}
};

var loadMap=ESS.Map.loadMap;
