// My Google map code
// Copyright 2008 David Muse

// detect mobile device
var     ismobile=(navigator.userAgent.indexOf("iPhone")!=-1 ||
			navigator.userAgent.indexOf("Android")!=-1);

function showTrailhead(lat,lng,zoom,thid) {
	var	oldmoveendext=top.map.moveEndExtension;
	top.map.moveEndExtension=function() {
		top.map.popupTrailhead(thid);
		top.map.moveEndExtension=oldmoveendext;
	}
	top.map.reCenterMap(lat,lng,zoom);
}

function showPointOfInterest(lat,lng,zoom,poiid) {
	var	oldmoveendext=top.map.moveEndExtension;
	top.map.moveEndExtension=function() {
		top.map.popupPointOfInterest(poiid);
		top.map.moveEndExtension=oldmoveendext;
	}
	top.map.reCenterMap(lat,lng,zoom);
}

function fitBounds(topleftlat,topleftlng,bottomrightlat,bottomrightlng) {
	if (topleftlat==0 || topleftlng==0 ||
		bottomrightlat==0 || bottomrightlng==0) {
		return;
	}
	var	bottomleft=new google.maps.LatLng(bottomrightlat,topleftlng);
	var	topright=new google.maps.LatLng(topleftlat,bottomrightlng);
	var	zoom=top.map.map.getBoundsZoomLevel(
			new google.maps.LatLngBounds(bottomleft,topright));
	top.map.reCenterMap(
		(topleftlat+bottomrightlat)/2,
		(topleftlng+bottomrightlng)/2,
		zoom);
}



// class to encapsulate a track (trail or route)
function track(map) {
	this.name=null;
	this.color=null;
	this.topleft=0;
	this.bottomright=0;
	this.start=0;
	this.end=0;
	this.label=null;
	this.polylines=new Array();
	this.zooms=new Array();
	this.minzoom=0;
	this.map=map;
}

track.prototype.removeOverlays=function() {
	polylinecount=this.polylines.length;
	for (var i=0; i<polylinecount; i++) {
		this.polylines[i]=null;
	}
	this.polylines.length=0;
	if (this.label) {
		this.map.map.removeOverlay(this.label);
		this.label=null;
	}
}

// class to encapsulate the map itself
function googlemap(elementid,center,zoom,draggablecursor,
			draggingcursor,navigationstartpage,maxzoom) {

	// are controls supposed to be hidden
	var	hidemostcontrols=!navigationstartpage;
	var	staticmap=(top.staticmap=="yes");

	// hack for nested functions
	var	that=this;

	// bail for incompatible browsers
	if (!google.maps.BrowserIsCompatible()) {
		return false;
	}

	// set up shutdown handler
	window.onunload=google.maps.Unload;

	// map types
	this.layers=[];
	this.mapname=[];
	this.maxzoom=maxzoom;
	this.maptype=G_PHYSICAL_MAP;

	// create a new map
	this.map=new google.maps.Map2(document.getElementById(elementid),
				{draggableCursor: draggablecursor,
					draggingCursor: draggingcursor
				});
	if (staticmap) {
		this.map.disableDragging();
	}

	if (!ismobile) {

		// enable smooth zooming and mouse-wheel zooming
		this.map.enableContinuousZoom();
		this.map.enableScrollWheelZoom();

		// add standard controls (except for map type)
		// and drag-zoom control
		if (!staticmap) {
			this.largemapcontrol=new google.maps.LargeMapControl3D();
			this.map.addControl(this.largemapcontrol);
		}
		this.scalecontrol=new google.maps.ScaleControl();
		this.map.addControl(this.scalecontrol);
		if (!hidemostcontrols) {
			this.map.addControl(new declinationcontrol());
			if (!staticmap) {
				this.map.addControl(new coordcontrol(this));
			}
		}

		if (!staticmap) {
			this.dragzoomcontrol=
				new DragZoomControl(
				{opacity: .2, border: "1px solid #aaaaaa"},
				{buttonHTML:
				"<img src=\"/trails/default/images/zoom.png\">",
				buttonStyle: {width: "22px", height: "22px"},
				buttonZoomingHTML:
				"<img src=\"/trails/default/images/zoom-zooming.png\">",
				buttonZoomingStyle: {width: "22px", height: "22px"},
				backButtonHTML:
				"<img src=\"/trails/default/images/zoom-back.png\">",
				backButtonStyle: {width: "22px", height:"22px"},
				backButtonEnabled: true,
				overlayRemoveTime: 0},
				{});
			this.dragzoomposition=
				new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT,
							new google.maps.Size(22,270));
			this.map.addControl(this.dragzoomcontrol,this.dragzoomposition);
		}
	}

	// set the center of the map
	this.map.setCenter(center,zoom);
	this.updateDeclination();

	// create map layers
	this.createMapLayers();

	// add navigation control
	if (!hidemostcontrols) {
		this.navcont=new navigationcontrol(navigationstartpage);
		this.map.addControl(this.navcont);
	}
	if (staticmap) {
		// in this case, we need the control so navigation to the
		// appropriate trail, route, etc. can occur, but we also want
		// to hide it
		document.getElementById("navcont").style.display="none";
	}

	if (!ismobile && !staticmap) {

		// add logo
		if (!hidemostcontrols) {
			this.map.addControl(new logocontrol());
		}

		// add the "show/hide controls" control
		if (!hidemostcontrols) {
			this.shcontrol=new showhidecontrol(this);
			this.map.addControl(this.shcontrol);
		}
	}

	// create arrays for trailheads
	this.thmarkers=new Array();
	this.showths=(ismobile)?false:true;

	// create arrays for points of interest
	this.poimarkers=new Array();
	this.showpois=(ismobile)?false:true;
	this.shownpois="minor";

	// create arrays for trail photos
	this.tpmarkers=new Array();
	this.showtps=(ismobile)?false:true;

	// create arrays for trails
	this.trails=new Array();
	this.currenttrailid=-1;
	this.drawntrailid=-1;
	this.currenttrailpolylineoverlays=new Array();
	this.currenttrailstarticon=null;
	this.currenttrailendicon=null;

	// flag for which trails are shown
	this.showntrails="major";

	// create arrays for routes
	this.routes=new Array();
	this.currentrouteid=-1;
	this.drawnrouteid=-1;
	this.currentroutepolylineoverlays=new Array();
	this.currentroutestarticon=null;
	this.currentrouteendicon=null;

	// start/end markers
	this.starticon=new google.maps.Icon(G_DEFAULT_ICON);
	this.starticon.image="/trails/default/images/starticon.png";
	this.starticon.iconSize=new google.maps.Size(20,34);
	this.endicon=new google.maps.Icon(G_DEFAULT_ICON);
	this.endicon.image="/trails/default/images/endicon.png";
	this.endicon.iconSize=new google.maps.Size(20,34);

	// coord marker and icon
	this.coordmarker=null;
	this.coordicon=new google.maps.Icon();
	this.coordicon.image="/trails/default/images/coordicon.png";
	this.coordicon.shadow="/trails/default/images/coordshadow.png";
	this.coordicon.iconSize=new google.maps.Size(12,20);
	this.coordicon.shadowSize=new google.maps.Size(22,20);
	this.coordicon.iconAnchor=new google.maps.Point(6,20);
	this.coordicon.infoWindowAnchor=new google.maps.Point(5,1);

	// create array for blue coord markers
	this.bcms=new Array();
	this.bcmid=0;

	// driving directions
	this.drivingdirections=null;

	// add an xmlhttprequest to deal with moveend events
	this.moveendrequest=new xmlHttpRequest(
			"/trails/trails.cgi/default/googlemapmoveend.xml");
	this.moveendrequest.xmlHttpRequestCompleteOk=function(data) {
		that.moveEndRequest(data);
	}

	// add an xmlhttprequest to deal with click events
	this.defaultclickrequest=new xmlHttpRequest(
			"/trails/trails.cgi/default/googlemapclick.xml");
	this.defaultclickrequest.xmlHttpRequestCompleteOk=function(data) {
		that.clickRequest(data);
	}
	this.clickrequest=this.defaultclickrequest;
	this.clickedcoords=null;
	this.clickcount=0;

	// add a set of xmlhttprequests to handle cookies
	this.getsessionstate=new xmlHttpRequest(
			"/trails/trails.cgi/default/getsessionstate.xml");
	this.getsessionstate.xmlHttpRequestCompleteOk=function(data) {
		that.getSessionStateRequest(data);
	}
	this.setsessionstate=new xmlHttpRequest(
			"/trails/trails.cgi/default/setsessionstate.xml");
	this.setsessionstate.xmlHttpRequestCompleteOk=function(data) {
		that.setSessionStateRequest(data);
	}

	// get session state (including map layers and overlays)
	this.getSessionState();

	// override session state with URL params
	this.handleUrlParams();

	// update layers and overlays
	this.updateLayersAndOverlays();

	// add the map type control
	if (!staticmap) {
		this.mapcont=new mapcontrol(this);
		this.map.addControl(this.mapcont);
	}
	this.map.setMapType(this.maptype);

	// add listeners
	google.maps.Event.bind(this.map,"moveend",this,this.moveend);
	if (!hidemostcontrols) {
		this.clicklistener=google.maps.Event.bind(this.map,"click",this,this.click);
	}

	// trail/route handler
	this.handleCurrentRouteAndTrail();

	// gpx files
	this.gpxfiles=new Array();
	this.gpxminlat=0;
	this.gpxmaxlat=0;
	this.gpxminlon=0;
	this.gpxmaxlon=0;
	this.gpxtrackstartindex=-1;
	this.gpxtrackendindex=-1;
	this.gpxtrackclicked=false;
	this.gpxtrackmapclickcount=0;

	return true;
}

googlemap.prototype.createMapLayers=function() {

	// hack for nested functions
	var	that=this;

	// base map layer
	this.baselayercc=new google.maps.CopyrightCollection("Data from");
	this.baselayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"Metacarta"));
	this.baselayer=new google.maps.TileLayer(this.baselayercc,1,this.maxzoom);
	this.baselayer.getTileUrl=function(point,zoom) {
		return that.wmsUrl("http://labs.metacarta.com/wms/vmap0",point,zoom,"basic","png","");
	}
	this.baselayershown=false;

	// google road layer
	this.googleroadlayershown=false;

	// openstreetmap road layer
	// http://www.openstreetmap.org/copyright
	this.osmroadlayercc=new google.maps.CopyrightCollection("Data from");
	this.osmroadlayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"OpenStreetMap.org"));
	this.osmroadlayer=new google.maps.TileLayer(this.osmroadlayercc,1,this.maxzoom);
	this.osmroadlayer.getTileUrl=function(point,zoom) {
		return "http://tile.openstreetmap.org/"+zoom+"/"+point.x+"/"+point.y+".png";
	}
	this.osmroadlayer.getOpacity=function() {
		if (that.map.getCurrentMapType().getName().indexOf("Topo")==-1 &&
			that.map.getCurrentMapType().getName().indexOf("Imagery")==-1) {
			return 1.0;
		} else {
			return 0.5;
		}
	}
	this.osmroadlayershown=false;

	// satellite layer
	G_HYBRID_MAP.getTileLayers()[0].getOpacity=function() {
		if (that.map.getCurrentMapType().
				getName().indexOf("Topo")==-1) {
			return 1.0;
		} else {
			return 0.7;
		}
	}
	this.satellitelayershown=false;

	// aerial photo layer
	// http://www.terraserver.com/terms.asp
	this.doqlayercc=new google.maps.CopyrightCollection("Data from");
	this.doqlayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"USGS via Terraserver"));
	this.doqlayer=new google.maps.TileLayer(this.doqlayercc,1,this.maxzoom);
	this.doqlayer.getTileUrl=function(point,zoom) {
		return that.wmsUrl("http://www.terraservice.net/ogcmap6.ashx",point,zoom,"DOQ","jpeg","&STYLES=");
	}
	this.doqlayer.getOpacity=function() {
		if (that.map.getCurrentMapType().
				getName().indexOf("Topo")==-1) {
			return 1.0;
		} else {
			return 0.7;
		}
	}
	this.doqlayershown=false;

	// mytopo map layer
	// http://map-pass.mytopo.com/disclaimer_popup.asp
	this.mytopolayercc=new google.maps.CopyrightCollection("Data from");
	this.mytopolayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"MyTOPO.com"));
	this.mytopolayer=new google.maps.TileLayer(this.mytopolayercc,1,this.maxzoom);
	this.mytopolayer.getTileUrl=function(point,zoom) {
		return "http://maps.mytopo.com/mytopo/tilecache.py/1.0.0/topoG/"+zoom+"/"+point.x+"/"+point.y+".png";
	};
	this.mytopolayershown=false;

	// terrain layer
	this.terrainlayershown=true;

	// topo map layer
	this.usgslayercc=new google.maps.CopyrightCollection("Data from");
	this.usgslayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"USGS via Terraserver"));
	this.usgslayer=new google.maps.TileLayer(this.usgslayercc,1,this.maxzoom);
	this.usgslayer.getTileUrl=function(point,zoom) {
		return that.wmsUrl("http://www.terraservice.net/ogcmap6.ashx",point,zoom,"DRG","jpeg","&STYLES=");
	};
	this.usgslayershown=false;

	// national forest boundaries layer
	this.nfbcc=new google.maps.CopyrightCollection("Data from");
	this.nfbcc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"USFS GIS Data"));
	this.nfboverlay=new google.maps.TileLayerOverlay(
			new google.maps.TileLayer(this.nfbcc,8,this.maxzoom,
		{
			tileUrlTemplate: "/trailsdata/tiles/gis/national_forest_boundaries/tile_{Z}_{X}_{Y}.png",
			isPng: true,
			opacity: 0.175
		})
	);
	this.nfboverlaywasshown=false;
	this.nfboverlayshown=false;

	// roadless areas overlay
	this.roadlesslayercc=new google.maps.CopyrightCollection("Data from");
	this.roadlesslayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"The Wild Wood Studios"));
	this.roadlesslayer=new google.maps.TileLayer(this.roadlesslayercc,1,this.maxzoom);
	this.roadlesslayer.getTileUrl=function(point,zoom) {
		return that.wmsUrl("http://thewildwoodstudios.com/cgi-bin/mapserv",point,zoom,"ira_fill2,ira_line,wild_hatch","gif","&map=../roadlessland/maps/ira.map&TRANSPARENT=TRUE");
	}
	this.roadlesslayer.getOpacity=function() {
		return 0.8;
	}
	this.roadlesslayerwasshown=false;
	this.roadlesslayershown=false;

	// trail overlays
	this.trailscc=new google.maps.CopyrightCollection("");
	this.majortrailsoverlay=new google.maps.TileLayerOverlay(
			new google.maps.TileLayer(this.trailscc,11,this.maxzoom,
		{
			tileUrlTemplate: "/trailsdata/tiles/major/tile_major_{Z}_{X}_{Y}.png",
			isPng: true
		})
	);
	this.map.addOverlay(this.majortrailsoverlay);
	this.majortrailsoverlaywasshown=true;
	this.majortrailsoverlayshown=true;
	this.minortrailsoverlay=new google.maps.TileLayerOverlay(
			new google.maps.TileLayer(this.trailscc,11,this.maxzoom,
		{
			tileUrlTemplate: "/trailsdata/tiles/minor/tile_minor_{Z}_{X}_{Y}.png",
			isPng: true
		})
	);
	this.minortrailsoverlaywasshown=false;
	this.minortrailsoverlayshown=false;
	this.obscuretrailsoverlay=new google.maps.TileLayerOverlay(
			new google.maps.TileLayer(this.trailscc,11,this.maxzoom,
		{
			tileUrlTemplate: "/trailsdata/tiles/obscure/tile_obscure_{Z}_{X}_{Y}.png",
			isPng: true
		})
	);
	this.obscuretrailsoverlaywasshown=false;
	this.obscuretrailsoverlayshown=false;

	// current percipitation overlay
	this.n0rlayercc=new google.maps.CopyrightCollection("Data from");
	this.n0rlayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"Weather Underground"));
	this.n0rlayer=new google.maps.TileLayer(this.n0rlayercc,1,this.maxzoom);
	this.n0rlayer.getTileUrl=function(point,zoom) {
		var	lULP=new google.maps.Point(
					point.x*256,(point.y+1)*256);
		var	lLRP=new google.maps.Point(
					(point.x+1)*256,point.y*256);
		var	lUL=G_NORMAL_MAP.getProjection().
					fromPixelToLatLng(lULP,zoom);
		var	lLR=G_NORMAL_MAP.getProjection().
					fromPixelToLatLng(lLRP,zoom);
		var	size=that.map.getSize();
		return "http://radblast.wunderground.com/cgi-bin/radar/WUNIDS_composite?maxlat="+lLR.y.toString()+"&maxlon="+lLR.x.toString()+"&minlat="+lUL.y.toString()+"&minlon="+lUL.x.toString()+"&type=N0R&frame=0&num=1&delay=25&width="+size.width.toString()+"&height="+size.height.toString()+"&png=1&smooth=1&min=0&noclutter=0&rainsnow=1&nodebug=0&theext=.png&timelabel=0&timelabel.x=0&timelabel.y=15&brand=&rand="+parseInt(10000*Math.random()).toString();
	}
	this.n0rlayer.getOpacity=function() {
		return 0.4;
	}
	this.n0roverlay=new google.maps.TileLayerOverlay(this.n0rlayer);
	this.n0roverlaywasshown=false;
	this.n0roverlayshown=false;

	// recent percipitation overlay
	this.ntplayercc=new google.maps.CopyrightCollection("Data from");
	this.ntplayercc.addCopyright(
			new google.maps.Copyright(0,
				new google.maps.LatLngBounds(
					new google.maps.LatLng(-180,-180),
					new google.maps.LatLng(180,180)),
				0,"Iowa State University"));
	this.ntplayer=new google.maps.TileLayer(this.ntplayercc,1,this.maxzoom);
	this.ntplayer.getTileUrl=function(point,zoom) {
		return that.wmsUrl("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/ntp.cgi",point,zoom,"nexrad-ntp","png","&TRANSPARENT=TRUE");
	}
	this.ntplayer.getOpacity=function() {
		return 0.4;
	}
	this.ntpoverlay=new google.maps.TileLayerOverlay(this.ntplayer);
	this.ntpoverlaywasshown=false;
	this.ntpoverlayshown=false;

	// panoramio layers
	this.panoramiolayer=new google.maps.Layer("com.panoramio.all");
	this.panoramiolayerwasshown=false;
	this.panoramiolayershown=false;

	// youtube layer (doesn't work yet)
	//this.youtubelayer=new google.maps.Layer("lmc:youtube");
	//this.youtubelayerwasshown=false;
	//this.youtubelayershown=false;

	// wikipedia layer
	this.wikipedialayer=new google.maps.Layer("org.wikipedia.en");
	this.wikipedialayerwasshown=false;
	this.wikipedialayershown=false;
}

googlemap.prototype.wmsUrl=function(url,point,zoom,layers,format,suffix) {
	var	lULP=new google.maps.Point(point.x*256,(point.y+1)*256);
	var	lLRP=new google.maps.Point((point.x+1)*256,point.y*256);
	var	lUL=G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP,zoom);
	var	lLR=G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP,zoom);
	return	url+"?VERSION=1.1.1&REQUEST=GetMap&SERVICE=WMS&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&EXCEPTIONS=application/vnd.ogc.se_inimage&LAYERS="+layers+"&FORMAT=image/"+format+"&BBOX="+lUL.x+","+lUL.y+","+lLR.x+","+lLR.y+suffix;
}

googlemap.prototype.reCenterMap=function(lat,lng,zoom) {
	this.map.setCenter(new google.maps.LatLng(lat,lng),zoom);
}

googlemap.prototype.updateMarkers=function() {

	if (ismobile) {
		return;
	}

	var	markersets=[this.thmarkers,this.poimarkers,this.tpmarkers];
	var	showflags=[this.showths,this.showpois,this.showtps];
	var	minzooms=[6,11,11];
	for (var i=0; i<markersets.length; i++) {

		var	markerset=markersets[i];
		var	showflag=showflags[i]
		var	minzoom=minzooms[i];

		var	zoom=this.map.getZoom();
		var	tmp=[];
		for (var id in markerset) {
			var	marker=markerset[id];
			if (marker) {
				if (showflag && this.inBounds(marker.coord) &&
						marker.obscurityMatches() &&
						zoom>=marker.minzoom) {
					marker.show();
					tmp[id]=marker;
				} else {
					if (this.inBounds(marker.coord) &&
								zoom>=minzoom) {
						tmp[id]=marker;
					}
					marker.hide();
				}
			}
		}
		markerset.length=0;
		for (var id in tmp) {
			var	marker=tmp[id];
			if (marker) {
				markerset[id]=marker;
			}
		}
	}
}

googlemap.prototype.updateTracks=function() {

	var	lists=[this.trails,this.routes];

	for (var i=0; i<lists.length; i++) {

		var	list=lists[i];

		var	tmp=[];
		for (var id in list) {
			var	item=list[id];
			if (item) {
				if (this.notVisible(item.topleft,
							item.bottomright) ||
					this.map.getZoom()<item.minzoom) {
					item.removeOverlays();
				} else {
					tmp[id]=item;
				}
			}
		}
		list.length=0;
		for (var id in tmp) {
			var	item=tmp[id];
			if (item) {
				list[id]=item;
			}
		}
	}
}

googlemap.prototype.updateDeclination=function() {
	var	deccont=document.getElementById("deccont");
	if (!deccont) {
		return;
	}
	var	center=this.map.getCenter();
	var	wmm=new WorldMagneticModel();
	// FIXME: get actual year
	var	dec=wmm.declination(0.0,center.lat(),center.lng(),2010.5);
	// round to 2 decimal places
	dec=Math.round(dec*Math.pow(10,2))/Math.pow(10,2);
	//deccont.innerHTML="<span style=\"background: white; border: 1px solid #aaaaaa; padding: 1px;\" title=\"Magnetic Declination\">"+dec+"&deg;</span>";
	deccont.innerHTML="<span title=\"Magnetic Declination\">"+dec+"&deg;</span>";
}

googlemap.prototype.moveEndRequest=function(data) {

	// get the xml response
	var	result=data.responseXML.documentElement.
					getElementsByTagName("result")[0];

	// get new trailheads, points of interest and trail photos
	var	tagnames=["th","poi","tph"];
	for (var i=0; i<tagnames.length; i++) {
		var	elements=result.getElementsByTagName(tagnames[i]);
		for (var j=0; j<elements.length; j++) {
			var	element=elements[j];
			var	fields=element.firstChild.data.split("|");
			var	id=fields[0];
			if (tagnames[i]=="th") {
				var	thm=new trailheadmarker;
				thm.initialize(this,
					new google.maps.LatLng(fields[1],
							fields[2]),id,fields[3],
							fields[4],"major");
				this.thmarkers[id]=thm;
			} else if (tagnames[i]=="poi") {
				var	poim=new poimarker;
				poim.initialize(this,
					new google.maps.LatLng(fields[1],
							fields[2]),id,fields[3],
							fields[4],fields[5]);
				this.poimarkers[id]=poim;
			} else if (tagnames[i]=="tph") {
				var	tpm=new trailphotomarker();
				tpm.initialize(this,
					new google.maps.LatLng(fields[1],
							fields[2]),id,fields[3],
							fields[4],fields[5]);
				this.tpmarkers[id]=tpm;
			}
		}
	}

	// get new trails and routes
	tagnames=["trl","rte"];
	for (var i=0; i<tagnames.length; i++) {
		var	elements=result.getElementsByTagName(tagnames[i]);
		for (var j=0; j<elements.length; j++) {

			var	element=elements[j];

			var	fields=element.getElementsByTagName("data")[0].
						firstChild.data.split("|");

			var	id=fields[0];
			var	trk=new track(this);
			trk.name=fields[1];
			trk.color=fields[2];
			trk.topleft=new google.maps.LatLng(
						fields[3],fields[4]);
			trk.bottomright=new google.maps.LatLng(
						fields[5],fields[6]);
			trk.start=new google.maps.LatLng(fields[7],fields[8]);
			trk.end=new google.maps.LatLng(fields[9],fields[10]);
			trk.minzoom=fields[11];

			var	polylines=element.getElementsByTagName("gepl");
			var	zooms=element.getElementsByTagName("zoom");
			var	pll=polylines.length;
			for (var pi=0; pi<pll; pi++) {

				// get the polyline
				var	pline=polylines[pi].firstChild.data;

				// unescape polyline
				pline=pline.replace(/\\\\/g,"\\");
				trk.polylines[pi]=pline;

				// get the zoom
				trk.zooms[pi]=zooms[pi].firstChild.data;
			}

			if (tagnames[i]=="trl") {
				this.trails[id]=trk;
			} else if (tagnames[i]=="rte") {
				this.routes[id]=trk;
			}
		}
	}

	// show/hide trailheads/pois/photos
	this.updateMarkers();

	// update trails/routes
	this.updateTracks();

	// update declination
	this.updateDeclination();

	this.moveEndExtension();
}

googlemap.prototype.moveEndExtension=function() {
}

googlemap.prototype.click=function(overlay,latlng,overlaylatlng) {

	// ignore doubleclicks...
	// allow it to fire on the first click but not the second
	if (this.clickcount) {
		this.clickcount=0;
		return;
	}
	this.clickcount++;
	setTimeout(function() {
		top.map.clickcount--;
		if (top.map.clickcount<0) {
			top.map.clickcount=0;
		}
	},400);

	// below zoom level 11, everything is really tightly clustered,
	// if we're zoomed out below that, don't do anything
	var	zoom=this.map.getZoom();
	if (zoom<11) {
		return;
	}

	// If overlay was a current trail/route polyline then we want to
	// proceed, but if it was a trailhead or point-of-interest then we
	// want to bail and allow the click handler for the overlay to handle
	// the click.
	if (overlay && !(overlay instanceof google.maps.Polyline)) {
		return;
	}

	// If no trails are currently being shown then don't do anything
	if (this.showntrails=="") {
		return;
	}

	// If no trails are in the frame, then don't do anything
	if (!this.trails.length) {
		return;
	}

	// If the user clicked on the map, then latlng will be defined,
	// if they clicked on a current trail/route polyline drawn on the
	// map then overlaylatlng will be defined.  We need to figure out
	// which to use.
	if (latlng==undefined) {
		latlng=overlaylatlng;
	}

	// get zoom, pixel from the map
	var	gmp=new google.maps.MercatorProjection(18);
	var	point=gmp.fromLatLngToPixel(latlng,zoom);

	// request trail name from the server
	var	params=new Array();
	params["x"]=point.x;
	params["y"]=point.y;
	params["lat"]=latlng.lat();
	params["lng"]=latlng.lng();
	params["zoom"]=zoom;
	params["showntrails"]=this.showntrails;
	var	visibletrails="";
	var	first=true;
	for (var trailid in this.trails) {
		if (first) {
			first=false;
		} else {
			visibletrails=visibletrails+",";
		}
		visibletrails=visibletrails+trailid;
	}
	params["visibletrails"]=visibletrails;
	this.clickedcoords=latlng;
	this.clickrequest.run(params,true);
}

googlemap.prototype.clickRequest=function(data) {

	// get the xml response
	var	result=data.responseXML.documentElement.
					getElementsByTagName("result")[0];

	// process trails
	var	trails=result.getElementsByTagName("trl");
	for (var i=0; i<trails.length; i++) {

		var	data=trails[i].firstChild.data.split("|");
		var	id=data[0];
		var	name=data[1];

		if (this.trails[id]==undefined) {
			continue;
		}

		// toggle trail label
		if (this.trails[id].label==null) {
			var	img=new Image();
			var	that=this;
			img.onload=function() {
				var	icon=new google.maps.Icon(
							G_DEFAULT_ICON);
				icon.image=img.src;
				icon.iconSize=new google.maps.Size(
							img.width,img.height);
				icon.shadow="/trails/default/images/invisible.png";
				icon.shadowSize=
					new google.maps.Size(
						img.width,img.height);
				icon.iconAnchor=
					new google.maps.Point(
						img.width/2,img.height/2);
				that.trails[id].label=
					new google.maps.Marker(
						that.clickedcoords,icon);
	
				google.maps.Event.addListener(
						that.trails[id].label,"click",
					function(point) { var	navframe=top.frames["navframe"]; if (navframe) { navframe.frames["innerframe"].document.location="/trails/trails.cgi/default/trail.html?trail_id="+id; } });
	
				that.map.addOverlay(that.trails[id].label);
			}
			img.src="http://chart.apis.google.com/chart?chst=d_text_outline&chld=000000|12|h|FFFFFF|_|"+escape(name);
			delete img;
		} else {
			this.map.removeOverlay(this.trails[id].label);
			this.trails[id].label=null;
		}
	}
}

// class to encapsulate a waypoint marker
function bluecoordmarker(id) {
	this.label=null;
	this.id=id;
}
bluecoordmarker.prototype=new marker;

bluecoordmarker.prototype.initialize=function(map,coord) {
	marker.prototype.initialize.call(this,map,coord,"","",0,"major");
	this.defaulticon=wpicon;
}

bluecoordmarker.prototype.show=function() {
	marker.prototype.show.call(this);
	var	img=new Image();
	var	that=this;
	img.onload=function() {
		var	icon=new google.maps.Icon(G_DEFAULT_ICON);
		icon.image=img.src;
		icon.iconSize=new google.maps.Size(img.width,img.height);
		icon.shadow="/trails/default/images/invisible.png";
		icon.shadowSize=new google.maps.Size(img.width,img.height);
		icon.iconAnchor=new google.maps.Point(img.width/2,img.height+6);
		that.label=new google.maps.Marker(that.coord,icon);
		that.map.map.addOverlay(that.label);
	}
	var	lat=that.coord.lat();
	var	lng=that.coord.lng();
	lat=Math.round(lat*Math.pow(10,6))/Math.pow(10,6);
	lng=Math.round(lng*Math.pow(10,6))/Math.pow(10,6);
	img.src="http://chart.apis.google.com/chart?chst=d_text_outline&chld=000000|12|h|FFFFFF|_|"+escape(lat+","+lng);
	delete img;
}

bluecoordmarker.prototype.popup=function() {

	// this method handles a click on the marker and usually pops up an
	// info window, but we want the marker to disappear when we click it...

	// remove the coord label
	if (this.label) {
		this.map.map.removeOverlay(this.label);
	}

	// hide the marker
	this.hide();

	// remove the marker from the array in the map class,
	// rebuild the array to get rid of it entirely
	this.map.bcms[this.id]=null;
	var	tmp=[];
	for (var id in this.map.bcms) {
		if (this.map.bcms[id]) {
			tmp[id]=this.map.bcms[id];
		}
	}
	this.map.bcms.length=0;
	for (var id in tmp) {
		var	marker=tmp[id];
		if (marker) {
			this.map.bcms[id]=marker;
		}
	}
}

googlemap.prototype.coords=function(overlay,latlng,overlaylatlng) {
	var	bcm=new bluecoordmarker(this.bcmid);
	bcm.initialize(this,latlng);
	bcm.show();
	this.bcms[this.bcmid++]=bcm;
}

googlemap.prototype.getVisibleCoords=function(coords) {
	var	outcoords=[];
	for (var i=0; i<coords.length; i++) {
		if (this.inBounds(coords[i])) {
			outcoords.push(coords[i]);
		}
	}
	return outcoords;
}

googlemap.prototype.inBounds=function(coord) {

	// get the map bounds
	var	bounds=this.map.getBounds();
	var	swlat=bounds.getSouthWest().lat();
	var	swlng=bounds.getSouthWest().lng();
	var	nelat=bounds.getNorthEast().lat();
	var	nelng=bounds.getNorthEast().lng();

	return (coord.lat()<nelat && coord.lat()>swlat &&
			coord.lng()<nelng && coord.lng()>swlng);
}

googlemap.prototype.notVisible=function(topleft,bottomright) {

	// get the map bounds
	var	bounds=this.map.getBounds();
	var	swlat=bounds.getSouthWest().lat();
	var	swlng=bounds.getSouthWest().lng();
	var	nelat=bounds.getNorthEast().lat();
	var	nelng=bounds.getNorthEast().lng();

	var	nlat=topleft.lat();
	var	slat=bottomright.lat();
	var	elng=bottomright.lng();
	var	wlng=topleft.lng();

	return (nlat<swlat || slat>nelat || elng<swlng || wlng>nelng);
}

googlemap.prototype.drawPolyline=function(polyline,linecolor,
						linewidth,overlays) {

	var	temppolylinecoords=new Array();
	var	i=0;
	for (var pi=0; pi<polyline.length; pi++) {

		temppolylinecoords.push(polyline[pi]);
		i++;

		// don't put more than 200
		// points in each polyline
		if (i==200 || pi==polyline.length-1) {

			// if there was only 1 point in the line,
			// fudge a second point so we can see it
			if (pi==0) {
				var	fakelat=parseFloat(polyline[pi].lat())+
									0.0002;
				var	fakelng=parseFloat(polyline[pi].lng())+
									0.0002;
				var	fakepoint=new google.maps.LatLng(
							fakelat,fakelng);
				temppolylinecoords.push(fakepoint);
			}
	
			// draw the polyline
			var	overlay=new google.maps.Polyline(
							temppolylinecoords,
							"#"+linecolor,
							linewidth);
			this.map.addOverlay(overlay);
			if (overlays!=null) {
				overlays.push(overlay);
			}
			temppolylinecoords=new Array();
			i=0;
		}
	}
}

googlemap.prototype.drawEncodedPolyline=function(polyline,zoom,
						linecolor,linewidth,
						opacity,overlays) {

	var	overlay=new google.maps.Polyline.fromEncoded({
				color: "#"+linecolor,
				weight: linewidth,
				opacity: opacity,
				points: polyline,
				levels: zoom,
				zoomFactor: 2,
				numLevels: 18
				});

	this.map.addOverlay(overlay);
	if (overlays!=null) {
		overlays.push(overlay);
	}
}

googlemap.prototype.popupTrailhead=function(thid) {
	var	thmarker=this.thmarkers[thid];
	if (thmarker) {
		thmarker.popup();
	}
}

googlemap.prototype.popupPointOfInterest=function(poiid) {
	var	poimarker=this.poimarkers[poiid];
	if (poimarker) {
		poimarker.popup();
	}
}

googlemap.prototype.popupTrailphoto=function(tpid) {
	var	tpmarker=this.tpmarkers[tpid];
	if (tpmarker) {
		tpmarker.popup();
	}
}

googlemap.prototype.showCoordMarker=function(lat,lon) {
	this.hideCoordMarker();
	var	coord=new google.maps.LatLng(lat,lon);
	if (this.inBounds(coord)) {
		this.coordmarker=new google.maps.Marker(
					coord,{icon:this.coordicon});
		this.map.addOverlay(this.coordmarker);
	}
}

googlemap.prototype.hideCoordMarker=function() {
	if (this.coordmarker) {
		this.map.removeOverlay(this.coordmarker);
		delete this.coordmarker;
		this.coordmarker=null;
	}
}

googlemap.prototype.moveend=function() {

	var	bounds=this.map.getBounds();
	var	params=new Array();
	params["showntrails"]=this.showntrails;
	params["zoom"]=this.map.getZoom();
	params["swlat"]=bounds.getSouthWest().lat();
	params["swlng"]=bounds.getSouthWest().lng();
	params["nelat"]=bounds.getNorthEast().lat();
	params["nelng"]=bounds.getNorthEast().lng();
	if (this.thmarkers.length) {
		params["trailheadids"]="";
		var	i=0;
		for (var key in this.thmarkers) {
			if (i>0) {
				params["trailheadids"]=
					params["trailheadids"]+",";
			}
			params["trailheadids"]=
					params["trailheadids"]+
					key;
			i++;
		}
	}
	if (this.poimarkers.length) {
		params["poiids"]="";
		var	i=0;
		for (var key in this.poimarkers) {
			if (i>0) {
				params["poiids"]=params["poiids"]+",";
			}
			params["poiids"]=params["poiids"]+key;
			i++;
		}
	}
	if (this.tpmarkers.length) {
		params["trailphotoids"]="";
		var	i=0;
		for (var key in this.tpmarkers) {
			if (i>0) {
				params["trailphotoids"]=
					params["trailphotoids"]+",";
			}
			params["trailphotoids"]=
					params["trailphotoids"]+
					key;
			i++;
		}
	}
	if (this.trails.length) {
		params["trailids"]="";
		var	i=0;
		for (var key in this.trails) {
			if (i>0) {
				params["trailids"]=params["trailids"]+",";
			}
			params["trailids"]=params["trailids"]+key;
			i++;
		}
	}
	if (this.routes.length) {
		params["routeids"]="";
		var	i=0;
		for (var key in this.routes) {
			if (i>0) {
				params["routeids"]=params["routeids"]+",";
			}
			params["routeids"]=params["routeids"]+key;
			i++;
		}
	}
	this.moveendrequest.run(params,true);
}

googlemap.prototype.handleCurrentRouteAndTrail=function() {

	// FIXME: race conditions galore between this and methods that set
	// currenttrailid and currentrouteid

	// if the current id switched, the hide the old trail
	if (this.currenttrailid!=-1 && this.drawntrailid!=-1 &&
				this.currenttrailid!=this.drawntrailid) {
		this.hideCurrentTrail();
	}

	// draw/hide the current trail
	var	currenttrailisdrawn=(this.currenttrailstarticon!=null);
	var	ct=this.trails[this.currenttrailid];
	var	currenttrailshouldbeshown=
			(ct!=undefined && this.map.getZoom()>=ct.minzoom &&
				!this.notVisible(ct.topleft,ct.bottomright));
	if (currenttrailisdrawn) {
		if (!currenttrailshouldbeshown) {
			this.hideCurrentTrail();
		}
	} else {
		if (currenttrailshouldbeshown) {
			this.showCurrentTrail();
		}
	}

	// if the current id switched, the hide the old route
	if (this.currentrouteid!=-1 && this.drawnrouteid!=-1 &&
				this.currentrouteid!=this.drawnrouteid) {
		this.hideCurrentRoute();
	}

	// draw/hide the current route
	var	currentrouteisdrawn=(this.currentroutestarticon!=null);
	var	cr=this.routes[this.currentrouteid];
	var	currentrouteshouldbeshown=
			(cr!=undefined && this.map.getZoom()>=cr.minzoom &&
				!this.notVisible(cr.topleft,cr.bottomright));
	if (currentrouteisdrawn) {
		if (!currentrouteshouldbeshown) {
			this.hideCurrentRoute();
		}
	} else {
		if (currentrouteshouldbeshown) {
			this.showCurrentRoute();
		}
	}

	var	that=this;
	setTimeout(function() {
		that.handleCurrentRouteAndTrail();
	},100);
}

googlemap.prototype.setCurrentTrail=function(trailid) {
	this.currenttrailid=trailid;
}

googlemap.prototype.setNoCurrentTrail=function() {
	this.currenttrailid=-1;
}

googlemap.prototype.showCurrentTrail=function() {
	var	ct=this.trails[this.currenttrailid];
	for (var i=0; i<ct.polylines.length; i++) {
		this.drawEncodedPolyline(
			ct.polylines[i],
			ct.zooms[i],
			"ff6c2f",5,0.4,
			this.currenttrailpolylineoverlays);
	}
	this.currenttrailstarticon=new google.maps.Marker(ct.start,
						{icon:this.starticon});
	this.currenttrailendicon=new google.maps.Marker(ct.end,
						{icon:this.endicon});
	this.map.addOverlay(this.currenttrailstarticon);
	this.map.addOverlay(this.currenttrailendicon);
	this.drawntrailid=this.currenttrailid;
}

googlemap.prototype.hideCurrentTrail=function() {
	var	polylinecount=this.currenttrailpolylineoverlays.length;
	for (var i=0; i<polylinecount; i++) {
		this.map.removeOverlay(
			this.currenttrailpolylineoverlays[i]);
	}
	this.currenttrailpolylineoverlays=new Array();
	if (this.currenttrailstarticon) {
		this.map.removeOverlay(this.currenttrailstarticon);
		this.currenttrailstarticon=null;
	}
	if (this.currenttrailendicon) {
		this.map.removeOverlay(this.currenttrailendicon);
		this.currenttrailendicon=null;
	}
	this.drawntrailid=-1;
}

googlemap.prototype.setCurrentRoute=function(routeid) {
	this.currentrouteid=routeid;
}

googlemap.prototype.setNoCurrentRoute=function() {
	this.currentrouteid=-1;
}

googlemap.prototype.showCurrentRoute=function() {
	var	cr=this.routes[this.currentrouteid];
	for (var i=0; i<cr.polylines.length; i++) {
		this.drawEncodedPolyline(
			cr.polylines[i],
			cr.zooms[i],
			"ff9d03",5,0.4,
			this.currentroutepolylineoverlays);
	}
	this.currentroutestarticon=new google.maps.Marker(cr.start,
						{icon:this.starticon});
	this.currentrouteendicon=new google.maps.Marker(cr.end,
						{icon:this.endicon});
	this.map.addOverlay(this.currentroutestarticon);
	this.map.addOverlay(this.currentrouteendicon);
	this.drawnrotueid=this.currentrouteid;
}

googlemap.prototype.hideCurrentRoute=function() {
	var	polylinecount=this.currentroutepolylineoverlays.length;
	for (var i=0; i<polylinecount; i++) {
		this.map.removeOverlay(
			this.currentroutepolylineoverlays[i]);
	}
	this.currentroutepolylineoverlays=new Array();
	if (this.currentroutestarticon) {
		this.map.removeOverlay(this.currentroutestarticon);
		this.currentroutestarticon=null;
	}
	if (this.currentrouteendicon) {
		this.map.removeOverlay(this.currentrouteendicon);
		this.currentrouteendicon=null;
	}
	this.drawnrotueid=-1;
}

googlemap.prototype.initDrivingDirections=function(div) {
	this.clearDrivingDirections();
	div.innerHTML="";
	this.drivingdirections=new google.maps.Directions(this.map,div);
}

googlemap.prototype.loadDrivingDirections=function(from,to) {
	this.drivingdirections.load("from: "+from+" to: "+to);
}

googlemap.prototype.clearDrivingDirections=function() {
	if (this.drivingdirections!=null) {
		var	polyline=this.drivingdirections.getPolyline();
		if (polyline!=null) {
			this.map.removeOverlay(polyline);
		}
		var	i=0;
		while (true) {
			var	marker=this.drivingdirections.getMarker(i);
			if (marker==null) {
				break;
			}
			this.map.removeOverlay(marker);
			i=i+1;
		}
	}
	this.drivingdirections=null;
}

googlemap.prototype.getShownTrails=function() {
	return this.showntrails;
}

googlemap.prototype.getShownPointsOfInterest=function() {
	return this.shownpois;
}

googlemap.prototype.getSessionState=function() {
	var	params=[];
	var	sessionid=getCookie("sessionid");
	if (sessionid) {
		params["sessionid"]=sessionid;
	}
	this.getsessionstate.run(params,false);
}

googlemap.prototype.getSessionStateRequest=function(data) {

	var	result=data.responseXML.documentElement.
				getElementsByTagName("result")[0];

	// create a session id cookie that will expire 7 days from now
	var	sessionid=result.getElementsByTagName("s")[0].firstChild.data;
	var	d=new Date();
	d.setDate(d.getDate()+7);
	setCookie("sessionid",sessionid,d,null,null);

	var	mo=result.getElementsByTagName("mo")[0].
				firstChild.data.split("|");

	var	topo=mo[0].split(":")[1];
	this.terrainlayershown=(topo=="terrain");
	this.usgslayershown=(topo=="usgs");
	this.mytopolayershown=(topo=="mytopo");

	var	roads=mo[1].split(":")[1];
	this.googleroadlayershown=(roads=="googleroad");
	this.osmroadlayershown=(roads=="osmroad");

	var	imagery=mo[2].split(":")[1];
	this.satellitelayershown=(imagery=="satellite");
	this.doqlayershown=(imagery=="doq");

	var	roadless=mo[3].split(":")[1];
	this.roadlesslayershown=(roadless!="");

	var	trails=mo[4].split(":")[1];
	this.obscuretrailsoverlayshown=(trails=="obscure");
	this.minortrailsoverlayshown=(trails=="obscure" || trails=="minor");
	this.majortrailsoverlayshown=
		(trails=="obscure" || trails=="minor" || trails=="major");

	var	precip=mo[5].split(":")[1];
	this.n0roverlayshown=(precip=="current");
	this.ntpoverlayshown=(precip=="recent");

	var	nfb=mo[6].split(":")[1];
	this.nfboverlayshown=(nfb!="");

	var	th=mo[7].split(":")[1];
	this.showths=(th!="");

	var	poi=mo[8].split(":")[1];
	this.shownpois=poi;
	this.showpois=(poi!="");

	var	tph=mo[9].split(":")[1];
	this.showtps=(tph!="");

	var	panoramio=mo[10].split(":")[1];
	this.panoramiolayershown=(panoramio!="");

	var	wikipedia=mo[11].split(":")[1];
	this.wikipedialayershown=(wikipedia!="");
}

googlemap.prototype.setSessionState=function() {

	var	params=new Array();

	var	sessionid=getCookie("sessionid");
	if (sessionid) {
		params["sessionid"]=sessionid;
	}

	if (this.terrainlayershown) {
		params["topo"]="terrain";
	} else if (this.usgslayershown) {
		params["topo"]="usgs";
	} else if (this.mytopolayershown) {
		params["topo"]="mytopo";
	}

	if (this.googleroadlayershown) {
		params["roads"]="googleroad";
	} else if (this.osmroadlayershown) {
		params["roads"]="osmroad";
	}

	if (this.satellitelayershown) {
		params["imagery"]="satellite";
	} else if (this.doqlayershown) {
		params["imagery"]="doq";
	}

	if (this.roadlesslayershown) {
		params["roadless"]="true";
	}

	if (this.obscuretrailsoverlayshown ||
		this.minortrailsoverlayshown ||
		this.majortrailsoverlayshown) {
		params["trails"]=this.showntrails;
	}

	if (this.n0roverlayshown) {
		params["precip"]="current";
	} else if (this.ntpoverlayshown) {
		params["precip"]="recent";
	}

	if (this.nfboverlayshown) {
		params["nfb"]="true";
	}

	if (this.showths) {
		params["th"]="true";
	}

	if (this.showpois) {
		params["poi"]=this.shownpois;
	}

	if (this.showtps) {
		params["tph"]="true";
	}

	if (this.panoramiolayershown) {
		params["panoramio"]="true";
	}

	if (this.wikipedialayershown) {
		params["wikipedia"]="true";
	}

	this.setsessionstate.run(params,true);
}

googlemap.prototype.setSessionStateRequest=function(data) {
	// does nothing
}

googlemap.prototype.handleUrlParams=function() {

	if (top.topo=="" || top.topo=="terrain" || top.topo=="usgs" || top.topo=="mytopo") {
		this.terrainlayershown=(top.topo=="terrain");
		this.usgslayershown=(top.topo=="usgs");
		this.mytopolayershown=(top.topo=="mytopo");
	}

	if (top.roads=="" || top.roads=="googleroad" || top.roads=="osmroad") {
		this.googleroadlayershown=(top.roads=="googleroad");
		this.osmroadlayershown=(top.roads=="osmroad");
	}

	if (top.imagery=="" || top.imagery=="satellite" || top.imagery=="doq") {
		this.satellitelayershown=(top.imagery=="satellite");
		this.doqlayershown=(top.imagery=="doq");
	}

	if (top.roadless=="" || top.roadless.length && top.roadless!="$(roadless)") {
		this.roadlesslayershown=(top.roadless=="yes");
	}

	if (top.trails=="" || top.trails=="obscure" || top.trails=="minor" || top.trails=="major") {
		this.obscuretrailsoverlayshown=(top.trails=="obscure");
		this.minortrailsoverlayshown=(top.trails=="obscure" || top.trails=="minor");
		this.majortrailsoverlayshown=(top.trails=="obscure" || top.trails=="minor" || top.trails=="major");
	}

	if (top.precip=="" || top.precip=="current" || top.precip=="recent") {
		this.n0roverlayshown=(top.precip=="current");
		this.ntpoverlayshown=(top.precip=="recent");
	}

	if (top.nfb!="$(nfb)") {
		this.nfboverlayshown=(top.nfb=="yes");
	}

	if (top.th!="$(th)") {
		this.showths=(top.th=="yes");
	}

	if (top.poi!="$(poi)") {
		this.shownpois=top.poi;
		this.showpois=(top.poi!="");
	}

	if (top.tph!="$(tph)") {
		this.showtps=(top.tph=="yes");
	}

	if (top.panoramio!="$(panoramio)") {
		this.panoramiolayershown=(top.panoramio=="yes");
	}

	if (top.wikipedia!="$(wikipedia)") {
		this.wikipedialayershown=(top.wikipedia=="yes");
	}

	if (top.latitude!="$(latitude)" && top.longitude!="$(longitude)") {
		this.map.panTo(new google.maps.LatLng(Number(top.latitude),Number(top.longitude)));
	}

	if (top.zoom!="$(zoom)") {
		this.map.setZoom(Number(top.zoom));
	}
}

googlemap.prototype.updateLayersAndOverlays=function() {

	// update map type
	this.layers=[];
	this.mapname="";
	if (this.usgslayershown) {
		this.layers.push(this.usgslayer);
		this.mapname=this.mapname+"Topo ";
	}
	if (this.terrainlayershown) {
		this.layers.push(G_PHYSICAL_MAP.getTileLayers()[0]);
		this.mapname=this.mapname+"Topo ";
	}
	if (this.mytopolayershown) {
		this.layers.push(this.mytopolayer);
		this.mapname=this.mapname+"Topo ";
	}
	if (this.satellitelayershown) {
		this.layers.push(G_HYBRID_MAP.getTileLayers()[0]);
		this.mapname=this.mapname+"Imagery ";
	}
	if (this.doqlayershown) {
		this.layers.push(this.doqlayer);
		this.mapname=this.mapname+"Imagery ";
	}
	if (this.googleroadlayershown) {
		if (this.layers.length) {
			this.layers.push(G_HYBRID_MAP.getTileLayers()[1]);
		} else {
			this.layers.push(G_NORMAL_MAP.getTileLayers()[0]);
		}
		this.mapname=this.mapname+"Road ";
	}
	if (this.osmroadlayershown) {
		this.layers.push(this.osmroadlayer);
		this.mapname=this.mapname+"Road ";
	}
	if (!this.layers.length) {
		this.layers.push(this.baselayer);
		this.mapname=this.mapname+"Base ";
	}
	if (this.roadlesslayershown) {
		this.layers.push(this.roadlesslayer);
		this.mapname=this.mapname+"Roadless ";
	}
	this.map.removeMapType(this.maptype);
	this.maptype=new google.maps.MapType(this.layers,
				G_NORMAL_MAP.getProjection(),this.mapname);
	var	that=this;
	this.maptype.getMaximumResolution=function() {
		return that.maxzoom;
	}
	this.map.addMapType(this.maptype);
	this.map.setMapType(this.maptype);

	// update overlays
	if (!this.obscuretrailsoverlaywasshown) {
		if (this.obscuretrailsoverlayshown) {
			this.map.addOverlay(this.obscuretrailsoverlay);
			this.obscuretrailsoverlaywasshown=true;
		}
	} else {
		if (!this.obscuretrailsoverlayshown) {
			this.map.removeOverlay(this.obscuretrailsoverlay);
			this.obscuretrailsoverlaywasshown=false;
		}
	}
	if (!this.minortrailsoverlaywasshown) {
		if (this.minortrailsoverlayshown) {
			this.map.addOverlay(this.minortrailsoverlay);
			this.minortrailsoverlaywasshown=true;
		}
	} else {
		if (!this.minortrailsoverlayshown) {
			this.map.removeOverlay(this.minortrailsoverlay);
			this.minortrailsoverlaywasshown=false;
		}
	}
	if (!this.majortrailsoverlaywasshown) {
		if (this.majortrailsoverlayshown) {
			this.map.addOverlay(this.majortrailsoverlay);
			this.majortrailsoverlaywasshown=true;
		}
	} else {
		if (!this.majortrailsoverlayshown) {
			this.map.removeOverlay(this.majortrailsoverlay);
			this.majortrailsoverlaywasshown=false;
		}
	}
	if (!this.n0roverlaywasshown) {
		if (this.n0roverlayshown) {
			this.map.addOverlay(this.n0roverlay);
			this.n0roverlaywasshown=true;
		}
	} else {
		if (!this.n0roverlayshown) {
			this.map.removeOverlay(this.n0roverlay);
			this.n0roverlaywasshown=false;
		}
	}
	if (!this.ntpoverlaywasshown) {
		if (this.ntpoverlayshown) {
			this.map.addOverlay(this.ntpoverlay);
			this.ntpoverlaywasshown=true;
		}
	} else {
		if (!this.ntpoverlayshown) {
			this.map.removeOverlay(this.ntpoverlay);
			this.ntpoverlaywasshown=false;
		}
	}
	if (!this.nfboverlaywasshown) {
		if (this.nfboverlayshown) {
			this.map.addOverlay(this.nfboverlay);
			this.nfboverlaywasshown=true;
		}
	} else {
		if (!this.nfboverlayshown) {
			this.map.removeOverlay(this.nfboverlay);
			this.nfboverlaywasshown=false;
		}
	}
	if (!this.panoramiolayerwasshown) {
		if (this.panoramiolayershown) {
			this.map.addOverlay(this.panoramiolayer);
			this.panoramiolayerwasshown=true;
		}
	} else {
		if (!this.panoramiolayershown) {
			this.map.removeOverlay(this.panoramiolayer);
			this.panoramiolayerwasshown=false;
		}
	}
	if (!this.wikipedialayerwasshown) {
		if (this.wikipedialayershown) {
			this.map.addOverlay(this.wikipedialayer);
			this.wikipedialayerwasshown=true;
		}
	} else {
		if (!this.wikipedialayershown) {
			this.map.removeOverlay(this.wikipedialayer);
			this.wikipedialayerwasshown=false;
		}
	}

	// update trailheads, points of interest and trail photos
	this.updateMarkers();

	this.setSessionState();
}

googlemap.prototype.addGpx=function(xml,doc,tracksdiv) {

	var	g=new gpxfile(this.gpxfiles.length,this.map,
						xml,doc,tracksdiv);
	g.show();
	this.gpxfiles.push(g);

	// update bounds containing gpx files
	if (this.gpxminlat==0 || g.minlat<this.gpxminlat) {
		this.gpxminlat=g.minlat;
	}
	if (this.gpxminlon==0 || g.minlon<this.gpxminlon) {
		this.gpxminlon=g.minlon;
	}
	if (this.gpxmaxlat==0 || g.maxlat>this.gpxmaxlat) {
		this.gpxmaxlat=g.maxlat;
	}
	if (this.gpxmaxlon==0 || g.maxlon>this.gpxmaxlon) {
		this.gpxmaxlon=g.maxlon;
	}
	fitBounds(this.gpxmaxlat,this.gpxminlon,
			this.gpxminlat,this.gpxmaxlon);
}

googlemap.prototype.addGpxByUrl=function(url,doc,tracksdiv) {

	var	that=this;

	var	loadgpx=new xmlHttpRequest(
			"/trails/trails.cgi/default/gpx/gpx.html?url="+
			escape(url));
	loadgpx.xmlHttpRequestCompleteOk=function(data) {

		// add gpx file
		var	xml=GXml.parse(data.responseText);
		if (!xml) {
			return;
		}
		that.addGpx(xml.documentElement,doc,tracksdiv);
	}

	loadgpx.run(null,false);
}

googlemap.prototype.clearGpxFiles=function() {
	for (var i=0; i<this.gpxfiles.length; i++) {
		this.gpxfiles[i].hide();
	}
	this.gpxfiles.length=0;
	this.gpxminlat=0;
	this.gpxmaxlat=0;
	this.gpxminlon=0;
	this.gpxmaxlon=0;
}

googlemap.prototype.findGpxFile=function(gpxid) {
	for (var i=0; i<this.gpxfiles.length; i++) {
		var	gpx=this.gpxfiles[i];
		if (gpx.id==gpxid) {
			return gpx;
		}
	}
	return null;
}

googlemap.prototype.findGpxTrack=function(gpxid,trackid) {
	var	gpxfile=this.findGpxFile(gpxid)
	if (gpxfile) {
		for (var i=0; i<gpxfile.trks.length; i++) {
			var	trk=gpxfile.trks[i];
			if (trk.id==trackid) {
				return trk;
			}
		}
	}
	return null;
}

googlemap.prototype.findGpxWayPoint=function(gpxid,wptid) {
	var	gpxfile=this.findGpxFile(gpxid)
	if (gpxfile) {
		for (var i=0; i<gpxfile.wpts.length; i++) {
			var	wpt=gpxfile.wpts[i];
			if (wpt.id==wptid) {
				return wpt;
			}
		}
	}
	return null;
}

googlemap.prototype.getGpxTracks=function() {
	var	data="";
	for (var i=0; i<this.gpxfiles.length; i++) {
		data=data+this.gpxfiles[i].getTracks();
	}
	return data;
}

googlemap.prototype.getGpxWayPoints=function() {
	var	data="";
	for (var i=0; i<this.gpxfiles.length; i++) {
		data=data+this.gpxfiles[i].getWayPoints();
	}
	return data;
}

googlemap.prototype.newGpxTrack=function() {

	var	file=this.gpxfiles[this.gpxfiles.length-1];
	if (!file) {
		return;
	} 

	var	gpx=file.xml.getElementsByTagName("gpx")[0];
	var	newtrk=file.xml.createElement("trk");
	var	newtrkname=file.xml.createElement("name");
	var	newtrknametext=file.xml.createTextNode(this.newTrackName());
	var	newtrkseg=file.xml.createElement("trkseg");
	newtrkname.appendChild(newtrknametext);
	newtrk.appendChild(newtrkname);
	newtrk.appendChild(newtrkseg);
	gpx.appendChild(newtrk);

	var	newgpxtrack=new gpxtrack(file,file.id,file.trks.length,
							file.map,newtrk,
							file.doc,
							file.tracksdiv);
	file.trks.push(newgpxtrack);
	newgpxtrack.show();
}

googlemap.prototype.removeGpxTrack=function(gpxid,trackid) {
	var	gpxfile=this.findGpxFile(gpxid);
	if (gpxfile) {
		gpxfile.removeGpxTrack(trackid);
	}
}

googlemap.prototype.copyGpxTrack=function(gpxid,trackid) {

	var	file=this.findGpxFile(gpxid);
	if (!file) {
		return;
	} 

	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	gpx=file.xml.getElementsByTagName("gpx")[0];
	var	newtrk=file.xml.createElement("trk");
	var	newtrkname=file.xml.createElement("name");
	var	newtrknametext=file.xml.createTextNode(this.newTrackName());
	var	newtrkseg=file.xml.createElement("trkseg");
	newtrkname.appendChild(newtrknametext);
	newtrk.appendChild(newtrkname);
	newtrk.appendChild(newtrkseg);
	gpx.appendChild(newtrk);

	var	trksegs=track.xml.getElementsByTagName("trkseg");
	for (var i=0; i<trksegs.length; i++) {
		var	trkseg=trksegs[i];
		var	trkpts=trkseg.getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length; j++) {
			var	trkpt=trkpts[j];
			newtrkseg.appendChild(trkpt.cloneNode(true));
		}
	}
	track.rebuild();

	var	newgpxtrack=new gpxtrack(file,file.id,file.trks.length,
							file.map,newtrk,
							file.doc,
							file.tracksdiv);
	file.trks.push(newgpxtrack);
	newgpxtrack.show();
}

googlemap.prototype.reverseGpxTrack=function(gpxid,trackid) {
	var	gpxtrk=this.findGpxTrack(gpxid,trackid);
	if (gpxtrk) {
		gpxtrk.reverse();
	}
}

googlemap.prototype.combineGpxTracksPopup=function(gpxid,trackid) {

	var	doc;
	var	name="";
	var	radiohtml="";
	for (var i=0; i<this.gpxfiles.length; i++) {
		var	gpxfile=this.gpxfiles[i];
		doc=gpxfile.doc;
		if (gpxfile.id==gpxid) {
			for (var j=0; j<gpxfile.trks.length; j++) {
				var	trk=gpxfile.trks[j];
				if (gpxfile.id==gpxid && trk.id==trackid) {
					name=trk.name;
				} else {
					radiohtml=radiohtml+"<div onmouseover=\"top.map.addGpxTrackHighlight("+trk.gpxid+","+trk.id+");\" onmouseout=\"top.map.removeGpxTrackHighlight("+trk.gpxid+","+trk.id+");\"><input type=\"radio\" name=\"track\" value=\""+trk.gpxid+","+trk.id+"\"> "+trk.name+"</div>";
				}
			}
		}
	}

	var	html="";
	html=html+"<b>Combine Tracks</b>";
	html=html+"<p>Select which track to prepend or append this track to and click Apply.</p>";
	html=html+"<form onsubmit=\"top.map.hideEditPopup(document); return false;\">";
	html=html+"<input type=\"submit\" name=\"cancel\" value=\"Cancel\">";
	html=html+"</form>";
	html=html+"<hr>";
	html=html+"<form name=\"combine\" onsubmit=\"top.map.combineGpxTracks("+gpxid+","+trackid+",document.combine); top.map.hideEditPopup(document); return false;\">";
	html=html+"<div onmouseover=\"top.map.addGpxTrackHighlight("+gpxid+","+trackid+");\" onmouseout=\"top.map.removeGpxTrackHighlight("+gpxid+","+trackid+");\">";
	html=html+"<input type=\"radio\" name=\"pend\" value=\"prepend\" checked> Prepend<br>";
	html=html+"<input type=\"radio\" name=\"pend\" value=\"append\"> Append<br>";
	html=html+"<img src=\"/trails/default/images/invisible.png\" width=\"10\">to...</div>";
	html=html+radiohtml;
	html=html+"<hr>";
	html=html+"<input type=\"submit\" name=\"submit\" value=\"Apply\">";
	html=html+"</form>";
	html=html+"<form onsubmit=\"top.map.hideEditPopup(document); return false;\">";
	html=html+"<input type=\"submit\" name=\"cancel\" value=\"Cancel\">";
	html=html+"</form>";
	this.showEditPopup(doc,html,"94%");
}

googlemap.prototype.combineGpxTracks=function(firstgpxid,firsttrackid,form) {

	var	firsttrack=this.findGpxTrack(firstgpxid,firsttrackid);
	var	ids=top.getCheckedValue(form.track).split(',');
	var	secondtrack=this.findGpxTrack(ids[0],ids[1]);
	var	pend=top.getCheckedValue(form.pend);
	if (!firsttrack || !secondtrack) {
		return;
	}

	var	primarytrack=null;
	var	secondarytrack=null;
	var	trksegs=null;
	if (pend=="append") {
		primarytrack=firsttrack;
		secondarytrack=secondtrack;
	} else {
		primarytrack=secondtrack;
		secondarytrack=firsttrack;
	}
	trksegs=primarytrack.xml.getElementsByTagName("trkseg");

	for (var i=0; i<trksegs.length; i++) {
		secondarytrack.xml.appendChild(trksegs[i]);
	}
	this.removeGpxTrack(primarytrack.gpxid,primarytrack.id);
	secondarytrack.rebuild();
}

/*googlemap.prototype.splitGpxTrack=function(gpxid,trackid) {

	// get the file and track
	var	file=this.findGpxFile(gpxid);
	if (!file) {
		return;
	} 
	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	html="<b>Split Track</b>";
	html=html+"<p>Click on a vertex to split the track in two at that point</p>";
	this.showEditPopup(track.doc,html,null);

	// clicking on the track splits it
	var	that=this;
	this.enableTrackEditing(file,track,false,
		function(file,track,index) {
			that.resetClickListener();
			that.disableTrackEditing(track);
			that.applySplit(file,track,index);
			that.hideEditPopup(track.doc);
		}
	);

	// clicking on the map aborts
	this.setUpMapClick(null,track,
		function(file,track) {
			track.polyline.disableEditing();
			that.hideEditPopup(track.doc);
		}
	);
}*/

googlemap.prototype.splitGpxTrackPopup=function(gpxid,trackid) {

	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	html="<b>Split Track</b>";
	html=html+"<p>Hover your mouse cursor over a set of coords to see a ";
	html=html+"marker for that point on the map.  Click a ";
	html=html+"&quot;Split Here&quot; link to split the track in two ";
	html=html+"between the points before and after the link.</p>";
	html=html+"<form onsubmit=\"top.map.hideEditPopup(document); return false;\">";
	html=html+"<input type=\"submit\" name=\"cancel\" value=\"Cancel\">";
	html=html+"</form>";
	html=html+"<hr>";
	var	trksegs=track.xml.getElementsByTagName("trkseg");
	var	index=0;
	for (var i=0; i<trksegs.length; i++) {
		var	trkpts=trksegs[i].getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length; j++) {
			var	trkpt=trkpts[j];
			var	lat=trkpt.attributes.getNamedItem("lat").value;
			var	lon=trkpt.attributes.getNamedItem("lon").value;
			html=html+"<div onmouseover=\"top.map.showCoordMarker("+lat+","+lon+");\" onmouseout=\"top.map.hideCoordMarker();\">";
			html=html+lat+" "+lon+"<br>";
			html=html+"</div>";
			if (j<trkpts.length-1 || i<trksegs.length-1) {
				html=html+"<div onmouseover=\"top.map.showCoordMarker("+lat+","+lon+");\" onmouseout=\"top.map.hideCoordMarker();\">";
				html=html+"<a href=\"javascript: top.map.splitGpxTrack("+gpxid+","+trackid+","+index+"); top.map.hideEditPopup(document);\">Split Here</a>";
				html=html+"</div>";
			}
			index++;
		}
	}
	html=html+"<hr>";
	html=html+"<form onsubmit=\"top.map.hideEditPopup(document); return false;\">";
	html=html+"<input type=\"submit\" name=\"cancel\" value=\"Cancel\">";
	html=html+"</form>";

	this.showEditPopup(track.doc,html,"94%");
}

googlemap.prototype.splitGpxTrack=function(gpxid,trackid,index) {

	var	file=this.findGpxFile(gpxid);
	if (!file) {
		return;
	} 

	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	gpx=file.xml.getElementsByTagName("gpx")[0];
	var	newtrk=file.xml.createElement("trk");
	var	newtrkname=file.xml.createElement("name");
	var	newtrknametext=file.xml.createTextNode(this.newTrackName());
	var	newtrkseg=file.xml.createElement("trkseg");
	newtrkname.appendChild(newtrknametext);
	newtrk.appendChild(newtrkname);
	newtrk.appendChild(newtrkseg);
	gpx.appendChild(newtrk);

	var	trksegs=track.xml.getElementsByTagName("trkseg");
	var	searchindex=0;
	for (var i=0; i<trksegs.length;) {
		var	trkseg=trksegs[i];
		if (searchindex>=index) {
			newtrk.appendChild(trkseg);
		} else {
			var	trkpts=trkseg.getElementsByTagName("trkpt");
			for (var j=0; j<trkpts.length;) {
				if (searchindex==index) {
					var	trkpt=trkpts[j];
					newtrkseg.appendChild(trkpt.cloneNode(true));
					j++;
				} else if (searchindex>=index) {
					var	trkpt=trkpts[j];
					newtrkseg.appendChild(trkpt);
				} else {
					j++;
				}
				searchindex++;
			}
			i++;
		}
	}
	track.rebuild();

	// get new track id; the highest id + 1
	var	newid=0;
	for (var i=0; i<file.trks.length; i++) {
		var	trk=file.trks[i];
		if (newid<trk.id) {
			newid=trk.id;
		}
	}
	newid++;

	var	newgpxtrack=new gpxtrack(file,file.id,newid,
						file.map,newtrk,
						file.doc,
						file.tracksdiv);
	file.trks.push(newgpxtrack);
	newgpxtrack.show();
}

googlemap.prototype.enableTrackEditing=function(file,track,allownewvertices,func) {

	// enable editing
	if (allownewvertices) {
		track.polyline.enableEditing();
	} else {
		track.polyline.enableEditing(
			{maxVertices:track.polyline.getVertexCount()});
	}

	// set up a click listener to call the function we passed in
	track.clicklistener=google.maps.Event.bind(track.polyline,"click",this,
		function(latlng,index) {
			this.gpxtrackclicked=true;
			if (typeof index=="number") {
				func(file,track,index);
			}
		}
	);
}

googlemap.prototype.disableTrackEditing=function(track) {
	google.maps.Event.removeListener(track.clicklistener);
	track.polyline.disableEditing();
}

googlemap.prototype.setUpMapClick=function(file,track,func) {
	this.setClickListener(
		function(overlay,latlng,overlaylatlng) {

			// When you click on a line's vertex, the line's
			// click listener and the map's click listener both get
			// called.  The map's listener gets called first for
			// some reason, sometimes more than once...

			// Handle the first invocation of this listener,
			// ignore the others.
			if (this.gpxtrackmapclickcount) {
				this.gpxtrackmapclickcount=0;
				return;
			}
			this.gpxtrackmapclickcount++;

			// Wait a bit...
			var	that=this;
			setTimeout(function() {

				// If the user clicked on the line, then the
				// line's listener should have fired by now and
				// we shouldn't do anything, just reset the
				// flag.  If it didn't fire then the user just
				// clicked on the map and we need to do stuff.
				if (that.gpxtrackclicked) {
					that.gpxtrackclicked=false;
				} else {
					that.resetClickListener();
					that.disableTrackEditing(track);
					func(file,track);
				}

				// while we're at it, reset our click counter
				that.gpxtrackmapclickcount--;
				if (that.gpxtrackmapclickcount<0) {
					that.gpxtrackmapclickcount=0;
				}
			},400);
		}
	);
}

googlemap.prototype.newTrackName=function() {
	for (var index=1; index<1000; index++) {
		var	name="New Track "+index;
		var	found=false;
		for (var i=0; i<this.gpxfiles.length && !found; i++) {
			var	gpxfile=this.gpxfiles[i];
			for (var j=0; j<gpxfile.trks.length && !found; j++) {
				var	trk=gpxfile.trks[j];
				if (trk.name==name) {
					found=true;
				}
			}
		}
		if (!found) {
			return name;
		}
	}
	return "New Track";
}

googlemap.prototype.applySplit=function(file,track,index) {

	var	gpx=file.xml.getElementsByTagName("gpx")[0];
	var	newtrk=file.xml.createElement("trk");
	var	newtrkname=file.xml.createElement("name");
	var	newtrknametext=file.xml.createTextNode(this.newTrackName());
	var	newtrkseg=file.xml.createElement("trkseg");
	newtrkname.appendChild(newtrknametext);
	newtrk.appendChild(newtrkname);
	newtrk.appendChild(newtrkseg);
	gpx.appendChild(newtrk);

	var	trksegs=track.xml.getElementsByTagName("trkseg");
	var	searchindex=0;
	for (var i=0; i<trksegs.length;) {
		var	trkseg=trksegs[i];
		if (searchindex>=index) {
			newtrk.appendChild(trkseg);
		} else {
			var	trkpts=trkseg.getElementsByTagName("trkpt");
			for (var j=0; j<trkpts.length;) {
				if (searchindex==index) {
					newtrkseg.appendChild(trkpts[j].cloneNode(true));
					j++;
				} else if (searchindex>index) {
					newtrkseg.appendChild(trkpts[j]);
				} else {
					j++;
				}
				searchindex++;
			}
			i++;
		}
	}
	track.rebuild();

	// get new track id; the highest id + 1
	var	newid=0;
	for (var i=0; i<file.trks.length; i++) {
		var	trk=file.trks[i];
		if (newid<trk.id) {
			newid=trk.id;
		}
	}
	newid++;

	var	newgpxtrack=new gpxtrack(file,file.id,newid,
						file.map,newtrk,
						file.doc,
						file.tracksdiv);
	file.trks.push(newgpxtrack);
	newgpxtrack.show();
}

googlemap.prototype.trimGpxTrack=function(gpxid,trackid) {

	// get the track
	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	html="<b>Trim Track</b>";
	html=html+"<p>Click two vertices to define a section, then ";
	html=html+"click on the map to remove that section.</p>";
	this.showEditPopup(track.doc,html,null);

	// clicking on the track sets start and end points for the trim
	var	that=this;
	this.enableTrackEditing(null,track,false,
		function(file,track,index) {
			that.setStartAndEndPoints(file,track,index);
		}
	);

	// clicking on the map applies the trim
	this.setUpMapClick(null,track,
		function(file,track) {
			track.removeTrimPolyline();
			that.applyTrim(file,track);
			that.hideEditPopup(track.doc);
		}
	);
}

googlemap.prototype.setStartAndEndPoints=function(file,track,index) {
	track.removeTrimPolyline();
	if (this.gpxtrackstartindex==-1) {
		this.gpxtrackstartindex=index;
		this.gpxtrackendindex=index;
	} else {
		if (index<this.gpxtrackstartindex) {
			this.gpxtrackstartindex=index;
		} else if (index>this.gpxtrackstartindex) {
			this.gpxtrackendindex=index;
		} else if (index==this.gpxtrackstartindex) {
			this.gpxtrackstartindex=-1;
			this.gpxtrackendindex=-1;
		}
	}
	track.addTrimPolyline(this.gpxtrackstartindex,this.gpxtrackendindex);
}

googlemap.prototype.applyTrim=function(file,track) {
	var	trksegs=track.xml.getElementsByTagName("trkseg");
	var	index=0;
	for (var i=0; i<trksegs.length;) {
		var	trkseg=trksegs[i];
		var	trkpts=trkseg.getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length;) {
			if (index>=this.gpxtrackstartindex &&
					index<this.gpxtrackendindex) {
				trkseg.removeChild(trkpts[j]);
			} else {
				j++;
			}
			index++;
		}
		i++;
	}
	track.rebuild();
	this.gpxtrackstartindex=-1;
	this.gpxtrackendindex=-1;
}

googlemap.prototype.smoothGpxTrackPopup=function(gpxid,trackid) {

	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	html="<b>Smooth Track</b>";
	html=html+"<p>Enter a threshold and click Apply to smooth a track.  ";
	html=html+"Each point which is closer to the previous point by ";
	html=html+"fewer feet than the threshold will be removed.</p>";
	html=html+"<form name=\"smoothform\" onsubmit=\"top.map.smoothGpxTrack("+gpxid+","+trackid+",document.smoothform.threshold.value); top.map.hideEditPopup(document); return false;\">";
	html=html+"Threshold (in feet) <input type=\"text\" name=\"threshold\" value=\"50\"><br>";
	html=html+"<input type=\"submit\" name=\"submit\" value=\"Apply\">";
	html=html+"</form>";
	html=html+"<form onsubmit=\"top.map.hideEditPopup(document); return false;\">";
	html=html+"<input type=\"submit\" name=\"cancel\" value=\"Cancel\">";
	html=html+"</form>";

	this.showEditPopup(track.doc,html,null);
}

googlemap.prototype.smoothGpxTrack=function(gpxid,trackid,threshold) {

	var	file=this.findGpxFile(gpxid);
	if (!file) {
		return;
	} 

	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	var	gpx=file.xml.getElementsByTagName("gpx")[0];
	var	trksegs=track.xml.getElementsByTagName("trkseg");
	var	oldlat=-1;
	var	oldlng=-1;
	for (var i=0; i<trksegs.length;) {
		var	trkseg=trksegs[i];
		var	trkpts=trkseg.getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length;) {
			var	trkpt=trkpts[j];
			var	lat=Number(trkpt.attributes.
						getNamedItem("lat").value);
			var	lon=Number(trkpt.attributes.
						getNamedItem("lon").value);
			if (oldlat>-1 && oldlat>-1 &&
				track.distance(oldlat,oldlon,
						lat,lon)<(threshold/5280)) {
				trkseg.removeChild(trkpts[j]);
			} else {
				oldlat=lat;
				oldlon=lon;
				j++;
			}
		}
		i++;
	}
	track.rebuild();
}

googlemap.prototype.editGpxTrack=function(gpxid,trackid) {

	// get the file and track
	var	file=this.findGpxFile(gpxid);
	if (!file) {
		return;
	} 
	var	track=this.findGpxTrack(gpxid,trackid);
	if (!track) {
		return;
	}

	// if the line has no vertices, enable drawing
	if (!track.polyline.getVertexCount()) {

		var	html="<b>Draw New Track</b>";
		html=html+"<p>Click points on the map to draw a new track.  Click on";
		html=html+"last point or double-click on the map to save.</p>";
		this.showEditPopup(track.doc,html,null);

		track.polyline.enableDrawing();
		var	that=this;
		var	endlinelistener=google.maps.Event.bind(track.polyline,"endline",this,
			function() {
				that.hideEditPopup(track.doc);
				google.maps.Event.removeListener(endlinelistener);
			}
		);
		return;
	}

	// if the line has vertices, enable editing
	var	html="<b>Edit Track</b>";
	html=html+"<p>Drag vertices to adjust the path of the track, then ";
	html=html+"click on the map apply your changes.</p>";
	this.showEditPopup(track.doc,html,null);

	// clicking on a vertex deletes it
	this.enableTrackEditing(file,track,true,
		function(file,track,index) {
			track.polyline.deleteVertex(index);
		}
	);

	// clicking on the map applies changes
	var	that=this;
	this.setUpMapClick(file,track,
		function(file,track) {
			that.applyEdit(file,track);
			that.hideEditPopup(track.doc);
		}
	);
}

googlemap.prototype.applyEdit=function(file,track) {

	// remove all existing trkpts
	var	trksegs=track.xml.getElementsByTagName("trkseg");
	for (var i=0; i<trksegs.length; i++) {
		var	trkseg=trksegs[i];
		var	trkpts=trkseg.getElementsByTagName("trkpt");
		while (trkpts.length) {
			trkseg.removeChild(trkpts[0]);
		}
	}

	// remove all but one trkseg
	for (var i=1; i<trksegs.length; i++) {
		track.xml.removeChild(trksegs[i]);
	}

	// create new trkpts vertices to remaining trkseg
	for (var i=0; i<track.polyline.getVertexCount(); i++) {
		var	vertex=track.polyline.getVertex(i);
		var	newtrkpt=file.xml.createElement("trkpt");
		newtrkpt.setAttribute("lat",vertex.lat());
		newtrkpt.setAttribute("lon",vertex.lng());
		trksegs[0].appendChild(newtrkpt);
	}

	// rebuild the track
	track.rebuild();
}

googlemap.prototype.showEditPopup=function(doc,html,height) {
	var	div=doc.getElementById("editpopup");
	div.style.display="inline";
	if (height) {
		div.style.height=height;
	} else {
		div.style.height="";
	}
	div.innerHTML=html;
}

googlemap.prototype.hideEditPopup=function(doc) {
	var	div=doc.getElementById("editpopup");
	div.style.display="none";
	div.innerHTML="";
}

googlemap.prototype.addGpxTrackHighlight=function(gpxid,trackid) {
	var	gpxtrk=this.findGpxTrack(gpxid,trackid);
	if (gpxtrk) {
		gpxtrk.addHighlight();
	}
}

googlemap.prototype.removeGpxTrackHighlight=function(gpxid,trackid) {
	var	gpxtrk=this.findGpxTrack(gpxid,trackid);
	if (gpxtrk) {
		gpxtrk.removeHighlight();
	}
}

function gpxfile(id,map,xml,doc,tracksdiv) {

	this.id=id;
	this.map=map;
	this.xml=xml;
	this.doc=doc;
	this.tracksdiv=tracksdiv;
	this.minlat=0;
	this.maxlat=0;
	this.minlon=0;
	this.maxlon=0;
	this.trks=new Array();
	this.wpts=new Array();

	// process tracks
	var	trks=this.xml.getElementsByTagName("trk");
	for (var i=0; i<trks.length; i++) {
		this.trks.push(new gpxtrack(this,this.id,i,
						this.map,trks[i],
						this.doc,
						this.tracksdiv));
	}

	// process waypoints
	var	wpts=this.xml.getElementsByTagName("wpt");
	for (var i=0; i<wpts.length; i++) {
		var	wpt=new waypointmarker();
		wpt.initialize(this,wpts[i],this,this.id,i);
		this.wpts.push(wpt);
	}
}

gpxfile.prototype.show=function() {
	for (var i=0; i<this.trks.length; i++) {
		this.trks[i].show();
	}
	for (var i=0; i<this.wpts.length; i++) {
		this.wpts[i].show();
	}
}

gpxfile.prototype.hide=function() {
	for (var i=0; i<this.trks.length; i++) {
		this.trks[i].hide();
	}
	for (var i=0; i<this.wpts.length; i++) {
		this.wpts[i].hide();
	}
}

gpxfile.prototype.removeGpxTrack=function(id) {
	var	tmp=[];
	for (var i=0; i<this.trks.length; i++) {
		var	trk=this.trks[i];
		if (trk.id==id) {
			trk.remove();
			this.xml.getElementsByTagName("gpx")[0].
						removeChild(trk.xml);
		} else {
			tmp.push(trk);
		}
	}
	this.trks.length=0;
	for (var i=0; i<tmp.length; i++) {
		var	trk=tmp[i];
		if (trk) {
			this.trks[i]=trk;
		}
	}
}

gpxfile.prototype.getTracks=function() {
	var	data="";
	var	trks=this.xml.getElementsByTagName("trk");
	for (var i=0; i<trks.length; i++) {
		data=data+(new XMLSerializer()).serializeToString(trks[i]);
	}
	return data;
}

gpxfile.prototype.getWayPoints=function() {
	var	data="";
	var	wpts=this.xml.getElementsByTagName("wpt");
	for (var i=0; i<wpts.length; i++) {
		data=data+(new XMLSerializer()).serializeToString(wpts[i]);
	}
	return data;
}

function gpxtrack(gpxfile,gpxid,id,map,xml,doc,tracksdiv) {

	// initialize members
	this.gpxfile=gpxfile;
	this.gpxid=gpxid;
	this.id=id;
	this.map=map;
	this.xml=xml;
	this.doc=doc;
	this.tracksdiv=tracksdiv;
	this.highlighted=false;
	this.clicklistener=null;
	this.dblclicklistener=null;

	this.divid="track-"+this.gpxid+"-"+this.id;
	var	divhtml="<div id=\""+this.divid+"\" "+
			"onmouseover=\"top.map.addGpxTrackHighlight("+
						this.gpxid+","+this.id+");\" "+
			"onmouseout=\"top.map.removeGpxTrackHighlight("+
						this.gpxid+","+this.id+");\">";
			"</div>";
	this.tracksdiv.innerHTML=this.tracksdiv.innerHTML+divhtml;

	this.clear();
	this.build();
}

gpxtrack.prototype.clear=function() {
	this.totaldistance=0;
	this.minelevation=0;
	this.maxelevation=0;
	this.totalclimbing=0;
	this.totaldescending=0;
	this.trackpointcount=0;
	this.name="";
	this.desc="";
}

gpxtrack.prototype.build=function() {

	this.name="Track "+(this.id+1);
	var	nametags=this.xml.getElementsByTagName("name");
	if (nametags) {
		var	nametag=nametags[0];
		if (nametag) {
			this.name=nametag.firstChild.data;
		}
	}

	this.desc="";
	var	desctags=this.xml.getElementsByTagName("desc");
	if (desctags) {
		var	desctag=desctags[0];
		if (desctag) {
			var	firstchild=this.desc=desctag.firstChild;
			if (firstchild) {
				this.desc=firstchild.data;
			}
		}
	}

	// build lat/lng array, get total distance, get min/max elevation,
	// get total climbing and descending, count track points
	var	latlngs=new Array();
	var	trksegs=this.xml.getElementsByTagName("trkseg");
	var	prevlat;
	var	prevlon;
	var	prevele;
	var	first=true;
	for (var i=0; i<trksegs.length; i++) {
		var	trkpts=trksegs[i].getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length; j++) {
			var	trkpt=trkpts[j];
			var	lat=Number(trkpt.attributes.
						getNamedItem("lat").value);
			var	lon=Number(trkpt.attributes.
						getNamedItem("lon").value);
			if (this.gpxfile.minlat==0 || lat<this.gpxfile.minlat) {
				this.gpxfile.minlat=lat;
			}
			if (this.gpxfile.minlon==0 || lon<this.gpxfile.minlon) {
				this.gpxfile.minlon=lon;
			}
			if (this.gpxfile.maxlat==0 || lat>this.gpxfile.maxlat) {
				this.gpxfile.maxlat=lat;
			}
			if (this.gpxfile.maxlon==0 || lon>this.gpxfile.maxlon) {
				this.gpxfile.maxlon=lon;
			}
			latlngs.push(new google.maps.LatLng(lat,lon));
			var	eletags=trkpt.getElementsByTagName("ele");
			if (eletags.length) {
				var	ele=parseFloat(eletags[0].
							firstChild.data);
				if (first || ele<this.minelevation) {
					this.minelevation=ele;
				}
				if (first || ele>this.maxelevation) {
					this.maxelevation=ele;
				}
				if (!first) {
					if (ele>prevele) {
						this.totalclimbing=
							this.totalclimbing+
								(ele-prevele);
					}
					if (ele<prevele) {
						this.totaldescending=
							this.totaldescending+
								(prevele-ele);
					}
				}
				if (first) {
					first=false;
				}
			}
			if (this.trackpointcount>0) {
				this.totaldistance=this.totaldistance+
					this.distance(prevlat,prevlon,lat,lon);
			}
			prevlat=lat;
			prevlon=lon;
			prevele=ele;
			this.trackpointcount++;
		}
	}

	// build polylines
	this.polyline=new google.maps.Polyline(latlngs,"#0000ff",3);
	this.highlightpolyline=new google.maps.Polyline(latlngs,"#ff6c2f",5);
	this.trimpolyline=null;
}

gpxtrack.prototype.rebuild=function() {
	this.hide();
	this.clear();
	this.build();
	this.show();
}

gpxtrack.prototype.show=function() {
	this.map.addOverlay(this.polyline);
	var	trackdiv=this.doc.getElementById(this.divid);
	if (trackdiv) {
		trackdiv.innerHTML=this.buildDescription();
	}
}

gpxtrack.prototype.hide=function() {
	this.removeHighlight();
	this.map.removeOverlay(this.polyline);
	var	trackdiv=this.doc.getElementById(this.divid);
	if (trackdiv) {
		trackdiv.innerHTML="";
	}
}

gpxtrack.prototype.remove=function() {
	this.removeHighlight();
	this.map.removeOverlay(this.polyline);
	var	trackdiv=this.doc.getElementById(this.divid);
	if (trackdiv) {
		this.tracksdiv.removeChild(trackdiv);
	}
}

gpxtrack.prototype.addHighlight=function() {
	if (!this.highlighted) {
		this.map.addOverlay(this.highlightpolyline);
		this.highlighted=true;
	}
}

gpxtrack.prototype.removeHighlight=function() {
	if (this.highlighted) {
		this.map.removeOverlay(this.highlightpolyline);
		this.highlighted=false;
	}
}

gpxtrack.prototype.addTrimPolyline=function(startindex,endindex) {
	if (startindex!=endindex) {
		var	latlngs=new Array();
		for (var i=startindex; i<=endindex; i++) {
			latlngs.push(this.polyline.getVertex(i));
		}
		this.trimpolyline=new google.maps.Polyline(latlngs,"#ff0000",3);
		this.map.addOverlay(this.trimpolyline);
	}
}

gpxtrack.prototype.removeTrimPolyline=function() {
	if (this.trimpolyline) {
		this.map.removeOverlay(this.trimpolyline);
		this.trimpolyline=null;
	}
}


gpxtrack.prototype.reverse=function() {
	var	trksegs=this.xml.getElementsByTagName("trkseg");
	for (var i=0; i<trksegs.length; i++) {
		var	trkseg=trksegs[i];
		var	trkpts=trkseg.getElementsByTagName("trkpt");
		for (var j=trkpts.length-2; j>=0; j--) {
			trkseg.appendChild(trkpts[j]);
		}
	}
	var	gpx=this.xml.getElementsByTagName("gpx")[0];
	for (var i=trksegs.length-2; i>=0; i--) {
		gpx.appendChild(trksegs[i]);
	}
	this.rebuild();
}

gpxtrack.prototype.distance=function(prevlat,prevlon,lat,lon) {
	var	prevlatrad=this.degreesToRadians(prevlat);
	var	prevlonrad=this.degreesToRadians(prevlon);
	var	latrad=this.degreesToRadians(lat);
	var	lonrad=this.degreesToRadians(lon);
	var	retval=Math.acos(Math.cos(prevlatrad)*Math.cos(prevlonrad)*Math.cos(latrad)*Math.cos(lonrad)+Math.cos(prevlatrad)*Math.sin(prevlonrad)*Math.cos(latrad)*Math.sin(lonrad)+Math.sin(prevlatrad)*Math.sin(latrad))*3963.1676;
	if (isNaN(retval)) {
		retval=0;
	}
	return retval;
}

gpxtrack.prototype.degreesToRadians=function(degrees) {
	return degrees*(Math.PI/180);
}

gpxtrack.prototype.elevationProfile=function() {

	// determine the y-axis labels; 1 per 500 feet
	var	ylabels="";
	var	min=Math.floor(this.minelevation/500)*500;
	var	max=(Math.floor(this.maxelevation/500)+1)*500;
	if (min==max) {
		min=max-500;
	}
	for (var i=min; i<=max; i=i+500) {
		ylabels=ylabels+"|"+i;
	}

	// determine the x-axis labels; 1 per 5 miles
	var	xlabels="";
	var	totaldistance=((Math.floor(this.totaldistance)/5)+1)*5;
	for (var i=0; i<totaldistance; i=i+5) {
		xlabels=xlabels+"|"+i;
	}

	// determine increment
	var	increment=1;
	if (this.trackpointcount>99) {
		increment=Math.floor(this.trackpointcount/99);
	}

	// get x and y coords
	var	xcoords="";
	var	ycoords="";
	var	interval=0;
	var	trksegs=this.xml.getElementsByTagName("trkseg");
	var	first=true;
	var	prevlat;
	var	prevlon;
	var	distance=0;
	for (var i=0; i<trksegs.length; i++) {
		var	trkpts=trksegs[i].getElementsByTagName("trkpt");
		for (var j=0; j<trkpts.length; j++) {
			if (interval==increment) {
				var	trkpt=trkpts[j];
				var	lat=trkpt.attributes.
						getNamedItem("lat").value;
				var	lon=trkpt.attributes.
						getNamedItem("lon").value;
				var	ele=0;
				var	eletags=trkpt.
						getElementsByTagName("ele");
				if (eletags.length) {
					ele=parseFloat(eletags[0].
							firstChild.data);
				}
				var	x=Math.floor((distance/
						this.totaldistance)*100);
				var	y=Math.floor(((ele-min)/(max-min))*100);
				if (!first) {
					xcoords=xcoords+",";
					ycoords=ycoords+",";
				} else {
					first=false;
				}
				xcoords=xcoords+x;
				ycoords=ycoords+y;
				interval=0;
				distance=distance+
					this.distance(prevlat,prevlon,lat,lon);
				prevlat=lat;
				prevlon=lon;
			} else {
				interval=interval+1;
			}
		}
	}

	// calculate width/height:
	// 80 pixels per 5 miles of distance (and 51 pixels for words)
	// 30 pixels per 500 feet of elevation (and 24 pixels for words)
	var	width=((Math.floor(this.totaldistance)/5)*80)+51;
	var	height=((max-min)/500*30)+24;

	// build the url
	return "http://chart.apis.google.com/chart?chs="+width+"x"+height+"&cht=lxy&chd=t:"+xcoords+"|"+ycoords+"&chco=0000FF&chm=B,76A4FB,0,0,0&chxt=x,y&chxl=0:"+xlabels+"+mi|1:"+ylabels+"+ft";
}

googlemap.prototype.setGpxTrackAttributes=function(gpxid,trackid,name,desc) {
	var	gpxtrk=this.findGpxTrack(gpxid,trackid);
	gpxtrk.setAttributes(name,desc);
}

gpxtrack.prototype.setAttributes=function(name,desc) {

	this.name=name;
	this.desc=desc;

	top.map.setGpxText(this.gpxfile,this.xml,"name",name);
	top.map.setGpxText(this.gpxfile,this.xml,"desc",desc);
}

googlemap.prototype.buildGpxTrackAttributesDiv=function(gpxid,trackid) {
	var	gpxtrk=this.findGpxTrack(gpxid,trackid);
	return gpxtrk.buildAttributesDiv();
}

gpxtrack.prototype.buildAttributesDiv=function() {

	var	html="<form style=\"margin: 0;\" name=\"tracknameform_"+this.gpxid+"_"+this.id+"\" onsubmit=\"top.map.setGpxTrackAttributes("+this.gpxid+","+this.id+",document.tracknameform_"+this.gpxid+"_"+this.id+".name.value,document.tracknameform_"+this.gpxid+"_"+this.id+".desc.value); document.getElementById('trackname-"+this.gpxid+","+this.id+"').innerHTML=top.map.buildGpxTrackAttributesDiv("+this.gpxid+","+this.id+"); return false;\">";
	html=html+"<div id=\"trackname-static-"+this.gpxid+","+this.id+"\" style=\"display: inline;\">";
	html=html+"<table>";
	html=html+"<tr><td rowspan=\"2\" align=\"right\"><input type=\"button\" name=\"button\" value=\"Edit\" onclick=\"editGpxTrackName("+this.gpxid+","+this.id+");\"></td>";
	html=html+"<td><b>"+this.name+"</b></td></tr>";
	html=html+"<tr><td>"+this.desc+"</td></tr>";
	html=html+"</table>";
	html=html+"</div>";
	html=html+"<div id=\"trackname-dynamic-"+this.gpxid+","+this.id+"\" style=\"display: none;\">";
	html=html+"<table>";
	html=html+"<tr><td rowspan=\"2\" align=\"right\"><input type=\"submit\" name=\"submit\" value=\"Save\"></td>";
	html=html+"<td><input type=\"text\" name=\"name\" value=\""+this.name+"\"></td></tr>";
	html=html+"<tr><td><input type=\"text\" name=\"desc\" value=\""+this.desc+"\"></td></tr>";
	html=html+"</table>";
	html=html+"</div>";
	html=html+"</form>";

	return html;
}

gpxtrack.prototype.buildDescription=function() {

	var	showelevation=(this.minelevation>0 || this.maxelevation>0);

	// display track name
	var	html="<div id=\"trackname-"+this.gpxid+","+this.id+"\">";
	html=html+this.buildAttributesDiv();
	html=html+"</div>";

	// "popup window" for editing
	html=html+"<div id=\"editpopup\" style=\"display: none; position: fixed; top: 3%; left: 3%; width: 94%; border-style: solid; border-width: 1px; border-color: black; background-color: white; padding: 2px; overflow: auto;\"></div>";

	// display elevation profile
	if (showelevation) {
		html=html+"<img src=\""+this.elevationProfile()+"\"><br>";
	}

	// display total distance
	var	td=Math.floor(this.totaldistance*100)/100;
	html=html+"<table><tr><td align=\"right\"><img src=\"/trails/default/images/distance.png\"></td><td align=\"left\">"+td+" mi</td>";

	// display elevation stats
	if (showelevation) {
		var	tc=Math.floor(this.totalclimbing*100)/100;
		var	td=Math.floor(this.totaldescending*100)/100;
		var	net=Math.floor((this.totalclimbing-
					this.totaldescending)*100)/100;
		html=html+"<td align=\"right\"><img src=\"/trails/default/images/climbing.png\"></td><td align=\"left\">"+tc+" ft</td>";
		html=html+"<td align=\"right\"><img src=\"/trails/default/images/descending.png\"></td><td align=\"left\">"+td+" ft</td>";
		html=html+"<td align=\"right\"><img src=\"/trails/default/images/net.png\"></td><td align=\"left\">"+net+" ft</td>";
	}

	html=html+"</tr></table>\n";

	// edit
	html=html+"<img src=\"/trails/default/images/edittrack.png\" onclick=\"top.map.editGpxTrack("+this.gpxid+","+this.id+");\" title=\"Edit Track\">";
	html=html+"<img src=\"/trails/default/images/smoothtrack.png\" onclick=\"top.map.smoothGpxTrackPopup("+this.gpxid+","+this.id+");\" title=\"Smooth Track\">";
	html=html+"<img src=\"/trails/default/images/trimtrack.png\" onclick=\"top.map.trimGpxTrack("+this.gpxid+","+this.id+");\" title=\"Trim Track\">";
	html=html+"<img src=\"/trails/default/images/reversetrack.png\" onclick=\"top.map.reverseGpxTrack("+this.gpxid+","+this.id+");\" title=\"Reverse Track\">";
	html=html+"<img src=\"/trails/default/images/splittrack.png\" onclick=\"top.map.splitGpxTrackPopup("+this.gpxid+","+this.id+");\" title=\"Split Track\">";
	html=html+"<img src=\"/trails/default/images/combinetracks.png\" onclick=\"top.map.combineGpxTracksPopup("+this.gpxid+","+this.id+");\" title=\"Combine Tracks\">";
	html=html+"<img src=\"/trails/default/images/copytrack.png\" onclick=\"top.map.copyGpxTrack("+this.gpxid+","+this.id+");\" title=\"Copy Track\">";
	html=html+"<img src=\"/trails/default/images/removetrack.png\" onclick=\"top.map.removeGpxTrack("+this.gpxid+","+this.id+");\" title=\"Remove Track\">";
	html=html+"\n";

	// finish up
	html=html+"<hr>\n";

	return html;
}

// class to encapsulate a marker
function marker() {
	this.height=70;
	this.html="";
	this.marker=null;
	this.defaulticon=null;
}

marker.prototype.initialize=function(map,coord,id,url,minzoom,obscurity) {
	this.map=map;
	this.coord=coord;
	this.id=id;
	this.url=url;
	if (url!="") {
		this.height=200;
	}
	this.minzoom=minzoom;
	this.obscurity=obscurity;
}

marker.prototype.obscurityMatches=function() {
	return true;
}

marker.prototype.show=function() {
	if (this.marker) {
		return;
	}
	var	that=this;
	if (this.url!="") {
		var	image=new Image();
		image.onload=function() {
			that.icon=new google.maps.Icon();
			that.icon.image="/trailsdata/thumbnails/"+that.url;
			that.icon.shadow=null;
			that.icon.iconSize=
				new google.maps.Size(image.width,
							image.height);
			that.icon.shadowSize=null;
			that.icon.iconAnchor=
				new google.maps.Point(image.width/2,
							image.width/2);
			that.icon.infoWindowAnchor=
				new google.maps.Point(image.width/2,1);
			that.marker=new google.maps.Marker(that.coord,
							{icon:that.icon});
			google.maps.Event.bind(that.marker,"click",
							that,that.popup);
			that.map.map.addOverlay(that.marker);
		}
		image.src="/trailsdata/thumbnails/"+that.url;
	} else {
		this.marker=new google.maps.Marker(this.coord,
						{icon:this.defaulticon});
		google.maps.Event.bind(this.marker,"click",this,this.popup);
		this.map.map.addOverlay(this.marker);
	}
}

marker.prototype.hide=function() {
	if (this.marker) {
		this.map.map.removeOverlay(this.marker);
		this.marker=null;
	}
}

marker.prototype.popup=function() {
	if (this.marker) {
		this.marker.openInfoWindowHtml(this.html);
	}
}

// icon for trailheads
var	thicon=new google.maps.Icon();
thicon.image="/trails/default/images/trailheadicon.png";
thicon.shadow=null;
thicon.iconSize=new google.maps.Size(12,12);
thicon.shadowSize=null;
thicon.iconAnchor=new google.maps.Point(6,6);
thicon.infoWindowAnchor=new google.maps.Point(7,1);

// class to encapsulate a trailhead marker
function trailheadmarker() {}
trailheadmarker.prototype=new marker;

trailheadmarker.prototype.initialize=function(map,coord,id,url,
						minzoom,obscurity) {
	marker.prototype.initialize.call(this,map,coord,id,url,
						minzoom,obscurity);
	this.html="<iframe id=\"trailheadiframe\" style=\"width: 228px; height: "+this.height+"px; border-style: none;\" src=\"/trails/trails.cgi/default/trailheadpopup.html?trailhead_id="+this.id+"\"></iframe>";
	this.defaulticon=thicon;
}

// icon for points of interest
var	poiicon=new google.maps.Icon();
poiicon.image="/trails/default/images/poiicon.png";
poiicon.shadow=null;
poiicon.iconSize=new google.maps.Size(12,12);
poiicon.shadowSize=null;
poiicon.iconAnchor=new google.maps.Point(6,6);
poiicon.infoWindowAnchor=new google.maps.Point(6,1);

// class to encapsulate a point of interest marker
function poimarker() {}
poimarker.prototype=new marker;

poimarker.prototype.initialize=function(map,coord,id,url,
						minzoom,obscurity) {
	marker.prototype.initialize.call(this,map,coord,id,url,
						minzoom,obscurity);
	this.html="<iframe style=\"width: 228px; height: "+this.height+"px; border-style: none;\" src=\"/trails/trails.cgi/default/pointofinterestpopup.html?point_of_interest_id="+this.id+"\"></iframe>";
	this.defaulticon=poiicon;
}

poimarker.prototype.obscurityMatches=function() {
	var	obscurity=this.map.shownpois;
	return (obscurity=="obscure" ||
		(obscurity=="minor" &&
			(this.obscurity=="minor" ||
				this.obscurity=="major")) ||
		(obscurity=="major" && this.obscurity=="major"));
}

// class to encapsulate a trail photo marker
function trailphotomarker() {}
trailphotomarker.prototype=new marker;

trailphotomarker.prototype.initialize=function(map,coord,id,url,
						minzoom,obscurity) {
	marker.prototype.initialize.call(this,map,coord,id,url,
						minzoom,obscurity);
	this.html="<iframe id=\"trailphotoiframe\" style=\"width: 200px; height: 120px; border-style: none;\" src=\"/trails/trails.cgi/default/trailphotopopup.html?photo_id="+this.id+"\"></iframe>";
}

trailphotomarker.prototype.obscurityMatches=function() {
	var	obscurity=this.map.showntrails;
	return (obscurity=="obscure" ||
		(obscurity=="minor" &&
			(this.obscurity=="minor" ||
				this.obscurity=="major")) ||
		(obscurity=="major" && this.obscurity=="major"));
}

// icon for waypoints
var	wpicon=new google.maps.Icon();
wpicon.image="/trails/default/images/waypointicon.png";
wpicon.shadow=null;
wpicon.iconSize=new google.maps.Size(9,9);
wpicon.shadowSize=null;
wpicon.iconAnchor=new google.maps.Point(4,4);
wpicon.infoWindowAnchor=new google.maps.Point(4,1);

// class to encapsulate a waypoint marker
function waypointmarker() {}
waypointmarker.prototype=new marker;

waypointmarker.prototype.initialize=function(map,xml,gpxfile,gpxid,id) {

	this.gpxfile=gpxfile;
	this.gpxid=gpxid;
	this.id=id;
	this.xml=xml;

	this.lat=xml.attributes.getNamedItem("lat").value;
	this.lon=xml.attributes.getNamedItem("lon").value;
	this.name=xml.getElementsByTagName("name")[0].firstChild.data;
	this.cmt=xml.getElementsByTagName("cmt")[0].firstChild.data;
	this.desc=xml.getElementsByTagName("desc")[0].firstChild.data;

	marker.prototype.initialize.call(this,map,
			new google.maps.LatLng(this.lat,this.lon),
			id,"",0,"major");

	this.html="";
	this.innerhtml="";
	this.buildHtml();

	this.defaulticon=wpicon;
}

googlemap.prototype.editWayPoint=function(gpxid,id) {
	document.getElementById("waypoint-static-"+gpxid+","+id).style.display="none";
	document.getElementById("waypoint-dynamic-"+gpxid+","+id).style.display="inline";
}

googlemap.prototype.setGpxWayPointAttributes=function(gpxid,wptid,name,cmt,desc) {
	var	wpt=this.findGpxWayPoint(gpxid,wptid);
	wpt.setAttributes(name,cmt,desc);
}

googlemap.prototype.setGpxText=function(gpxfile,xml,element,value) {
	var	tag=xml.getElementsByTagName(element)[0];
	if (tag) {
		tag.removeChild(tag.childNodes[0]);
		tag.appendChild(gpxfile.xml.createTextNode(value));
	} else {
		tag=gpxfile.xml.createElement(element);
		tag.appendChild(gpxfile.xml.createTextNode(value));
		xml.appendChild(tag);
	}
}

googlemap.prototype.setClickListener=function(listener) {
	if (this.clicklistener) {
		google.maps.Event.removeListener(this.clicklistener);
	}
	this.clicklistener=google.maps.Event.bind(this.map,"click",this,listener);
}

googlemap.prototype.resetClickListener=function() {
	this.setClickListener(this.click);
}

googlemap.prototype.setClickRequest=function(clickrequest) {
	this.clickrequest=clickrequest;
}

googlemap.prototype.resetClickRequest=function() {
	this.clickrequest=this.defaultclickrequest;
}

waypointmarker.prototype.setAttributes=function(name,cmt,desc) {

	this.name=name;
	this.cmt=cmt;
	this.desc=desc;

	top.map.setGpxText(this.gpxfile,this.xml,"name",name);
	top.map.setGpxText(this.gpxfile,this.xml,"cmt",cmt);
	top.map.setGpxText(this.gpxfile,this.xml,"desc",desc);

	this.buildHtml();

	var	div=document.getElementById("waypoint_"+this.gpxid+"_"+this.id);
	if (div) {
		div.innerHTML=this.innerhtml;
	}
}

waypointmarker.prototype.buildHtml=function() {
	this.buildInnerHtml();
	var	html="<div style=\"width: 320px; height: 100px;\" id=\"waypoint_"+this.gpxid+"_"+this.id+"\">";
	html=html+this.innerhtml;
	html=html+"</div>";
	this.html=html;
}

waypointmarker.prototype.buildInnerHtml=function() {
	var	html="<form style=\"margin: 0;\" name=\"waypointform_"+this.gpxid+"_"+this.id+"\" ";
	html=html+"onsubmit=\"top.map.setGpxWayPointAttributes("+this.gpxid+","+this.id+",";
	html=html+"document.waypointform_"+this.gpxid+"_"+this.id+".name.value,";
	html=html+"document.waypointform_"+this.gpxid+"_"+this.id+".cmt.value,";
	html=html+"document.waypointform_"+this.gpxid+"_"+this.id+".desc.value); return false;\">";
	html=html+"<div style=\"display: inline;\" id=\"waypoint-static-"+this.gpxid+","+this.id+"\">";
	html=html+"<b>Name:</b> "+this.name+"<br>";
	html=html+"<b>Comment:</b> "+this.cmt+"<br>";
	html=html+"<b>Description:</b> "+this.desc+"<br>";
	html=html+"<b>Coords:</b> "+this.lat+","+this.lon+"<br>";
	html=html+"<input type=\"button\" name=\"edit\" value=\"Edit\" onclick=\"top.map.editWayPoint("+this.gpxid+","+this.id+");\">";
	html=html+"</div>";
	html=html+"<div style=\"display: none;\" id=\"waypoint-dynamic-"+this.gpxid+","+this.id+"\">";
	html=html+"<b>Name:</b> <input type=\"text\" name=\"name\" value=\""+this.name+"\"><br>";
	html=html+"<b>Comment:</b> <input type=\"text\" name=\"cmt\" value=\""+this.cmt+"\"><br>";
	html=html+"<b>Description:</b> <input type=\"text\" name=\"desc\" value=\""+this.desc+"\"><br>";
	html=html+"<b>Coords:</b> "+this.lat+","+this.lon+"<br>";
	html=html+"<input type=\"submit\" name=\"submit\" value=\"Save\">";
	html=html+"</div>";
	html=html+"</form>";
	this.innerhtml=html;
}

function navigationcontrol(startpage) {
	this.startpage=startpage;
}

navigationcontrol.prototype=new google.maps.Control();

navigationcontrol.prototype.initialize=function(map) {
	var	navcont=document.createElement("div");
	navcont.id="navcont";
	this.defaultheight="84%";
	this.defaultwidth="460px";
	if (ismobile) {
		this.defaultwidth="95%";
	}
	navcont.style.height=this.defaultheight;
	navcont.style.width=this.defaultwidth;
	navcont.style.display="block";
	navcont.innerHTML=
"<div onmouseup=\"toggleNavVisibility();\" style=\"width: 100px; color: black; background-color: white; padding: 1px; border: 1px solid #aaaaaa; cursor: pointer;\" id=\"navtoggle\"><center><b>Navigation</b></center></div>"+
"<iframe style=\"position: absolute; top: 19px;\" id=\"navframe\" name=\"navframe\" src=\""+this.startpage+"\"></iframe>";
	map.getContainer().appendChild(navcont);
	var	navframe=document.getElementById("navframe");
	if (navigator.appName=="Microsoft Internet Explorer") {
		navframe.style.filter="alpha(opacity=80)";
	} else {
		navframe.style.opacity="0.8";
	}
	return navcont;
}

navigationcontrol.prototype.getDefaultPosition=function() {
	var	left=100;
	if (ismobile) {
		left=7;
	}
	return new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT,
						new google.maps.Size(left,7));
}

var	navclickcount=0;
function toggleNavVisibility() {

	// ignore doubleclicks...
	// allow it to fire on the first click but not the second
	if (navclickcount) {
		navclickcount=0;
		return;
	}
	navclickcount++;
	setTimeout(function() {
		navclickcount--;
		if (navclickcount<0) {
			navclickcount=0;
		}
	},400);

	var	navcont=document.getElementById("navcont");
	var	navtoggle=document.getElementById("navtoggle");
	var	navframe=document.getElementById("navframe");
	if (navframe.style.display=="none") {
		navframe.style.display="block";
		navcont.style.height=top.map.navcont.defaultheight;
		navcont.style.width=top.map.navcont.defaultwidth;
	} else {
		navframe.style.display="none";
		navcont.style.height=navtoggle.style.height;
		navcont.style.width=navtoggle.style.width;
	}
}

function coordcontrol(map) {
	this.map=map;
	this.set=false;
}

coordcontrol.prototype=new google.maps.Control();

coordcontrol.prototype.initialize=function(map) {
	this.coordcont=document.createElement("div");
	this.coordcont.id="coordcont";
	this.coordcont.style.width="22px";
	this.coordcont.style.height="22px";
	this.coordcont.style.padding="2px";
	this.coordcont.style.border="1px solid black";
	this.coordcont.style.cursor="pointer";
	var	img=document.createElement("img");
	img.src="/trails/default/images/coords.png";
	var	that=this;
	img.onclick=function() {
		if (!that.set) {
			var	dragobject=that.map.map.getDragObject()
			dragobject.setDraggableCursor("crosshair");
			dragobject.setDraggingCursor("crosshair");
			that.coordcont.style.border="1px solid blue";
			that.map.setClickListener(that.map.coords);
			that.set=true;
		} else {
			var	dragobject=that.map.map.getDragObject()
			dragobject.setDraggableCursor("default");
			dragobject.setDraggingCursor("default");
			that.coordcont.style.border="1px solid black";
			that.map.resetClickListener();
			that.set=false;
		}
	}
	this.coordcont.appendChild(img);
	map.getContainer().appendChild(this.coordcont);
	return this.coordcont;
}

coordcontrol.prototype.getDefaultPosition=function() {
	return new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT,
						new google.maps.Size(22,326));
}

function declinationcontrol() {}
declinationcontrol.prototype=new google.maps.Control();

declinationcontrol.prototype.initialize=function(map) {
	var	declinationcont=document.createElement("div");
	declinationcont.id="deccont";
	map.getContainer().appendChild(declinationcont);
	return declinationcont;
}

declinationcontrol.prototype.getDefaultPosition=function() {
	return new google.maps.ControlPosition(G_ANCHOR_BOTTOM_LEFT,
						new google.maps.Size(200,10));
}

function logocontrol() {}
logocontrol.prototype=new google.maps.Control();

logocontrol.prototype.initialize=function(map) {
	var	logo=document.createElement("div");
	logo.id="logocont";
	logo.innerHTML="<img src=\"/trails/default/images/logo.png\">";
	map.getContainer().appendChild(logo);
	return logo;
}

logocontrol.prototype.getDefaultPosition=function() {
	return new google.maps.ControlPosition(G_ANCHOR_BOTTOM_LEFT,
						new google.maps.Size(262,11));
}

function showhidecontrol(googlemap) {
	this.googlemap=googlemap;
}

showhidecontrol.prototype=new google.maps.Control();

showhidecontrol.prototype.initialize=function(map) {
	var showhidediv=document.createElement("div");
	showhidediv.id="showhidediv";
	showhidediv.style.width="110px";
	showhidediv.style.color="black";
	showhidediv.style.backgroundColor="white";
	showhidediv.style.padding="1px";
	showhidediv.style.border="1px solid #aaaaaa";
	showhidediv.style.cursor="pointer";
	var	that=this;
	google.maps.Event.addDomListener(showhidediv,"click",function() {
		var	navcontdiv=document.getElementById("navcont");
		var	logocontdiv=document.getElementById("logocont");
		var	mapcontroldiv=document.getElementById("mapcont");
		var	coordcontroldiv=document.getElementById("coordcont");
		if (navcontdiv.style.display=="none") {
			navcontdiv.style.display="block";
			logocontdiv.style.display="block";
			mapcontroldiv.style.display="block";
			coordcontroldiv.style.display="block";
			showhidediv.innerHTML="<center><b>Hide Controls</b></center>";
			that.googlemap.map.addControl(
				that.googlemap.largemapcontrol);
			that.googlemap.map.addControl(
				that.googlemap.dragzoomcontrol,
				that.googlemap.dragzoomposition);
		} else {
			navcontdiv.style.display="none";
			logocontdiv.style.display="none";
			mapcontroldiv.style.display="none";
			coordcontroldiv.style.display="none";
			showhidediv.innerHTML="<center><b>Show Controls</b></center>";
			that.googlemap.map.removeControl(
				that.googlemap.largemapcontrol);
			that.googlemap.map.removeControl(
				that.googlemap.dragzoomcontrol);
		}
	});
	showhidediv.innerHTML="<center><b>Hide Controls</b></center>";
	map.getContainer().appendChild(showhidediv);
	return showhidediv;
}

showhidecontrol.prototype.getDefaultPosition=function() {
	return new google.maps.ControlPosition(G_ANCHOR_BOTTOM_LEFT,
						new google.maps.Size(500,14));
}

function mapcontrol(googlemap) {
	this.googlemap=googlemap;
}

mapcontrol.prototype=new google.maps.Control();

mapcontrol.prototype.initialize=function(map) {
	var	mapcont=document.createElement("div");
	mapcont.id="mapcont";
	this.defaultheight="360px";
	this.defaultwidth="330px";
	mapcont.style.height="19px";
	mapcont.style.width="70px";
	mapcont.innerHTML=
"<div onmouseup=\"toggleMapControlVisibility();\" style=\"width: 70px; position: absolute; color: black; background-color: white; padding: 1px; border: 1px solid #aaaaaa; cursor: pointer;\" id=\"maptoggle\"><center><b>Map</b></center></div>"+
"<iframe style=\"display: none; position: absolute; top: 19px; left: 2px;\" id=\"mapframe\" name=\"mapframe\" src=\"/trails/default/mapoptions.html\"></iframe>";
	map.getContainer().appendChild(mapcont);
	return mapcont;
}

mapcontrol.prototype.getDefaultPosition=function() {
	return new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT,
						new google.maps.Size(9,7));
}

var	mapclickcount=0;
function toggleMapControlVisibility() {

	// ignore doubleclicks...
	// allow it to fire on the first click but not the second
	if (mapclickcount) {
		mapclickcount=0;
		return;
	}
	mapclickcount++;
	setTimeout(function() {
		mapclickcount--;
		if (mapclickcount<0) {
			mapclickcount=0;
		}
	},400);

	var	mapcont=document.getElementById("mapcont");
	var	maptoggle=document.getElementById("maptoggle");
	var	mapframe=document.getElementById("mapframe");
	if (mapframe.style.display=="none") {
		mapframe.style.display="block";
		mapcont.style.height=top.map.mapcont.defaultheight;
		mapcont.style.width=top.map.mapcont.defaultwidth;
		maptoggle.style.left="260px";
	} else {
		mapframe.style.display="none";
		mapcont.style.height=maptoggle.style.height;
		mapcont.style.width=maptoggle.style.width;
		maptoggle.style.left="0px";
	}
}

function updateMapConfig(mapconfig) {

	// determine whether topo is required and what kind
	top.map.terrainlayershown=false;
	top.map.usgslayershown=false;
	top.map.mytopolayershown=false;
	if (mapconfig.topo.checked) {
		var	toporadio=mapconfig.toporadio;
		for (var i=0; i<toporadio.length; i++) {
			if (toporadio[i].checked) {
				if (toporadio[i].value=="usgs") {
					top.map.usgslayershown=true;
				} else if (toporadio[i].value=="terrain") {
					top.map.terrainlayershown=true;
				} else if (toporadio[i].value=="mytopo") {
					top.map.mytopolayershown=true;
				}
			}
		}
	}

	// determine whether roads are required
	top.map.googleroadlayershown=false;
	top.map.osmroadlayershown=false;
	if (mapconfig.roads.checked) {
		var	roadradio=mapconfig.roadradio;
		for (var i=0; i<roadradio.length; i++) {
			if (roadradio[i].checked) {
				if (roadradio[i].value=="googleroad") {
					top.map.googleroadlayershown=true;
				} else if (roadradio[i].value=="osmroad") {
					top.map.osmroadlayershown=true;
				}
			}
		}
	}

	// determine whether imagery is required and what kind
	top.map.satellitelayershown=false;
	top.map.doqlayershown=false;
	if (mapconfig.imagery.checked) {
		var	imageryradio=mapconfig.imageryradio;
		for (var i=0; i<imageryradio.length; i++) {
			if (imageryradio[i].checked) {
				if (imageryradio[i].value=="satellite") {
					top.map.satellitelayershown=true;
				} else if (imageryradio[i].value=="doq") {
					top.map.doqlayershown=true;
				}
			}
		}
	}

	// determine if roadless areas are required
	top.map.roadlesslayershown=mapconfig.roadless.checked;

	// add/remove appropriate trail overlays
	var	majortrails=false;
	var	minortrails=false;
	var	obscuretrails=false;
	if (mapconfig.trailscheck.checked) {
		var	trailsradio=mapconfig.trailsradio;
		for (var i=0; i<trailsradio.length; i++) {
			if (trailsradio[i].checked) {
				majortrails=(trailsradio[i].value=="major");
				minortrails=(trailsradio[i].value=="minor");
				obscuretrails=(trailsradio[i].value=="obscure");
			}
		}
	}
	top.map.showntrails="";
	top.map.obscuretrailsoverlayshown=false;
	top.map.minortrailsoverlayshown=false;
	top.map.majortrailsoverlayshown=false;
	if (obscuretrails) {
		top.map.showntrails="obscure";
		top.map.obscuretrailsoverlayshown=true;
		top.map.minortrailsoverlayshown=true;
		top.map.majortrailsoverlayshown=true;
	} else if (minortrails) {
		top.map.showntrails="minor";
		top.map.minortrailsoverlayshown=true;
		top.map.majortrailsoverlayshown=true;
	} else if (majortrails) {
		top.map.showntrails="major";
		top.map.majortrailsoverlayshown=true;
	}

	// add/remove current/recent percipitation
	top.map.n0roverlayshown=false;
	top.map.ntpoverlayshown=false;
	if (mapconfig.precipcheck.checked) {
		var	precipradio=mapconfig.precipradio;
		for (var i=0; i<precipradio.length; i++) {
			if (precipradio[i].checked) {
				top.map.n0roverlayshown=
					(precipradio[i].value=="current");
				top.map.ntpoverlayshown=
					(precipradio[i].value=="recent");
			}
		}
	}

	// add/remove national forest boundaries
	top.map.nfboverlayshown=mapconfig.nfb.checked;

	// add/remove trailheads
	top.map.showths=mapconfig.trailheads.checked;

	// add/remove points of interest
	var	majorpois=false;
	var	minorpois=false;
	var	obscurepois=false;
	if (mapconfig.poischeck.checked) {
		var	poisradio=mapconfig.poisradio;
		for (var i=0; i<poisradio.length; i++) {
			if (poisradio[i].checked) {
				majorpois=(poisradio[i].value=="major");
				minorpois=(poisradio[i].value=="minor");
				obscurepois=(poisradio[i].value=="obscure");
			}
		}
	}
	if (obscurepois) {
		top.map.shownpois="obscure";
	} else if (minorpois) {
		top.map.shownpois="minor";
	} else if (majorpois) {
		top.map.shownpois="major";
	} else {
		top.map.shownpois="";
	}
	top.map.showpois=mapconfig.poischeck.checked;

	// add/remove photo layers
	top.map.showtps=mapconfig.trailphotos.checked;
	top.map.panoramiolayershown=mapconfig.panoramio.checked;

	// add/remove wikipedia layer
	top.map.wikipedialayershown=mapconfig.wikipedia.checked;

	// update the map
	top.map.updateLayersAndOverlays();
}
