// ****************************************************************
// ****************************************************************
// COPYRIGHT 2006, Vertex Software
// CREATED:  1/23/2006
// AUTHOR:   Ahmed
// ****************************************************************
// ****************************************************************\
var gLocationPlotInfo = new Array();
var gRemovedLocationPlotInfo = new Array();
var gCenterLatitude = "";
var gCenterLogitude = "";
var gCurrentZoomLevel = "";
var eZoomLevelToIconColorMapping = {
	2 : "black",
	3 : "orange",
	4 : "yellow",
	5 : "green",
	6 : "blue",
	7 : "brown",
	8 : "purple",
	9 : "red",
	10 : "orange"
	};
var gIconColorInMapView = "green";
var gIconColorOutOfMapView = "yellow";
var gColoredIconPrefix = "http://labs.google.com/ridefinder/images/mm_20_";
var gColoredIconExt = ".png";
var gZoomLevelToScaleMapping = {
	4 : { scale:100, distance:1 },
	5 : { scale:100, distance:2 },
	6 : { scale:123, distance:5 },
	7 : { scale:123, distance:10 },
	8 : { scale:123, distance:20 },
	9 : { scale:62, distance:20 }
	};
// zoom level 5 - 100px = 2mi; 
// zoom level 6 - 123px = 5mi;  - requires 246px for 5 mi radius, 492 px for 10 mi radius
// zoom level 7 - 123px = 10mi; - requires 246px for 10 mi radius, 492 px for 20 mi radius
// zoom level 8 - 123px = 20mi; - requires 246px for 20 mi radius, 492 px for 40 mi radius
var gLocationIDList = new Array();
var gLatLongExistingPairs = new Array();
var gCurrentMap = {};
var gCurrentMapDivID = "";		// 30OCT2008 RFM - Added for chnage to Focus
var gMapSizeInPixels = 425;
var gUsingUniqueIconColors = false;
var gUsingNumberedIcons = false;
var gCurrentMarkerNumber = 0;
var gDefaultMapMarker = "http://labs.google.com/ridefinder/images/mm_20_green.png";
var gDefaultMapMarkersFolder = "/img/mapicons/";
// 02OCT2008 RFM - Changed default template to dynamically add link to LocationName only if PageURL is set
//var gLocationMarkerInfoWindowContent = "<div class=\"gPopupInfo\"><div style=\"vertical-align:middle\"><b><a href=\"{{PageURL}}\">{{LocationName}}</a></b><br />{{Address}}<br />{{City}}, {{State}} {{Zip}}</div><br clear=\"all\"/><span style=\"MARGIN-TOP: 8px; DISPLAY: block; MARGIN-BOTTOM: 8px\"><b>Start Address:</b><br /></span>{{StartAddressField}}<br /><a href=\"javascript:GetDirections()\">get directions</a><br />{{DestAddressField}}</div>";
var gLocationMarkerInfoWindowContent = "<div class=\"gPopupInfo\"><div style=\"vertical-align:middle\"><b>{{LocationName}}</b><br />{{Address}}<br />{{City}}, {{State}} {{Zip}}</div><br clear=\"all\"/><span style=\"MARGIN-TOP: 8px; DISPLAY: block; MARGIN-BOTTOM: 8px\"><b>Start Address:</b><br /></span>{{StartAddressField}}<br /><a href=\"javascript:GetDirections()\">get directions</a><br />{{DestAddressField}}</div>";
// 14NOV2008 RFM - Changed to function call - getAsutinCenterPoint - to avoid error when google has not loaded
//var gAustinCenterPoint = new GPoint(-97.7427777777778,30.2669444444444);
var gLatLongBoundsInfo = "";
var gCurrentMapBounds = null;
var gDefaultIconObj = null;
var gDefaultIconSize = null;
var gDefaultShadowSize = null;
var gDefaultIconAnchor = null;
var gDefaultInfoWindowAnchor = null;
var gDefaultIconPropsLoaded = false;
var gNumberedIconSize = null;
var gNumberedShadowSize = null;
var gNumberedIconAnchor = null;
var gNumberedInfoWindowAnchor = null;
var gNumberedIconPropsLoaded = false;
var gNoScrollIntoView = false;
var gNoAlerts = false;
var gLocationPlottedHandler;
var agt=navigator.userAgent.toLowerCase();
var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));


//****************************************************************
// GetAustinCenterPoint
//****************************************************************
function GetAustinCenterPoint() { 
	try {
		// 12JUN2009 KBM - version 2 now uses GLatLng for plotting center points instead of GPoint - long and lat params are also flipped
		//if (!GetAustinCenterPoint.point) GetAustinCenterPoint.point = new GPoint(-97.7427777777778,30.2669444444444);
		if (!GetAustinCenterPoint.point) GetAustinCenterPoint.point = new GLatLng(30.2669444444444, -97.7427777777778);
		return GetAustinCenterPoint.point;
		}
	catch (error) {
		window.status = ( "GetAustinCenterPoint: " + error.description  );
		}
	return {};
	}


//****************************************************************
// SetMapSize
//****************************************************************
function SetMapSize( size ) {
	try {
		gMapSizeInPixels = size;
		}
	catch (error) {
		window.status = "SetMapSize: " + error.description;
		}
	}


//****************************************************************
// ClearLocationPlotInfo
//****************************************************************
function ClearLocationPlotInfo(  ) {
	try {
		gLocationPlotInfo = new Array();
		}
	catch (error) {
		window.status = "ClearLocationPlotInfo: " + error.description;
		}
	}


//****************************************************************
// InitializeMap
//****************************************************************
function InitializeMap( centerPoint, zoomLevel, mapDivID ) {
	var map = {};
	try {
		if (!mapDivID || typeof mapDivID!="string") mapDivID = "map";
		// 12JUN2009 KBM - Switched to version 2
		//map = new GMap(document.getElementById(mapDivID));
		map = new GMap2(document.getElementById(mapDivID));
		if (!map) return;
		gCurrentMap = map;
		gCurrentMapDivID = mapDivID;
		// 12JUN2009 KBM - centerAndZoom is now setCenter in version 2 and uses GLatLng object instead of GPoint - also can't call anything on GMap2 object until setCenter has been called
		// 15JUN2009 KBM - Version 2 contains "getBoundsZoomLevel" API call so if zoom == -1 right here, call GetZoomLevelFromLocations
		//map.centerAndZoom(centerPoint, zoomLevel );
		if( zoomLevel == -1 ) {
			zoomLevel = GetZoomLevelFromLocations();
			}
		map.setCenter( centerPoint, zoomLevel );
		map.addControl(new GSmallZoomControl());
		// 2OCT2008 KBM - Added functionality to click on map and copy coordinates to clipboard
		// 03OCT2008 RFM - Removed since it causes a security warning on client pages
		//GEvent.addListener(map, 'click', new Function( "overlay", "point", "clipboardData.setData( \"Text\", String(point))") );
		}
	catch (error) {
		window.status = "InitializeMap: " + error.description;
		}
	return map;
	}


//****************************************************************
// InitializeMapAndPlotAllLocations
//****************************************************************
function InitializeMapAndPlotAllLocations( mapDivID, attempt ) {
	var map = {};
	try {
		if (!mapDivID) mapDivID = "map"; 
		if (typeof GGeoXml == "undefined" || typeof GPoint == "undefined") {
			// Wait for google to load
			if (attempt == 2) {
				document.getElementById(mapDivID).innerHTML =  "<div style='font-size:20px;font-weight:bold;padding:40px'>Sorry, unable to connect to <a href='http://maps.google.com/'>http://maps.google.com/</a>.</div>";
				return;
				} 
			window.setTimeout( function() { InitializeMapAndPlotAllLocations(mapDivID, 2) }, 2000 );
			return;
			} 
		var map = {};
		var centerPoint = "";
		var zoomLevel = "";
		var kmlOverlay;
		if (gLocationIDList.length==0) BuildLocationIDList();
		if (gLocationIDList.length==0) {
			centerPoint = GetAustinCenterPoint();
			zoomLevel = 9;
			}
		else if (gCenterLatitude != "" && gCenterLongitude != "") {
			// 12JUN2009 KBM - Version 2 uses GLatLng to plot center point insted of GPoint - lat and lng params are also flipped
			//centerPoint = new GPoint(gCenterLongitude,gCenterLatitude);
			centerPoint = new GLatLng( gCenterLatitude, gCenterLongitude );
			zoomLevel = gCurrentZoomLevel || 9;
			}
		else {
			centerPoint = GetCenterPointFromLocations();
			// 15JUN2009 KBM - Version 2 has a native API call "getBoundsZoomLevel" but can't be called until GMap2 object is initialized. So for now, set to -1 so can check after map is initialized
			//zoomLevel = GetZoomLevelFromLocations();
			zoomLevel = -1;
			}
		// 02OCT2008 KBM - Changed to actually use zoomLevel if one is passed (which is stored in gCurrentZoomLevel)
		if ( gCurrentZoomLevel ) zoomLevel = gCurrentZoomLevel;
		map = InitializeMap( centerPoint, zoomLevel, mapDivID);
		// 01082008 KM - Added ability to use a KML file
		// **** NOTE **** KML will NOT work on dev because google map services needs to be able to access the XML file
		if (gKMLOverlay) {
			kmlOverlay = new GGeoXml(gKMLOverlay);
			map.addOverlay(kmlOverlay);
			}
		// 01OCT2008 RFM - Factore PlotLocations, moved to end, and allowed for Long/Lat determination
		if (LocationsRequireCoordinates()) {
			LookupLocationCoordinates( function() { PlotLocations( map ) } );
			}
		else { 
			PlotLocations( map );
			}
		}
	catch (error) {
		window.status = "InitializeMapAndPlotAllLocations: " + error.description;
		}
	return map;
	}



//-----------------------------------------------------------
// LookupLocationCoordinates
//-----------------------------------------------------------
function LookupLocationCoordinates( completionFunction, nextItem ) {
	try {
		if (!nextItem) nextItem = 0; 
		var locationID = gLocationIDList[nextItem];
		var locationInfo = gLocationPlotInfo[locationID];
		if (!locationInfo) return; 
		var destination = locationInfo.Address + " " + locationInfo.City + "," + locationInfo.State + " " + locationInfo.Zip;
		//window.status = "Searching for " + destination;
		// 19DEC2008 RFM - Was not passing City, State, Zip
		//new GClientGeocoder().getLatLng( locationInfo.Address, function(point) {
		new GClientGeocoder().getLatLng( destination, function(point) {
			if (point != null) {
				locationInfo.Longitude = point.lng();
				locationInfo.Latitude = point.lat();
				// 24MAR2009 RFM - Added for LookupLocationName
				locationInfo.Point = point;
				if (locationInfo.LookupName) LookupLocationCoordinates.requiresLocationNames = true; 
				// 24MAR2009 RFM - added checl for udefined 
				if (typeof gLocationUpdateURL != null && typeof gLocationUpdateURL != "undefined") UpdateLocation( locationID, locationInfo.Latitude, locationInfo.Longitude );
				}
			nextItem += 1;
			if (nextItem < gLocationIDList.length) {
				LookupLocationCoordinates( completionFunction, nextItem );
				}
			else if (completionFunction) {
				if (LookupLocationCoordinates.requiresLocationNames) {
					LookupLocationNames( completionFunction );
					}
				else {
					completionFunction(); 
					}
				}
			} );	
		}
	catch (error) {
		window.status = ( "LookupLocationCoordinates: " + error.description  );
		}
	}



//-----------------------------------------------------------
// LookupLocationNames
//-----------------------------------------------------------
function LookupLocationNames( completionFunction, nextItem ) {
	try {
		if (!nextItem) nextItem = 0; 
		var locationID = gLocationIDList[nextItem];
		var locationInfo = gLocationPlotInfo[locationID];
		if (!locationInfo) return; 
		new GClientGeocoder().getLocations(locationInfo.Point, function(response) {
			if (response && response.Status.code == 200) {
				var place = response.Placemark[0];
				locationInfo.City = place.AddressDetails.Country.AdministrativeArea.Locality ? place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName : "";
				// 08MAY2009 RFM - Added for locations that dont' return Locality
				if (!locationInfo.City && place.AddressDetails.Country.AdministrativeArea.AddressLine) locationInfo.City = place.AddressDetails.Country.AdministrativeArea.AddressLine[0]; 
				locationInfo.State = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
				}
			else {
				window.status = "LookupLocationNames - error: " +  response.Status.code;
				location.City = [ response.Status.code,locationInfo.Latitude,locationInfo.Longitude]
				}
			nextItem += 1;
			if (nextItem < gLocationIDList.length) {
				LookupLocationNames( completionFunction, nextItem );
				}
			else if (completionFunction) {
				completionFunction(); 
				}
			} );	
		}
	catch (error) {
		window.status = ( "LookupLocationNames: " + error.description  );
		}
	}


//-----------------------------------------------------------
// UpdateLocation
//-----------------------------------------------------------
function UpdateLocation( locationID, latitude, longitude  ) {
	try {
		if (typeof $ == "undefined") return; 
		var url = gLocationUpdateURL;
		if (!url.match(/^http/gi)) url = "http://" + document.location.hostname + url;
		// Fire and forget
		$.get(url, { UpdateLocation:1, Latitude:latitude, Longitude:longitude, ID:locationID } );
		window.status = "Update location " + locationID;
		}
	catch (error) {
		alert( "UpdateLocation: " + error.description  );
		}
	}


//-----------------------------------------------------------
// LocationsRequireCoordinates
//-----------------------------------------------------------
function LocationsRequireCoordinates( ) {
	try {
		if (gLocationIDList.length == 0) return false; 
		var locationInfo = gLocationPlotInfo[gLocationIDList[0]];
		if (!locationInfo) return false;
		if (!locationInfo.Longitude || locationInfo.Longitude == 0) return true; 
		}
	catch (error) {
		window.status = ( "LocationsRequireCoordinates: " + error.description );
		}
	return false;
	}



//-----------------------------------------------------------
// PlotLocations
//-----------------------------------------------------------
function PlotLocations( map ) {
	try {
		// 25MAR2009 RFM - Added default for map
		if (!map) map = gCurrentMap; 
		for (var locationID in gLocationPlotInfo) {
			PlotLocation( locationID, map );
			}
		// 19JUN2009 RFM - Added for GEoCodeEditor
		//if (gLocationIDList.length==1) {
		if (gLocationIDList.length==1 && !SetNoChangeFocus.noChange) {
			FocusOnLocation(gLocationIDList[0]);
			}
		}
	catch (error) {
		alert( "PlotLocations: " + error.description  );
		}
	}


//-----------------------------------------------------------
// PlotLocation
//-----------------------------------------------------------
function PlotLocation( locationID, map ) {
	var plotted = true;
	try {
		if (gLocationPlotInfo[locationID] == null) return;
		// 26MAY2009 RFM - Added ability to determine set NoInfoBalloon from location
		//var locationMarker = CreateMarker(locationID,true);
		var locationMarker = CreateMarker(locationID, typeof gLocationPlotInfo[locationID].NoInfoBalloon != "undefined" ? false : true);
		// 15OCT2008 KBM - Added code to catch unplottable location - Coordinates with CreateMarker to return empty string if no Longitude or Latitude
		//map.addOverlay(locationMarker);
		//gLocationPlotInfo[locationID].Marker = locationMarker;
		if ( locationMarker ) {
			map.addOverlay(locationMarker);
			gLocationPlotInfo[locationID].Marker = locationMarker;
			}
		else {
			var locationInfo = gLocationPlotInfo[locationID];
			// 27MAY2009 RFM - SHP - Added to suppress error
			if (!gNoAlerts) alert("Sorry, could not plot the specified location.\n" + locationInfo.Address + " " + locationInfo.City + "," + locationInfo.State + " " + locationInfo.Zip);
			plotted = false;
			}
		// 27MAY2009 RFM - SHP - Added to allow error handler
		if (gLocationPlottedHandler && typeof gLocationPlottedHandler == "function") gLocationPlottedHandler(gLocationPlotInfo[locationID], plotted);
		}
	catch (error) {
		alert( "PlotLocation: " + error.description  );
		}
	return plotted;
	}


//===========================================================
// PlotAddress
// 27MAY2009 RFM - SHP - added to allow client-side code to
// plot new addresses.
//===========================================================
function PlotAddress( locationName, address, city, state, zip, noInfoBalloon, clearMap, longitude, latitude ) {
	try {
		if (clearMap) {
			ClearOverlays(); 
			ClearLocationPlotInfo();
			}
		// 19JUN2009 RFM - Added empty location check
		if (!longitude && !latitude && !address && !city && !state && !zip) return; 
		// 19JUN2009 RFM - Added longitude/latitude for GeoCodeEditor
		//var locationRecord = { ID:new Date().getTime(), LocationName:locationName || "", Address:address, City:city, State:state, Zip:zip };
		var locationRecord = { ID:new Date().getTime(), LocationName:locationName || "", Address:address, City:city, State:state, Zip:zip, Longitude:longitude||"", Latitude:latitude||"" };
		if (noInfoBalloon) locationRecord.NoInfoBalloon = true;
		// 19JUN2009 RFM - need to set gLocationIDList - hey not my API
		gLocationIDList[0] = locationRecord.ID;
		gLocationPlotInfo[locationRecord.ID] = locationRecord;
		// 19JUN2009 RFM - changed for GeoCodeEditor
		// InitializeMapAndPlotAllLocations();
		if (!longitude || !latitude) {
			LookupLocationCoordinates( function() { PlotLocations() } );
			}
		else {
			PlotLocations();
			}
		}
	catch (error) {
		alert( "PlotAddress: " + error.description  );
		}
	}


//****************************************************************
// ClearOverlays
// Added since not using GMap2 yet and GMap does not support
//****************************************************************
function ClearOverlays( ) {
	try {
		for (var item in gLocationPlotInfo) {
			var locationPlotInfo = gLocationPlotInfo[item];
			if (locationPlotInfo.Marker) gCurrentMap.removeOverlay(locationPlotInfo.Marker);
			}
		}
	catch (error) {
		alert( "ClearOverlays: " + error.description  );
		}
	}


//****************************************************************
// FocusOnLocation
// 02OCT2008 KBM - Added ability to specify zoomLevel so it does not automatically go to 3; for ARC vaccinations world map
// Also added maxLatitude to restrict the zooming
//function FocusOnLocation( locationID ) {
//****************************************************************
function FocusOnLocation( locationID, zoomLevel, maxLatitude ) {
	try {
		var locationInfo = gLocationPlotInfo[locationID];
		// 19JUN2009 RFM - Added empty location check
		if (!locationInfo.Longitude && !locationInfo.Latitude && !locationInfo.Address && !locationInfo.City && !locationInfo.State && !locationInfo.Zip) return; 
		// 03OCT2008 KBM - Changed to test if locationInfo.Latitude is greater than maxLatitude to restrict centering
		//var LocationCoordinates = new GPoint(locationInfo.Longitude, locationInfo.Latitude);
		var latitude = ( maxLatitude && locationInfo.Latitude > maxLatitude ? maxLatitude : locationInfo.Latitude );
		// 12JUN2009 KBM - GMap2 (version 2) uses setCenter instead of centerAndZoom and uses GLatLng isntead of GPoint object - lat and lng params are flipped
		//var LocationCoordinates = new GPoint(locationInfo.Longitude, latitude);
		var LocationCoordinates = new GLatLng( latitude, locationInfo.Longitude );
		// 02OCT2008 KBM - Had to change so zoomLevel does NOT go to 3 if zoomLevel is passed
		//gCurrentMap.centerAndZoom(LocationCoordinates,3);
		// 25MAR2009 RFM - and had to change so that gCurrentZoomLevel is that was set initially!
		// 12JUN2009 KBM - GMap2 (version 2) uses setCenter instead of centerAndZoom and uses GLatLng isntead of GPoint object
		// Also, zoomLevels have REVERSED in this version, so 14 in v2 is equivalent to 3 in v1
		//gCurrentMap.centerAndZoom(LocationCoordinates, zoomLevel || gCurrentZoomLevel || 3);
		gCurrentMap.setCenter(LocationCoordinates, zoomLevel || gCurrentZoomLevel || 14);
		var thisMarker = gLocationPlotInfo[locationID].Marker;
		// 02Mar06 - The following two lines are needed to address layering issue when multiple markers exist at exact same geocode.
		gCurrentMap.removeOverlay(thisMarker);
		gCurrentMap.addOverlay(thisMarker);
		GEvent.trigger(thisMarker,"click");	//grab marker object from global Info object and trigger its click event
		// 30OCT2008 RFM - Focus on map in case clicked link is below map
		// 10APR2009 RFM - Added ability to disablr focus via SetNoScrollIntoView
		// 17JUN2009 RFM - Added ability to disablr focus via NoScrollIntoView record attribute - SHP
		if (gCurrentMapDivID && !gNoScrollIntoView && !locationInfo.NoScrollIntoView) document.getElementById( gCurrentMapDivID ).scrollIntoView(true);
		}
	catch (error) {
		window.status = "FocusOnLocation: " + error.description;
		}
	}


//****************************************************************
// RemoveLocation
//****************************************************************
function RemoveLocation( locationID ) {
	try {
		gRemovedLocationPlotInfo[locationID] = gLocationPlotInfo[locationID];
		gLocationPlotInfo[locationID] = null;
		gLatLongBoundsInfo = "";
		BuildLocationIDList( );
		centerPoint = GetCenterPointFromLocations();
		zoomLevel = GetZoomLevelFromLocations();
		window.setTimeout(InitializeMapAndPlotAllLocations,500)
		}
	catch (error) {
		window.status = "RemoveLocation: " + error.description;
		}
	}


//****************************************************************
// RestoreLocation
//****************************************************************
function RestoreLocation( locationID ) {
	try {
		gLocationPlotInfo[locationID] = gRemovedLocationPlotInfo[locationID]
		gRemovedLocationPlotInfo[locationID] = null;
		gLatLongBoundsInfo = "";
		BuildLocationIDList( );
		centerPoint = GetCenterPointFromLocations();
		zoomLevel = GetZoomLevelFromLocations();
		window.setTimeout(InitializeMapAndPlotAllLocations,500)
		}
	catch (error) {
		window.status = "RemoveLocation: " + error.description;
		}
	}


//****************************************************************
// GetDirections
//****************************************************************
function GetDirections() {
	try {
		var startAddress = document.getElementsByName("startAddr")[0].value;
		var destAddress = document.getElementsByName("destAddr")[0].value;
		var directionsWindow = window.open('http://maps.google.com/maps?saddr=' + startAddress + '&daddr=' + destAddress, "Printer", "width=760,height=600,resizable=yes,scrollbars=yes,menubar=yes");
		}
	catch (error) {
		window.status = "GetDirections:" + error.description;
		}
	}


//****************************************************************
// FindFacilitiesByAddress
//****************************************************************
function FindFacilitiesByAddress( ) {
	try {
		var address = document.getElementsByName("VisitorAddress")[0].value;
		var zipcode = document.getElementsByName("VisitorZip")[0].value;
		alert("ZIP: " + zipcode);
		if (zipcode=="") {
			document.getElementById("ZipError").innerHTML = "Please enter a valid zip code.";
			return;
			}
		else if (!zipcode.match(/^[0-9]{5}(\-[0-9]{4})?$/)) {
			document.getElementById("ZipError").innerHTML = "Please enter a valid zip code.";
			return;
			}
		else {
			document.getElementById("ZipError").innerHTML = "";
			}
		document.location.href = "/LocateFacilityByAddress/" + address + "/ZipCode/" + zipcode;
		}
	catch (error) {
		window.status = "FindFacilitiesByAddress: " + error.description;
		}
	}


//****************************************************************
// FindFacilitiesByService
//****************************************************************
function FindFacilitiesByService( ) {
	try {
		var ServiceID = document.getElementsByName("ServiceID")[0].value;
		if (ServiceID=="") {
			document.getElementById("ServiceError").innerHTML = "Please select a service area.";
			return;
			}
		document.location.href = "/LocateFacilityByService/" + ServiceID;
		}
	catch (error) {
		window.status = "FindFacilitiesByService: " + error.description;
		}
	}


//****************************************************************
// ClearInputField
//****************************************************************
function ClearInputField( element, defaultValue ) {
	try {
		if (element.value==defaultValue) element.value = "";
		}
	catch (error) {
		window.status = "ClearInputField: " + error.description;
		}
	}


//****************************************************************
// ResetInputField
//****************************************************************
function ResetInputField( element, defaultValue ) {
	try {
		if (element.value=="") element.value = defaultValue;
		}
	catch (error) {
		window.status = "ResetInputField: " + error.description;
		}
	}


//-----------------------------------------------------------
// BuildLocationIDList
//-----------------------------------------------------------
function BuildLocationIDList( ) {
	try {
		gLocationIDList = new Array();
		for (var locationID in gLocationPlotInfo) {
			if (gLocationPlotInfo[locationID] == null) continue
			gLocationIDList.push(locationID);
			var locationInfo = gLocationPlotInfo[locationID];
			var latLongPairID = locationInfo.Latitude + ":" + locationInfo.Longitude;
			if (!gLatLongExistingPairs[latLongPairID]) {
				gLatLongExistingPairs[latLongPairID] = {};
				gLatLongExistingPairs[latLongPairID].incrementVal = 0.0001;
				}
			else {
				gLocationPlotInfo[locationID].Longitude = Number(gLocationPlotInfo[locationID].Longitude) + gLatLongExistingPairs[latLongPairID].incrementVal;
				gLocationPlotInfo[locationID].Latitude = Number(gLocationPlotInfo[locationID].Latitude) + gLatLongExistingPairs[latLongPairID].incrementVal;
				gLatLongExistingPairs[latLongPairID].incrementVal += 0.0001;
				}
			for (var attribute in locationInfo) {
				locationInfo[attribute] = unescape(locationInfo[attribute]);
				}
			}
		}
	catch (error) {
		window.status = "BuildLocationIDList: " + error.description;
		}
	}


//-----------------------------------------------------------
// BuildPopUpHTML
//-----------------------------------------------------------
function BuildPopUpHTML( locationID ) {
	var returnValue = "";
	try {
		var locationInfo = gLocationPlotInfo[locationID];
		var templateHTML =  gLocationMarkerInfoWindowContent;
		if (locationInfo.PopupHTML) templateHTML = locationInfo.PopupHTML;
		// 03OCT2008 RFM - SPDBTags get replaced when used in DBPage detail so have to be converted to alternate tags.
		// Since SPTage still supported in otehr cases, need to convert to alternate format
		templateHTML = templateHTML.replace(/\{\{/g,"||");
		templateHTML = templateHTML.replace(/\}\}/g,"||");
		// 02OCT2008 RFM - Dynamically add link only if PageURL is set.
		if (locationInfo.PageURL) {
			templateHTML = templateHTML.replace( "||LocationName||", "<a href=\"||PageURL||\">||LocationName||</a>" );
			}
		for (var attribute in locationInfo) {
			// 02OCT2008 RFM - use Regex to accommocate multipel uses of the saem tag and standard tag values
			//templateHTML = templateHTML.replace("||"+attribute+"||",locationInfo[attribute]);
			templateHTML = templateHTML.replace( new RegExp( "\\|\\|" + attribute + "\\|\\|", "g" ), locationInfo[attribute] || "" );
			}
		var destinationAddress = locationInfo.Address + " " + locationInfo.City + "," + locationInfo.State + " " + locationInfo.Zip;
		var startFieldHTML = "<input size=\"30\" maxlength=\"75\" name=\"startAddr\" value=\"\" />";
		var destFieldHTML = "<input type=\"hidden\" name=\"destAddr\" value=\"" + destinationAddress + "\"/>";
		templateHTML = templateHTML.replace("||StartAddressField||",startFieldHTML);
		templateHTML = templateHTML.replace("||DestAddressField||",destFieldHTML);
		// 02OCT2008 RFM - Replace all tags that not already replaced
		templateHTML = templateHTML.replace( /\|\|{[^\|]+\|\|/g, "" );
		returnValue = (templateHTML);
		}
	catch (error) {
		window.status = "BuildPopUpHTML: " + error.description;
		}
	return returnValue;
	}


//-----------------------------------------------------------
// LoadDefaultIconProps
//-----------------------------------------------------------
function LoadDefaultIconProps( ) {
	try {
		gDefaultIconSize = new GSize(12, 20);
		gDefaultShadowSize = new GSize(27, 24);
		gDefaultIconAnchor = new GPoint(9, 24);
		gDefaultInfoWindowAnchor = new GPoint(5, 10);
		gDefaultIconPropsLoaded = true;
		}
	catch (error) {
		window.status = "LoadDefaultIconProps: " + error.description;
		}
	}


//-----------------------------------------------------------
// LoadNumberedIconProps
//-----------------------------------------------------------
function LoadNumberedIconProps( ) {
	try {
		gNumberedIconSize = new GSize(20, 34);
		gNumberedShadowSize = new GSize(37, 34);
		gNumberedIconAnchor = new GPoint(9, 34);
		gNumberedInfoWindowAnchor = new GPoint(9, 20);
		gNumberedIconPropsLoaded = true;
		}
	catch (error) {
		window.status = "LoadNumberedIconProps: " + error.description;
		}
	}


//-----------------------------------------------------------
// LoadDefaultIconObj
//-----------------------------------------------------------
function LoadDefaultIconObj( ) {
	try {
		if (!gDefaultIconObj) {
			gDefaultIconObj = new GIcon();
			gDefaultIconObj.image = gDefaultMapMarker;
			gDefaultIconObj.shadow = "/img/mapicons/marker_shadow.png";
			gDefaultIconObj.iconSize = gDefaultIconSize;
			gDefaultIconObj.shadowSize = gDefaultShadowSize;
			gDefaultIconObj.iconAnchor = gDefaultIconAnchor;
			gDefaultIconObj.infoWindowAnchor = gDefaultInfoWindowAnchor;
			}
		}
	catch (error) {
		window.status = "LoadDefaultIconObj: " + error.description;
		}
	}


//-----------------------------------------------------------
// ApplyNumberedIconProps
//-----------------------------------------------------------
function ApplyNumberedIconProps( iconObj ) {
	try {
		iconObj.iconSize = gNumberedIconSize;
		iconObj.shadowSize = gNumberedShadowSize;
		iconObj.iconAnchor = gNumberedIconAnchor;
		iconObj.infoWindowAnchor = gNumberedInfoWindowAnchor;
		}
	catch (error) {
		window.status = ": " + error.description;
		}
	return iconObj;
	}


//-----------------------------------------------------------
// CreateIcon
//-----------------------------------------------------------
function CreateIcon( locationID ) {
	var iconObj = "";
	try {
		if (!gDefaultIconPropsLoaded) {
			LoadDefaultIconProps();
			}
		if (!gNumberedIconPropsLoaded) { 
			LoadNumberedIconProps(); 
			}
		if (!gDefaultIconObj) { 
			LoadDefaultIconObj(); 
			}
		iconObj = new GIcon(gDefaultIconObj);
		// 12212007 KM - Added to support a custom location marker image
		if (gLocationPlotInfo[locationID].CustomImage) {
			iconObj.image = gDefaultMapMarkersFolder + gLocationPlotInfo[locationID].CustomImage + (is_ie ? ".gif" : ".png");
			}
		else if (gUsingNumberedIcons && gLocationPlotInfo[locationID].MarkerColor) {//Used for colored/numbered markers
			var color = gLocationPlotInfo[locationID].MarkerColor;
			var markerNumber = gLocationPlotInfo[locationID].MarkerNumber;
			iconObj.image = gDefaultMapMarkersFolder + "marker_" + color + "_" + markerNumber + (is_ie ? ".gif" :".png");
			iconObj = ApplyNumberedIconProps(iconObj);
			}
		else if (gLocationPlotInfo[locationID].MarkerNumber && gLocationPlotInfo[locationID].MarkerNumber==0) {	//for Visitor's location
			var color = "red";
			iconObj.image = gColoredIconPrefix + color + gColoredIconExt;
			}
		else if (gUsingNumberedIcons) {//Used for default color numbered markers
			iconObj.image = gDefaultMapMarkersFolder + "marker_green_" + gLocationPlotInfo[locationID].MarkerNumber + ".png";
			iconObj = ApplyNumberedIconProps(iconObj);
			}
		}
	catch (error) {
		window.status = "CreateIcon: " + error.description;
		}
	return iconObj;
	}


//-----------------------------------------------------------
// CreateMarker
//-----------------------------------------------------------
function CreateMarker( locationID, withPopupHTML ) {
	var marker = "";
	try {
		var locationInfo = gLocationPlotInfo[locationID];
		// 15OCT2008 KBM - If no Longitude and Latitude, return empty object
		if ( !locationInfo.Longitude || !locationInfo.Latitude ) {
			return "";
			}
		var point = new GPoint(locationInfo.Longitude, locationInfo.Latitude);
		marker = new GMarker( point, CreateIcon(locationID) );
		if (withPopupHTML) {
			var popupHTML = BuildPopUpHTML(locationID);
			GEvent.addListener(marker,'click',function() {
				marker.openInfoWindowHtml(popupHTML  );
				}
			);
		}
		}
	catch (error) {
		window.status = "CreateMarker: " + error.description;
		}
	return marker;
	}



//-----------------------------------------------------------
// GetCenterPointFromLocations
//-----------------------------------------------------------
function GetCenterPointFromLocations( locationIDList, defaultCenter ) {
	var pointToReturn = defaultCenter || GetAustinCenterPoint();
	try {
		if (!locationIDList ) locationIDList = gLocationIDList;
		if (locationIDList.length==1) {
			var centerLocation = gLocationPlotInfo[locationIDList[0]];
			// 01OCT2008 RFM - Return default if long & lat not provided
			if (!centerLocation.Longitude || !centerLocation.Latitude) {
				return pointToReturn;
				}
			// 12JUN2009 KBM - Version 2 now uses GLatLng to plot center points instead of GPoint - lat and lng params are flipped
			//pointToReturn = new GPoint(centerLocation.Longitude,centerLocation.Latitude);
			pointToReturn = new GLatLng( centerLocation.Latitude, centerLocation.Longitude );
			}
		else {
			var latLongBoundsInfo = GetBoundsOfPlots( locationIDList );
			var middleLatitude = Number(latLongBoundsInfo.minLatBound + latLongBoundsInfo.maxLatBound)/2;
			var middleLongitude = Number(latLongBoundsInfo.minLongBound + latLongBoundsInfo.maxLongBound)/2;
			// 12JUN2009 KBM - Version 2 now uses GLatLng to plot center points instead of GPoint - lat and lng params are flipped
			//pointToReturn = new GPoint(middleLongitude,middleLatitude);
			pointToReturn = new GLatLng( middleLatitude, middleLongitude );
			}
		}
	catch (error) {
		window.status = "GetCenterPointFromLocations: " + error.description;
		}
	return pointToReturn;
	}


//-----------------------------------------------------------
// GetZoomLevelFromLocations
//-----------------------------------------------------------
function GetZoomLevelFromLocations( locationIDList ) {
	var zoomLevel = 10;
	try {
		if ( !locationIDList ) locationIDList = gLocationIDList;
		if ( locationIDList.length==1 ) return 4;
		var latLongBoundsInfo = GetBoundsOfPlots( locationIDList );
		// 15JUN2009 KBM - Switched to version 2 which has a "getBoundsZoomLevel" API call that is more reliable than this code
		/*//One deg diff equals 69.04 miles
		/var latitudeDiff = Math.abs(latLongBoundsInfo.minLatBound - latLongBoundsInfo.maxLatBound);
		var latitudeDifferenceInMiles = latitudeDiff * 69.04;
		//One deg diff equals 59.96 miles
		var longitudeDiff = Math.abs(latLongBoundsInfo.minLongBound - latLongBoundsInfo.maxLongBound);
		var longitudeDifferenceInMiles = longitudeDiff * 59.96;
		for (var level=4; level<10; level++) {
			var maxDistanceAtThisLevel = (gMapSizeInPixels/gZoomLevelToScaleMapping[level].scale) * gZoomLevelToScaleMapping[level].distance;
			if (latitudeDifferenceInMiles<maxDistanceAtThisLevel && longitudeDifferenceInMiles<maxDistanceAtThisLevel) {
				// 12JUN2009 KBM - Zoom Levels apparently have changed in v2 so subtract 1 to get same zoom effect as in V1
				//return level;
				//alert(latLongBoundsInfo.minLatBound + " " + latLongBoundsInfo.maxLatBound + " " + latLongBoundsInfo.minLongBound + " " + latLongBoundsInfo.maxLongBound );
				return level - 1;
				}
			}*/
		if( typeof gCurrentMap.getBoundsZoomLevel == "function" )
			return gCurrentMap.getBoundsZoomLevel( new GLatLngBounds( new GLatLng(latLongBoundsInfo.minLatBound, latLongBoundsInfo.minLongBound), new GLatLng(latLongBoundsInfo.maxLatBound, latLongBoundsInfo.maxLongBound) ) );
		}
	catch (error) {
		window.status = "GetZoomLevelFromLocations: " + error.description;
		}
	return zoomLevel;
	}


//-----------------------------------------------------------
// GetBoundsOfPlots
//-----------------------------------------------------------
function GetBoundsOfPlots( locationIDList ) {
	var record = { minLatBound:null, maxLatBound: null, minLongBound:null, maxLongBound:null };
	try {
		if (gLatLongBoundsInfo) return gLatLongBoundsInfo;
		var minLatBound = null;
		var maxLatBound = null;
		var minLongBound = null;
		var maxLongBound = null;
		// alert("GetBoundsOfPlots locationIDList.length: " + locationIDList.length);
		for (var item=0; item<locationIDList.length-1; item++) {
			var locationInfo = gLocationPlotInfo[locationIDList[item]];
			var nextLocationInfo = gLocationPlotInfo[locationIDList[item+1]];
			if (item==0) {
				minLatBound = Math.min(locationInfo.Latitude,nextLocationInfo.Latitude);
				maxLatBound = Math.max(locationInfo.Latitude,nextLocationInfo.Latitude);
				minLongBound = Math.min(locationInfo.Longitude,nextLocationInfo.Longitude);
				maxLongBound = Math.max(locationInfo.Longitude,nextLocationInfo.Longitude);
				}
			else {
				minLatBound = Math.min(minLatBound,nextLocationInfo.Latitude);
				maxLatBound = Math.max(maxLatBound,nextLocationInfo.Latitude);
				minLongBound = Math.min(minLongBound,nextLocationInfo.Longitude);
				maxLongBound = Math.max(maxLongBound,nextLocationInfo.Longitude);
				}
			// alert("GetBoundsOfPlots gLatLongBoundsInfo: " + minLatBound + maxLatBound + minLongBound + maxLongBound);
			}
		if (locationIDList.length==1) {
			minLatBound = gLocationPlotInfo[locationIDList[0]].Latitude;
			maxLatBound = minLatBound;
			minLongBound = gLocationPlotInfo[locationIDList[0]].Longitude;
			maxLongBound = minLongBound;
			}
		else if (locationIDList.length==0) {
			minLatBound = GetAustinCenterPoint().y;
			maxLatBound = minLatBound;
			minLongBound = GetAustinCenterPoint().y;
			maxLongBound = minLongBound;
			}
		record.minLatBound = Number(minLatBound);
		record.maxLatBound = Number(maxLatBound);
		record.minLongBound = Number(minLongBound);
		record.maxLongBound = Number(maxLongBound);
		gLatLongBoundsInfo = record;
		// alert("GetBoundsOfPlots gLatLongBoundsInfo: " + gLatLongBoundsInfo);
		}
	catch (error) {
		window.status = "GetBoundsOfPlots: " + error.description;
		}
	return record;
	}


//-----------------------------------------------------------
// IncrementMarkerCount
//-----------------------------------------------------------
function IncrementMarkerCount( locationID ) {
	try {
		gCurrentMarkerNumber++;
		gLocationPlotInfo[locationID].MarkerNumber = gCurrentMarkerNumber;
		}
	catch (error) {
		window.status = "IncrementMarkerCount: " + error.description;
		}
	}


//-----------------------------------------------------------
// IsLocationWithinBounds
//-----------------------------------------------------------
function IsLocationWithinBounds( locationID ) {
	var result = false;
	try {
		var locationInfo = gLocationPlotInfo[locationID];
		if (!gCurrentMapBounds) gCurrentMapBounds = gCurrentMap.getBoundsLatLng();
		if (locationInfo.Longitude > gCurrentMapBounds.minX && locationInfo.Longitude < gCurrentMapBounds.maxX && locationInfo.Latitude > gCurrentMapBounds.minY && locationInfo.Latitude < gCurrentMapBounds.maxY) {
			result = true;
			}
		}
	catch (error) {
		window.status = "IsLocationWithinBounds: " + error.description;
		}
	return result;
	}


//===========================================================
// SetLocationMarkerInfoWindowTemplate
//===========================================================
function SetLocationMarkerInfoWindowTemplate( template ) {
	gLocationMarkerInfoWindowContent = template;
	}


//===========================================================
// SetNoScrollIntoView
//===========================================================
function SetNoScrollIntoView( noScroll ) {
	gNoScrollIntoView = noScroll;
	}


//===========================================================
// SetCurrentZoomLevel
// 27MAY2009 RFM - SHP
// PARAMETERS:
//	zoomLevel:integer		1 is farthest, 20, closest
//===========================================================
function SetCurrentZoomLevel( zoomLevel) {
	gCurrentZoomLevel = zoomLevel;
	}


//===========================================================
// SetLocationPlottedHandler
// Accepts function that will be after PlotLocation has been 
// called. Function is passed locationInfo and status. Status
// is true if plotted, false if not.
//===========================================================
function SetLocationPlottedHandler( handler ) {
	gNoAlerts = true;
	gLocationPlottedHandler = handler;
	}



//===========================================================
// SetMapHandler
// Accepts function that will be called whenever event occurs.
// Valid events are: click, dblclick, singlerightclick, move, movestart,
// moveend, zoomend, maptypechanged, infowindowopen, infowindowbeforeclose,
// addoverlay. See http://code.google.com/apis/maps/documentation/reference.html#Events
// for details on function parameters;
// Function is passed the point clicked.
// 19JUN2009 RFM - Added for AddForm Field Action
//===========================================================
function SetMapHandler( event, handler ) {
	try {
		if (!gCurrentMap || !handler) return;
		GEvent.addListener(gCurrentMap, event, handler );
		}
	catch (error) {
		alert( "SetMapHandler: " + error.description  );
		}
	}



//===========================================================
// SetMapDoubleClickHandler
// 19JUN2009 RFM - Added for GEoCodeEditor
//===========================================================
function SetMapDoubleClickHandler( handler) {
	try {
		if (!gCurrentMap) return; 
		SetMapHandler( 'dblclick', handler );
		gCurrentMap.disableDoubleClickZoom() ;
		gCurrentMap.getDragObject().setDraggableCursor("pointer");
		}
	catch (error) {
		alert( "SetMapDoubleClickHandler: " + error.description  );
		}
	}


//===========================================================
// SetNoChangeFocus
// If set to true, map will not be focused on newly added location.
// 19JUN2009 RFM - Added for GEoCodeEditor
//===========================================================
function SetNoChangeFocus( noChange ) {
	SetNoChangeFocus.noChange = noChange;
	}



//===========================================================
// ShiftMapView
// Shifts the map view after each plot by h, v. Used to shift
// Balloon down so it doesnt' hit the border. See 
// http://setonplasticsurgery.vertexdev1.vertex.com/contactplasticsurgeons
//===========================================================
function ShiftMapView( h, v, timeout ) {
	try {
		SetLocationPlottedHandler( function() { 
			GEvent.addListener(gCurrentMap, "infowindowopen", function() { 
				window.setTimeout( function() {
					gCurrentMap.getDragObject().moveBy( new GSize( h, v ) );
					}, timeout || 500 );
				} );
			} );
		}
	catch (error) {
		alert( "ShiftMapView: " + error.description  );
		}
	}