/*
  GMapToGPX 5.8
  Originally based in part on the "Improved MSN and Google GPS GPX Waypoint 
  Extraction" bookmarklet described at http://badsegue.org/archives/2005/04/21/

  Josh Larios <hades@elsewhere.org>
  August 3, 2005 - May 7, 2008

  WARNING: Highly dependent on internal formats that aren't part of
  the API, so subject to complete breakdown at any time, outside my
  control.

  Gmap-pedometer elevation code courtesy of Mathew O'Brien.

  3/05/2007 - HeyWhatsThat.com code by mk -at- heywhatsthat.com
  10/09/2007 - Allpoints speed improvement by Kyle Yost

  TO DO: Separate out gpx writing and point/track extraction, so I can load
  some array up front regardless of whether it's a google map, a pedometer, 
  heywhatsthat, or whatever, and then just print the damn gpx once.
*/

var error = 0;
var version = '5.8';
var googledoc = ""; // will hold retrieved google info
var routes = new Array();
var milestones = new Array();
var yelp = new Array();
var googlepage; // Will hold the page element that gets toggled. May change.
var charset;
var routeindex = new Array();

function callInProgress (xmlhttp) {
    switch (xmlhttp.readyState) {
    case 1: case 2: case 3:
	return true;
	break;
	// Case 4 and 0
    default:
	return false;
	break;
    }
}

// Synchronous, with an alarm to catch timeouts (30 seconds)
// No idea if this is the best way to do this, but for sure the best way I
// came up with at 3 in the morning.
function loadXMLDoc(url) {
    var req;
    var timeoutid;
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
	showstatusdiv('Loading...');
	timeoutid = window.setTimeout( function(){if(callInProgress(req)){req.abort();}}, 30000);
        req.open("GET", url, false);
        req.send(null);
	window.clearTimeout(timeoutid);
	hidestatusdiv();
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if (req) {
	    showstatusdiv('Loading...');
	    timeoutid = window.setTimeout( function(){if(callInProgress(req)){req.abort();}}, 30000);
            req.open("GET", url, false);
            req.send();
	    window.clearTimeout(timeoutid);
            hidestatusdiv();
        }
    }
    
    if (req.readyState == 4) {
        // only if "OK"
        if (req.status == 200) {
            return(req.responseText);
        } else {
	    showstatusdiv('Error ' + req.status + ' getting google data: ' + req.statusText);
	    return('');
        }
    } else {
	showstatusdiv('Error: loadXMLDoc continued with readystate: ' + req.readyState);
	return('');
    }
}


function hidestatusdiv() { 
    var statusbox;
    if (statusbox = document.getElementById("statusbox")) {
	document.body.removeChild(statusbox);
    }
}

function showstatusdiv(boxcontents) {
    hidestatusdiv();
    z=document.body.appendChild(document.createElement("div"));
    z.id = "statusbox";
    z.style.position = "absolute";	
    if (self.pageYOffset != null) {	
	z.style.top = self.pageYOffset + "px";	
    } else if (document.documentElement.scrollTop != null) {
	z.style.top = document.documentElement.scrollTop + "px";	
    }
    z.style.width = "50%";
    z.style.left = "0px";
    z.style.background = "#ffffff";
    z.style.border = ".3em solid #ff0000";
    z.style.padding = ".3em 1.3em .3em .3em";
    z.style.zIndex = "1000";
    
    z.innerHTML = '<div style="position: absolute; border: 1px solid black; top: 0px; right: 0px;"><span style="padding: .3em; font-weight: bold;"><a style="text-decoration: none;" title="Close status box" href="#" onclick="javascript:hidestatusdiv();">X</a></span></div>';
    z.innerHTML += boxcontents;
}

// This function is from Google's polyline utility.
function decodeLine (encoded) {
    var len = encoded.length;
    var index = 0;
    var positions = [];
    var lat = 0;
    var lng = 0;
    
    while (index < len) {
	var b;
	var shift = 0;
	var result = 0;
	do {
	    b = encoded.charCodeAt(index++) - 63;
	    result |= (b & 0x1f) << shift;
	    shift += 5;
	} while (b >= 0x20);
	var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
	lat += dlat;
	
	shift = 0;
	result = 0;
	do {
	    b = encoded.charCodeAt(index++) - 63;
	    result |= (b & 0x1f) << shift;
	    shift += 5;
	} while (b >= 0x20);
	var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
	lng += dlng;

	positions.push({"lat": round(lat * 1e-5), "lon": round(lng * 1e-5)});
    }
    
    return positions;
}

function StringBuffer() {
   this.buffer = [];
 }

StringBuffer.prototype.append = function append(string) {
   this.buffer.push(string);
   return this;
 };

StringBuffer.prototype.toString = function toString() {
   return this.buffer.join("");
 };



function gmaptogpxdiv(dtype) { 
    var mypoints = null;
    
    var qtype = 0;
    var subtype = 0;
    
    /* 
       Determine which type of data we're extracting -- a route, or points of
       interest. (Or gmap-pedometer/heywhatsthat.) 
    */
    
    if (googledoc.match(/id:["']route0["']/)) {
	qtype = 2;
	
	/* FIXME 
	   errorbox('<p>2008.05.07 - GMapToGPX is currently unable to process driving directions, due to a change in the underlying Google Maps code. I\'m working on it.</p>');
	   closebox();
	   return(0);
	   END FIXME */
	
	// Doing this as a regexp was causing firefox to stall out. bah.
	var encpointblob=googledoc.slice(googledoc.indexOf('polylines:['));
	encpointblob=encpointblob.slice(0, encpointblob.indexOf(']]>'));
	var encmatch = encpointblob.match(/id:.?([^']+).?, *points:.?([^'"]+).?/g);
	for (var i = 0; i < encmatch.length; i++) {		
	    var id = encmatch[i].replace(/id:.?([^'"]+).?.*/, "$1");
	    var encpoints = encmatch[i].replace(/.*, *points:.?([^\'\"]+).?/, "$1");
	    // Stupid backslashes. *mutter*
	    routes[id] = decodeLine(encpoints.replace(/\\\\/g, '\\'));
	}

	// Get the milestone descriptions
	var msaddrmatch;
	if (msaddrmatch = googledoc.match(/<div id=.?sxaddr.?>.*?<\/div>/g)) {
	    for (var i = 0; i < msaddrmatch.length; i++) {
		milestones[parseInt(i)] = deamp(msaddrmatch[i].replace(/<div id=.?sxaddr.?><div[^>]+>(.*?)<\/div>/, "$1"));
	    }
	}
	
	// get the route segment descriptions
	var routematch;
	var segmatch;
	var routeindexmatch;
	var indexmatch;
	if (routematch = googledoc.match(/<table class=.?ddr_steps.*?<\/table>/g)) { 
	    // This needs serious work. I can't remember what the variable
	    // names are for. I sense a Great Renaming in the future.

	    if (routeindexmatch = googledoc.match(/\{steps:\[.*?\]\}/g)) {
		for (var route = 0; route < routeindexmatch.length; route++) {
		    routeindex[route] = new Array();
		    if (indexmatch = routeindexmatch[route].match(/\{polyline:\d+,ppt:\d+\}/g)) {
			for (var i = 0; i < indexmatch.length; i++) {
			    routeindex[route][i] = indexmatch[i].replace(/\{polyline:\d+,ppt:(\d+)\}/, "$1");
			}
		    }
		}
	    }
	    
	    if (segmatch = googledoc.match(/<td class=.?dirsegtext.*?id=.?dirsegtext.*?<\/tr>/g)) {
		for (var s = 0; s < segmatch.length; s++) {
		    var route = segmatch[s].replace(/.*dirsegtext_([0-9]+)_([0-9]+).*/, "$1");
		    var index = segmatch[s].replace(/.*dirsegtext_([0-9]+)_([0-9]+).*/, "$2");
		    index = routeindex[route][index];
		    var desc = deamp(segmatch[s].replace(/.*dirsegtext[^>]+>(.*?)<\/td>.*/, "$1"));
		    if(routes["route" + route][index]) routes["route" + route][index].desc = deamp(desc);
		}
	    }
	}
    } else  if (googledoc.match(/id:'(A|addr)'/)) {
	qtype = 1;
	routes['poi'] = new Array();
	
	var markers=googledoc.slice(googledoc.indexOf('markers:['));
	/*	markers=markers.slice(0, markers.indexOf('polylines: [')); */
	
	var pois = markers.match(/{id:'([A-Z]|addr|loc\d+)'.*?\}/g);
	for (var i = 0; i < pois.length; i++) {	
	    var lat = pois[i].replace(/.*lat:(.*?),.*/, "$1");
	    var lon = pois[i].replace(/.*lng:(.*?),.*/, "$1");
	    var desc = pois[i].replace(/.*laddr:'(.*?)',.*/, "$1");
  	    desc = desc.replace(/ @.*/, "");
	    desc = desc.replace(/(.*) \((.*)\)/, "$2 ($1)");
	    routes['poi'].push({"lat": round(lat), "lon": round(lon), "desc": deamp(desc)});
	}
    }
    
    /* gmap-pedometer.com */
    if ( (qtype==0) && (self.gLatLngArray) && (self.gLatLngArray[0]) ) { 
	qtype = 3; 
    }

    
    /* HeyWhatsThat.com list of peaks visible from given location */
    if (qtype == 0 && location.href.match(/heywhatsthat.com/i) && peaks && peaks.length) {
	qtype = 4;	
	subtype = 1;
    }

    /* Yelp.com search */
    if (qtype == 0 && location.href.match(/yelp.com/i) && document.body.innerHTML.match('result_plot_obj.map.addOverlay')) {
	qtype = 4;
	subtype = 2;
	var yelpmatch = document.body.innerHTML.match(/result_plot_obj.map.addOverlay.*?\)\)/g);
	for (var i = 0; i < yelpmatch.length; i++) {	
	    yelp[i] = new Array();
	    yelp[i].name = deamp(yelpmatch[i].replace(/.*<h3>(.*?)<\/h3>.*/, "$1"));
	    yelp[i].addr = deamp(yelpmatch[i].replace(/.*<address[^>]*>(.*?)<\/address>.*/, "$1"));
	    yelp[i].lon = yelpmatch[i].replace(/.*Yelp.TSRUrl.*?,.*?,.*?, (.*?),.*/, "$1");
	    yelp[i].lat = yelpmatch[i].replace(/.*Yelp.TSRUrl.*?,.*?,.*?,.*?, (.*?),.*/, "$1");
	}
    }
    /* Yelp.com single location */
    //    var json_biz = {"city":"Seattle","zip":"98102","review_count":6,"name":"Tacos Guaymas - CLOSED","neighborhoods":["Capitol Hill"],"photos":[],"address1":"213 Broadway East","avg_rating":4.000000,"longitude":-122.320999,"address2":null,"phone":"(206) 860-7345","state":"WA","latitude":47.620201,"id":"PidHplYWockrwJpijqUwsg","categories":{}};
    if (qtype == 0 && location.href.match(/yelp.com/i) && json_biz) {
	qtype = 4;
	subtype = 2;
	yelp[0] = new Array();
	yelp[0].name = json_biz.name;
	yelp[0].lat = json_biz.latitude;
	yelp[0].lon = json_biz.longitude;
	yelp[0].addr = json_biz.address1 + ", ";
	if (json_biz.address2 != null) {
	    yelp[0].addr += json_biz.address2 + ", ";
	}
	yelp[0].addr += json_biz.city + ", " + json_biz.state + " " + json_biz.zip;
    }

    /* findmespot.com */
    if (qtype == 0 && location.href.match(/findmespot.com/i) && document.getElementsByTagName('iframe')[1] && document.getElementsByTagName('iframe')[1].contentDocument.getElementById('mapForm:inputHidden1').value) {
	qtype = 4;
	subtype = 3;
    }



    if (qtype==0) {
	errorbox('<p>There doesn\'t seem to be any extractable data on this page.</p><p>If there is, but it\'s not detected, please visit the <a href="http://www.elsewhere.org/GMapToGPX/">project homepage</a> and leave a bug report, including a link to the page you\'re on right now.</p><p><strong>Note:</strong> Google Maps mashups (that is, a page with a Google Map on it, but not at google.com) do not automatically work with this utility. If you would like to see GMapToGPX work with your Google Maps mashup site, please leave a comment on the project page.</p>');
	closebox();
	return(0); 
    }


    /* t contains the text that will be injected into a <div> overlay */
    var t="<div style='border:3px dotted orange;padding:2px;background-color:#FFFFFF;margin-left:auto;margin-right:auto;'>";
    
    if (navigator.userAgent.match(/Safari/)) {
	t+='\
<style type="text/css">\
.menubar ul {\
	margin:0; \
}\
.menubar li {\
	display:inline;\
	list-style:none;\
	margin:0 5px 0 5px;\
}\
\
.menubar a , .menubar a:visited {\
	padding: 1px 10px 1px 10px;\
	text-decoration:none;\
        background-color: #3399CC;\
	color:white;\
	font-family:Verdana, Sans-Serif;\
	font-size:9pt;\
	-moz-border-radius:10px;\
	border:2px inset black;\
	font-weight: bolder;\
}\
\
.menubar select {\
    background-color: transparent;\
    color: #333333;\
    margin-top: 2px;\
    border: 1px solid #efefef;\
}\
\
.menubar a:hover , .menubar select:hover {\
	border:2px outset black;\
   	background-color:white;\
	color:blue;\
	text-decoration:none;\
}\
</style>\
';
    }
	
    
    t+="<div style='background-color:gray;'>";
    t+='<ul class="menubar">';
    t+='<li class="menubar">GMapToGPX v' + version + '</li>';
    if ((qtype > 1) && (qtype != 4)) {
	if (dtype != "route") {
	    t+='<li class="menubar"><a href="javascript:reload(0)" title="Turn points as a route">Route</a></li>'; 
	} 
	if (dtype != "track") {
	    t+='<li class="menubar"><a href="javascript:reload(1)" title="Turn points as a track">Track</a></li>';
	}
	if (dtype != "points") {
	    t+='<li class="menubar"><a href="javascript:reload(2)" title="Turn points as waypoints">Points</a></li>';
	}
	if ((qtype ==2) && (dtype != "allpoints")) {
	    t+='<li class="menubar"><a href="javascript:reload(3)" title="ALL points as a track (potentially very large)">Full</a></li>';
	}
    }
    t+='<li class="menubar"><a href="javascript:loadabout()">About</a></li>';
    t+='<li class="menubar"><a href="javascript:closebox()">CLOSE [X]</a></li></ul></div>';
    t+='<textarea rows="20" cols="60">';
    
    /* This part of the GPX is going to be the same no matter what. */
    t+= '<?xml version="1.0" encoding="' + charset + '"?>\n' + 
	'<gpx version="1.1"\n' + 
	'     creator="GMapToGPX ' + version + ' - http://www.elsewhere.org/GMapToGPX/"\n' + 
	'     xmlns="http://www.topografix.com/GPX/1/1"\n' + 
	'     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' + 
	'     xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">\n';
    
    if ((qtype==2) && (dtype=="allpoints")) {
	var title = "Driving directions";
	t+= '   <trk>\n';
	t+= '      <name>Google Driving Directions Track</name>\n';
	var buf = new StringBuffer;
	for (var key in routes) {
	    var route = routes[key];
	    buf.append("      <trkseg>\n");
	    for(i=0;i<route.length;i++) {
		if (i == route.length - 1) {
		    route[i].desc = milestones[1 + parseInt(key.replace(/route/,''))];
		} else if ((route[i].lat == route[i+1].lat) && (route[i].lon ==
								route[i+1].lon)) {
		    continue;
		}
		buf.append("      <trkpt lat=\"");
		buf.append(route[i].lat);
		buf.append("\" lon=\"");
		buf.append(route[i].lon);
		buf.append("\">");
		if (route[i].desc) {
		    buf.append("<cmt>");
		    buf.append(deamp(route[i].desc));
		    buf.append("</cmt>");
		}
		buf.append("</trkpt>\n");
	    }
	    buf.append("      </trkseg>\n");
	}
	buf.append("   </trk>\n");
	t+= buf.toString();

    } else if (qtype == 3) {
//	var pl = location.href + returnPermalinkString();
	var pl = "Permalink temporarily unavailable.";
	var elevationArrayTested = true;                
	// 1st make sure that the gmap elevation array is the same length as 
	// the LatLng array
	if ( (typeof(elevationArray) != 'undefined') && (gLatLngArray.length == elevationArray.length) ) {
	    // Next test all of the elevation data in the array, looking for 
	    // bad elevation data
	    // -1.79769313486231E+308 means no valid elevation value was 
	    // found at that point
	    for (var e =0;e<elevationArray.length;e++){
		if (self.elevationArray[e] == "-1.79769313486231E+308") 
		    {
			elevationArrayTested = false;
		    }
	    }
	} else {
	    elevationArrayTested = false;
	}

	if (dtype == "track") {
	    t+= '   <trk>\n';
	    t+= '      <name>Gmaps Pedometer Track</name>\n' +
		'      <cmt>Permalink: &lt;![CDATA[\n' + pl + '\n]]>\n</cmt>\n';
	    t+= '      <trkseg>\n';
	} else if (dtype == "route") {
	    t+= '   <rte>\n';
	    t+= '      <name>Gmaps Pedometer Route</name>\n' +
		'      <cmt>Permalink: &lt;![CDATA[\n' + pl + '\n]]>\n</cmt>\n';
	}
	for(var i=0;i<self.gLatLngArray.length;i++){
	    if (dtype == "track") {
		t+= '      <trkpt ';
	    } else if (dtype == "route") {
		t+= '      <rtept ';
	    } else if (dtype == "points") {
		t+= '      <wpt ';
	    }
	    t+= 'lat="' + round(self.gLatLngArray[i].y) + '" ' +
		'lon="' + round(self.gLatLngArray[i].x) + '">\n';


	    if ( elevationArrayTested == true ) {
		var currentElevation = 0;
		currentElevation = self.elevationArray[i];              
		currentElevation = round(currentElevation * 0.3048)     
		    t+= '         <ele>' + currentElevation  + '</ele>\n';
	    }

	    t+= '         <name>' + (i ? 'Turn ' + i : 'Start') + '</name>\n';

	    if (dtype == "track") {
		t+= '      </trkpt>\n';
	    } else if (dtype == "route") {
		t+= '      </rtept>\n';
	    } else if (dtype == "points") {
		t+= '      </wpt>\n';
	    }
	}
	if (dtype == "track") {
	    t+= '      </trkseg>\n';
	    t+= '   </trk>\n';
	} else if (dtype == "route") {
	    t+= '   </rte>\n';
	}
    } else if (qtype == 4 && subtype == 1) {
	/* HeyWhatsThat.com list of peaks */
	for (var i = 0; i < peaks.length; i++) {
	    var p = peaks[i];
	    t+= '   <wpt lat="' + p.lat + '" lon="' + p.lon + '">\n' +
		'      <ele>' + p.elev + '</ele>\n' +
		'      <name>' + p.name + '</name>\n' +
		'      <cmt>' + p.name + '</cmt>\n' +
		'   </wpt>\n';
	}
    } else if (qtype == 4 && subtype == 2) {
	for (var i = 0; i < yelp.length; i++) {
	    var p = yelp[i];
	    t+= '   <wpt lat="' + p.lat + '" lon="' + p.lon + '">\n' +
		'      <name>' + p.name + '</name>\n' +
		'      <cmt>' + p.addr + '</cmt>\n' +
		'   </wpt>\n';
	}
    } else if (qtype == 4 && subtype == 3) {
          var spotdata = document.getElementsByTagName('iframe')[1].contentDocument.getElementById('mapForm:inputHidden1').value;
          var loc_array = spotdata.split(",");
          var loc_length = loc_array.length - 1;
	  t += '  <trk><trkseg>\n';
          for(var i=0;i<loc_length;i++){
            var loc_point = loc_array[i].split("||");
            var esn = loc_point[0];
            var lat = loc_point[1];
            var lon = loc_point[2];
            var type = loc_point[3];
            var dtime = loc_point[4];
	    t+= '   <trkpt lat="' + lat + '" lon="' + lon + '">\n' +
		'      <name>' + i + '-' + type + '</name>\n' +
		'      <cmt>' + type + ' ' + esn +  ' @ ' + dtime + '</cmt>\n' +
		'      <desc>' + type + ' ' + esn + ' @ ' + dtime + '</desc>\n' +
		'   </trkpt>\n';
          }
	  t += '  </trkseg></trk>\n';
    } else if (qtype == 2) {
	/* If we're on a page with driving directions, spit out a route. */
	var title = "Driving directions";
	
	if (dtype == "track") {
	    t+= '   <trk>\n';
	} 
	
	var turn = 1;
	var milestone = 1;
	
	for (var key in routes) {
	    var route = routes[key];
	    var routeno = key.replace(/route/, '');
	    routeno = parseInt(routeno);
	    if (dtype == "track") {
		t+= '   <trkseg>\n';
	    } else if (dtype == "route") {
		t+= '   <rte>\n';
	    }
	    
	    if ((dtype=="track") || (dtype=="route")) {
		t+= '      <name>' + key + '</name>\n';
		t+= '      <cmt>' + milestones[routeno] + " to " + milestones[routeno + 1] + '</cmt>\n'; 
		t+= '      <desc>' + milestones[routeno] + " to " + milestones[routeno + 1] + '</desc>\n'; 
	    }

	    for(i=0;i<route.length;i++){	
		if ((i != route.length - 1) && (route[i].desc == undefined)) {
		    continue;
		} 
		// Only print turn points and milestones (last point is an
		// undescribed milestone; first point should always have a
		// description).
		switch(dtype) {
		case 'track':
		    t+= '      <trkpt ';
		    break;
		case 'route':
		    t+= '      <rtept ';
		    break;
		case 'points':
		    t+= '      <wpt ';
		    break;
		}
		t+= 'lat="' + route[i].lat + '" ' +
		    'lon="' + route[i].lon + '">\n' +
		    '         <name>';
		if (i == route.length - 1) {
		    route[i].desc = milestones[routeno+1];

		    t += 'GMLS-' + ((milestone < 100) ? '0' : '') + 
			((milestone < 10) ? '0' : '') + milestone;
		    milestone += 1;
		    turn -= 1;
		} else {
		    t += 'GRTP-' + ((turn < 100) ? '0' : '') + 
			((turn < 10) ? '0' : '') + turn;
		}
		t += '</name>\n' +
		    '         <cmt>' + route[i].desc + '</cmt>\n' +
		    '         <desc>' + route[i].desc + '</desc>\n';

		switch(dtype) {
		case 'track':
		    t+= '      </trkpt>\n';
		    break;
		case 'route':
		    t+= '      </rtept>\n';
		    break;
		case 'points':
		    t+= '      </wpt>\n';
		    break;
		}
		turn++;
	    }
	    if (dtype == "track") {
		t+= '   </trkseg>\n';
	    } else if (dtype == "route") {
		t+= '   </rte>\n';
	    }
	}
	
	if (dtype == "track") {
	    t+= '   </trk>\n';
	} 
	
	
    } else if (qtype == 1) {
	/* This is a page with points of interest - spit out waypoints. */
	for(i=0;i<routes['poi'].length;i++){
	    var point = routes['poi'][i];
	    t+= '   <wpt lat="' + point.lat + '" lon="' + point.lon + '">\n' +
		'      <name>' + point.desc + '</name>\n' +
		'      <cmt>' + point.desc.replace(/(.*) \((.*)\)/, "$2 ($1)") + '</cmt>\n' +
		'      <desc>' + point.desc.replace(/(.*) \((.*)\)/, "$2 ($1)") + '</desc>\n' +
		'   </wpt>\n';
	}
    } else {
	errorbox('An unknown error occurred. Please leave a bug report at the <a href="http://www.elsewhere.org/GMapToGPX/">project homepage</a> and include a link to the page you\'re on right now.');
	error = 1;
    }
    
    t+='</gpx>\n';
    t+='</textarea>';
    t+="<div style='background-color:gray;'>";
    t+='<ul class="menubar">';
    t+='<li class="menubar"><a href="javascript:loadabout()">About</a></li>';
    t+='<li class="menubar"><a href="javascript:closebox()">CLOSE [X]</a></li></ul></div></div>';
    displaybox(t);
}

function displaybox(boxcontents) {
    closebox();
    if (googlepage=document.getElementById("page")) {
	googlepage.style.display='none';
    }
    var z=document.body.appendChild(document.createElement("div"));
    z.id = "gpxbox";
    /* I don't know about this stuff; it came from badsegue. */
    z.style.position = "absolute";	
    if (self.pageYOffset != null) {	
	z.style.top = self.pageYOffset + "px";	
    } else if (document.documentElement.scrollTop != null) {
	z.style.top = document.documentElement.scrollTop + "px";	
    }
    z.style.width = "99%";
    z.style.zIndex = "1000";
    z.innerHTML = boxcontents;
}

function closebox() { 
    var gpxbox;
    if (gpxbox = document.getElementById("gpxbox")) {
	document.body.removeChild(gpxbox);
    }
    if (googlepage != undefined) {
	googlepage.style.display='block';
    }
}



function loadabout() {
    var about = '<span style="font-size: x-small;"><p><a href="http://www.elsewhere.org/GMapToGPX/">GMapToGPX</a> Extractor ' + version + '<br />';
    about += 'A project of <a href="http://www.elsewhere.org/">Communications From Elsewhere</a></p>';
    about += '<p>Usage:<ul>';
    about += '<li>"Track" displays driving directions as a GPX track with one or more track segments, depending on the number of milestones in the directions.</li>';
    about += '<li>"Route" displays driving directions as one or more GPX routes.</li>';
    about += '<li>"Full" displays driving directions as a GPX track containing one or more track segments, each of which contains every single point on the line Google Maps draws to represent the segment. Use with caution, as long routes may produce huge results.</li>';
    about += '<li>"Points" displays driving directions as a list of waypoints for each turn in the route. The waypoints will be in order, but this option is mainly intended for devices which can only handle waypoints, not tracks or routes. In most cases, you should use another option.</li>';
    about += '<li>For single or multiple address searches, there are no display options. You get a list of individual waypoints.</li>';
    about += '</ul>If you have questions or comments, please visit the <a href="http://www.elsewhere.org/GMapToGPX/">project homepage</a>.</p></span>';
    showstatusdiv(about);
}

function errorbox(a) {
    var err = '<a href="http://www.elsewhere.org/GMapToGPX/">GMapToGPX</a> v' + version + " (ERROR)<br />" + a;
    showstatusdiv(err);
}


/* Clean up floating point math errors */
function round(a) {
    return parseInt(a*1E+5)/1E+5;
}

function reload(t) {
    closebox();
    if (t==0) {
	gmaptogpxdiv("route");
    } else if (t=="1") {
	gmaptogpxdiv("track");
    } else if (t=="2") {
	gmaptogpxdiv("points");
    } else if (t=="3") {
	gmaptogpxdiv("allpoints");
    }
}



function deamp(a) {
    a = a.replace(/<br *\/>(.+)/g, ", $1");
    a = a.replace(/<br *\/>/g, '');
    a = a.replace(/&#39;/g, '\'');
    a = a.replace(/\\047/g, '\'');
    a = a.replace(/\\042/g, '\"');
    a = a.replace(/&#160;/g, ' ');
    a = a.replace(/<\/*b>/g, '');
    a = a.replace(/<wbr\/*>/g, '');
    a = a.replace(/<div[^>]*?>.*?<\/div>/g, ' ');
    a = a.replace(/\\\'/g, '\''); 
    a = a.replace(/\\\"/g, '\"');
    a = a.replace(/\\x26/g, '&');
    a = a.replace(/&/g, '&amp;');  
    a = a.replace(/&amp;amp;amp;/g, '&amp;amp;');
    a = a.replace(/\\n/g, '');
    a = a.replace(/\\t/g, '');
    a = a.replace(/\s+/g, ' ');
    a = a.replace(/^\s+/, '');
    a = a.replace(/\s+$/, '');
    return a;
}
		  

/* main() */
if (document.location.hostname.indexOf('google') >= 0) {
    for (var i = 0; i < document.links.length; i++) {
	if (document.links.item(i).innerHTML && (document.links.item(i).style.display != 'none') && (document.links.item(i).innerHTML.indexOf('view_as_kml') >= 0)) {
	    var kmlurl = document.links.item(i).href; 
	    kmlurl = kmlurl.replace(/output=nl/, 'output=kml');
	    errorbox('This is a "My Maps" page, which means that the original KML used to create it is available. Please <a href="' + kmlurl + '">download the KML file</a> (using this link, not the one provided by Google) and convert it using <a href="http://www.gpsvisualizer.com/convert" target="_new">GPSVisualizer</a>.');
	    error = 1;
	    break;
	}

	// bar_icon_link is the "link to this page" icon. If they change 
	// its name, I need to fix that here.
	if (document.links.item(i).innerHTML && (document.links.item(i).innerHTML.indexOf('bar_icon_link') >= 0)) {
	    var googleurl = document.links.item(i).href;
	    googleurl = googleurl.replace(/&view=text/, '');
	    googledoc = loadXMLDoc(googleurl);

	    charset=googledoc.slice(googledoc.indexOf('charset='));
	    charset=charset.slice(8, charset.indexOf('"'));
	    
	    googledoc = googledoc.replace(/\\x3e/g, '>');
	    googledoc = googledoc.replace(/\\x3c/g, '<');
	    googledoc = googledoc.replace(/\\x26/g, '&');
	    googledoc = googledoc.replace(/\\x42/g, '"');
	    googledoc = googledoc.replace(/\\x3d/g, '=');

	    googledoc = googledoc.replace(/\\u003e/g, '>');
	    googledoc = googledoc.replace(/\\u003c/g, '<');
	    googledoc = googledoc.replace(/\\u0026/g, '&');

	    googledoc = googledoc.replace(/\\042/g, '"');

	    googledoc = googledoc.replace(/"*polylines"*:\s*/g, 'polylines:');
	    googledoc = googledoc.replace(/"*markers"*:\s*/g, 'markers:');
	    googledoc = googledoc.replace(/"*id"*:\s*/g, 'id:');
	    googledoc = googledoc.replace(/"*lat"*:\s*/g, 'lat:');
	    googledoc = googledoc.replace(/"*lng"*:\s*/g, 'lng:');
	    googledoc = googledoc.replace(/"*laddr"*:\s*/g, 'laddr:');
	    googledoc = googledoc.replace(/"*points"*:\s*/g, 'points:');
	    googledoc = googledoc.replace(/\\"/g, '"');
	    googledoc = googledoc.replace(/\"/g, '\'');
	    continue;
	}
    }
}

charset = charset ? charset : "UTF-8";

/* This bit of code was causing Safari to seriously freak out, hence the 
   stylesheet being included above in t, but only for Safari.  */
if (! navigator.userAgent.match(/Safari/)) {
    var styleObject = document.getElementsByTagName("HEAD")[0].appendChild(document.createElement("link"));
    styleObject.rel="Stylesheet";
    styleObject.type="text/css";
    styleObject.href="http://www.elsewhere.org/GMapToGPX/menubar.css";
    styleObject.id="sst_css";
}

if (error != 1) {
/* Default action. If it's not a route, the argument doesn't matter. */
  gmaptogpxdiv("route");
} else {
  closebox();
}
