/* 
v2.16	25/11/10	Chris Cant - Facebook etc added to main results
v2.15	31/10/10	Chris Cant - Add Twitter and RSS icons
v2.14	10/10/10	Chris Cant - FaceBook version support
v2.13	27/8/10	Chris Cant - solid green location marker added
v2.12	13/8/10	Chris Cant - red markers removed; location red marker added
v2.11	18/7/10	Chris Cant - idDebug not displayed until error arises - AddDebugMsg() added
v2.10	13/6/10	Chris Cant - Location stored in URL hash; logFail added; ThisSearchZoom added; use idInfo not alert
v2.9	8/6/10	Chris Cant - Miles not listed for now
v2.8	7/6/10	Chris Cant - Msg at end of results
v2.7	6/6/10	Chris Cant - Search and Geolocation logging at Google Analytics
v2.6	27/5/10	Chris Cant - InitCentre and InitZoom support
v2.5	23/5/10	Chris Cant - Only show detect if handheld, ie orientation change
v2.4	21/5/10	Chris Cant - Or detect added
v2.3	20/5/10	Chris Cant - Groups page shows popup info below
v2.2	12/5/10	Chris Cant - Highlight msg; smaller icons; wider init zoom
v2.1	8/5/10	Chris Cant - List count and date; always ShowGoogleMap
v2.0	6/5/10	Chris Cant - Only use local kml
v1.15	25/4/10	Chris Cant - Pending search detect
v1.14	25/4/10	Chris Cant - Add geolocation check
v1.13	23/4/10	Chris Cant - Add check that location within UK
v1.12 22/4/10	Chris Cant - Use AJAX Search API for search as it finds postcodes better
v1.11 19/4/10	Chris Cant - handheld disables map
v1.10 17/4/10	Chris Cant - longresults used
v1.9 16/4/10	Chris Cant - showGroupMap() called
v1.8 15/4/10	Chris Cant - Various jslint suggested updates
v1.7 14/4/10	Chris Cant - Only show search marker
v1.6 14/4/10	Chris Cant - Nearest group search list
v1.5b 12/4/10	Chris Cant - Fix PageQuery
v1.5 11/4/10	Chris Cant - Disabled group search
v1.4 10/4/10	Chris Cant
Used by these Freegle Find a Group cases:
- Mapplet: http://maps.iLoveFreegle.org/freegle_mapplet.xml
- Gadget: http://maps.iLoveFreegle.org/freegle_gadget.xml http://maps.iLoveFreegle.org/freegle_gadget.php
- Large web page: eg http://maps.iLoveFreegle.org/
- Iframe, ie small web page: http://maps.iLoveFreegle.org/frmap.php
- Facebook app: iframe http://maps.iLoveFreegle.org/facebook_findagroup.php
- Facebook app: iframe http://maps.iLoveFreegle.org/facebook_findagroup2.php
More technical info here:
http://chriscant.phdcc.com/2010/04/freegle-find-group-mashup.html

// http://frgl.it/if
// http://frgl.it/it
// http://frgl.it/ir
// http://frgl.it/iy
*/
/*jslint */
/*global navigator,google,GIcon,GSize,GPoint,GClientGeocoder,GlocalSearch,GBrowserIsCompatible,document,GMap2,GLatLng,GMapTypeControl,GSmallMapControl,GGeoXml,_IG_FetchXmlContent,GDownloadUrl,GXml,alert,address,GMarker,GEvent,unescape,window,showGroupMap,InitialCentreLat,InitialCentreLng,InitialZoom,_gaq,encodeURIComponent,decodeURIComponent */
var ver = "js2.16";

var map = null;					// map object, (null if handheld map disabled)

var group_info = false; // Group info div: may not be present	// 2.3

var mapplet = false;	// Use mapplet API
var longresults = false; // Use more search results
var show_facebook = false;	// Show group markers with facebook icons
var isHandheld = false; // 1.11	// 2.5
var ShowGoogleMap = true;	// 2.1

var SearchZoom = 10;	// Zoom factor after search

var idInfo = null;		// info DOM element
var idDebug = null;		// debug DOM element
var detect = null;		// detect DOM element	// 1.14
var ordetect = null;	// 2.4

var FreegleGroupsKML_URL = "http://maps.ilovefreegle.org/kml/Freegle.kml"; // 2.0
var FreegleLocalKML = "/kml/Freegle.kml"; // 2.0

var stdMarkerIconURL = "http://maps.ilovefreegle.org/FreegleHeartMesmerFinal32h_small.gif"; // 2.0	// 2.2
// 2.14 var redMarkerIconURL = "http://maps.ilovefreegle.org/FreegleHeartMesmerFinal32h_red2_small.gif"; // 2.0	// 2.2
//var YouAreHereIconURL = "http://maps.ilovefreegle.org/FreegleHeartMesmerFinal32h_red2_small.gif"; // 2.12
//var YouAreHereIconURL = "http://maps.ilovefreegle.org/green_arrow_mo_01_svg_thumb_medium.png"; // 2.12
//var YouAreHereIconURL = "http://maps.ilovefreegle.org/YouAreHere.gif"; // 2.0

var fbMarkerIconURL = "http://maps.ilovefreegle.org/FreegleHeartMesmerFinal32fb2_small.gif"; // 2.14

var YouAreHereIconURL = "http://maps.ilovefreegle.org/arrow_solid_40.png"; // 2.13


// Used in showAddress
var localSearch = false;	// 1.12
var geocoder = new GClientGeocoder();

var geolocator = null;	// 1.14

// Array to store separately loaded KML results
var Groups = []; // 1.6

var KmlLoaded = false;	// true if we've got some GroupPlacemark data
var KmlError = false;		// not used currently

var SearchPending = false;	// Search requested before KML loaded	// 1.15

var UK_North = 60.919755; // 1.13..
var UK_South = 49.724479;
var UK_West = -8.305664;
var UK_East = 1.845703;		// ..1.13

var stdMarkerIcon = false;	// 2.0..
// 2.14 var redMarkerIcon = false;
var fbMarkerIcon = false; // 2.14
var YouAreHereIcon = false;	// 2.12

var YouAreHereMarker = false; // ..2.0

// Initialise map and search form
function initialise(big, _mapplet, _longresults, _show_facebook) {	// 2.14
	try {
		// Remember settings
		mapplet = _mapplet;
		longresults = big;
		if (_longresults !== undefined) {
			longresults = _longresults;
		}
		if (_show_facebook !== undefined) {	// 2.14..
			show_facebook = _show_facebook;
		}																// ..2.14
		
		
		// Give up if browser isn't Maps compatible
		if (!mapplet && !GBrowserIsCompatible()) { return; }

		// Get info and debug DOM element and add our version
		idInfo = document.getElementById("idInfo");
		idDebug = document.getElementById("idDebug");
		idDebug.style.display = "none";
		idDebug.innerHTML += "/" + ver;
		detect = document.getElementById("detect");	// 1.14
		ordetect = document.getElementById("ordetect"); // 2.4

		// Show the search div which is hidden by default so not seen by noscript users
		var jsBlock = document.getElementById("jsBlock");
		if (jsBlock) {
			jsBlock.style.display = "block";
		}

		try {							// 1.11..
			isHandheld = (window.onorientationchange !== undefined); // 2.5
		}
		catch (err2) { } // ..1.11
		ShowGoogleMap = true; // Could be set to !isHandheld	// 2.1
		
		try {							// 1.12..
			localSearch = new GlocalSearch();
		}
		catch (err3) { } // ..1.12


		// 1.14..
		// See if geolocator available
		try {
			if (typeof (navigator.geolocation) === 'undefined') {
				geolocator = google.gears.factory.create('beta.geolocation');
			} else {
			geolocator = navigator.geolocation;
			}
		} catch (err4) { }

		if (isHandheld && geolocator) {	// 2.5
			if (detect) {
				ordetect.style.display = "inline"; // 2.4
				detect.style.display = "inline";
			}
		}
		// ..1.14

		if (ShowGoogleMap) {	// 1.11	// 2.1
			// Get map object and set height
			if (mapplet) {
				map = new GMap2();
			}
			else {
				var MapHeight = queryString2('h', 'up_Height');
				if (MapHeight) {
					document.getElementById("map_canvas").style.height = MapHeight;
				}

				map = new GMap2(document.getElementById("map_canvas"));
				map.enableScrollWheelZoom();	// 1.11
			}

			group_info = document.getElementById("group_info");	// 2.3

			// 2.0..
			stdMarkerIcon = new GIcon();
			stdMarkerIcon.image = stdMarkerIconURL;
			stdMarkerIcon.iconSize = new GSize(15, 19);					// 2.2..
			stdMarkerIcon.iconAnchor = new GPoint(7, 19);
			stdMarkerIcon.infoWindowAnchor = new GPoint(7, 0);	// ..2.2

			/* 2.14
			redMarkerIcon = new GIcon();
			redMarkerIcon.image = redMarkerIconURL;
			redMarkerIcon.iconSize = new GSize(15, 19);					// 2.2..
			redMarkerIcon.iconAnchor = new GPoint(7, 19);
			redMarkerIcon.infoWindowAnchor = new GPoint(7, 0);	// ..2.2*/

			// 2.14..
			fbMarkerIcon = new GIcon();
			fbMarkerIcon.image = fbMarkerIconURL;
			fbMarkerIcon.iconSize = new GSize(23, 32);
			fbMarkerIcon.iconAnchor = new GPoint(15, 32);
			fbMarkerIcon.infoWindowAnchor = new GPoint(15, 20);
			// ..2.14
			

			YouAreHereIcon = new GIcon();												// 2.12..
			YouAreHereIcon.image = YouAreHereIconURL;
			//YouAreHereIcon.iconSize = new GSize(15, 19);
			//YouAreHereIcon.iconAnchor = new GPoint(7, 19);
			//YouAreHereIcon.infoWindowAnchor = new GPoint(7, 0);	// ..2.12
			YouAreHereIcon.iconSize = new GSize(40, 40);
			YouAreHereIcon.iconAnchor = new GPoint(39, 0);
			YouAreHereIcon.infoWindowAnchor = new GPoint(39, 0); // ..2.12
			// ..2.0

			// Get SearchZoom
			if (!mapplet) {
				var SearchZoomParam = queryString2('z','up_SearchZoom');
				if (isInteger(SearchZoomParam)) {
					SearchZoomParam = parseInt(SearchZoomParam, 10);
					if (SearchZoomParam >= 1 && SearchZoomParam <= 15) {
						SearchZoom = SearchZoomParam;
					}
				}
			}

			// Set UK centre
			// 2.6..
			var InitCentre = new GLatLng(55, -2.5);	// UKcentre
			var InitZoom = 5;
			try {
				if (InitialCentreLat && InitialCentreLng && InitialZoom) {
					InitCentre = new GLatLng(InitialCentreLat, InitialCentreLng);
					InitZoom = InitialZoom;
				}
			}
			catch (err5) { }
			map.setCenter(InitCentre, InitZoom); // 2.2
			// ..2.6

			// 2.7..
			try {
				var TestGAQ = _gaq;
			}
			catch (err6) {
				_gaq = null;
			}
			// ..2.7

			// Use appropriate map controls
			if (!mapplet) {
				if (big) {
					map.setUIToDefault();
					map.addControl(new GMapTypeControl());
				}
				else {
					map.addControl(new GSmallMapControl());
				}
			}

			// Load Freegle KML
			// 2.0 var geoXml = new GGeoXml(FreegleGroupsKML_URL);

			// Add Freegle KML overlay
			// 2.0 map.addOverlay(geoXml);
		} // ShowGoogleMap

		// Initiate separate load of (separate) KML into memory
		if (mapplet) {
			_IG_FetchXmlContent(FreegleGroupsKML_URL, processKML);
		}
		else {
			GDownloadUrl(FreegleLocalKML, processKML);
		}

		// Set focus to search text box if required
		var SetFocus = true;
		if (!mapplet) {
			var SetFocusParam = queryString2('f','up_Focus');
			if (SetFocusParam == "0") {
				SetFocus = false;
			}
		}
		if (SetFocus) {
			document.getElementById("search").focus();
		}
	}
	catch (err) {
		AddDebugMsg("initialise failed " + err);	// 2.11
		logFail('initialiseFail', err);	// 2.10
		KmlError = true;
	}
}

// Process received KML, ie store in GroupPlacemark arrays, and do any pending search
function processKML(data, responseCode) {
	var xml = data;	// 2.13a
	try {
		// Load XML
		if (!mapplet) {
			xml = GXml.parse(data);
		}

		// 2.1..
		var kml_date = false;
		var kml_dates = xml.documentElement.getElementsByTagName("date");
		if (kml_dates.length > 0) {
			kml_date = kml_dates[0].firstChild.nodeValue;
			var kmldlen = kml_date.length;
			if (kmldlen > 6 && (kml_date.substring(kmldlen - 6) == " +0000")) {
				kml_date = kml_date.substring(0, kmldlen - 6);
			}
		}
		// ..2.1

		// Find all placemarks
		var Placemarks = xml.documentElement.getElementsByTagName("Placemark");
		var count = 0;
		var len = Placemarks.length;
		//if( len>5) len = 5
		for (var i = 0; i < len; i++) {
			// Get sub-items
			var Placemark = Placemarks.item(i);
			var names = Placemark.getElementsByTagName("name");
			var descriptions = Placemark.getElementsByTagName("description");
			var points = Placemark.getElementsByTagName("Point");
			if (names.length > 0 && descriptions.length > 0 && points.length > 0) {
				var point = points[0];
				var coordinatess = point.getElementsByTagName("coordinates");
				if (coordinatess.length > 0) {
					// Get name, description and co-ordinates
					var name = names[0];
					var description = descriptions[0];
					var coordinates = coordinatess[0];

					var GroupName = name.firstChild.nodeValue;
					var GroupDescription = description.firstChild.nodeValue;

					var strLatLng = coordinates.firstChild.nodeValue; // <coordinates>-21.577148,57.040730,0.000000</coordinates>
					var splitLatLng = strLatLng.split(",");
					strLatLng = splitLatLng[1] + "," + splitLatLng[0];	// Reverse
					var GroupPoint = GLatLng.fromUrlValue(strLatLng);

					var group = new Group(GroupName, GroupDescription, GroupPoint); // 2.3
					
					// 2.0..
					if (ShowGoogleMap) {	// 2.1
						var icon = stdMarkerIcon;															// 2.14..
						if (show_facebook && (group.facebookUrl!==null)) { icon = fbMarkerIcon; }
						group.marker = createGroupMarker(group, icon); // 2.3	// ..2.14
					}

					Groups.push(group); // 1.6	// 2.3
					// ..2.0
				}
			}
			count++;
		}
		// Only mark as loaded if we've actually got something
		if (count > 0) { KmlLoaded = true; }

		// Do any pending querystring search
		if (!mapplet) {
			var GetSearch = queryString('search');
			if (!GetSearch) {
				GetSearch = queryString2('s', 'up_Search');
			}
			if (GetSearch !== null && GetSearch.length > 0) {
				document.getElementById("search").value = GetSearch;
				doSearch();
			}
			else {	// 2.10..
				try {
					GetSearch = decodeURIComponent(window.location.hash);
					if ( (GetSearch.length > 2) && (GetSearch.substr(0,9)!="#up_Focus")) {	// Sent by gadget

						GetSearch = GetSearch.substr(1).replace(/_/g, " ");
						document.getElementById("search").value = GetSearch;
						doSearch();
					}
				}
				catch (err1) { }
			} // ..2.10
		}

		// 1.15..
		if (SearchPending && KmlLoaded) {
			doSearch();
		}
		// ..1.15

		// Add dots to debug to show we're loaded
		idDebug.innerHTML += "..";	// 2.11
		// 2.1..
		if (longresults && KmlLoaded) {
			idDebug.innerHTML += " " + count + " groups";	// 2.11
			if (kml_date) {
				idDebug.innerHTML += ", " + kml_date;	// 2.11
			}
		}
		// ..2.1
	}
	catch (err) {
		AddDebugMsg("processKML failed " + err);	// 2.11
		AddDebugMsg("processKML xml:" + xml); // 2.13a
		logFail('processKMLFail', err); // 2.10
		KmlError = true;
	}
}

// 2.11
function AddDebugMsg(msg)
{
	if( idDebug) {
		idDebug.innerHTML += "<br />"+msg;
		idDebug.style.display = "block";
	}
}

// String trim function,copied from online
String.prototype.trim = function() { return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); };

// 1.14..
// Do geolocation lookup
// Always return false so link isn't followed
function detectLocation() {

	if (!geolocator) {alert("No geolocator available");  return false; }

	try {
		geolocator.getCurrentPosition(displayGeolocation, noGeolocation);
	}
	catch (Err) { }
	
	return false;
}

// Geolocation error function
function noGeolocation(positionError) {
	if (_gaq) {	// 2.7..
		_gaq.push(['_trackEvent', 'Groups', 'GeolocationFail', positionError.message]);
	}						// ..2.7
	idInfo.innerHTML = "Sorry, cannot detect your location";
	idInfo.style.display = "block";
	AddDebugMsg("positionError: " + positionError.message); // 2.11

	if (detect) {
		detect.style.display = "none";
	}
	if (ordetect) {
		ordetect.style.display = "none";
	}
}

// Displays result of geolocation lookup
// Sometimes called twice
function displayGeolocation(location) {
	try {
		if (detect) {
			detect.style.display = "none";
		}

		idInfo.innerHTML = "";
		idInfo.style.display = "none";

		try {
			showGroupMap();
		}
		catch (err1) { }

		AddDebugMsg("Accuracy: " + location.coords.accuracy);	// 2.11

		if (_gaq) {	// 2.7..
			var Msg = location.coords.latitude + ", " + location.coords.longitude + ", " + location.coords.accuracy;
			_gaq.push(['_trackEvent', 'Groups', 'GeolocationOK', Msg]);
		}						// ..2.7

		if (location.coords.accuracy > 10000) {
			ordetect.style.display = "none";	// 2.7
			idInfo.innerHTML = "Accurate location not available";
			idInfo.style.display = "block";
			return false;
		}
		
		var latlng = new GLatLng(location.coords.latitude, location.coords.longitude);
		if (localSearch) {
			// Get address into search box
			localSearch.setSearchCompleteCallback(null, showGeolocationAddress);
			localSearch.execute(latlng);
		}

		showAddressResult(latlng, "You are here");
	}
	catch (err) {
		AddDebugMsg("displayGeolocation Failed " + err);	// 2.11
		idInfo.innerHTML = "Sorry we have a problem...";
		logFail('displayGeolocationFail', err); // 2.10
		idInfo.style.display = "block";
	}
}

function showGeolocationAddress() {
	if (localSearch.results[0]) {
		var address = localSearch.results[0].addressLines.join(", ");
		var search = document.getElementById("search");
		if (search) {
			search.value = address;
			if (_gaq) {	// 2.7..
				_gaq.push(['_trackEvent', 'Groups', 'Geolocation', address]);
			}						// ..2.7
		}
	}
}
// ..1.14

// Handle a search - called directly by submit onclick handler
// Always return false so form isn't submitted
function doSearch() {

	try { // 1.9
		showGroupMap();
	}
	catch (err1) { }

	idInfo.innerHTML = "";
	idInfo.style.display = "none";

	try {
		// Get and check search
		var search = document.getElementById("search").value;
		search = search.trim();

		// 2.7..
		if (_gaq) {
			_gaq.push(['_trackEvent', 'Groups', 'Search', search]);
		}
		// ..2.7
		
		if (search.length === 0) {
			idInfo.innerHTML = "Please enter a place name to search for"; // 2.10
			idInfo.style.display = "block"; // 2.10
			return false;
		}
		window.location.hash = encodeURIComponent(search.replace(/\s/g, "_")); // 2.10	// spaces -> underscores

		// Do a google address lookup
		var address = search + ", UK";
		if (mapplet) {
			geocoder.getLatLngAsync(address, function(latlng) { showAddressResult(latlng, address); });
		}
		else {
			if (localSearch) {	// 1.12..
				showAddressAJAX(address);
			}
			else {							// ..1.12
				geocoder.getLatLng(address, function(latlng) { showAddressResult(latlng, address); });
			}
		}
	}
	catch (err) {
		AddDebugMsg("doSearch Failed " + err);	// 2.11
		idInfo.innerHTML = "Sorry we have a problem...";
		logFail('doSearchFail', err); // 2.10
		idInfo.style.display = "block";
	}
	return false;
}

// 1.12..
// Use AJAX Search API to do search
function showAddressAJAX(address) {
	localSearch.setSearchCompleteCallback(null,
	function() {
		var latlng = false;
		if (localSearch.results[0]) {
			var resultLat = localSearch.results[0].lat;
			var resultLng = localSearch.results[0].lng;
			latlng = new GLatLng(resultLat, resultLng);
		}
		showAddressResult(latlng, address);
	});

	localSearch.execute(address);
}
// ..1.12

// Show the results of a Google address lookup
function showAddressResult(latlng, address) {
	try {
		if (!latlng) {
			idInfo.innerHTML = address + " not found";	// 2.10
			idInfo.style.display = "block"; // 2.10
		}
		else {
			var lat = latlng.lat();
			var lng = latlng.lng();
			if (lat < UK_South || lat > UK_North || lng < UK_West || lng > UK_East) {	// 1.13
				if (address != "You are here") {	// 1.14
					idInfo.innerHTML = address + " not found"; // 2.10
					idInfo.style.display = "block"; // 2.10
				}
				return; // 1.13
			}

			var ThisSearchZoom = SearchZoom; // 2.10

			if (!KmlLoaded) {	// 1.15..
				SearchPending = true;
			}
			else {						// ..1.15
				// 1.6..
				idInfo.style.display = "block";

				// Get distance to all groups
				for (var gno = 0; gno < Groups.length; gno++) {
					Group = Groups[gno];
					Group.DistanceToSearch = latlng.distanceFrom(Group.point);
				}
				// Sort groups by distance
				Groups.sort(NearestGroupSort);

				// Get max count to list
				var MaxNearest = 2;
				if (longresults) { MaxNearest = 5; }
				if (MaxNearest > Groups.length) { MaxNearest = Groups.length; }

				//NearestGroupDistance = 2;

				// Build info list of nearest groups
				var ShownInfo = "";

				var FurthestDistance = 0; // 2.10
				var NearestDistance = 0;	// 2.12

				for (gno = 0; gno<MaxNearest; gno++) {	// 2.10
					Group = Groups[gno];

					// 2.0..
					/* 2.12 if (ShowGoogleMap) {	// 2.1
						map.removeOverlay(Group.marker);
						Group.marker = createGroupMarker(Group, redMarkerIcon);	// 2.3
					}*/

					var DistanceInMiles = Math.round(Group.DistanceToSearch / 1000 * 0.621371192);
					if (gno === 0) {
						NearestDistance = DistanceInMiles;
					}
					
					if (DistanceInMiles > FurthestDistance) {
						FurthestDistance = DistanceInMiles;
					}

					var DistanceText = ""; // 2.8
					/*var DistanceText = " " + DistanceInMiles + " mile";
					if (DistanceInMiles != 1) {
					DistanceText = DistanceText + "s";
					}*/

					// 2.14.. 2.15..
					var ShownLink = Group.Link;
					if (show_facebook) {
						if (Group.facebookUrl !== null) {
							ShownLink = AddMiniIconLink(Group.name, Group.facebookUrl, "Facebook", "http://frgl.it/if");
							ShownLink += " <a target='_top' href='" + Group.facebookUrl + "' title='" + Group.name + " Facebook group'>" + Group.name + "</a>";
							ShownLink += " also on ";
							ShownLink += AddMiniIconLink(Group.name, Group.URL, "Yahoo", "http://frgl.it/iy");
							ShownLink += AddMiniIconLink(Group.name, Group.twitterUrl, "Twitter", "http://frgl.it/it");
							ShownLink += AddMiniIconLink(Group.name, Group.rssUrl, "RSS Feed", "http://frgl.it/ir");
						}
						else {
							ShownLink = AddMiniIconLink(Group.name, Group.URL, "Yahoo", "http://frgl.it/iy");
							ShownLink += " " + Group.Link;
						}
					}
					/*else {	// !show_facebook
						if (longresults) {
							var Icons = "<a href='" + Group.URL + "' target='_top' title='Yahoo group'>" +
							"<img src='http://frgl.it/iy' alt='Yahoo' style='border:0;'/> " +
							"</a>";
							if (Group.facebookUrl != null) {
							Icons += "<a href='" + Group.URL + "' target='_top' title='Facebook page'>" +
							"<img src='http://frgl.it/if' alt='Facebook' style='border:0;'/> " +
							"</a>";
							}
							// ShownLink = Icons + ShownLink;
						}
					}*/
					// 2.16..
					if (longresults) {
						ShownInfo += "<li>" + ShownLink + DistanceText;

						if (!show_facebook && Group.facebookUrl !== null) {
							ShownInfo += "<ul><li style='list-style: none;'><span style='font-size:smaller;'>";
							ShownInfo += "also on ";
							ShownInfo += "<span style='font-family:lucida grande,tahoma,verdana,arial,sans-serif;'>";
							ShownInfo += "<a target='_top' href='" + Group.facebookUrl + "' title='" + Group.name + " Facebook group'>facebook</a>";
							ShownInfo += AddMiniIconLink(Group.name, Group.facebookUrl, "Facebook", "http://frgl.it/if");
							ShownInfo += AddMiniIconLink(Group.name, Group.twitterUrl, "Twitter", "http://frgl.it/it");
							ShownInfo += AddMiniIconLink(Group.name, Group.rssUrl, "RSS Feed", "http://frgl.it/ir");
							ShownInfo += "</span></span></li></ul>";
						}
						
						ShownInfo += "</li>"; // 2.10
					}
					else {
						ShownInfo += ShownLink + DistanceText; // 2.10
						ShownInfo += AddMiniIconLinkNoSpace(Group.name, Group.facebookUrl, "Facebook", "http://frgl.it/if") + " ";
						ShownInfo += " ";
					}
					// ..2.16
					// ..2.14
				}
				if (longresults) {
					var info_list = ShownInfo;
					ShownInfo = "<p>Your local Freegle groups:</p><ul style='margin-top:0;";
					if (show_facebook) { ShownInfo += "list-style:none;"; }
					ShownInfo += "'>" + info_list + "</ul>" + // 2.8
								"<p>The area covered by each group varies - check each group description for details.</p>"; // 2.8

					// 2.3	if (ShowGoogleMap && longresults) {	// 2.2..
					// 2.3		ShownInfo += "<p>These groups are shown with red markers on the map.</p>";
					// 2.3	} // ..2.2
				}
				// ..2.15

				// 2.10..
				if (FurthestDistance > 100) { ThisSearchZoom -= 4; }
				else if (FurthestDistance > 60) { ThisSearchZoom -= 3; }
				else if (FurthestDistance > 30) { ThisSearchZoom -= 2; }
				else if (FurthestDistance > 15) { ThisSearchZoom -= 1; }
				else if (FurthestDistance < 6) { ThisSearchZoom += 1; }
				// ..2.10

				idInfo.innerHTML = ShownInfo;
				idInfo.style.display = "block";
			}

			if (ShowGoogleMap) {	// 1.11	2.1
				// Add marker at search spot
				if (YouAreHereMarker) {	// 2.0..
					map.removeOverlay(YouAreHereMarker);
				}												// ..2.0
				map.setCenter(latlng, ThisSearchZoom); // 2.10
				//if (NearestDistance > 1) {	// 2.12..
					YouAreHereMarker = new GMarker(latlng, { icon: YouAreHereIcon, title: address }); // 1.12	// 2.0
					map.addOverlay(YouAreHereMarker); // 2.0
				//}														// .2.12
			}
			// ..1.6
		}
	}
	catch (err) {
		AddDebugMsg("showAddressResult Failed " + err); // 2.11
		idInfo.innerHTML = "Sorry we have a problem...";
		logFail('showAddressResultFail', err);
		idInfo.style.display = "block";
	}
}

// 2.16..
// 2.15..
function AddMiniIconLink(GroupName, url, descr, icon) {
	return " "+AddMiniIconLinkNoSpace(GroupName, url, descr, icon);
}
// ..2.15

function AddMiniIconLinkNoSpace(GroupName, url, descr, icon) {
	if (url === null) {return ""; }
	return "<a target='_top' href='" + url + "' title='" + GroupName + " "+descr+"'>" +
				"<img src='"+icon+"' alt='"+descr+"' style='border:0;'/></a>";
}
// ..2.16

// Used to create a marker with associated info bubble closure
// 2.3..
function createGroupMarker( group, MarkerIcon) {
	var marker = new GMarker(group.point, { icon: MarkerIcon, title: group.name });
	GEvent.addListener(marker, "click", function() {
		/* 2.15 if (group_info) {
			group_info.style.display = "block";
			group_info.innerHTML = group.descriptionBig;
			//map.setCenter(group.point);
		}
		else {*/
			marker.openInfoWindowHtml(group.description);
		//}
	});
	map.addOverlay(marker);
	return marker;
}
// ..2.3

// Group object
function Group(name, description, point) {	// 2.0
	this.name = name;
	this.description = description;
	this.descriptionBig = description;	// 2.3
	this.point = point; // GLatLng
	this.marker = null; // 2.0	// 2.3

	// 2.3..
	var startBig = '<p style="font-size:small;">';
	var endBig = "</p>";
	var endBigPos = description.lastIndexOf(endBig);
	if( (description.indexOf(startBig) === 0) && (endBigPos!=-1)) {
		this.descriptionBig = description.substring(startBig.length);
		this.descriptionBig = this.descriptionBig.substring(0, description.length - endBig.length);
	}
	// ..2.3
	// 2.15..
	this.facebookUrl = extractURL(description, 'http://www.facebook.com');
	this.twitterUrl = extractURL(description, 'http://twitter.com/');
	this.rssUrl = extractURL(description, 'http://direct.ilovefreegle.org/rss');
	// ..2.15
	
	this.URL = "";
	this.Link = "";

	var hrefpos = description.indexOf("href=\"");
	if (hrefpos != -1) {
		var hrefpos1 = hrefpos + 6;
		var hrefpos2 = description.indexOf("\"", hrefpos1);
		if (hrefpos2 != -1) {

			// Extract group URL
			this.URL = description.substring(hrefpos1, hrefpos2);

			// Add target to description			
			// 2.0 this.description = description.substring(0, hrefpos) + "target='_top' " + description.substring(hrefpos);

			// Get plain link
			this.Link = "<a href='" + this.URL + "' target='_top'>" + name + "</a>";
		}
	}

	this.DistanceToSearch = 0;
}

// 2.15..
function extractURL(description, urlstart) {
	var urlstartpos = description.indexOf(urlstart);
	if (urlstartpos == -1) { return null; }
	var urlendpos = description.indexOf("'", urlstartpos);
	if (urlendpos == -1) { return null; }
	return description.substr(urlstartpos, urlendpos - urlstartpos + 1);
}
// ..2.15

// Sort used to find nearest groups
function NearestGroupSort(a, b) {
	return a.DistanceToSearch - b.DistanceToSearch;
}

// PageQuery: used by queryString
// Amended http://www.eggheadcafe.com/articles/20020107.asp
function PageQuery(q) {
	if (q.length > 1) { this.q = q.substring(1, q.length); }
	else { this.q = null; }

	this.keyValuePairs = [];
	if (q) {
		for (var i = 0; i < this.q.split("&").length; i++) {
			this.keyValuePairs[i] = this.q.split("&")[i];
		}
	}
	this.getKeyValuePairs = function() { return this.keyValuePairs; };
	this.getValue = function(s) {
		for (var j = 0; j < this.keyValuePairs.length; j++) {
			if (this.keyValuePairs[j].split("=")[0] == s) {
				var kv = this.keyValuePairs[j].split("="); // CC..
				if (kv.length < 2) { return false; }
				var v = kv[1];														// ..CC
				return unescape(v.replace(/\x2b/g, " ")); // Convert plus to space, and %xx
			}
		}
		return false;
	};
	this.getParameters = function() {
		var a = new Array(this.getLength());
		for (var j = 0; j < this.keyValuePairs.length; j++) {
			var kv = this.keyValuePairs[j].split("=");	// CC..
			if (kv.length < 2) { return false; }
			var v = kv[1];															// ..CC
			a[j] = unescape(v.replace(/\x2b/g, " ")); // Convert plus to space, and %xx
		}
		return a;
	};
	this.getLength = function() { return this.keyValuePairs.length; };
}

// Get requested querystring variable from location URL
function queryString(key) {
	if (window.location.search) {
		var page = new PageQuery(window.location.search);
		return page.getValue(key);
	}
	return false;
}

// Get queryString from first key1, or if not from second (legacy) key2
function queryString2(key1, key2) {
	var qsv = queryString(key1);
	if (!qsv) {
		qsv = queryString(key2);
	}
	return qsv;
}

// Return true if string represents an integer
function isInteger(s) {
	if (s === undefined) { return false; }
	if ((s === null) || (s.length === 0)) { return false; }
	return (s % 1) === 0; // From http://www.peterbe.com/plog/isint-function comment
}

// 2.10..
function logFail(event, err) {
	if (_gaq) {
		_gaq.push(['_trackEvent', 'Groups', event, err]);
	}
}
// ..2.10

