// Copyright 2008 David Muse

function googlemap(elementid,

			center,
			zoom,
			maxzoom,

			staticmap,

			draggablecursor,
			draggingcursor,

			navigationstartpage,

			mapmoveendurl,
			mapclickurl,
			trailnameclickfunction,

			getsessionstateurl,
			setsessionstateurl,

			dragzoombuttonimage,
			dragzoomzoomingimage,
			dragzoombackimage,

			coordcontrolimage,

			logoimage,

			trailstarticonimage,
			trailstarticonimagewidth,
			trailstarticonimageheight,
			trailstarticonimageanchorx,
			trailstarticonimageanchory,
			trailendiconimage,
			trailendiconimagewidth,
			trailendiconimageheight,
			trailendiconimageanchorx,
			trailendiconimageanchory,

			coordiconimage,
			coordiconimagewidth,
			coordiconimageheight,
			coordiconimageanchorx,
			coordiconimageanchory,

			bluecoordiconimage,
			bluecoordiconimagewidth,
			bluecoordiconimageheight,
			bluecoordiconimageanchorx,
			bluecoordiconimageanchory,
			bluecoordiconimageinfowindowanchorx,
			bluecoordiconimageinfowindowanchory,

			trailheadiconimage,
			trailheadiconimagewidth,
			trailheadiconimageheight,
			trailheadiconimageanchorx,
			trailheadiconimageanchory,
			trailheadiconimageinfowindowanchorx,
			trailheadiconimageinfowindowanchory,
			trailheadpopupurl,

			poiiconimage,
			poiiconimagewidth,
			poiiconimageheight,
			poiiconimageanchorx,
			poiiconimageanchory,
			poiiconimageinfowindowanchorx,
			poiiconimageinfowindowanchory,
			poipopupurl,

			trailphotopopupurl,

			mapcontrolurl,
			mapcontrolwidth,
			mapcontrolheight

			) {

	// are controls supposed to be hidden
	var	hidemostcontrols=!navigationstartpage;

	// 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();
	}

	// 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.coordcont=new coordcontrol(this,coordcontrolimage);
			this.map.addControl(this.coordcont);
		}
	}

	if (!staticmap) {
		this.dragzoomcontrol=
			new DragZoomControl(
			{opacity: .2, border: "1px solid #aaaaaa"},
			{buttonHTML:
			"<img src=\""+dragzoombuttonimage+"\">",
			buttonStyle: {width: "22px", height: "22px"},
			buttonZoomingHTML:
			"<img src=\""+dragzoomzoomingimage+"\">",
			buttonZoomingStyle: {width: "22px", height: "22px"},
			backButtonHTML:
			"<img src=\""+dragzoombackimage+"\">",
			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 (!staticmap) {

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

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

	// trailhead markers and icon
	this.thmarkers=new Array();
	this.showths=true;
	this.thicon=new google.maps.Icon();
	this.thicon.image=trailheadiconimage,
	this.thicon.iconSize=new google.maps.Size(
					trailheadiconimagewidth,
					trailheadiconimageheight);
	this.thicon.iconAnchor=new google.maps.Point(
					trailheadiconimageanchorx,
					trailheadiconimageanchory);
	this.thicon.infoWindowAnchor=new google.maps.Point(
					trailheadiconimageinfowindowanchorx,
					trailheadiconimageinfowindowanchory);
	this.thpopupurl=trailheadpopupurl;

	// points of interest markers and icon
	this.poimarkers=new Array();
	this.showpois=true;
	this.shownpois="minor";
	this.poiicon=new google.maps.Icon();
	this.poiicon.image=poiiconimage;
	this.poiicon.iconSize=new google.maps.Size(
					poiiconimagewidth,
					poiiconimageheight);
	this.poiicon.iconAnchor=new google.maps.Point(
					poiiconimageanchorx,
					poiiconimageanchory);
	this.poiicon.infoWindowAnchor=new google.maps.Point(
					poiiconimageinfowindowanchorx,
					poiiconimageinfowindowanchory);
	this.poipopupurl=poipopupurl;

	// create arrays for trail photos
	this.tpmarkers=new Array();
	this.showtps=true;
	this.tppopupurl=trailphotopopupurl;

	// 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 marker icons
	this.starticon=new google.maps.Icon();
	this.starticon.image=trailstarticonimage;
	this.starticon.iconSize=new google.maps.Size(
					trailstarticonimagewidth,
					trailstarticonimageheight);
	this.starticon.iconAnchor=new google.maps.Point(
					trailstarticonimageanchorx,
					trailstarticonimageanchory);
	this.endicon=new google.maps.Icon();
	this.endicon.image=trailendiconimage;
	this.endicon.iconSize=new google.maps.Size(
					trailendiconimagewidth,
					trailendiconimageheight);
	this.endicon.iconAnchor=new google.maps.Point(
					trailendiconimageanchorx,
					trailendiconimageanchory);

	// coord marker and icon
	this.coordmarker=null;
	this.coordicon=new google.maps.Icon();
	this.coordicon.image=coordiconimage,
	this.coordicon.iconSize=new google.maps.Size(
					coordiconimagewidth,
					coordiconimageheight);
	this.coordicon.iconAnchor=new google.maps.Point(
					coordiconimageanchorx,
					coordiconimageanchory);

	// blue coord markers and icon
	this.bcms=new Array();
	this.bcmid=0;
	this.bluecoordicon=new google.maps.Icon();
	this.bluecoordicon.image=bluecoordiconimage,
	this.bluecoordicon.iconSize=new google.maps.Size(
					bluecoordiconimagewidth,
					bluecoordiconimageheight);
	this.bluecoordicon.iconAnchor=new google.maps.Point(
					bluecoordiconimageanchorx,
					bluecoordiconimageanchory);
	this.bluecoordicon.infoWindowAnchor=new google.maps.Point(
					bluecoordiconimageinfowindowanchorx,
					bluecoordiconimageinfowindowanchory);

	// add an xmlhttprequest to deal with moveend events
	this.moveendrequest=new xmlHttpRequest(mapmoveendurl);
	this.moveendrequest.xmlHttpRequestCompleteOk=function(data) {
		that.moveEndRequest(data);
	}

	// add an xmlhttprequest to deal with click events
	this.defaultclickrequest=new xmlHttpRequest(mapclickurl);
	this.defaultclickrequest.xmlHttpRequestCompleteOk=function(data) {
		that.clickRequest(data);
	}
	this.trailnameclickfunction=trailnameclickfunction;
	this.clickrequest=this.defaultclickrequest;
	this.clickedcoords=null;
	this.clickcount=0;

	// add a set of xmlhttprequests to handle cookies
	this.getsessionstate=new xmlHttpRequest(getsessionstateurl);
	this.getsessionstate.xmlHttpRequestCompleteOk=function(data) {
		that.getSessionStateRequest(data);
	}
	this.setsessionstate=new xmlHttpRequest(setsessionstateurl);
	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(mapcontrolurl,
						mapcontrolwidth,
						mapcontrolheight);
		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();

	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.mapname.indexOf("Topo")==-1 &&
			that.mapname.indexOf("Imagery")==-1) {
			return 1.0;
		} else {
			return 0.5;
		}
	}
	this.osmroadlayershown=false;

	// satellite layer
	G_HYBRID_MAP.getTileLayers()[0].getOpacity=function() {
		if (that.mapname.indexOf("Topo")==-1) {
			return 1.0;
		} else {
			return 0.5;
		}
	}
	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://terraserver-usa.com/ogcmap6.ashx",point,zoom,"DOQ","jpeg","&STYLES=");
	}
	this.doqlayer.getOpacity=function() {
		if (that.mapname.indexOf("Topo")==-1) {
			return 1.0;
		} else {
			return 0.5;
		}
	}
	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/firstworks/tilecache.py/1.0.0/topoG/"+zoom+"/"+point.x+"/"+point.y+".jpg";
	};
	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://terraserver-usa.com/ogcmap6.ashx",point,zoom,"DRG","jpeg","&STYLES=");
	};
	this.usgslayer.getOpacity=function() {
		return 0.5;
	}
	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"));
	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;

	// wilderness boundaries layer
	this.wbcc=new google.maps.CopyrightCollection("Data from");
	this.wbcc.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"));
	this.wboverlay=new google.maps.TileLayerOverlay(
			new google.maps.TileLayer(this.wbcc,8,this.maxzoom,
		{
			tileUrlTemplate: "/trailsdata/tiles/gis/wilderness_boundaries/tile_{Z}_{X}_{Y}.png",
			isPng: true,
			opacity: 0.175
		})
	);
	this.wboverlaywasshown=false;
	this.wboverlayshown=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;

	// 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.fitBounds=function(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=this.map.getBoundsZoomLevel(
			new google.maps.LatLngBounds(bottomleft,topright));
	this.reCenterMap(
		(topleftlat+bottomrightlat)/2,
		(topleftlng+bottomrightlng)/2,
		zoom);
}

googlemap.prototype.printableMap=function(topleftlat,topleftlng,
					bottomrightlat,bottomrightlng,
					trailsystemid) {

	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	bounds=new google.maps.LatLngBounds(bottomleft,topright);
	var	lat=(topleftlat+bottomrightlat)/2;
	var	lng=(topleftlng+bottomrightlng)/2;
	
	var	width=800;
	var	height=600;
	var	zoom=0;
	var	scalediv=document.getElementById("scalediv");
	var	scalemap=new google.maps.Map2(scalediv);
	scalemap.setCenter(this.map.getCenter(),this.map.getZoom());
	scalemap.addMapType(G_PHYSICAL_MAP);
	scalemap.setMapType(G_PHYSICAL_MAP);
	scalemap.setUIToDefault();
	for (;;) {
		scalediv.style.height=height+"px";
		scalediv.style.width=width+"px";
		scalediv.style.display="block";
		scalemap.checkResize();
		zoom=scalemap.getBoundsZoomLevel(bounds);
		if (zoom>=15) {
			break;
		}
		width=width+200;
		height=height+200;
	}
	if (zoom>17) {
		zoom=17;
	}
	scalediv.style.height="200px";
	scalediv.style.width="200px";
	scalediv.style.display="none";
	delete scalemap;
	window.open("/trails/trails.cgi/default/index.html?staticmap=yes&summary=yes&width="+width+"&height="+height+"&trail_system_id="+trailsystemid+"&zoom="+zoom,"Printable Map");
}

googlemap.prototype.showTrailhead=function(lat,lng,zoom,thid) {
	var	oldmoveendext=this.moveEndExtension;
	this.moveEndExtension=function() {
		this.popupTrailhead(thid);
		this.moveEndExtension=oldmoveendext;
	}
	this.reCenterMap(lat,lng,zoom);
}

googlemap.prototype.showPointOfInterest=function(lat,lng,zoom,poiid) {
	var	oldmoveendext=this.moveEndExtension;
	this.moveEndExtension=function() {
		this.popupPointOfInterest(poiid);
		this.moveEndExtension=oldmoveendext;
	}
	this.reCenterMap(lat,lng,zoom);
}

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

googlemap.prototype.updateMarkers=function() {

	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();
	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 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(
							this.thicon,
							this.thpopupurl);
				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(
							this.poiicon,
							this.poipopupurl);
				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(
							this.tppopupurl);
				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++;
	var	that=this;
	setTimeout(function() {
		that.clickcount--;
		if (that.clickcount<0) {
			that.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();
				icon.image=img.src;
				icon.iconSize=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);
	
				if (that.trailnameclickfunction) {
					google.maps.Event.addListener(
						that.trails[id].label,"click",
						function(point) {
							that.trailnameclickfunction(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;
		}
	}
}

googlemap.prototype.coords=function(overlay,latlng,overlaylatlng) {
	var	bcm=new bluecoordmarker(this.bluecoordicon,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.animateTrail=function(id) {
	this.animate(this.trails[id]);
}

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.animateRoute=function(id) {
	this.animate(this.routes[id]);
}

googlemap.prototype.animate=function(thing) {

	if (!thing || !thing.polylines) {
		return;
	}

	var	vertices=new Array();
	for (var p=0; p<thing.polylines.length; p++) {
		var	overlay=new google.maps.Polyline.fromEncoded({
						points: thing.polylines[p],
						levels: thing.zooms[p],
						zoomFactor: 2,
						numLevels: 18
						});
		for (var v=0; v<overlay.getVertexCount(); v++) {
			vertices.push(overlay.getVertex(v));
		}
	}

	var	that=this;
	var	v=0;
	var	vmax=vertices.length-1;
	var	z=this.map.getZoom();
	var	inc=1;
	for (var y=19; y>z; y=y-2) {
		inc++;
	}
	function moveAnimation() {
		while (!that.inBounds(vertices[v]) && v<vmax) {
			v++;
		}
		var	vertex=vertices[v];
		that.showCoordMarker(vertex.lat(),vertex.lng());
		if (v==vmax) {
			setTimeout(function() {
				that.hideCoordMarker();
			},200);
		} else {
			v=v+inc;
			if (v>vmax) {
				v=vmax;
			}
			setTimeout(function() {
				moveAnimation();
			},200);
		}
	}
	moveAnimation();
}

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");

	// this layer isn't available any more
	var	roadless=mo[3].split(":")[1];

	var	trails=mo[4].split(":")[1];
	this.obscuretrailsoverlayshown=(trails=="obscure");
	this.minortrailsoverlayshown=(trails=="obscure" || trails=="minor");
	this.majortrailsoverlayshown=
		(trails=="obscure" || trails=="minor" || trails=="major");
	if (this.obscuretrailsoverlayshown) {
		this.showntrails='obscure';
	} else if (this.minortrailsoverlayshown) {
		this.showntrails='minor';
	} else if (this.majortrailsoverlayshown) {
		this.showntrails='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!="");

	// these were added after the initial implementation...
	if (mo.length>12) {
		var	wb=mo[12].split(":")[1];
		this.wboverlayshown=(wb!="");
	}
}

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.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.wboverlayshown) {
		params["wb"]="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
}

// FIXME: modularize this
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.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.wb!="$(wb)") {
		this.wboverlayshown=(top.wb=="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 ";
	}
	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.wboverlaywasshown) {
		if (this.wboverlayshown) {
			this.map.addOverlay(this.wboverlay);
			this.wboverlaywasshown=true;
		}
	} else {
		if (!this.wboverlayshown) {
			this.map.removeOverlay(this.wboverlay);
			this.wboverlaywasshown=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.updateMapConfig=function(mapconfigform) {

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

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

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

	// add/remove appropriate trail overlays
	var	majortrails=false;
	var	minortrails=false;
	var	obscuretrails=false;
	if (mapconfigform.trailscheck.checked) {
		var	trailsradio=mapconfigform.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");
			}
		}
	}
	this.showntrails="";
	this.obscuretrailsoverlayshown=false;
	this.minortrailsoverlayshown=false;
	this.majortrailsoverlayshown=false;
	if (obscuretrails) {
		this.showntrails="obscure";
		this.obscuretrailsoverlayshown=true;
		this.minortrailsoverlayshown=true;
		this.majortrailsoverlayshown=true;
	} else if (minortrails) {
		this.showntrails="minor";
		this.minortrailsoverlayshown=true;
		this.majortrailsoverlayshown=true;
	} else if (majortrails) {
		this.showntrails="major";
		this.majortrailsoverlayshown=true;
	}

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

	// add/remove national forest boundaries
	this.nfboverlayshown=mapconfigform.nfb.checked;

	// add/remove wilderness boundaries
	this.wboverlayshown=mapconfigform.wb.checked;

	// add/remove trailheads
	this.showths=mapconfigform.trailheads.checked;

	// add/remove points of interest
	var	majorpois=false;
	var	minorpois=false;
	var	obscurepois=false;
	if (mapconfigform.poischeck.checked) {
		var	poisradio=mapconfigform.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) {
		this.shownpois="obscure";
	} else if (minorpois) {
		this.shownpois="minor";
	} else if (majorpois) {
		this.shownpois="major";
	} else {
		this.shownpois="";
	}
	this.showpois=mapconfigform.poischeck.checked;

	// add/remove photo layers
	this.showtps=mapconfigform.trailphotos.checked;
	this.panoramiolayershown=mapconfigform.panoramio.checked;

	// add/remove wikipedia layer
	this.wikipedialayershown=mapconfigform.wikipedia.checked;

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

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;
}


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.iconSize=
				new google.maps.Size(image.width,
							image.height);
			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);
	}
}


function bluecoordmarker(defaulticon,id) {
	this.label=null;
	this.bcmid=id;
	this.defaulticon=defaulticon;
	this.startsrc="http://chart.apis.google.com/chart?chst=d_text_outline&chld=000000|12|h|FFFFFF|_|";
}

bluecoordmarker.prototype=new marker;

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

bluecoordmarker.prototype.show=function() {
	marker.prototype.show.call(this);
	this.lat=this.coord.lat();
	this.lng=this.coord.lng();
	this.lat=Math.round(this.lat*Math.pow(10,6))/Math.pow(10,6);
	this.lng=Math.round(this.lng*Math.pow(10,6))/Math.pow(10,6);
	this.buildTextIcon(this.lat+","+this.lng);
}

bluecoordmarker.prototype.buildTextIcon=function(text) {
	var	that=this;
	var	img=new Image();
	img.onload=function() {
		var	icon=new google.maps.Icon();
		icon.image=img.src;
		icon.iconSize=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);
		google.maps.Event.bind(that.label,"click",that,that.coordclick);
		that.map.map.addOverlay(that.label);
	}
	img.src=this.startsrc+escape(text);
	delete img;
}

bluecoordmarker.prototype.coordclick=function() {
	this.map.map.removeOverlay(this.label);
	this.bcmeditdiv=document.createElement("div");
	this.bcmeditdiv.id="edit_"+this.bcmid;
	var	height=20;
	var	width=100;
	this.bcmeditdiv.style.height=height+"px";
	this.bcmeditdiv.style.width=width+"px";
	this.bcmeditdiv.style.display="block";
	this.bcmeditdiv.style.position="absolute";
	var	point=this.map.map.fromLatLngToContainerPixel(this.coord);
	this.bcmeditdiv.style.top=point.y-height-this.defaulticon.iconSize.height/2-3+"px";
	this.bcmeditdiv.style.left=point.x-width/2-this.defaulticon.iconSize.width-2+"px";
	this.bcmeditdiv.innerHTML="<form name=\"bcmeditform_"+this.bcmid+"\" onsubmit=\"top.map.bcms["+this.bcmid+"].updateText(text.value); return false;\"><input type=\"text\" name=\"text\" value=\""+this.lat+","+this.lng+"\"></form>";
	this.map.map.getContainer().appendChild(this.bcmeditdiv);
}

bluecoordmarker.prototype.updateText=function(text) {
	this.map.map.getContainer().removeChild(this.bcmeditdiv);
	this.buildTextIcon(text);
}

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;
		}
	}
}


function trailheadmarker(defaulticon,popupurl) {
	this.defaulticon=defaulticon;
	this.popupurl=popupurl;
}

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=\""+this.popupurl+"?trailhead_id="+this.id+"\"></iframe>";
}


function poimarker(defaulticon,popupurl) {
	this.defaulticon=defaulticon;
	this.popupurl=popupurl;
}

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=\""+this.popupurl+"?point_of_interest_id="+this.id+"\"></iframe>";
}

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"));
}


function trailphotomarker(popupurl) {
	this.popupurl=popupurl;
}

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=\""+this.popupurl+"?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"));
}


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;
	}
}


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

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

navigationcontrol.prototype.initialize=function(map) {
	this.defaultheight="84%";
	this.defaultwidth="460px";

	this.navcontdiv=document.createElement("div");
	this.navcontdiv.id="navcont";
	this.navcontdiv.style.height=this.defaultheight;
	this.navcontdiv.style.width=this.defaultwidth;
	this.navcontdiv.style.display="block";
	this.navcontdiv.innerHTML=
"<div onmouseup=\"top.map.navcont.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(this.navcontdiv);
	var	navframe=document.getElementById("navframe");
	if (navigator.appName=="Microsoft Internet Explorer") {
		navframe.style.filter="alpha(opacity=80)";
	} else {
		navframe.style.opacity="0.8";
	}
	return this.navcontdiv;
}

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

navigationcontrol.prototype.show=function() {
	this.navcontdiv.style.display="block";
}

navigationcontrol.prototype.hide=function() {
	this.navcontdiv.style.display="none";
}

navigationcontrol.prototype.toggleNavVisibility=function() {

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

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

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

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=this.image;
	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));
}

coordcontrol.prototype.show=function() {
	this.coordcont.style.display="block";
}

coordcontrol.prototype.hide=function() {
	this.coordcont.style.display="none";
}


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(image) {
	this.image=image;
}

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

logocontrol.prototype.initialize=function(map) {
	this.logodiv=document.createElement("div");
	this.logodiv.id="logocont";
	this.logodiv.innerHTML="<img src=\""+this.image+"\">";
	map.getContainer().appendChild(this.logodiv);
	return this.logodiv;
}

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

logocontrol.prototype.show=function() {
	this.logodiv.style.display="block";
}

logocontrol.prototype.hide=function() {
	this.logodiv.style.display="none";
}

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() {
		if (that.googlemap.navcont.navcontdiv.style.display=="none") {
			that.googlemap.navcont.show();
			that.googlemap.logocont.show();
			that.googlemap.mapcont.show();
			that.googlemap.coordcont.show();
			that.googlemap.map.addControl(
				that.googlemap.largemapcontrol);
			that.googlemap.map.addControl(
				that.googlemap.dragzoomcontrol,
				that.googlemap.dragzoomposition);
			showhidediv.innerHTML="<center><b>Hide Controls</b></center>";
		} else {
			that.googlemap.navcont.hide();
			that.googlemap.logocont.hide();
			that.googlemap.mapcont.hide();
			that.googlemap.coordcont.hide();
			that.googlemap.map.removeControl(
				that.googlemap.largemapcontrol);
			that.googlemap.map.removeControl(
				that.googlemap.dragzoomcontrol);
			showhidediv.innerHTML="<center><b>Show Controls</b></center>";
		}
	});
	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(url,width,height) {
	this.url=url;
	this.mapclickcount=0;
	this.defaultwidth=width+"px";
	this.defaultheight=height+"px";
}

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

mapcontrol.prototype.initialize=function(map) {
	this.mapcontdiv=document.createElement("div");
	this.mapcontdiv.id="mapcont";
	this.mapcontdiv.style.height="19px";
	this.mapcontdiv.style.width="70px";
	this.mapcontdiv.innerHTML=
"<div onmouseup=\"top.map.mapcont.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=\""+this.url+"\"></iframe>";
	map.getContainer().appendChild(this.mapcontdiv);
	return this.mapcontdiv;
}

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

mapcontrol.prototype.toggleMapControlVisibility=function() {

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

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

mapcontrol.prototype.show=function() {
	this.mapcontdiv.style.display="block";
}

mapcontrol.prototype.hide=function() {
	this.mapcontdiv.style.display="none";
}

