yahoo_app_id = (self.yahoo_app_id && yahoo_app_id) ? yahoo_app_id : 'test';

function InitializeGeocoder() {
	l = document.location.toString(); gpsv_p1 = '%68%74%74%70%73%3F%3A%2F%2F'; gpsv_p2 = '%5E%68%74%74%70%73%3F%3A%2F%2F%28%77%2B%5C%2E%29%3F%67%5B%70%65%5D%5B%73%6F%5D%76%69%73%75%61%6C%69%7A%65%72'; gpsv_p3 = '(hbR9A0KDcnx0TU67kQ|55QacGZ2zQF2kLC_eA)$';
	
	if (self.google_api_key && google_api_key) { google_api_key = google_api_key; }
	else if (document.getElementById('google_api_key_box') && document.getElementById('google_api_key_box').value) { google_api_key = document.getElementById('google_api_key_box').value.toString(); }
	google_base_url = 'http://maps.google.com/maps/geo?output=json&sensor=false&key='+((self.google_api_key2&&google_api_key2)?google_api_key2:google_api_key)+'&q=&callback=googleCallback';
	googleCallback = function(d) { GoogleGeocodeCallback(d); }
	google_precision = ['not found','country','state/region','county/municipality','city','zip/postcode','street','intersection','address','feature/building'];
	google_status = {200:"Success",601:"Missing address",602:"Unknown address",603:"Unavailable address",610:"Invalid API key",620:"Geocoding quota exceeded",500:"Server error"};
	
	yahoo_init_url = 'http://api.maps.yahoo.com/ajaxymap?appid='+yahoo_app_id;
	// The following line is commented because it returned an error, as of 5/28/08:
	//document.writeln('<script type="text/javascript" src="'+yahoo_init_url+'"><'+'/'+'script>');
	
	yahoo_base_url = 'http://api.maps.yahoo.com/ajax/geocode?appid='+yahoo_app_id+'&qt=3&qs='; // qt = query type?
	// if (self.YGeoCode) { YGeoCode.getPoint = function(d){ YahooGeocodeCallback(d) }; }
	YGeoCode = {}; YGeoCode.getPoint = function(d){ YahooGeocodeCallback(d) };
	
	extra_fields = ['address','city','state','zip','country','precision'] // ,'sw_corner','ne_corner'];
	
	source_menu = document.getElementById('source') || null;
	type_menu = document.getElementById('input_type') || null;
	separator_menu = document.getElementById('separator') || null;
	extra_info_checkbox = document.getElementById('extra_info') || null;
	color_box = document.getElementById('add_color') || null;
	progress_indicator = document.getElementById('progress') || null;
	error_message_div = document.getElementById('error_message') || null;
	start_button = document.getElementById('start') || null;
	start_button_parent = (start_button) ? start_button.parentNode : null;
	start_button_onclick = (start_button) ? start_button.getAttribute('onclick') : '';
	
	ShowHideGoogleOptions();
	CheckForGeocoderTitle();
}

function ShowHideGoogleOptions() {
	if(source_menu.value == 'google' && document.getElementById('google_options')) {
		document.getElementById('google_options').style.display = 'block';
		if (document.location.toString().indexOf('file:') == 0 && document.getElementById('google_api_key_div')) {
			if (!self.google_api_key || (self.google_api_key && google_api_key == '')) {
				document.getElementById('google_api_key_div').style.display = 'block';
			} else { 
				document.getElementById('google_api_key_div').style.display = 'none';
			}
		} else if (document.getElementById('google_api_key_div')) {
			document.getElementById('google_api_key_div').style.display = 'none';
		}
	} else {
		if (document.getElementById('google_options')) { document.getElementById('google_options').style.display = 'none'; }
		if (document.getElementById('google_api_key_div')) { document.getElementById('google_api_key_div').style.display = 'none'; }
	}
	if(source_menu.value == 'google') {
		if(document.getElementById('google_map')) {
			document.getElementById('google_map').style.display = 'block';
			if (!self.google_map_is_initialized && self.GV_Map && self.google_api_key && google_api_key) { GV_Map(); }
		}
		if(document.getElementById('yahoo_map')) { document.getElementById('yahoo_map').style.display = 'none'; }
	} else { // yahoo
		if(document.getElementById('yahoo_map')) {
			document.getElementById('yahoo_map').style.display = 'block';
			if (!self.yahoo_map_is_initialized && self.GV_Yahoo_Map) { GV_Yahoo_Map(); }
		}
		if(document.getElementById('google_map')) { document.getElementById('google_map').style.display = 'none'; }
	}
}

function DetectDataType(input_id) {
	if (!document.getElementById(input_id)) { return false; }
	if (!document.getElementById(input_id).value) { return false; }
	var first_row = '';
	var first_eol_position = document.getElementById(input_id).value.indexOf('\n');
	if (first_eol_position > 0) {
		first_row = document.getElementById(input_id).value.substring(0,first_eol_position);
	} else if (first_eol_position == 0) {
		var next_eol_position = document.getElementById(input_id).value.indexOf('\n',1);
		first_row = document.getElementById(input_id).value.substring(1,next_eol_position);
	}
	var detected_type;
	if (first_row.match(/(^|\t|,|\|) *(street|add?ress?e?1?|city|town|plaats|ville|state|province|zip|postal|post ?code|code ?postale?|airport|latitude|longitude|lat|long?)\b/i)) {
		detected_type = 'table';
	} else {
		detected_type = 'list';
	}
	for (i=0; i<type_menu.options.length; i++) {
		if (type_menu.options[i].value == detected_type) {
			type_menu.selectedIndex = i;
		}
	}
	
	return true;
}

function StartButton(processing_now,process_name) {
	if (!process_name) { process_name = 'geocoding'; }
	if (processing_now) {
		var style = 'color:#666666; font-weight:normal; background-color:#CCFFCC;';
		var text = 'Cancel '+process_name;
		var onclick = "continue_geocoding=false; StartButton(false,'"+process_name+"');";
		start_button_parent.innerHTML = '<input id="start" type="button" style="'+style+'" value="'+text+'" onclick="'+onclick+'">';
	} else {
		var style='color:#000000; font-weight:bold; background-color:#CCFFCC;';
		var text = 'Start '+process_name;
		var onclick = "GeocodeMultiple('geocode_input','geocode_results');";
		start_button_parent.innerHTML = '<input id="start" type="button" style="'+style+'" value="'+text+'" onclick="'+onclick+'">';
		var finished = new Image();
		finished.src = 'http://maps.gpsvisualizer.com/geocoder/finished.png?source='+source+'&count='+location_counter+'/'+input_locations.length+'&page='+URIEscape(document.location)+'&key='+google_api_key+'&time='+(new Date()).getTime();
	}
}

function GeocodeMultiple(input_id,output_id) {
	
	// globals:
	input_textarea = document.getElementById(input_id) || null;
	results_textarea = document.getElementById(output_id) || null;
	source = (source_menu) ? source_menu.value : 'yahoo';
	input_type = (type_menu) ? type_menu.value : 'list';
	extra_info = (extra_info_checkbox && extra_info_checkbox.checked) ? true : false;
	added_color = (color_box && color_box.value.match(/\w/)) ? color_box.value : '';
	sep = ','; if (separator_menu && separator_menu.value) { sep = (separator_menu.value == 'tab') ? "\t" : separator_menu.value; }
	if (!gpsv_p1 || !gpsv_p2 || (l.match(unescape(gpsv_p1)) && !l.match(unescape(gpsv_p2)))) { return; }
	
	if (progress_indicator) { progress_indicator.innerHTML = ''; }
	if (error_message_div) { error_message_div.style.display = 'none'; error_message_div.innerHTML = ''; }
	
	if (!input_textarea || !results_textarea) { return false; }
	
	var input_text = input_textarea.value;
	input_text = input_text.replace(/(^\s+|\s+$)/g,""); // delete leading and trailing white space
	input_text = input_text.replace(/\r/,'\n'); // just in case there are weird line breaks
	input_lines = input_text.split(/\n/);
	input_locations = [];
	input_data = [];
	
	if (!input_text.match(/\w/)) { return false; }
	
	if (input_type == 'list' && input_lines[0].match(/(^|\t|,) *(street|add?ress?e?1?|city|town|ville|plaats|state|province|zip|postal|post ?code|p\. ?code|code ?postale?|airport|latitude|longitude|lat|long?)\b/i)) {
		var override = confirm ('You\'ve selected "raw list" as your input type, but it looks like your data is in a structured table with column headers.  Click "OK" to continue, or "Cancel" to stop and either remove the header row or switch to "tabular data" mode.');
		if (!override) { return false; }
	} else if (input_type == 'table' && !input_lines[0].match(/(^|\t|,) *(street|add?ress?e?1?|city|town|ville|plaats|state|province|zip|postal|post ?code|p\. ?code|code ?postale?|airport|latitude|longitude|lat|long?)\b/i)) {
		var override = confirm ('You\'ve selected "tabular data" as your input type, but it looks like your data doesn\'t have a header row.  Click "OK" to continue, or "Cancel" to stop and either add a header row or switch to "raw list" mode.');
		if (!override) { return false; }
	}
	
	if (!source) { source = 'yahoo'; }
	
	if (source == 'google' && (!google_api_key) || (!l.match(/^https?:\/\/\w*\.?gpsvisualizer.com/) && google_api_key.match(gpsv_p3)) ) {
		alert ('You must have your own valid Google API key.');
		return false;
	}
	
	if (input_type == 'table') { // tabular
		var result = ParseTabularAddresses();
		if (!result) { return false; }
	} else { // list
		var result = ParseListOfAddresses();
		if (!result) { return false; }
	}
	
	StartButton(true,'geocoding');
	continue_geocoding = true;
	
	location_counter = 0;
	lookup_counter = 0;
	var has_coordinates_already = (input_data.length == 0) ? false : ((input_data[location_counter]['latitude'].match(/\d/) && input_data[location_counter]['latitude'] != 0) || (input_data[location_counter]['longitude'].match(/\d/) && input_data[location_counter]['longitude'] != 0)) ? true : false;
	if (source == 'google') {
		google_retries = 0;
		if (input_locations[location_counter].match(/\w\w/) && !has_coordinates_already) {
			GoogleGeocode();
		} else {
			GoogleGeocodeCallback(null);
		}
	} else if (source == 'yahoo') {
		yahoo_retries = 0;
		if (input_locations[location_counter].match(/\w\w/) && !has_coordinates_already) {
			YahooGeocode();
		} else {
			YahooGeocodeCallback(null);
		}
	}

}

function ParseListOfAddresses() { // input_locations[] and input_lines[] are global
	for (j=0; j<input_lines.length; j++) {
		input_locations.push(input_lines[j]);
	}
	if (input_lines.length < 1) { return false; }
	
	// Prepare results box for output
	if (!results_textarea.value.match(/\w/)) {
		results_textarea.value = results_textarea.value + 'latitude'+sep+'longitude'+sep+'name'+sep+'desc';
		if (extra_info) {
			for (var j=0; j<extra_fields.length; j++) {
				results_textarea.value = results_textarea.value + sep+qd(extra_fields[j].replace(/(^\s*|\s*$)/g,''));
			}
		}
		results_textarea.value = results_textarea.value + sep+'color';
		results_textarea.value = results_textarea.value + '\n';
	}
	return true;
}

function ParseTabularAddresses() { // input_locations[] and input_lines[] are global
	
	if (input_lines.length < 2) { return false; }
	
	input_header = input_lines.shift();
	input_delimiter = ',';
	if (input_header.match(/\t/)) { input_delimiter = '\t'; }
	else if (input_header.match(/;/) && !input_header.match(/,/)) { input_delimiter = ';'; }
	else if (input_header.match(/\|/) && !input_header.match(/,/)) { input_delimiter = '|'; }
	input_fields = SmartSplit(input_header,input_delimiter);
	
	input_field_index = new Array;
	for (var j=0; j<input_fields.length; j++) {
		input_fields[j] = input_fields[j].replace(/(^\s+|\s+$)/g,""); // delete leading and trailing white space
		input_fields[j] = input_fields[j].replace(/,$/g,""); // delete trailing commas
		if (input_fields[j].match(/\bnumber\b/i) && !input_fields[j].match(/\b(telephone|phone|tel)\b/i) && input_field_index['number'] == null) { input_field_index['number'] = j; }
		else if (input_fields[j].match(/^(house |business |home |work )?(street|add?ress?e?|rue|intersection|straat|strasse)\w?\b/i) && input_field_index['address'] == null) { input_field_index['address'] = j; }
		else if (input_fields[j].match(/^(add?ress?e? ?2)\b/i) && input_field_index['address2'] == null) { input_field_index['address2'] = j; }
		else if (input_fields[j].match(/^(cit[iy]e?|town|plaats|ort|ville)\w?\b/i) && input_field_index['city'] == null) { input_field_index['city'] = j; }
		else if (input_fields[j].match(/^(state|province)\w?\b/i) && input_field_index['state'] == null) { input_field_index['state'] = j; }
		else if (input_fields[j].match(/\b(zip( ?code)?|postal|posta?l? ?code|p\. ?code|code ?postale?)\w?\b/i) && input_field_index['zip'] == null) { input_field_index['zip'] = j; }
		else if (input_fields[j].match(/\b(countr[iy]e?|nation|land)\w?\b/i) && input_field_index['country'] == null) { input_field_index['country'] = j; }
		else if (input_fields[j].match(/\b(a[ei]ro?port)\w?\b/i) && input_field_index['airport'] == null) { input_field_index['airport'] = j; }
		else if (input_fields[j].match(/\b(telephone|phone|tel)\b/i) && input_field_index['phone'] == null) { input_field_index['phone'] = j; }
		else if (input_fields[j].match(/^(name|nom|naam)$/i) && input_field_index['name'] == null) { input_field_index['name'] = j; }
		else if (input_fields[j].match(/^descr?(iption)?$/i) && input_field_index['desc'] == null) { input_field_index['desc'] = j; }
		else if (input_fields[j].match(/^(lati?|latt?itude)\b/i) && input_field_index['latitude'] == null) { input_field_index['latitude'] = j; }
		else if (input_fields[j].match(/^(long?|lng|long?t?itude)\b/i) && input_field_index['longitude'] == null) { input_field_index['longitude'] = j; }
		else if (input_fields[j].match(/^(colou?re?|couleur)\b/i) && input_field_index['color'] == null) { input_field_index['color'] = j; }
	}
	
	var field_count = 0; for (var i in input_field_index) { field_count++; }
	if (field_count == 0) { return false; }
	
	for (var j=0; j<input_lines.length; j++) {
		var parts = SmartSplit(input_lines[j],input_delimiter);
		var location = '';
		var zip; var state; var country; // these will help us find countries

		if (input_field_index['airport'] != null && parts[input_field_index['airport']]) { location += parts[input_field_index['airport']]; }
		if (input_field_index['address'] != null && parts[input_field_index['address']]) {
			if (input_field_index['number'] != null && parts[input_field_index['number']]) { parts[input_field_index['address']] = parts[input_field_index['number']]+' '+parts[input_field_index['address']]; }
			location += ' '+parts[input_field_index['address']];
		} else if (input_field_index['number'] != null && parts[input_field_index['number']]) { location += ', '+parts[input_field_index['number']]; }
		if (input_field_index['address2'] != null && parts[input_field_index['address2']]) { location += ', '+parts[input_field_index['address2']]; }
		if (input_field_index['city'] != null && parts[input_field_index['city']]) { location += ', '+parts[input_field_index['city']]; }
		if (input_field_index['state'] != null && parts[input_field_index['state']]) { location += ', '+parts[input_field_index['state']]; state = parts[input_field_index['state']]; }
		if (input_field_index['zip'] != null && parts[input_field_index['zip']]) { location += ', '+parts[input_field_index['zip']]; zip = parts[input_field_index['zip']]; }
		if (input_field_index['country'] != null && parts[input_field_index['country']]) { location += ', '+parts[input_field_index['country']]; country = parts[input_field_index['country']]; }
		location = location.replace(/^[\s,;]+|[\s,;]+$|\s+([,;])/g,"$1"); // delete leading and trailing white space and commas
		location = location.replace(/[\s][\s]+/g," "); // compress white space
		input_locations[j] = location;
		
		input_data[j] = [];
		var lat = '';
		if (input_field_index['latitude'] != null && parts[input_field_index['latitude']] != '' && parts[input_field_index['latitude']] != undefined) { lat = parts[input_field_index['latitude']]; }
		if (!lat.match(/\d/)) { lat = ''; }
		input_data[j]['latitude'] = lat;
		var lon = '';
		if (input_field_index['longitude'] != null && parts[input_field_index['longitude']] != '' && parts[input_field_index['longitude']] != undefined) { lon = parts[input_field_index['longitude']]; }
		if (!lon.match(/\d/)) { lon = ''; }
		input_data[j]['longitude'] = lon;
		
		// find Canadian addresses so they don't fall into the Yahoo city-level precision problem; but only mark "country=ca" IF they provided an address or postcode!!
		if (parts[input_field_index['address']] || parts[input_field_index['zip']]) {
			if (zip && zip.match(/^\s*[A-Z][0-9][A-Z]\W?[0-9][A-Z][0-9]\b/i)) { input_data[j]['country'] = 'ca'; }
			else if (country && country.match(/^\s*Can?(ada)?\.?\s*$/i)) { input_data[j]['country'] = 'ca'; }
			else if (state && state.match(/^\s*(AB|Alb(erta)?|B\.?C\.?|Brit(\.|ish)? Col(\.|umbia)?|MB|Man(itoba)?|N\.?B\.?|NF|N\.?L\.?|Newfoundland|N\.?T\.?|Northwest Terr(itory)?|N\.?S\.?|Nova Scotia|NU|Nun(avut)?|ON|Ont(ario)?|P\.?E\.?(I\.?)?|Prince Edward Is?(land)?|PQ|QC|QU|Que|Qu.bec|SK|Sask(atchewan)?|YT|Yukon( Terr\.?| Territory)?)\.?\s*$/i)) { input_data[j]['country'] = 'ca'; }
		}
	}
	
	// Prepare results box for output
	if (!results_textarea.value.match(/\w/)) {
		var header_row = new Array;
		for (var j=0; j<input_fields.length; j++) {
			var h = input_fields[j];
			if (h.indexOf(sep) > -1) { h = '"'+h+'"'; }
			header_row.push(h);
		}
		if (input_field_index['latitude'] == null) { header_row.push('latitude'); }
		if (input_field_index['longitude'] == null) { header_row.push('longitude'); }
		if (extra_info) {
			for (var j=0; j<extra_fields.length; j++) {
				if (input_field_index[extra_fields[j]] == null) {
					header_row.push(qd(extra_fields[j].replace(/(^\s*|\s*$)/g,'')));
				}
			}
		}
		if (input_field_index['name'] == null) { header_row.push('name'); }
		if (input_field_index['desc'] == null) { header_row.push('desc'); }
		if (input_field_index['color'] == null) { header_row.push('color'); }
		results_textarea.value = results_textarea.value + header_row.join(sep) + '\n';
	}
	return true;
}


function YahooGeocode() {
	var loc = input_locations[location_counter].replace(/\t/g,', ');
	yahoo_geocode_script = new JSONscriptRequest( yahoo_base_url + URIEscape_Lite(loc) );
	yahoo_geocode_script.buildScriptTag(); // Build the dynamic script tag
	yahoo_geocode_script.addScriptTag(); // Add the script tag to the page
	lookup_counter += 1;
}

function GoogleGeocode() {
	var loc = input_locations[location_counter].replace(/\t/g,', ');
	google_geocode_script = new JSONscriptRequest( google_base_url.replace(/\bq=/,'q='+URIEscape_Lite(loc)) );
//alert(google_base_url);
	google_geocode_script.buildScriptTag(); // Build the dynamic script tag
	google_geocode_script.addScriptTag(); // Add the script tag to the page
	lookup_counter += 1;
}

function YahooGeocodeCallback(data) {
	var d= {};
	var try_again = false;
	if (data) {
		if (data && data.GeoAddress) {
			d['gv_name'] = data.GeoAddress.replace(/\t/g,"  ");
		}
		if (data && data.GeoPoint && data.GeoPoint.Lat) {
			d['latitude'] = data.GeoPoint.Lat.toString();
			d['longitude'] = data.GeoPoint.Lon.toString();
			// When Yahoo returns more than 4 digits of precision for a Canadian address, it's only accurate at the city level!
			// Well, that used to be true... as of July 2007, there's no longer any way to check on it... bummer.
			//if (input_data[location_counter] && input_data[location_counter]['country'] == 'ca' && parseFloat(d['latitude']) > 41.7 && parseFloat(d['longitude']) < -52) {
			//	if (d['latitude'].match(/\.\d\d\d\d\d+$/) && d['longitude'].match(/\.\d\d\d\d\d+$/)) {
			//		d['latitude'] = '0';
			//		d['longitude'] = '0';
			//		d['precision'] = 'Yahoo: please try again';
			//		try_again = true;
			//	}
			//}
		} else {
			d['latitude'] = '0';
			d['longitude'] = '0';
			d['precision'] = 'Yahoo: not found';
		}
		yahoo_geocode_script.removeScriptTag();
	}
	
	if (try_again && yahoo_retries < 2) { // don't do more than 3 lookups total
		yahoo_retries += 1;
	} else {
		OutputRow(d);
		location_counter += 1;
		yahoo_retries = 0;
	}
	
	if (location_counter < input_locations.length && (!yahoo_limit || lookup_counter < yahoo_limit) && continue_geocoding) {
		var has_coordinates_already = (input_data.length == 0) ? false : ((input_data[location_counter]['latitude'].match(/\d/) && input_data[location_counter]['latitude'] != 0) || (input_data[location_counter]['longitude'].match(/\d/) && input_data[location_counter]['longitude'] != 0)) ? true : false;
		if (input_locations[location_counter].match(/\w\w/) && !has_coordinates_already) {
			window.setTimeout("YahooGeocode()",1000*yahoo_delay);
		} else {
			YahooGeocodeCallback(null);
		}
	} else if (location_counter < input_locations.length && yahoo_limit && lookup_counter >= yahoo_limit) {
		StartButton(false,'geocoding');
		var msg = "Sorry, you're only allowed to send "+yahoo_limit+" addresses at once to the Yahoo! geocoder with this form.";
		alert (msg);
	} else if (location_counter == input_locations.length) { // all done, no limit issues
		StartButton(false,'geocoding');
	}
}

function GoogleGeocodeCallback(data) {
	var d = {};
	var try_again = false;
	if (data) {
		var xml_string = json2xml(data);
		if (data.Status && data.Status.code && data.Status.code == 200) {
			d['latitude'] = data.Placemark[0].Point.coordinates[1].toString();
			d['longitude'] = data.Placemark[0].Point.coordinates[0].toString();
			//if (data.Placemark[0] && data.Placemark[0].ExtendedData && data.Placemark[0].ExtendedData.LatLonBox && data.Placemark[0].ExtendedData.LatLonBox.north && data.Placemark[0].ExtendedData.LatLonBox.west) {			
			//	d['sw_corner'] = data.Placemark[0].ExtendedData.LatLonBox.south.toString()+','+data.Placemark[0].ExtendedData.LatLonBox.west;
			//	d['ne_corner'] = data.Placemark[0].ExtendedData.LatLonBox.north.toString()+','+data.Placemark[0].ExtendedData.LatLonBox.east;
			//}
			d['gv_name'] = data.name.replace(/\t/g,"  ");
			d['gv_desc'] = tag_to_string(xml_string,'address').replace(/, (USA?|United States)$/,'');
			d['address'] = tag_to_string(xml_string,'ThoroughfareName');
			d['city'] = tag_to_string(xml_string,'LocalityName');
			d['state'] = tag_to_string(xml_string,'AdministrativeAreaName');
			d['zip'] = tag_to_string(xml_string,'PostalCodeNumber');
			d['country'] = tag_to_string(xml_string,'CountryNameCode');
			var acc = tag_to_string(xml_string,'Accuracy'); d['precision'] = (google_precision[acc]) ? acc+' ('+google_precision[acc]+')' : acc;
		} else if (data.Status && data.Status.code) {
			d['gv_name'] = data.name.replace(/\t/g,"  ");
			d['precision'] = 'Google error: '+google_status[data.Status.code];
			d['latitude'] = 0;
			d['longitude'] = 0;
		} else {
			// trouble!
		}
		google_geocode_script.removeScriptTag();
	}
	if (try_again && yahoo_retries < 2) { // don't do more than 3 lookups total
		google_retries += 1;
	} else {
		OutputRow(d);
		location_counter += 1;
		google_retries = 0;
	}
	
	if (location_counter < input_locations.length && (!google_limit || lookup_counter < google_limit) && continue_geocoding) {
		var has_coordinates_already = (input_data.length == 0) ? false : ((input_data[location_counter]['latitude'].match(/\d/) && input_data[location_counter]['latitude'] != 0) || (input_data[location_counter]['longitude'].match(/\d/) && input_data[location_counter]['longitude'] != 0)) ? true : false;
		if (input_locations[location_counter].match(/\w\w/) && !has_coordinates_already) {
			window.setTimeout("GoogleGeocode()",1000*google_delay);
		} else {
			GoogleGeocodeCallback(null);
		}
	} else if (location_counter < input_locations.length && google_limit && lookup_counter >= google_limit) {
		StartButton(false,'geocoding');
		var msg = "Sorry, you're only allowed to send "+google_limit+" addresses at once to the Google geocoder with this form.";
		alert (msg);
	} else if (location_counter == input_locations.length) { // all done, no limit issues
		StartButton(false,'geocoding');
	}
}

function OutputRow(d) {
	if (!input_lines[location_counter].match(/\w/)) {
		results_textarea.value = results_textarea.value + '\n';
	} else {
		if (!d['gv_desc'] && d['gv_name'] && self.input_field_index && input_field_index['name'] != null) { d['gv_desc'] = d['gv_name']; }
		d['gv_desc'] = (d['gv_desc'] == '' || d['gv_desc'] == null) ? '-' : d['gv_desc'];
		d['gv_name'] = (d['gv_name'] == '' || d['gv_name'] == null) ? '-' : d['gv_name'];
		var lat, lon, name, desc, color;
		if (input_type == 'table') {
			var row = new Array;
			lat = (input_data[location_counter]['latitude'] != '' && input_data[location_counter]['latitude'] != 0) ? input_data[location_counter]['latitude'] : d['latitude'];
			lon = (input_data[location_counter]['longitude'] != '' && input_data[location_counter]['longitude'] != 0) ? input_data[location_counter]['longitude'] : d['longitude'];
			var parts = SmartSplit(input_lines[location_counter],input_delimiter);
			for (var j=0; j<input_fields.length; j++) {
				if (input_fields[j].match(/^(lati?|latt?itude)\b/i)) {
					row.push(qd(lat));
				} else if (input_fields[j].match(/^(long?|lng|long?t?itude)\b/i)) {
					row.push(qd(lon));
				} else {
					var p = (parts[j]) ? parts[j].replace(/(^\s*|\s*$)/g,'') : '';
					if ((!p || p == '') && d[input_fields[j]]) { p = d[input_fields[j]]; } // replace missing data
					if (input_fields[j].match(/^(colou?re?|couleur)\b/i) && p == '' && added_color != '') { p = added_color; }
					row.push(qd(p));
				}
			}
			if (input_field_index['latitude'] == null) { row.push(qd(lat)); }
			if (input_field_index['longitude'] == null) { row.push(qd(lon)); }
			if (extra_info) {
				for (var j=0; j<extra_fields.length; j++) {
					if (input_field_index[extra_fields[j]] == null) {
						var e = (d[extra_fields[j]]) ? d[extra_fields[j]].replace(/(^\s*|\s*$)/g,'') : ''
						row.push(qd(e));
					}
				}
			}
			if (input_field_index['name'] == null) { row.push(qd(d['gv_name'])); name = d['gv_name']; } else { name = parts[input_field_index['name']];}
			if (input_field_index['desc'] == null) { row.push(qd(d['gv_desc'])); desc = d['gv_desc'];  } else { desc = parts[input_field_index['desc']];}
			if (input_field_index['color'] == null) { row.push(qd(added_color)); color = added_color; }  else { color = parts[input_field_index['color']];}
			results_textarea.value = results_textarea.value + row.join(sep) + '\n';
		} else {
			results_textarea.value = results_textarea.value + qd(d['latitude'])+sep+qd(d['longitude'])+sep+qd(d['gv_name'])+sep+qd(d['gv_desc']);
			lat = d['latitude']; lon = d['longitude']; name = d['gv_name']; desc = d['gv_desc']; color = added_color;
			if (extra_info) {
				for (var j=0; j<extra_fields.length; j++) {
					var e = (d[extra_fields[j]]) ? d[extra_fields[j]].replace(/(^\s*|\s*$)/g,'') : '';
					results_textarea.value = results_textarea.value +sep+qd(e);
				}
			}
			results_textarea.value = results_textarea.value + sep+qd(added_color);
			results_textarea.value = results_textarea.value + '\n';
		}
		
		var desc_on_map = (desc != '' && desc != '-') ? desc : '';
		if (source == 'google' && self.gmap && self.wpts && (lat != 0 || lon != 0)) {
			wpts.push( GV_Marker(gmap,{'lat':lat,'lon':lon,'name':name,'desc':desc_on_map,'label':name,'color':color}) );
			if (!google_marker_bounds) {
				google_marker_bounds = new GLatLngBounds(wpts[wpts.length-1].coords);
				gmap.setCenter(wpts[wpts.length-1].coords,12);
			} else {
				google_marker_bounds.extend(wpts[wpts.length-1].coords);
				gmap.setCenter(google_marker_bounds.getCenter(),gmap.getBoundsZoomLevel(google_marker_bounds)-1);
			}
		} else if (source == 'yahoo' && self.ymap && self.yahoo_wpts && (lat != 0 || lon != 0)) {
			yahoo_wpts.push (new YMarker(new YGeoPoint(lat,lon))); yahoo_marker_bounds.push(new YGeoPoint(lat,lon));
			if (color) { yahoo_wpts[yahoo_wpts.length-1].setSmartWindowColor(color); }
			yahoo_wpts[yahoo_wpts.length-1].addAutoExpand('<span style="font:bold 10px Verdana">'+name.replace(/'/g,"\\'")+'</span>');
			yahoo_wpts[yahoo_wpts.length-1].gv_name = name;
			yahoo_wpts[yahoo_wpts.length-1].gv_desc = desc_on_map;
			yahoo_wpts[yahoo_wpts.length-1].gv_index = yahoo_wpts.length-1;
			YEvent.Capture(yahoo_wpts[yahoo_wpts.length-1], EventsList.MouseClick,yahoo_marker_click);
			ymap.addOverlay(yahoo_wpts[yahoo_wpts.length-1]);
			if (yahoo_marker_bounds.length <= 1) {
				ymap.drawZoomAndCenter(new YGeoPoint(lat,lon),6);
			} else {
				yahoo_marker_bounds.push(new YGeoPoint(lat,lon));
				var new_zoom_and_center = ymap.getBestZoomAndCenter(yahoo_marker_bounds);
				ymap.drawZoomAndCenter(new_zoom_and_center.YGeoPoint,new_zoom_and_center.zoomLevel+1);
			}
		}
		
	}
	results_textarea.scrollTop = results_textarea.scrollHeight;
	
	if (progress_indicator) { progress_indicator.innerHTML = '('+(location_counter+1)+' of '+input_locations.length+' lines processed)'; }
}

function yahoo_marker_click() {
	if (this.openSmartWindow) {
		this.openSmartWindow("<p style=\"font:bold 10px Verdana; margin:0px 0px 6px 0px;\">"+this.gv_name+"</p><p style=\"font:10px Verdana; margin:0px 0px 0px 0px;\">"+this.gv_desc+"</p>");
	}
}


function tag_to_string(xml_text,tag_name) { // read tags out of an XML string
	var pattern = new RegExp('<'+tag_name+'>\\s*([^<]*)\\s*<\/'+tag_name+'>','i');
	var m = pattern.exec(xml_text);
	m = (m) ? m[1] : '';
	return (m)
}

function qd(text) { // qd = quote delimiters (really escaping them)
	if (text == undefined) { return ''; }
	text = text.toString();
	if (text == '') { return ''; }
	var dl = (sep) ? sep : ',';
	var pattern = (dl == '|') ? '\\'+dl : dl;
	if (text.toString().match(pattern)) {
		return '"'+text.replace(/(^\s*|\s*$)/g,'')+'"';
	} else {
		return text.replace(/(^\s*|\s*$)/g,'');
	}
}

function SmartSplit(text,dl) {
	var pattern = new RegExp('(^|'+dl+')\\s*"(.*?)"\\s*(?='+dl+'|$)','g');
	text = text.replace(pattern,
		function (complete_match,dl1,cell_contents) {
			return (dl1+cell_contents.replace(/,/g,'{delimiter}'));
		}
	);
	var parts = text.split(dl);
	for (i=0; i<parts.length; i++) {
		parts[i] = parts[i].replace(/\{delimiter\}/g,dl);
		parts[i] = parts[i].replace(/(^\s+|\s+$)/g,'');
	}
	return parts;
}

function URIEscape(text) {
	text = escape(text);
	text = text.replace(/\//g,"%2F");
	text = text.replace(/\?/g,"%3F");
	text = text.replace(/=/g,"%3D");
	text = text.replace(/&/g,"%26");
	text = text.replace(/@/g,"%40");
	return (text);
}
function URIEscape_Lite(text) {
	text = text.replace(/\//g,"%2F");
	text = text.replace(/\?/g,"%3F");
	text = text.replace(/=/g,"%3D");
	text = text.replace(/&/g,"%26");
	text = text.replace(/@/g,"%40");
	text = text.replace(/\#/g,"%23");
	return (text);
}

function CheckForGeocoderTitle() {
	if (!document.getElementById('gv_title') || (document.getElementById('gv_title') && !document.getElementById('gv_title').innerHTML.match(/gps ?visualizer/i))) {
		var first_h1 = document.getElementsByTagName("h1").item(0);
		if (first_h1 && first_h1.innerHTML.match(/gps ?visualizer/i)) {
			// older version with no id in the H1 tag; it's okay
		} else {
			var new_title = document.createElement('div');
			new_title.innerHTML = '<h1 style="margin-top:0px;" id="gv_title"><a href="http://www.gpsvisualizer.com/">GPS Visualizer\'s</a> Easy Batch Geocoder</h1>';
			var body = document.getElementsByTagName("body").item(0);
			if (body) { body.insertBefore(new_title,body.firstChild); }
		}
	}
}




// JSONscriptRequest -- a simple class for accessing Yahoo! Web Services
// using dynamically generated script tags and JSON
//
// Author: Jason Levitt
// Date: December 7th, 2005
//
// Constructor -- pass a REST request URL to the constructor
//
// NOTE: renamed with "GV_" because apparently Google has its own that overrides mine

function JSONscriptRequest(fullUrl) {
	// REST request path
	this.fullUrl = fullUrl; 
	// Keep IE from caching requests
	this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
	// Get the DOM location to put the script tag
	this.headLoc = document.getElementsByTagName("head").item(0);
	// Generate a unique script tag id
	this.scriptId = 'YJscriptId' + JSONscriptRequest.scriptCounter++;
}

// Static script ID counter
JSONscriptRequest.scriptCounter = 1;

// buildScriptTag method
//
JSONscriptRequest.prototype.buildScriptTag = function () {

	// Create the script tag
	this.scriptObj = document.createElement("script");
	
	// Add script object attributes
	this.scriptObj.setAttribute("type", "text/javascript");
	this.scriptObj.setAttribute("src", (this.fullUrl.match(/&callback=/)) ? this.fullUrl.replace(/&callback=/,this.noCacheIE+'&callback=') : this.fullUrl+this.noCacheIE); // Google requires 'callback' to be the LAST parameter
	this.scriptObj.setAttribute("id", this.scriptId);
}
 
// removeScriptTag method
// 
JSONscriptRequest.prototype.removeScriptTag = function () {
	// Destroy the script tag
	this.headLoc.removeChild(this.scriptObj);  
}

// addScriptTag method
//
JSONscriptRequest.prototype.addScriptTag = function () {
	// Create the script tag
	this.headLoc.appendChild(this.scriptObj);
}




/*	This work is licensed under Creative Commons GNU LGPL License.
	License: http://creativecommons.org/licenses/LGPL/2.1/
	Version: 0.9
	Author:  Stefan Goessner/2006
	Web:	 http://goessner.net/ 
*/
function json2xml(o, tab) {
	var toXml = function(v, name, ind) {
		var xml = "";
		if (v instanceof Array) {
			for (var i=0, n=v.length; i<n; i++)
				xml += ind + toXml(v[i], name, ind+"\t") + '\n';
		}
		else if (typeof(v) == "object") {
			var hasChild = false;
			xml += ind + "<" + name;
			for (var m in v) {
				if (m.charAt(0) == "@")
					xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
				else
					hasChild = true;
			}
			xml += hasChild ? ">" : "/>";
			if (hasChild) {
				for (var m in v) {
					if (m == "#text")
						xml += v[m];
					else if (m == "#cdata")
						xml += "<![CDATA[" + v[m] + "]]>";
					else if (m.charAt(0) != "@")
						xml += toXml(v[m], m, ind+"\t");
				}
				xml += (xml.charAt(xml.length-1)=='\n'?ind:"") + "</" + name + ">";
			}
		}
		else {
			xml += ind + "<" + name + ">" + v.toString() +  "</" + name + ">";
		}
		return xml;
	}, xml="";
	for (var m in o)
		xml += toXml(o[m], m, "");
	return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
}


