/*jslint undef: true, sloppy: true, plusplus: true */
/*global $, ActiveXObject, addInput, createMarker, createOSMarker, createOSPhoto, createOSStation, createPhoto, createStation, DisplayControl, displayMetadata, document, doNothing, downloadUrl, ELabel, expandMap, google, latLongOpenLayers, latLongOpenSpace, loadingMessage, OpenLayers, OpenSpace, openSpaceLatLong, setBorderStyle, setButtonStyle, setDropdownStyle, shrinkMap, toggleLabels, toggleMarkers, togglePhotos, toggleTube, toggleWalk, window, XMLHttpRequest */

// Set up variables
var objMap;
var objOSMap;
var objOSGrid;

var arrWalkLines = [];
var arrTubeLines = [];
var arrStationLabels = [];
var arrMarkerLabels = [];
var arrMarkers = [];
var arrOSMarkers = [];
var arrPhotoLabels = [];
var arrPhotos = [];
var arrOSPhotos = [];

var boolGoogleMapShowing = true;
var boolDisplayShowing = false;
var boolWalkShowing = true;
var boolTubeShowing = false;
var boolLabelShowing = false;
var boolMarkerShowing = false;
var boolPhotoShowing = false;
var boolFullSize = false;
var boolGoogleEarth = false;

var intMapWidth = 576;
var intMapHeight = 600;

var strStationImage = "../images/google_earth/placemark_circle.png";
var intStationWidth = 16;
var intStationHeight = 16;
var intStationAnchorX = 8;
var intStationAnchorY = 8;

var strMarkerImage = "http://labs.google.com/ridefinder/images/mm_20_blue.png";
var intMarkerWidth = 12;
var intMarkerHeight = 20;
var intMarkerAnchorX = 6;
var intMarkerAnchorY = 20;

var strPhotoImage = "http://labs.google.com/ridefinder/images/mm_20_white.png";
var intPhotoWidth = 12;
var intPhotoHeight = 20;
var intPhotoAnchorX = 6;
var intPhotoAnchorY = 20;

$(window).resize(function () {
	if (boolFullSize && !$.browser.msie) {
		expandMap();
	}
});

// Load a map from an XML file
function load(strPath, strXmlFile, strPostfix, boolSimple, boolPhotos, boolTube, strEndPoint, boolMarkers, boolOSMap, boolAddPhotoLink) {
	var displayControl, displayControlDiv, intToggleZoom, mapOptions, objBounds, objLoadingDiv, objLoadingMessage, objToggleCentre, objVectorLayer, strMapKey, strOSMapLink, strSimpleParameter;

	// Insert 'Expand map', 'Google Earth' and 'OS map' links
	if (boolSimple) {
		strMapKey = '';
	} else {
		strMapKey = '<div id="mapKey"><img src="http://labs.google.com/ridefinder/images/mm_20_blue.png" style="vertical-align: middle" width="12" height="20" alt="Blue marker" /> = Point of Interest&nbsp;';
		if (boolPhotos) {
			strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_white.png" style="vertical-align: middle" width="12" height="20" alt="White marker" /> = Photo&nbsp;';
		}
		strMapKey += '<img style="vertical-align: top;" src="../images/google_earth/placemark_circle.png" width="20" height="20" alt="White circle" />= ' + strEndPoint + '</div>';
		if (boolOSMap && !boolSimple) {
			strMapKey += '<div id="mapOSKey"><img src="http://labs.google.com/ridefinder/images/mm_20_blue.png" style="vertical-align: middle" width="12" height="20" alt="Blue marker" /> = Point of Interest&nbsp;';
			if (boolPhotos) {
				strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_white.png" style="vertical-align: middle" width="12" height="20" alt="White marker" /> = Photo&nbsp;';
			}
			strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_red.png" style="vertical-align: middle" width="12" height="20" alt="Red marker" /> = ' + strEndPoint + '</div>';
		}
	}

	if (boolOSMap && !boolSimple) {
		strOSMapLink = ' | <a id="OSMap">OS map</a></div>';
	} else {
		strOSMapLink = '';
	}

	if (strXmlFile === 'google_maps_data') {
		$("#map").before('<div id="mapFullScreen">' + strMapKey + '<div id="mapLinks"><a href="../downloads/kmz/london_underground.kmz">Google Earth</a> | <a id="expandMap">Full screen</a>' + strOSMapLink + '</div>');
	} else {
		$("#map").before('<div id="mapFullScreen">' + strMapKey + '<div id="mapLinks"><a href="' + strPath + strXmlFile + '&amp;gpx=1">Download for GPS</a> | <a href="' + strPath + strXmlFile + '&amp;kml=1">Google Earth</a> | <a id="expandMap">Full screen</a>' + strOSMapLink + '</div>');
	}

	// Hide OS map key
	$("#mapOSKey").hide();

	// Activate 'Expand map' link
	$('a#expandMap').toggle(function () {
		expandMap();
	}, function () {
		shrinkMap();
	});

	// Activate 'OS map' link
	if (boolOSMap && !boolSimple) {
		$('a#OSMap').toggle(function () {
			objToggleCentre = latLongOpenSpace(objMap.getCenter().lat(), objMap.getCenter().lng());
			intToggleZoom = objMap.getZoom() - 6;
			if (intToggleZoom < 0) {
				intToggleZoom = 0;
			}
			if (intToggleZoom > 10) {
				intToggleZoom = 10;
			}
			objOSMap.setCenter(objToggleCentre, intToggleZoom);
			$("#map").hide();
			$("#mapKey").hide();
			$("#mapOS").show();
			$("#mapOSKey").show();
			$('a#OSMap').text("Google map");
			boolGoogleMapShowing = false;
		}, function () {
			objToggleCentre = openSpaceLatLong(objOSMap.getCenter());
			intToggleZoom = objOSMap.getZoom() + 6;
			objMap.setCenter(new google.maps.LatLng(objToggleCentre.lat, objToggleCentre.lon));
			objMap.setZoom(intToggleZoom);
			$("#mapOS").hide();
			$("#mapOSKey").hide();
			$("#map").show();
			$("#mapKey").show();
			$('a#OSMap').text("OS map");
			boolGoogleMapShowing = true;
		});
	}

	// If we need to show markers, tick checkbox
	boolMarkerShowing = boolMarkers;

	// Create Google map
	mapOptions = {
		zoom: 4,
		overviewMapControl: true,
		mapTypeId: google.maps.MapTypeId.TERRAIN,
		scaleControl: true,
		zoomControl: true,
		zoomControlOptions: {style: google.maps.ZoomControlStyle.SMALL},
		mapTypeControl: true,
		mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}
	};
	objMap = new google.maps.Map(document.getElementById("map"), mapOptions);

	// Add display control
	if (!boolSimple) {
		displayControlDiv = document.createElement('div');
		displayControl = new DisplayControl(displayControlDiv, boolPhotos, boolTube);
		objMap.controls[google.maps.ControlPosition.TOP_RIGHT].push(displayControlDiv);
	}

	// Create OS map
	if (boolOSMap && !boolSimple) {
		objOSMap = new OpenSpace.Map('mapOS');
		objOSGrid = new OpenSpace.GridProjection();
		objVectorLayer = objOSMap.getVectorLayer();
		objOSMap.addLayer(objVectorLayer);
	}

	// Create loading message
	objLoadingDiv = document.createElement("div");
	objLoadingDiv.setAttribute("id", "loadingMessage");
	objLoadingMessage = document.createElement("div");
	if (boolSimple || !boolTube) {
		objLoadingMessage.innerHTML = "<strong>Drawing map...</strong><br /><br /><img src=\"../images/google_maps/loading.gif\" />";
		boolTubeShowing = true;
	} else {
		objLoadingMessage.innerHTML = "<strong>Drawing map...</strong><br /><br /><img src=\"../images/google_maps/loading.gif\" /><br /><br />This may take some time; if you're in a rush, <a href=\"../route/map.php?simple=1&amp;route=" + strXmlFile + strPostfix + "\">try this simpler version</a>";
	}
	objLoadingDiv.appendChild(objLoadingMessage);
	objLoadingDiv.style.position = "relative";
	objMap.getDiv().appendChild(objLoadingDiv);
	objLoadingDiv.style.top = ((objMap.getDiv().offsetHeight - objLoadingDiv.offsetHeight) / 2) + "px";
	objLoadingDiv.style.left = ((objMap.getDiv().offsetWidth - objLoadingDiv.offsetWidth) / 2) + "px";

	// Set up bounds object for calculating Google map size
	objBounds = new google.maps.LatLngBounds();

	// If we're not loading a data file, pass on simple parameter
	if (strXmlFile === 'google_maps_data') {
		strSimpleParameter =  '';
	} else {
		strSimpleParameter = '&amp;simple=' + (boolSimple ? '1' : '0');
	}

	// Load map XML
	downloadUrl(strPath + strXmlFile + strPostfix + strSimpleParameter, function (objData) {
		var arrDescription, arrMarkerNodes, arrMetadata, arrName, arrOSPointObjects, arrPaths, arrPhotoNodes, arrPoints, arrPointObjects, arrPosition, arrSections, arrStations, fltHeight, fltOpacity, fltWidth, i, intPath, j, objNE, objOSBounds, objOSCentrePoint, objOSLineFeature, objOSLineString, objOSPoint, objOSStyle, objPoint, objSW, objTubeLine, objWalkLine, objXml, strColour, strLine;

		objXml = objData.responseXML;

		// Skip to the path node with id=strXmlFile
		arrPaths = objXml.documentElement.getElementsByTagName("path");
		for (intPath = 0; intPath < arrPaths.length; intPath++) {
			if (strXmlFile === 'google_maps_data') {
				if (arrPaths[intPath].getAttribute("id") === 'all') { break; }
			} else {
				if (arrPaths[intPath].getAttribute("id") === strXmlFile) { break; }
			}
		}

		// Pull out position node and centre maps
		arrPosition = arrPaths[intPath].getElementsByTagName("position");
		objMap.setCenter(new google.maps.LatLng(parseFloat(arrPosition[0].getAttribute("lat")), parseFloat(arrPosition[0].getAttribute("lng"))));
		if (boolOSMap && !boolSimple) {
			objOSCentrePoint = latLongOpenSpace(parseFloat(arrPosition[0].getAttribute("lat")), parseFloat(arrPosition[0].getAttribute("lng")));
			objOSMap.setCenter(objOSCentrePoint, 5);
		}

		// Pull out walk metadata and display
		arrMetadata = arrPaths[intPath].getElementsByTagName("meta");
		displayMetadata(arrMetadata[0].getAttribute("stations"), arrMetadata[0].getAttribute("markers"), arrMetadata[0].getAttribute("photos"), arrMetadata[0].getAttribute("walk"), arrMetadata[0].getAttribute("tube"), arrMetadata[0].getAttribute("crow"), boolPhotos, boolTube, strXmlFile, strPostfix, boolAddPhotoLink);

		// Get station nodes and add to map
		arrStations = arrPaths[intPath].getElementsByTagName("station");
		for (i = 0; i < arrStations.length; i++) {
			objPoint = new google.maps.LatLng(parseFloat(arrStations[i].getAttribute("lat")), parseFloat(arrStations[i].getAttribute("lng")));
			arrName = arrStations[i].getElementsByTagName("name");
			arrDescription = arrStations[i].getElementsByTagName("description");
			createStation(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
			if (boolOSMap && !boolSimple) {
				objOSPoint = latLongOpenSpace(parseFloat(arrStations[i].getAttribute("lat")), parseFloat(arrStations[i].getAttribute("lng")));
				createOSStation(objOSPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue);
			}
		}

		if (!boolSimple) {
			// Get marker nodes and add to map
			arrMarkerNodes = arrPaths[intPath].getElementsByTagName("marker");
			for (i = 0; i < arrMarkerNodes.length; i++) {
				objPoint = new google.maps.LatLng(parseFloat(arrMarkerNodes[i].getAttribute("lat")), parseFloat(arrMarkerNodes[i].getAttribute("lng")));
				arrName = arrMarkerNodes[i].getElementsByTagName("name");
				arrDescription = arrMarkerNodes[i].getElementsByTagName("description");
				createMarker(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
				if (boolOSMap) {
					objOSPoint = latLongOpenSpace(parseFloat(arrMarkerNodes[i].getAttribute("lat")), parseFloat(arrMarkerNodes[i].getAttribute("lng")));
					createOSMarker(objOSPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue);
				}
			}

			if (boolPhotos) {
				// Get photo nodes and add to map
				arrPhotoNodes = arrPaths[intPath].getElementsByTagName("photo");
				for (i = 0; i < arrPhotoNodes.length; i++) {
					objPoint = new google.maps.LatLng(parseFloat(arrPhotoNodes[i].getAttribute("lat")), parseFloat(arrPhotoNodes[i].getAttribute("lng")));
					arrName = arrPhotoNodes[i].getElementsByTagName("name");
					arrDescription = arrPhotoNodes[i].getElementsByTagName("description");
					createPhoto(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue);
					if (boolOSMap) {
						objOSPoint = latLongOpenSpace(parseFloat(arrPhotoNodes[i].getAttribute("lat")), parseFloat(arrPhotoNodes[i].getAttribute("lng")));
						createOSPhoto(objOSPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue);
					}
				}
			}

			// Get walk sections and add polyline to map
			arrSections = arrPaths[intPath].getElementsByTagName("section");
			for (i = 0; i < arrSections.length; i++) {
				arrPointObjects = [];
				arrOSPointObjects = [];
				arrPoints = arrSections[i].getElementsByTagName("point");
				for (j = 0; j < arrPoints.length; j++) {
					objPoint = new google.maps.LatLng(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
					arrPointObjects.push(objPoint);
					objBounds.extend(objPoint);
					if (boolOSMap) {
						objOSPoint = latLongOpenLayers(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
						arrOSPointObjects.push(objOSPoint);
						if (objOSBounds) {
							objOSBounds.extend(objOSPoint);
						} else {
							objOSBounds = new OpenLayers.Bounds(objOSPoint.lon, objOSPoint.lat, objOSPoint.lon, objOSPoint.lat);
						}
					}
				}
				strLine = arrSections[i].getAttribute("line");
				strColour = '#0000ff';
				fltOpacity = 0.8;
				switch (strLine) {
				case 'bakerloo':
					strColour = '#ab6612';
					break;
				case 'central':
					strColour = '#df002c';
					break;
				case 'circle':
					strColour = '#f7dc7f';
					break;
				case 'district':
					strColour = '#0d6928';
					break;
				case 'east_london':
					strColour = '#f3ba22';
					break;
				case 'jubilee':
					strColour = '#363b3f';
					break;
				case 'hammersmith_and_city':
					strColour = '#f5a6b3';
					break;
				case 'metropolitan':
					strColour = '#8b004c';
					break;
				case 'northern':
					strColour = '#000000';
					break;
				case 'piccadilly':
					strColour = '#002d73';
					break;
				case 'victoria':
					strColour = '#0076bd';
					break;
				case 'waterloo_and_city':
					strColour = '#89cbc1';
					break;
				default:
					strColour = '#0000ff';
					fltOpacity = 0.4;
				}
				objWalkLine = new google.maps.Polyline({
					path: arrPointObjects,
					strokeColor: strColour,
					strokeWeight: 5,
					strokeOpacity: fltOpacity
				});
				objWalkLine.setMap(objMap);
				arrWalkLines.push(objWalkLine);
				if (boolOSMap && !boolSimple) {
					objOSStyle = {strokeColor: strColour, strokeOpacity: fltOpacity, strokeWidth: 5};
					objOSLineString = new OpenLayers.Geometry.LineString(arrOSPointObjects);
					objOSLineFeature = new OpenLayers.Feature.Vector(objOSLineString, null, objOSStyle);
					objVectorLayer.addFeatures([objOSLineFeature]);
				}
			}
		}

		if (boolTube) {
			// Get tube sections and add polyline to map
			arrSections = arrPaths[intPath].getElementsByTagName("tube");
			for (i = 0; i < arrSections.length; i++) {
				arrPointObjects = [];
				arrPoints = arrSections[i].getElementsByTagName("point");
				for (j = 0; j < arrPoints.length; j++) {
					objPoint = new google.maps.LatLng(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
					arrPointObjects.push(objPoint);
					objBounds.extend(objPoint);
				}
				if (boolSimple) {
					strLine = arrSections[i].getAttribute("line");
					strColour = '#ff0000';
					fltOpacity = 0.8;
					switch (strLine) {
					case 'bakerloo':
						strColour = '#ab6612';
						break;
					case 'central':
						strColour = '#df002c';
						break;
					case 'circle':
						strColour = '#f7dc7f';
						break;
					case 'district':
						strColour = '#0d6928';
						break;
					case 'east_london':
						strColour = '#f3ba22';
						break;
					case 'jubilee':
						strColour = '#363b3f';
						break;
					case 'hammersmith_and_city':
						strColour = '#f5a6b3';
						break;
					case 'metropolitan':
						strColour = '#8b004c';
						break;
					case 'northern':
						strColour = '#000000';
						break;
					case 'piccadilly':
						strColour = '#002d73';
						break;
					case 'victoria':
						strColour = '#0076bd';
						break;
					case 'waterloo_and_city':
						strColour = '#89cbc1';
						break;
					default:
						strColour = '#ff0000';
					}
				} else {
					strColour = '#ff0000';
					fltOpacity = 0.4;
				}
				objTubeLine = new google.maps.Polyline({
					path: arrPointObjects,
					strokeColor: strColour,
					strokeWeight: 5,
					strokeOpacity: fltOpacity
				});
				if (boolTubeShowing) {
					objTubeLine.setMap(objMap);
				}
				arrTubeLines.push(objTubeLine);
			}
		}

		// Zoom
		if (arrPosition[0].getAttribute("zoom")) {
			objMap.setZoom(parseFloat(arrPosition[0].getAttribute("zoom")));
			if (boolOSMap && !boolSimple) {
				objOSMap.setCenter(objOSCentrePoint, parseFloat(arrPosition[0].getAttribute("zoom")));
			}
		} else {
			objSW = objBounds.getSouthWest();
			objNE = objBounds.getNorthEast();
			fltHeight = 0.1 * (objNE.lat() - objSW.lat());
			fltWidth = 0.1 * (objNE.lng() - objSW.lng());
			objBounds = new google.maps.LatLngBounds(new google.maps.LatLng(objSW.lat() - fltHeight, objSW.lng() - fltWidth), new google.maps.LatLng(objNE.lat() + fltHeight, objNE.lng() + fltWidth));
			objMap.fitBounds(objBounds);
			if (boolOSMap && !boolSimple) {
				objOSMap.setCenter(objOSBounds.getCenterLonLat(), objOSMap.getZoomForExtent(objOSBounds));
			}
		}

		// Show markers if applicable
		if (boolMarkers) {
			boolMarkerShowing = false;
			toggleMarkers();
		}

		// Remove loading message
		loadingMessage(objLoadingDiv, objLoadingMessage, "");

		// Hide OS map
		$("#mapOS").hide();
	});
}

// Creates display control
function DisplayControl(objContainer, boolPhotos, boolTube) {
	var objButtonDiv, objDropdownArrow, objDropdownDiv;

	objButtonDiv = document.createElement("div");
	setButtonStyle(objButtonDiv);
	setBorderStyle(objContainer);
	objDropdownArrow = document.createElement("small");
	objDropdownArrow.innerHTML = "&#9660;";
	if ($.browser.msie) {
		objDropdownArrow.style.styleFloat = "right";
	} else {
		objDropdownArrow.style.cssFloat = "right";
	}
	objButtonDiv.appendChild(objDropdownArrow);
	objButtonDiv.appendChild(document.createTextNode("Display options"));
	objContainer.appendChild(objButtonDiv);

	objDropdownDiv = document.createElement("div");
	addInput(objDropdownDiv, "Walking route", boolWalkShowing, toggleWalk);
	if (boolTube) { addInput(objDropdownDiv, "Tube line", boolTubeShowing, toggleTube); }
	addInput(objDropdownDiv, "Points of interest", boolMarkerShowing, toggleMarkers);
	if (boolPhotos) { addInput(objDropdownDiv, "Photos", boolPhotoShowing, togglePhotos); }
	addInput(objDropdownDiv, "Labels", boolLabelShowing, toggleLabels);
	setDropdownStyle(objDropdownDiv);
	objContainer.appendChild(objDropdownDiv);

	google.maps.event.addDomListener(objButtonDiv, "click", function () {
		if (boolDisplayShowing) {
			objDropdownDiv.style.display = "none";
			boolDisplayShowing = false;
		} else {
			objDropdownDiv.style.display = "block";
			boolDisplayShowing = true;
		}
	});
	google.maps.event.addDomListener(objButtonDiv, "mouseover", function () {
		objButtonDiv.style.border = "1px solid rgb(103, 138, 199)";
	});
	google.maps.event.addDomListener(objButtonDiv, "mouseout", function () {
		objButtonDiv.style.border = "1px solid rgb(169, 187, 223)";
	});
}

// Styles display control button
function setButtonStyle(objButton) {
	objButton.style.direction = "ltr";
	objButton.style.overflow = "hidden";
	objButton.style.textAlign = "left";
	objButton.style.position = "relative";
	objButton.style.color = "black";
	objButton.style.fontFamily = "Arial,sans-serif";
	objButton.style.fontSize = "12px";
	objButton.style.lineHeight = "160%";
	objButton.style.padding = "0pt 6px";
	objButton.style.border = "1px solid rgb(169, 187, 223)";
	objButton.style.fontWeight = "bold";
	objButton.style.backgroundColor = "white";
	$(objButton).css("box-shadow", "2px 2px 3px rgba(0, 0, 0, 0.35)");

	if ($.browser.mozilla) {
		$(objButton).css("background", "-moz-linear-gradient(center top , rgb(254, 254, 254), rgb(243, 243, 243))");
		$(objButton).css("-moz-user-select", "none");
		$(objButton).css("border-radius", "2px 2px 2px 2px");
		$(objButton).css("box-shadow", "2px 2px 3px rgba(0, 0, 0, 0.35)");
		$(objButton).css("-moz-box-shadow", "2px 2px 3px rgba(0, 0, 0, 0.35)");
	} else if ($.browser.webkit) {
		$(objButton).css("background-image", "-webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(254, 254, 254)), to(rgb(243, 243, 243)))");
		$(objButton).css("-webkit-box-shadow", "rgba(0, 0, 0, 0.347656) 2px 2px 3px");
		$(objButton).css("-webkit-user-select", "none");
	} else if ($.browser.msie) {
		objButton.unselectable = true;
	}
}

// Styles display control border
function setBorderStyle(objButton) {
	objButton.style.margin = "5px";
	objButton.style.zIndex = "11";
	objButton.style.position = "absolute";
	objButton.style.cursor = "pointer";
	objButton.style.textAlign = "left";
	objButton.style.width = "120px";
	objButton.style.right = "0px";
	objButton.style.top = "0px";
}

// Creates display control dropdown
function addInput(dropdown, text, checked, toggleFunction) {
	var objInput, objLabel;

	objInput = document.createElement("input");
	objInput.type = "checkbox";
	objInput.style.verticalAlign = "middle";
	objInput.onclick = toggleFunction;
	objLabel = document.createElement("label");
	objLabel.style.verticalAlign = "middle";
	objLabel.style.cursor = "pointer";
	objLabel.appendChild(objInput);
	if (checked) {
		objInput.checked = true;
	}
	objLabel.appendChild(document.createTextNode(text));
	dropdown.appendChild(objLabel);
	dropdown.appendChild(document.createElement("br"));
}

// Styles display control dropdown
function setDropdownStyle(dropdown) {
	dropdown.style.backgroundColor = "white";
	dropdown.style.zIndex = "-1";
	dropdown.style.paddingTop = "1px";
	dropdown.style.borderWidth = "0pt 1px 1px";
	dropdown.style.borderStyle = "none solid solid";
	dropdown.style.borderColor = "rgb(169, 187, 223)";
	$(dropdown).css("box-shadow", "2px 2px 3px rgba(0, 0, 0, 0.35)");
	if ($.browser.mozilla) {
		$(dropdown).css("-moz-box-shadow", "2px 2px 3px rgba(0, 0, 0, 0.35)");
	}
	if ($.browser.webkit) {
		$(dropdown).css("-webkit-box-shadow", "rgba(0, 0, 0, 0.347656) 2px 2px 3px");
	}
	dropdown.style.position = "relative";
	dropdown.style.textAlign = "left";
	dropdown.style.display = "none";
	dropdown.style.fontSize = "12px";
}

// Creates a station at the given point with the given text label
function createStation(objPoint, strText, strTitle) {
	var objInfoWindow, objLabel, objMarker, objMarkerImage;

	objMarkerImage = new google.maps.MarkerImage(strStationImage,
		new google.maps.Size(intStationWidth, intStationHeight),
		new google.maps.Point(0, 0),
		new google.maps.Point(intStationAnchorX, intStationAnchorY),
		new google.maps.Size(intStationWidth, intStationHeight)
		);

	objMarker = new google.maps.Marker({
		position: objPoint,
		map: objMap,
		icon: objMarkerImage
	});

	objInfoWindow = new google.maps.InfoWindow({
		content: strText
	});

	google.maps.event.addListener(objMarker, "click", function () {
		objInfoWindow.open(objMap, objMarker);
	});

	objLabel = new ELabel(objMap, objPoint, strTitle, "markerTooltip", new google.maps.Size(6, 0), 50);
	objLabel.setMap(objMap);
	arrStationLabels.push(objLabel);
	objLabel.hide();

	google.maps.event.addListener(objMarker, "mouseover", function () {
		objLabel.setOpacity(100);
		objLabel.show();
	});
	google.maps.event.addListener(objMarker, "mouseout", function () {
		if (!boolLabelShowing) {
			objLabel.hide();
		}
		objLabel.setOpacity(50);
	});
}

// Creates a station at the given point on an OS map with the given text label
function createOSStation(objPoint, strText) {
	var objIcon, objInfoWindowAnchor, objMarker, objOffset, objSize;

	objSize = new OpenLayers.Size(12, 20);
	objOffset = new OpenLayers.Pixel(-6, -20);
	objInfoWindowAnchor = new OpenLayers.Pixel(6, 10);
	objIcon = new OpenSpace.Icon('http://labs.google.com/ridefinder/images/mm_20_red.png', objSize, objOffset, null, objInfoWindowAnchor);
	objMarker = objOSMap.createMarker(objPoint, objIcon, strText, new OpenLayers.Size(250, 360));
}

// Creates a marker at the given point with the given text label
function createMarker(objPoint, strText, strTitle) {
	var objInfoWindow, objLabel, objMarker, objMarkerImage;

	objMarkerImage = new google.maps.MarkerImage(strMarkerImage,
		new google.maps.Size(intMarkerWidth, intMarkerHeight),
		new google.maps.Point(0, 0),
		new google.maps.Point(intMarkerAnchorX, intMarkerAnchorY),
		new google.maps.Size(intMarkerWidth, intMarkerHeight)
		);

	objMarker = new google.maps.Marker({
		position: objPoint,
		map: null,
		icon: objMarkerImage
	});

	arrMarkers.push(objMarker);

	objInfoWindow = new google.maps.InfoWindow({
		content: strText
	});

	google.maps.event.addListener(objMarker, "click", function () {
		objInfoWindow.open(objMap, objMarker);
	});

	objLabel = new ELabel(objMap, objPoint, strTitle, "markerTooltip", new google.maps.Size(7, -7), 50);
	objLabel.setMap(objMap);
	arrMarkerLabels.push(objLabel);
	objLabel.hide();
	google.maps.event.addListener(objMarker, "mouseover", function () {
		objLabel.setOpacity(100);
		objLabel.show();
	});
	google.maps.event.addListener(objMarker, "mouseout", function () {
		if (!boolLabelShowing) {
			objLabel.hide();
		}
		objLabel.setOpacity(50);
	});
}

// Creates a marker at the given point on an OS map with the given text label
function createOSMarker(objPoint, strText) {
	var objIcon, objInfoWindowAnchor, objMarker, objOffset, objSize;
	objSize = new OpenLayers.Size(12, 20);
	objOffset = new OpenLayers.Pixel(-6, -20);
	objInfoWindowAnchor = new OpenLayers.Pixel(6, 10);
	objIcon = new OpenSpace.Icon('http://labs.google.com/ridefinder/images/mm_20_blue.png', objSize, objOffset, null, objInfoWindowAnchor);
	objMarker = objOSMap.createMarker(objPoint, objIcon, strText, new OpenLayers.Size(250, 360));
	objMarker.display(false);
	arrOSMarkers.push(objMarker);
}

// Creates a photo at the given point with the given text label
function createPhoto(objPoint, strText) {
	var objInfoWindow, objMarker, objMarkerImage;

	objMarkerImage = new google.maps.MarkerImage(strPhotoImage,
		new google.maps.Size(intPhotoWidth, intPhotoHeight),
		new google.maps.Point(0, 0),
		new google.maps.Point(intPhotoAnchorX, intPhotoAnchorY),
		new google.maps.Size(intPhotoWidth, intPhotoHeight)
		);

	objMarker = new google.maps.Marker({
		position: objPoint,
		map: null,
		icon: objMarkerImage
	});

	arrPhotos.push(objMarker);

	objInfoWindow = new google.maps.InfoWindow({
		content: strText
	});

	google.maps.event.addListener(objMarker, "click", function () {
		objInfoWindow.open(objMap, objMarker);
	});

	// Uncomment to show labels for photos
	//var objLabel = new ELabel(objMap, objPoint, strTitle, "markerTooltip", new google.maps.Size(7, -7), 50);
	//objLabel.setMap(objMap);
	//arrPhotoLabels.push(objLabel);
	//objLabel.hide();
	//
	//google.maps.event.addListener(objMarker,"mouseover", function () {
	//	objLabel.setOpacity(100);
	//	objLabel.show();
	//});
	//google.maps.event.addListener(objMarker,"mouseout", function () {
	//	if (!boolLabelShowing)  {
	//		objLabel.hide();
	//	}
	//	objLabel.setOpacity(50);
	//});
}

// Creates a marker at the given point on an OS map with the given text label
function createOSPhoto(objPoint, strText) {
	var objIcon, objInfoWindowAnchor, objMarker, objOffset, objSize;
	objSize = new OpenLayers.Size(12, 20);
	objOffset = new OpenLayers.Pixel(-6, -20);
	objInfoWindowAnchor = new OpenLayers.Pixel(6, 10);
	objIcon = new OpenSpace.Icon('http://labs.google.com/ridefinder/images/mm_20_white.png', objSize, objOffset, null, objInfoWindowAnchor);
	objMarker = objOSMap.createMarker(objPoint, objIcon, strText, new OpenLayers.Size(250, 360));
	objMarker.display(false);
	arrOSPhotos.push(objMarker);
}

// Zoom map to full screen
function expandMap() {
	var intHeight, intWidth, objCentre;

	objMap.setOptions({zoomControlOptions: {style: google.maps.ZoomControlStyle.DEFAULT}});
	intWidth = 0;
	intHeight = 0;

	if (typeof (window.innerWidth) === 'number') {
		// Non-IE
		intWidth = window.innerWidth;
		intHeight = window.innerHeight;
	} else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
		// IE 6+ in standards compliant mode
		intWidth = document.documentElement.clientWidth;
		intHeight = document.documentElement.clientHeight;
	} else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
		// IE 4 compatible
		intWidth = document.body.clientWidth;
		intHeight = document.body.clientHeight;
	}

	intHeight -= 22;
	intWidth -= 14;

	$('#map').show();
	if (objOSMap) {
		$('#mapOS').show();
	}
	objCentre = objMap.getCenter();
	$('#header').hide();
	$('#mainSearch').hide();
	$('#sharing').hide();
	$('#logo').hide();
	$('#navigation').hide();
	$('#siteNavigation').hide();
	$('#sidebar').hide();
	$('#sidebarAdvert').hide();
	$('#topSlot').hide();
	$('#bottomSlot').hide();
	$('#article p').hide();
	$('#article h2').hide();
	$('#article ul').hide();
	$('#copyrightMessage').hide();
	$('#wrapper').css('margin', '5px auto auto auto');
	$('#wrapper').width(intWidth);
	$('#article').width(intWidth - 12);
	$('#article').css('background', '#fff top no-repeat');
	$('#article').css('margin-left', '0');
	$('#mapDropdown').hide();
	$('#map').css('margin', 0);
	$('#map').width(intWidth - 12);
	$('#map').height(intHeight - 22 - 5);
	$('#mapOS').css('margin', 0);
	$('#mapOS').width(intWidth - 12);
	$('#mapOS').height(intHeight - 22 - 5);
	$('#mapFullScreen').css('margin-top', '0');
	$('a#expandMap').text('Shrink map');
	google.maps.event.trigger(objMap, 'resize');
	if (objOSMap) {
		objOSMap.updateSize();
	}
	if (boolGoogleMapShowing) {
		objMap.panTo(objCentre);
		if (objOSMap) {
			$('#mapOS').hide();
		}
	} else {
		objMap.setCenter(objCentre);
		$('#map').hide();
	}
	boolFullSize = true;
}

// Return map to normal size
function shrinkMap() {
	var objCentre;

	objMap.setOptions({zoomControlOptions: {style: google.maps.ZoomControlStyle.SMALL}});
	$('#map').show();
	if (objOSMap) {
		$('#mapOS').show();
	}
	objCentre = objMap.getCenter();
	$('a#expandMap').text('Full screen');
	$('#mapFullScreen').css('margin-top', '1em');
	$('#mapOS').height(intMapHeight + 'px');
	$('#mapOS').width(intMapWidth + 'px');
	$('#mapOS').css('margin', '0em 0em 1em 0em');
	$('#map').height(intMapHeight + 'px');
	$('#map').width(intMapWidth + 'px');
	$('#map').css('margin', '0em 0em 1em 0em');
	$('#mapDropdown').show();
	$('#article').width('578px');
	$('#article').css('margin-left', '204px');
	$('#article').css('background', 'url(../images/common/sprites/common_horizontal.png) #fff -591px 0px no-repeat');
	$('#wrapper').width('960px');
	$('#wrapper').css('margin', '33px auto 5px auto');
	$('#copyrightMessage').show();
	$('#article ul').show();
	$('#article h2').show();
	$('#article p').show();
	$('#bottomSlot').show();
	$('#topSlot').show();
	$('#sidebarAdvert').show();
	$('#sidebar').show();
	$('#siteNavigation').show();
	$('#navigation').show();
	$('#logo').show();
	$('#sharing').show();
	$('#mainSearch').show();
	$('#header').show();
	google.maps.event.trigger(objMap, 'resize');
	if (objOSMap) {
		objOSMap.updateSize();
	}
	if (boolGoogleMapShowing) {
		objMap.panTo(objCentre);
		if (objOSMap) {
			$('#mapOS').hide();
		}
	} else {
		objMap.setCenter(objCentre);
		$('#map').hide();
	}
	boolFullSize = false;
}

// Update loading message
function loadingMessage(objLoadingDiv, objLoadingMessage, strText) {
	if (strText) {
		objLoadingMessage.nodeValue = strText;
		objLoadingDiv.style.display = "block";
	} else {
		objLoadingMessage.nodeValue = "";
		objLoadingDiv.style.display = "none";
	}
}

// Display walk metadata
function displayMetadata(intStations, intMarkers, intPhotos, fltWalk, fltTube, fltCrow, boolPhotos, boolTube, strXmlFile, strPostfix, boolAddPhotoLink) {
	var strMarkup  = '';
	if (boolAddPhotoLink && strXmlFile !== 'google_maps_data') {
		strMarkup += '<li title="A link to photographs from this walk"><strong>Photos</strong><br /><a href="../photo_library/index.php?route=' + strXmlFile + '&amp;submitted=1' + strPostfix + '">View photos from this walk</a></li>';
		strMarkup += '<li title="A link to station photographs from this walk"><strong>Stations</strong><br /><a href="../photo_library/index.php?stations=1&amp;route=' + strXmlFile + '&amp;submitted=1' + strPostfix + '">View stations on this walk</a></li>';
	}
	strMarkup += '<li><strong>Walking Distance</strong><br />' + fltWalk + ' miles</li>';
	if (boolTube) {
		strMarkup += '<li><strong>Distance by Tube</strong><br />' + fltTube + ' miles</li>';
	}
	strMarkup += '<li><strong>As the Crow Flies</strong><br />' + fltCrow + ' miles</li>';
	if (boolTube) {
		strMarkup += '<li><strong>Tube Stations</strong><br />' + intStations + ' stations</li>';
	} else {
		strMarkup += '<li><strong>Accommodation</strong><br />' + intStations + ' stops</li>';
	}
	strMarkup += '<li><strong>Points of Interest</strong><br />' + intMarkers + ' points</li>';
	if (boolPhotos) {
		strMarkup += '<li><strong>Photographs</strong><br />' + intPhotos + ' photos</li>';
	}
	$('#authorInfo').prepend(strMarkup);
}

// Convert from latitude/longitude to OpenSpace.MapPoint
function latLongOpenSpace(fltLatitude, fltLongitude) {
	var objPoint = objOSGrid.getMapPointFromLonLat(new OpenLayers.LonLat(fltLongitude, fltLatitude));
	return objPoint;
}

// Convert from latitude/longitude to OpenLayers.Geometry.Point
function latLongOpenLayers(fltLatitude, fltLongitude) {
	var objPoint = objOSGrid.getMapPointFromLonLat(new OpenLayers.LonLat(fltLongitude, fltLatitude));
	return new OpenLayers.Geometry.Point(objPoint.lon, objPoint.lat);
}

// Convert from OpenSpace.MapPoint to latitude/longitude
function openSpaceLatLong(objLonLat) {
	var objPoint = objOSGrid.getLonLatFromMapPoint(objLonLat);
	return objPoint;
}

// Download XML file asynchronously and call back
function downloadUrl(strUrl, objCallback) {
	var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
	request.onreadystatechange = function () {
		if (request.readyState === 4) {
			request.onreadystatechange = doNothing;
			objCallback(request, request.status);
		}
	};
	request.open('GET', strUrl, true);
	request.send(null);
}

// Empty function for use in downloadUrl()
function doNothing() {}

// Toggle display of walking route
function toggleWalk() {
	var i;

	if (boolWalkShowing) {
		for (i = 0; i < arrWalkLines.length; i++) {
			arrWalkLines[i].setMap(null);
		}
		boolWalkShowing = false;
	} else {
		for (i = 0; i < arrWalkLines.length; i++) {
			arrWalkLines[i].setMap(objMap);
		}
		boolWalkShowing = true;
	}
}

// Toggle display of Tube line
function toggleTube() {
	var i;

	if (boolTubeShowing) {
		for (i = 0; i < arrTubeLines.length; i++) {
			arrTubeLines[i].setMap(null);
		}
		boolTubeShowing = false;
	} else {
		for (i = 0; i < arrTubeLines.length; i++) {
			arrTubeLines[i].setMap(objMap);
		}
		boolTubeShowing = true;
	}
}

// Toggle display of markers
function toggleMarkers() {
	var i;

	if (boolMarkerShowing) {
		for (i = 0; i < arrMarkers.length; i++) {
			arrMarkers[i].setMap(null);
		}
		for (i = 0; i < arrOSMarkers.length; i++) {
			arrOSMarkers[i].display(false);
		}
		if (boolLabelShowing) {
			for (i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].setMap(null);
			}
		}
		boolMarkerShowing = false;
	} else {
		for (i = 0; i < arrMarkers.length; i++) {
			arrMarkers[i].setMap(objMap);
		}
		for (i = 0; i < arrOSMarkers.length; i++) {
			arrOSMarkers[i].display(true);
		}
		if (boolLabelShowing) {
			for (i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].setMap(objMap);
			}
		}
		boolMarkerShowing = true;
	}
}

// Toggle display of photos
function togglePhotos() {
	var i;

	if (boolPhotoShowing) {
		for (i = 0; i < arrPhotos.length; i++) {
			arrPhotos[i].setMap(null);
		}
		for (i = 0; i < arrOSPhotos.length; i++) {
			arrOSPhotos[i].display(false);
		}
		if (boolLabelShowing) {
			for (i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].setMap(null);
			}
		}
		boolPhotoShowing = false;
	} else {
		for (i = 0; i < arrPhotos.length; i++) {
			arrPhotos[i].setMap(objMap);
		}
		for (i = 0; i < arrOSPhotos.length; i++) {
			arrOSPhotos[i].display(true);
		}
		if (boolLabelShowing) {
			for (i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].setMap(objMap);
			}
		}
		boolPhotoShowing = true;
	}
}

// Toggle display of labels
function toggleLabels() {
	var i;

	if (boolLabelShowing) {
		for (i = 0; i < arrStationLabels.length; i++) {
			arrStationLabels[i].hide();
		}
		if (boolMarkerShowing) {
			for (i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].hide();
			}
		}
		if (boolPhotoShowing) {
			for (i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].hide();
			}
		}
		boolLabelShowing = false;
	} else {
		for (i = 0; i < arrStationLabels.length; i++) {
			arrStationLabels[i].show();
		}
		if (boolMarkerShowing) {
			for (i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].show();
			}
		}
		if (boolPhotoShowing) {
			for (i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].show();
			}
		}
		boolLabelShowing = true;
	}
}

