	// Parse and return a new location from raw data 
	// Location class constructor (CMapLocation) is game-specific
function umCreateMapLocation (LocData) {
	var Loc = new CMapLocation(LocData);
	return Loc;
}

	// Find a current location by its ID
function umFindCurrentLocation (ID) {
	var Index;

	for (Index = 0; Index < umLocations.length; ++Index) {
		if (umLocations[Index].ID == ID) return umLocations[Index];
	}

	return null;
}

	// Map state class constructor
function CMapState(Center, Zoom, SearchText, NumResults, TotalResults, StartRow, Bounds) {
	this.Center 	  = Center;
	this.Zoom   	  = Zoom;
	this.SearchText   = SearchText;
	this.NumResults   = NumResults;
	this.TotalResults = TotalResults,
	this.StartRow     = StartRow;
	this.MapBounds    = Bounds;
	this.ShowDisabled = false;
}

	// Is the map currently displaying search results
CMapState.prototype.HasSearch = function() {
	return (this.SearchText != null && this.SearchText != "");
};

	// Update the center/zoom from the current map
CMapState.prototype.Update = function(Map) {
	this.Center    = Map.getCenter();
	this.Zoom      = Map.getZoom();
	this.MapBounds = Map.getBounds();
};

	// Sets the map center/zoom
CMapState.prototype.ZoomTo = function(Map, Center, Zoom) {

	Map.setCenter(Center, Zoom, umCustomMapType);

	this.Center = Center;
	this.Zoom   = Zoom;	
	this.MapBounds = Map.getBounds();
};

	// Sets the map center keeping the current zoom
CMapState.prototype.MoveTo = function(Map, Center) {
	Map.setCenter(Center);
	this.Center = Center;
	this.MapBounds = Map.getBounds();
};

	// Reset the search results
CMapState.prototype.ResetSearch = function() {
	this.NumResults   = 0;
	this.StartRow     = 0;
	this.SearchText   = "";
	this.TotalResults = 0;
	this.ShowDisabled = false;

	umClearSearchResults();
};

var umMapState = new CMapState(umMapDefaultCenter, umMapDefaultZoom, "", 0, 0, 0, umMapBounds);

	// Display the map help window
function umShowHelp() {
	var NewWindow = window.open(umBasePath + "maphelp.html", "MapHelpWindow", "width=750, height=500, menubar=no, toolbar=no, status=no, scrollbars=yes");
	NewWindow.focus();
}

	// Reset the search contents and values
function umResetSearch() {
	var InputBox = document.getElementById("umSearchText");
	if (InputBox) InputBox.value = "";

	umMapState.ResetSearch();
	umDeleteLocations();

	umGetMarkers(umMapState);

	umUpdateLink();
}	

	// Resets the map view and labels to a default state
function umResetView() {
	var InputBox = document.getElementById("umSearchText");
	if (InputBox) InputBox.value = "";

	umMapState.ResetSearch();
	umDeleteLocations();
	umUpdateLink();

	umMapState.ZoomTo(umMap, umMapDefaultCenter, umMapDefaultZoom, umCustomMapType);
}

	// Create a map link using a location and custom zoom level
function umCreateLinkZoom(Loc, Zoom) {
	var newLinkTo = umMapURL + '?locx=' + umConvertLngToLocX(Loc.lng()).toFixed(0) + '&locy=' + umConvertLatToLocY(Loc.lat()).toFixed(0) + '&zoom=' + Zoom;

	if (umMapState.HasSearch()) {
		newLinkTo += "&search=" + escape(umMapState.SearchText);
		if (umMapState.StartRow > 0) newLinkTo += "&startsearch=" + escape(umMapState.StartRow);
	}
	return newLinkTo;
}	

	// Create a map link using a custom location and the current zoom level
function umCreateLink(Loc) {
	return umCreateLinkZoom(Loc, umMap.getZoom());
}

	// Retrieve a specific input parameter
function umGetURLParam(strParamName) {
	var strReturn = "";
	var strHref = window.location.href;

	if ( strHref.indexOf("?") > -1 ){
		var strQueryString = strHref.substr(strHref.indexOf("?")).toLowerCase();
		var aQueryString = strQueryString.split("&");

		for ( var iParam = 0; iParam < aQueryString.length; iParam++ ){
			if ( aQueryString[iParam].indexOf(strParamName + "=") > -1 ){
				var aParam = aQueryString[iParam].split("=");
				strReturn = aParam[1];
				break;
			}
		}
	}
	return strReturn;
}

	// Called to start a search for disabled locations
function umSearchDisableLocs() {
	umSearchFunction(0, true);
}

	// Called to start a search for locations
function umSearchFunction(StartSearchRow, ShowDisabled) {
	umMapState.ResetSearch();    
	umDeleteLocations();
 	if (ShowDisabled) umMapState.ShowDisabled = true;
	
	var InputBox = document.getElementById("umSearchText");

	if (InputBox) {
		var SearchText = InputBox.value;
		umMapState.SearchText = SearchText.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
		InputBox.value        = umMapState.SearchText;

		if (StartSearchRow) {
			umMapState.StartRow = parseInt(StartSearchRow);
		}
		else {
			umMapState.StartRow = 0;
		}

		umGetMarkers(umMapState);
	}

	umUpdateLink();
}

	// Add a text string to the end of the search result element
function umAddSearchResultText(Text, DivName) {
	var SearchResults = document.getElementById("umSearchResults");
	if (!SearchResults) return;

	var NewDiv = document.createElement("div");
	NewDiv.setAttribute("divname", DivName);
	NewDiv.innerHTML = Text;
// can't just append if we want alphabetical order
//	SearchResults.appendChild(NewDiv);

	var Found = 0;
	for (Index = 0; Index<SearchResults.childNodes.length; Index++) {
		var TName = SearchResults.childNodes[Index].getAttribute("divname");
		if (TName > DivName) {
			SearchResults.insertBefore(NewDiv, SearchResults.childNodes[Index]);
			Found = 1;
			break;
		}
	}
	if (!Found) {
		SearchResults.appendChild(NewDiv);
	}

	return NewDiv;
}


function umTestClick() {
	//alert(umUpdateLink());
	location.replace(umUpdateLink());
}

	// Clear any displayed search results
function umClearSearchResults() {
	var SearchResults = document.getElementById("umSearchResults");
	if (!SearchResults) return;

	for (Index = SearchResults.childNodes.length - 1; Index >= 0; --Index) {
		SearchResults.removeChild(SearchResults.childNodes[Index]);
	}
}

	// Set the map to a specific Oblivion location at the current zoom level
function umShowLocation(X, Y) {
	var Point = umConvertLocToLatLng(X, Y);
	umMapState.MoveTo(umMap, Point);
}

	// Set the map to a specific Oblivion location and zoom level
function umShowLocationZoom(X, Y, Zoom) {
	var Point = umConvertLocToLatLng(X, Y);
	umMapState.ZoomTo(umMap, Point, Zoom);
}

	// Custom tile function to convert a tile X/Y and zoom into a specific image URL
function umCustomGetTileUrl (Tile, Zoom) {

	if (Zoom >=umMinMapZoom && Zoom<=umMaxMapZoom) {
		var MaxX = Math.floor(umNumMapTilesX/Math.pow(2, umBaseMapZoom - Zoom));
		var MaxY = Math.floor(umNumMapTilesY/Math.pow(2, umBaseMapZoom - Zoom));
				
		if (Tile.x < MaxX && Tile.y < MaxY && Tile.x >= 0 && Tile.y >= 0) {
			return umImagePath + umGameDir + "/zoom" + Zoom + "/" + umGamePrefix + "-" + Tile.x + "-" + Tile.y + "-" + Zoom + ".jpg"; 
		}
	}

	return umImagePath + umOorMapTile;
}

	// Updates the search form from input parameters
function umUpdateSearchFromInput(InputValues) {
	var SearchText = InputValues["search"];
	umMapState.SearchText = SearchText;

	if (SearchText && SearchText != "") {
		umMapState.SearchText = unescape(SearchText);
		umMapState.StartRow   = parseInt(InputValues["startsearch"]);
		var InputBox = document.getElementById("umSearchText");
		if (InputBox) InputBox.value = umMapState.SearchText;
	}
}

	// Updates the map parameters from any input parameters
function umUpdateMapFromInput(InputValues) {
	var Lat      = InputValues["lat"];
	var Lng      = InputValues["lng"];
	var Zoom     = InputValues["zoom"];
	var LocX     = InputValues["locx"];
	var LocY     = InputValues["locy"];
	var CellX    = InputValues["cellx"];
	var CellY    = InputValues["celly"];
	var CenterOn = InputValues["centeron"];
	var ZoomB;
	if (Zoom == null || Zoom == "") {
		Zoom = umMapLinkZoomedValue;
		ZoomB = umMapDefaultZoom;
	}
	else {
		ZoomB = Zoom = parseInt(Zoom);
	}

	if (Lat && Lat != "") {
		umMapState.ZoomTo(umMap, new GLatLng(parseFloat(Lat), parseFloat(Lng)), Zoom);
	} 
	else if (LocX && LocX != "") {
		umMapState.ZoomTo(umMap, umConvertLocToLatLng(parseFloat(LocX), parseFloat(LocY)), Zoom);
	}

	else if (CellX && CellX != "") {
		var X = parseFloat(CellX) * umObCellSize;
		var Y = parseFloat(CellY) * umObCellSize;

		umMapState.ZoomTo(umMap, umConvertLocToLatLng(X, Y), Zoom);
	}
	else if (CenterOn && CenterOn != "") {
		umMapState.ZoomTo(umMap, umMapDefaultCenter, umMapDefaultZoom);
		umGetCenterOnMarker(umMapState, unescape(CenterOn), Zoom);
		umNoInitialGetMarkers = true;
	}
	else {
		umMapState.ZoomTo(umMap, umMapDefaultCenter, ZoomB);
	}

	umUpdateSearchFromInput(InputValues);
}

	// If the map position is out of range, move it back
function umCheckMapBounds() {

	if (umMapBounds.contains(umMap.getCenter())) {
		return;
	}

		// Find the nearest allowed point and move there
	var Center = umMap.getCenter();
	var X = Center.lng();
	var Y = Center.lat();

	var AmaxX = umMapBounds.getNorthEast().lng();
	var AmaxY = umMapBounds.getNorthEast().lat();
	var AminX = umMapBounds.getSouthWest().lng();
	var AminY = umMapBounds.getSouthWest().lat();

	if (X > 0.0)   {X = AminX;}	//Avoid wrapping
	if (Y < 0.0)   {Y = AmaxY;}

	if (X < AminX) {X = AminX;}
	if (X > AmaxX) {X = AmaxX;}
	if (Y < AminY) {Y = AminY;}
	if (Y > AmaxY) {Y = AmaxY;}

	umMapState.MoveTo(umMap, new GLatLng(Y, X));
}

	// Convert an Oblivion location to a map latitude/longitude
function umConvertLocToLatLng(X, Y) {
	var Lng = -180.0 + ((X/umObCellSize + umObCellOffsetX)/65536.0 * 360.0);
	var Lat = 90.0 - ((umObCellOffsetY - Y/umObCellSize)/65536.0 * 180.0);
	return new GLatLng(Lat, Lng);
}

	// Convert a longitude to an Obivion X location
function umConvertLngToLocX(Lng) {
	if (Lng > 0) Lng = -180.0;
	if (Lng < umMapBounds.getSouthWest().lng()) Lng = umMapBounds.getSouthWest().lng();
	if (Lng > umMapBounds.getNorthEast().lng()) Lng = umMapBounds.getNorthEast().lng();
	return (((Lng + 180.0) * 65536.0 / 360.0 - umObCellOffsetX) * umObCellSize);
}
   
	// Convert a latitude to an Obivion X location
function umConvertLatToLocY(Lat) {
	if (Lat < 0) Lat = 90.0;
	if (Lat < umMapBounds.getSouthWest().lat()) Lat = umMapBounds.getSouthWest().lat();
	if (Lat > umMapBounds.getNorthEast().lat()) Lat = umMapBounds.getNorthEast().lat();
	return (((90.0 - Lat) * 65536.0 / 180.0  - umObCellOffsetY) * -umObCellSize);
}

	// Used to show/hide div elements
function umShowFloat(Element) {	document.getElementById(Element).style.display = 'block'; }
function umHideFloat(Element) {	document.getElementById(Element).style.display = 'none'; }

	// Delete all currently displayed map locations
function umDeleteLocations() {
	var Index;

	for (Index = 0; Index < umLocations.length; ++Index) {
		if (umLocations[Index].Label) umMap.removeTLabel(umLocations[Index].Label);
		if (umLocations[Index].LabelListener) GEvent.removeListener(umLocations[Index].LabelListener);
	}

	umLocations = [];
}

	// Main map setup function to be called on page load
function umSetupMap(ShowOverview, ShowZoom, ShowPan, MapDragging, InputValues) {

	if (!GBrowserIsCompatible()) {
		alert("Sorry, the Google Maps API is not compatible with this browser!");
		return;
	}

	umSetupEuclideanProjection();

	umMapTileLayer =[new GTileLayer(new GCopyrightCollection("Tamriel: uesp.net"), umMinMapZoom, umMaxMapZoom)];
	umMapTileLayer[0].getCopyright = function(a,b) { return { prefix:"Tamriel: &copy;", copyrightTexts:["uesp.net"] }; }

	umMapTileLayer[0].getTileUrl = umCustomGetTileUrl;
	umCustomMapType = new GMapType(umMapTileLayer, new EuclideanProjection(umMaxMapZoom + 1), "Euclidean", { errorMessage:"No Data Available" } );
	//umCustomMapType = new GMapType(umMapTileLayer, G_NORMAL_MAP.getProjection(), "test", { errorMessage:"No Data Available" } );
	umCustomMapType.numZoomLevels = 8;

	umMap = new GMap2(document.getElementById("umMap"), { mapTypes:[umCustomMapType] } );
	//umMap.addControl(new GMapTypeControl());
	umMap.enableContinuousZoom();
	umMap.enableScrollWheelZoom();

	if (MapDragging != null && MapDragging == false) umMap.disableDragging();

	if (ShowPan && ShowZoom) 
		umMap.addControl(new GLargeMapControl());
	else if (ShowPan)
		umMap.addControl(new GSmallMapControl());
	else if (ShowZoom)
		umMap.addControl(new GSmallZoomControl());

	if (!InputValues) {
		var InputValues = [];
		InputValues['lat'] = umGetURLParam("lat");
		InputValues['lng'] = umGetURLParam("lng");
		InputValues['zoom'] = umGetURLParam("zoom");
		InputValues['locx'] = umGetURLParam("locx");
		if (!InputValues['locx'] || InputValues['locx'] == "") {
		   InputValues['locx'] = umGetURLParam("oblocx");
		}
		InputValues['locy'] = umGetURLParam("locy");
		if (!InputValues['locy'] || InputValues['locy'] == "") {
		   InputValues['locy'] = umGetURLParam("oblocy");
		}
		InputValues['cellx'] = umGetURLParam("cellx");
		InputValues['celly'] = umGetURLParam("celly");
		InputValues['search'] = umGetURLParam("search");
		InputValues['startsearch'] = umGetURLParam("startsearch");
		InputValues['centeron'] = umGetURLParam("centeron");
	}	
	else {
	}

	umUpdateMapFromInput(InputValues);

		// Add a move listener to restrict the bounds range
	GEvent.addListener(umMap, "move", function() {
		umCheckMapBounds();
	});

		// ======== Add a map overview ==========
	if (ShowOverview) umMap.addControl(new GOverviewMapControl(new GSize(150,150)), new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(2, 2)));
	
	if (!umNoInitialGetMarkers) {
		umGetMarkers(umMapState);
	}

	umUpdateLink();
	
	//GEvent.addListener(umMap, "moveend", umUpdateMarkers);
	GEvent.addListener(umMap, "moveend", umUpdateDiffMarkers);

}

	// Creates the content for a new map label
function umMakeLabelContent(LabelID, Location, PopupContent) {
	var Content = Location.Name;
	var Result = "";
// add the box that contains the map marker icon
// box fills the entire label region, and includes vertical padding if needed to
// allow text above/below icon
	Result += '<div style="position: relative; left: 1px; top: 1px;';
	Result += ' background: url(' + umGetMapMarkerIcon(Location.Type) + ') no-repeat ';
// position icon and add padding
	switch (Location.LabelPosition) {
		case 1: Result += 'left top; padding-top: 16px;'; break;
		case 2: Result += 'center top; padding-top: 16px;'; break;
		case 3: Result += 'right top; padding-top: 16px;'; break;
		case 4: Result += 'right center;'; break;
		case 5: Result += 'right bottom; padding-bottom: 16px'; break;
		case 6: Result += 'center bottom; padding-bottom: 16px'; break;
		case 7:Result += 'left bottom; padding-bottom: 16px'; break;
		case 9: Result += 'center;'; break;
		case 8:
		default: Result += 'left center;'; break;
	}
	Result += '">';
// add the box that contains all the text
	Result += '<div id="umLabelArea" style="position:relative;';
// padding if placing text adjacent to icon
	switch (Location.LabelPosition) {
		case 4: Result += ' padding-right:16px;'; break;
		case 8: Result += ' padding-left:18px;'; break;
	}
	Result += '"><nobr>';
// black version of text
   	Result += Content;
// yellow version of text, offset relative to black
	Result += '<div style="position: absolute; left: -1px; top: -1px; color: #ffff99;';
	switch (Location.LabelPosition) {
		case 8: Result += ' padding-left:18px;'; break;
	}
	Result += '">' + Content + '</div>';
// close the text tags
   	Result += '</nobr></div>';
	Result += '<div id="' + LabelID + '" style="position: absolute; display: none;">';
   	Result += '<div class="umLabelPopup"><nobr>';
   	Result += PopupContent + '</nobr></div></div></div>';
   	return Result;
   }

	// Adds a new map label using the given information
function umCreateMapLabel (Location, Popup) {
	var label = new TLabel();

	label.id = 'Label' + umLocations.length + umLocationIDCounter;
	label.anchorLatLng = Location.MapPoint;
// position of entire label box relative to anchor point
// anchorPoint sets the corner of the label box where anchor (lat/long point) is located
//  (note this name is position of anchor relative to label;
//   on edit box, the name is the position of the label relative to the anchor and 
//   therefore is the reverse)
// markerOffset specifies where relative to that corner the exact center of the anchor
// (i.e., center of 16x16 map marker icon) is located
	switch (Location.LabelPosition) {
		case 1:
			label.anchorPoint = 'topLeft';
			label.markerOffset = new GSize(8,8);
			break;
		case 2:
			label.anchorPoint = 'topCenter';
			label.markerOffset = new GSize(0,8);
			break;
		case 3:
			label.anchorPoint = 'topRight';
			label.markerOffset = new GSize(-8,8);
			break;
		case 4:
			label.anchorPoint = 'midRight';
			label.markerOffset = new GSize(-8,0);
			break;
		case 5:
			label.anchorPoint = 'bottomRight';
			label.markerOffset = new GSize(-8,-8);
			break;
		case 6:
			label.anchorPoint = 'bottomCenter';
			label.markerOffset = new GSize(0,-8);
			break;
		case 7:
			label.anchorPoint = 'bottomLeft';
			label.markerOffset = new GSize(8,-8);
			break;
		case 9:
			label.anchorPoint = 'center';
			label.markerOffset = new GSize(0,0);
			break;
		case 8:
		default:
			label.anchorPoint = 'midLeft';
			label.markerOffset = new GSize(8,0);
			break;
	}
	var PopupID = label.id + "popup"
	label.content = umMakeLabelContent(PopupID, Location, Popup);
	label.percentOpacity = 100;
	umMap.addTLabel(label);

	++umLocationIDCounter;
	Location.Label = label;

	//Location.LabelListener = GEvent.addListener(label, "click", function() {
		//alert("Test");
		//umOnClickMapLabel(Location.ID);
	//});

	Location.LabelListener = GEvent.addDomListener(document.getElementById(label.id), "click", function() {
		//alert("Test");
		umOnClickMapLabel(Location.ID);
	});

	return label; 
}


function umOnClickMapLabel(LocationID) {
	var Location = umFindCurrentLocation(LocationID);

	if (Location) {
		var Content = umMakeLocationInfoContent(Location);
		umMap.openInfoWindowHtml(Location.MapPoint, Content, {pixelOffset: new GSize(0,-8)} );
	}

		// Edit location if needed
	umEditLocation(LocationID);
}

	// Updates an existing map label
function umUpdateMapLabel(Location) {

 	if (!Location) return;

		// Remove an existing label
	if (Location.Label) {
		umMap.removeTLabel(Location.Label);
		Location.Label = null;
	}

	if (Location.LabelListener) {
		GEvent.removeListener(Location.LabelListener);
		Location.LabelListener = null;
	}

	var Content = "<nobr>" + Location.X + ", " + Location.Y + ", " + Location.Z + "</nobr>";
	umCreateMapLabel(Location, Content);
}

	// Parse out locations from a get location request 
function umParseMapLocations(xmlDoc, Locations) {
	var Count = 0;

	for (var i = 0; i < Locations.length; i++) {
		var Location = umCreateMapLocation(Locations[i]);
		var Content = "<nobr>" + Location.X + ", " + Location.Y + ", " + Location.Z + "</nobr>";

		if (umFindCurrentLocation(Location.ID) == null) {
			umLocations[umLocations.length] = Location;
		
			umCreateMapLabel(Location, Content);
			umAddSearchResult(Location);
			++Count;
		}
	}
	return Count;
}

	// Sets the current results header text
function umUpdateResultsText() {
	var Header = document.getElementById("umSearchResultHeader");
	var Text;

	if (!Header) return;

	if (umLocations.length > 0) {
		if (umMapState.HasSearch()) {
			Text = "<div id='umResultTitle'>Results <b>" + (umMapState.StartRow  + 1) + "-" + (umMapState.StartRow + umLocations.length) + "</b> of <b>" + umMapState.TotalResults  + "</b> matching locations for <B>" + umMapState.SearchText + "</B>.</div>";
		} else {
			Text = "<div id='umResultTitle'>Showing locations <b>" + (umMapState.StartRow  + 1) + "-" + (umMapState.StartRow + umLocations.length) + "</b> of <b>" + umMapState.TotalResults  + "</b> in current map area.</div>";
		}
	} 
	else if (umMapState.HasSearch()) {
		Text = "<div id='umResultTitle'>Found no matching locations for <b>" + umMapState.SearchText + "</b>.</div>";
	}
	else {
		Text = "<div id='umResultTitle'>Found no locations to display.</div>";
	}


	Header.innerHTML = Text;
}

	// Parse out locations from a get location request 
function umParseGetMapLocations(xmlDoc, Locations) {
	var RowCountData = xmlDoc.documentElement.getElementsByTagName("rowcount");
	var TotalRowCount = 0;
	var RowCount = 0;
	var StartRow = 0;

	//alert("umParseGetMapLocations");

	if (RowCountData.length > 0) {
		TotalRowCount = parseInt(RowCountData[0].getAttribute("totalrows"));
		RowCount      = parseInt(RowCountData[0].getAttribute("rowcount"));
		StartRow      = parseInt(RowCountData[0].getAttribute("startrow"));	
	}
	
	umMapState.StartRow      = StartRow;
	umMapState.TotalResults  = TotalRowCount;
	umMapState.NumResults    = RowCount;

	umParseMapLocations(xmlDoc, Locations);
	umUpdateResultsText();

	var Link = "<center>";

		// Previous link
	if (StartRow > 0) {
		var NewStart = StartRow - RowCount;
		if (NewStart < 0) NewStart = 0;
		Link += "<a href='' onClick='umSearchFunction(" + NewStart + "); return(false);'><b>Prev</b></A> &nbsp; &nbsp; &nbsp;";
	}

		// Next link
	if (StartRow + RowCount + 1 < TotalRowCount) {
		var NewStart = StartRow + RowCount;
		if (NewStart > TotalRowCount) NewStart = TotalRowCount - 1;
		if (NewStart < 0) NewStart = 0;
		Link += "<a href='' onClick='umSearchFunction(" + NewStart + "); return(false);'><b>Next</b></a>";
	}

	Link += "</center>";
	var SearchControls = document.getElementById("umSearchControls");
	if (SearchControls) SearchControls.innerHTML = Link;
}


	// Parse a response from a get location request
function umParseGetMapRequest(Request) {
	var xmlDoc = Request.responseXML;

	if (xmlDoc == null || xmlDoc.documentElement == null) {
		umUpdateResultsText();
		umWaitingForReponse = 0;
		return;
	}

 	var Locations = xmlDoc.documentElement.getElementsByTagName("location");
	
	if (Locations != null && Locations.length) {
		umParseGetMapLocations(xmlDoc, Locations);
	}
	else {
		umUpdateResultsText();
	}

	umWaitingForReponse = 0;
}


	// Makes a get query request string from a map state and oblivion bounding box
function umMakeGetQueryBounds(MapState, SWX, SWY, NEX, NEY) {
	var QueryStr = umGamePath + umGetMapURL + "?zoom=" + MapState.Zoom + "&BottomLeftX=" + SWX + "&BottomLeftY=" + SWY + "&TopRightX=" + NEX + "&TopRightY=" + NEY;

	if (MapState.HasSearch()) {
		QueryStr += "&SearchText=" + escape(MapState.SearchText);
	}

	if (MapState.StartRow) {
		QueryStr += "&StartRow=" + escape(MapState.StartRow);
	}

	if (MapState.ShowDisabled) {
		QueryStr += "&ShowDisabled=1";
	}

	return QueryStr;
}

	// Makes a get query request string from a map state and centeron location
function umMakeGetQueryCenterOn (MapState, CenterOn) {
	var QueryStr = umGamePath + umGetMapURL + "?centeron=" + escape(CenterOn);
	return QueryStr;
}

	// Uses an XmlHttpRequest to get map locations using the input information
function umGetMarkers(MapState) {
	var Request     = GXmlHttp.create();
	var BottomLeftX = umConvertLngToLocX(MapState.MapBounds.getSouthWest().lng());
	var BottomLeftY = umConvertLatToLocY(MapState.MapBounds.getSouthWest().lat());
	var TopRightX   = umConvertLngToLocX(MapState.MapBounds.getNorthEast().lng());
	var TopRightY   = umConvertLatToLocY(MapState.MapBounds.getNorthEast().lat());

	QueryStr = umMakeGetQueryBounds(MapState, BottomLeftX, BottomLeftY, TopRightX, TopRightY);
	Request.open('GET', QueryStr, true);

	Request.onreadystatechange = function () {
		if (Request.readyState == 4) {
			umParseGetMapRequest(Request);
		}
	}

	umWaitingForReponse = 1;
	Request.send(null);
}

	// Updates the 'linkto' anchor on the page
function umUpdateLink() {
	umMapState.Update(umMap);
	
	var newLinkTo = umCreateLink(umMap.getCenter());
	var LinkTo    = document.getElementById("umLinkTo");
	if (LinkTo) LinkTo.href = newLinkTo;
	return newLinkTo;
}

	// Updates locations when the map moves
function umUpdateMarkers() {
	umUpdateLink();

	if (umMapState.HasSearch()) return;
	if (umWaitingForReponse) return;

	umMapState.ResetSearch();
	umDeleteLocations();

	umGetMarkers(umMapState);
}


function umUpdateDiffMarkers() {

	if (umNoInitialGetMarkers) {
		umNoInitialGetMarkers = false;
		umMapState.Update(umMap);
		umGetMarkers(umMapState);
		return;
	}

	var OldBounds = umMapState.MapBounds;
	var OldZoom   = umMapState.Zoom;

	//alert("diff update");
	var OldSWX = umConvertLngToLocX(OldBounds.getSouthWest().lng());
	var OldSWY = umConvertLatToLocY(OldBounds.getSouthWest().lat());
	var OldNEX = umConvertLngToLocX(OldBounds.getNorthEast().lng());
	var OldNEY = umConvertLatToLocY(OldBounds.getNorthEast().lat());

	//alert(umMapState.TotalResults);

		// Update link and current map state
	umUpdateLink();

	//alert(umMapState.TotalResults);

	var NewSWX = umConvertLngToLocX(umMapState.MapBounds.getSouthWest().lng());
	var NewSWY = umConvertLatToLocY(umMapState.MapBounds.getSouthWest().lat());
	var NewNEX = umConvertLngToLocX(umMapState.MapBounds.getNorthEast().lng());
	var NewNEY = umConvertLatToLocY(umMapState.MapBounds.getNorthEast().lat());

	if (umMapState.HasSearch()) return;
	//if (umWaitingForReponse) return;

	//umMapState.ResetSearchFromArea();
	//umMapState.ResetSearch();
	//umDeleteLocations();

	//alert(umMapState.MapBounds);
	umDeleteLocationsFromArea(umMapState.MapBounds, umMapState.Zoom);

	//alert(umMapState.TotalResults);

	if (umMapState.Zoom > OldZoom) {
		//alert(OldSWX + ", " + OldSWY + ", " + OldNEX + ", " + OldNEY);
		umAddMarkersBoundsZoom(umMapState, NewSWX, NewSWY, NewNEX, NewNEY, OldZoom);	
	}

		// Get more locations on the west side
	if (NewSWX < OldSWX) {
		var SWX = NewSWX;
		var NEX = OldSWX;
		var SWY = NewSWY;
		var NEY = NewNEY;
		umAddMarkersBounds(umMapState, SWX, SWY, NEX, NEY);
	}

		// Get more locations on the east side
	if (NewNEX > OldNEX) {
		var SWX = OldNEX;
		var NEX = NewNEX;
		var SWY = NewSWY;
		var NEY = NewNEY;
		umAddMarkersBounds(umMapState, SWX, SWY, NEX, NEY);
	}

		// Get more locations on the north side
	if (NewNEY > OldNEY) {
		var SWX = NewSWX;
		var NEX = NewNEX;
		var SWY = OldNEY;
		var NEY = NewNEY;
		umAddMarkersBounds(umMapState, SWX, SWY, NEX, NEY);
	}

		// Get more locations on the south side
	if (NewSWY < OldSWY) {
		var SWX = NewSWX;
		var NEX = NewNEX;
		var SWY = NewSWY;
		var NEY = OldSWY;
		//alert(OldSWX + ", " + OldSWY + ", " + OldNEX + ", " + OldNEY + " to " + NewSWX + ", " + NewSWY + ", " + NewNEX + ", " + NewNEY);
		umAddMarkersBounds(umMapState, SWX, SWY, NEX, NEY);
	}

}

	// Delete all locations no longer in the give map area
function umDeleteLocationsFromArea(Bounds, Zoom) {
	var Index;
	var SearchResults = document.getElementById("umSearchResults");

	for (Index = umLocations.length-1; Index >= 0 ; --Index) {

		if (!Bounds.contains(umLocations[Index].MapPoint) || umLocations[Index].DisplayLevel > Zoom) {
			if (umLocations[Index].Label) umMap.removeTLabel(umLocations[Index].Label);
			if (umLocations[Index].LabelListener) GEvent.removeListener(umLocations[Index].LabelListener);

			if (umLocations[Index].ResultElement && SearchResults) {
				SearchResults.removeChild(umLocations[Index].ResultElement);
			}

			umLocations.splice(Index, 1);
			--umMapState.NumResults;
			--umMapState.TotalResults;
		}
	}
	//alert(umMapState.NumResults + " of " + umMapState.TotalResults);
}

	// Uses an XmlHttpRequest to get map locations using the input information
function umAddMarkersBounds(MapState, SWX, SWY, NEX, NEY) {
	var Request = GXmlHttp.create();

	QueryStr = umMakeGetQueryBounds(MapState, SWX, SWY, NEX, NEY);
	Request.open('GET', QueryStr, true);

	Request.onreadystatechange = function () {
		if (Request.readyState == 4) {
			umParseAddMapRequest(Request);
		}
	}

	Request.send(null);
}


	// Uses an XmlHttpRequest to get map locations using the input information
function umAddMarkersBoundsZoom(MapState, SWX, SWY, NEX, NEY, MinZoom) {
	var Request = GXmlHttp.create();

	QueryStr = umMakeGetQueryBounds(MapState, SWX, SWY, NEX, NEY);
	QueryStr += "&minzoom=" + escape(MinZoom);
	Request.open('GET', QueryStr, true);

	Request.onreadystatechange = function () {
		if (Request.readyState == 4) {
			umParseAddMapRequest(Request);
		}
	}

	umWaitingForReponse = 1;
	Request.send(null);
}


	// Uses an XmlHttpRequest to get map locations using the input information
function umGetCenterOnMarker(MapState, CenterOn, Zoom) {
	var Request = GXmlHttp.create();

	QueryStr = umMakeGetQueryCenterOn(MapState, CenterOn);
	Request.open('GET', QueryStr, true);

	Request.onreadystatechange = function () {
		if (Request.readyState == 4) {
			umParseCenterOnMapRequest(Request, Zoom);
		}
	}

	umWaitingForReponse = 1;
	Request.send(null);
}


	// Parse a response from an add location request
function umParseAddMapRequest(Request) {
	var xmlDoc = Request.responseXML;

 	var Locations = xmlDoc.documentElement.getElementsByTagName("location");
	var AddedCount = 0;

	if (Locations.length) {
		AddedCount = umParseMapLocations(xmlDoc, Locations);
	}

	var RowCountData = xmlDoc.documentElement.getElementsByTagName("rowcount");
	var TotalRowCount = 0;
	var RowCount = 0;
	var StartRow = 0;

	if (RowCountData.length > 0) {
		TotalRowCount = parseInt(RowCountData[0].getAttribute("totalrows"));
		RowCount      = parseInt(RowCountData[0].getAttribute("rowcount"));
		StartRow      = parseInt(RowCountData[0].getAttribute("startrow"));	
	}
	
	umMapState.StartRow      = StartRow;
	umMapState.TotalResults += TotalRowCount - (RowCount - AddedCount);
	umMapState.NumResults   += AddedCount;

	umUpdateResultsText();
}


	// Parse a response from a centeron location request
function umParseCenterOnMapRequest(Request, Zoom) {
	var xmlDoc = Request.responseXML;
 	var Locations = xmlDoc.documentElement.getElementsByTagName("location");

	if (Locations.length) {
		var Location = umCreateMapLocation(Locations[0]);

		var Content = "<nobr>" + Location.X + ", " + Location.Y + ", " + Location.Z + "</nobr>";
		//Content = "TESTING";

		umLocations[umLocations.length] = Location;
		umCreateMapLabel(Location, Content);
		umAddSearchResult(Location);

		umMapState.StartRow      = 0;
		umMapState.TotalResults  = 1;
		umMapState.NumResults    = 1;
		umUpdateResultsText();

		umMapState.ZoomTo(umMap, Location.MapPoint, Zoom);

		if (umMapState.HasSearch()) {
			umGetMarkers(umMapState);
		}
	}
	else {
		umGetMarkers(umMapState);
	}

	umUpdateLink();
}

function umGetLabelPositionLabel(LabelPosition) {
	switch (LabelPosition) {
	 	case 1: return 'Bottom Right';
		case 2: return 'Bottom Center';
		case 3: return 'Bottom Left';
		case 4: return 'Middle Left';
		case 5: return 'Top Left';
		case 6: return 'Top Center';
		case 7: return 'Top Right';
		case 8: return 'Middle Right';
		case 9: return 'Center';
	}
}

