GC Google Map Enhancements

By Lil Devil Last update Feb 6, 2011 — Installed 13,019 times.

There are 4 previous versions of this script.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

/*
Geocaching Google Map Enhancements
http://www.lildevil.org/greasemonkey/geocaching-google-map-enhancements

--------------------------------------------------------------------------------

This is a Greasemonkey user script.

Follow the instructions on http://www.lildevil.org/greasemonkey/
to install Greasemonkey and this user script.

--------------------------------------------------------------------------------
*/

// ==UserScript==
// @name           GC Google Map Enhancements
// @description    This script enhances the geocaching.com Google map page by increasing the map size, hiding disabled caches, showing cache names, and much more!
// @namespace      http://www.lildevil.org/greasemonkey/
// @version        1.3
// @copyright      2010-2011, Lil Devil
// @license        Attribution-Noncommercial-Share Alike; http://creativecommons.org/licenses/by-nc-sa/3.0/
// @include        http://www.lildevil.org/greasemonkey/versions
// @include        http://*.geocaching.com/map/*
// ==/UserScript==

// the following is for http://www.jslint.com/
/*jslint bitwise: true, browser: true, nomen: false, undef: true */
/*global $ GM_deleteValue GM_getValue GM_log GM_openInTab GM_registerMenuCommand GM_setValue GM_xmlhttpRequest escape localStorage unescape xPath XPathResult unsafeWindow window Add_Prototypes Adjust_List_Height Adjust_Map_Height Backwards_Compatibility Change_Label_Type Check_for_Update Early_Initialization firstElementChild Get_Google_Link Get_Logged_In_User Get_Offsets Get_Parent Get_URL_Parameter Handle_Filter_Click Hide_Columns Init_PM_Filters Late_Initialization LD_Close_Options_Window LD_Create_Options_Window LD_Open_Options_Window LD_addScript LD_addStyle LD_deleteValue LD_getValue LD_log LD_removeStyle LD_setValue Open_Google_Earth Open_Google_Maps Read_Filter_States Set_Label_Style Toggle_Background_Ovals Toggle_Cache_States Toggle_Filters_Visibility Toggle_Option_Appearance Toggle_Smooth_Zooming Update_Filter_States newElement */

(function(){

Check_for_Update('GC Google Map Enhancements', '1.3');

var mapContainer = $('map-content');
var topLinkBar = $('MapOptions');
if (!mapContainer || !topLinkBar) { return; }

Add_Prototypes();

var MinusButtonGray = 'data:image/gif;base64,' +
	'R0lGODlhDAAMAOMMAIqvwoywwo6xxJG0xZO1xpa3x5i4yJq6yZ27y5+9zKG+zaTAzqXBz6XBz6XBz6XBzyH5BAEAAA8ALAAAAAAM' +
	'AAwAAAQt8IFJ6wsv6x3E/o8wgNtAkBpRPEXrtqzxGHRNzweaHYj+IAlfQuEjLo7I5CMCADs=';

var PlusButtonGray = 'data:image/gif;base64,' +
	'R0lGODlhDAAMAOMMAIqvwoywwo6xxJG0xZO1xpa3x5i4yJq6yZ27y5+9zKG+zaTAzqXBz6XBz6XBz6XBzyH5BAEAAA8ALAAAAAAM' +
	'AAwAAAQ58IFJ6wsv6x3EFp4mDNtAagOxEapGFE8hz3JsPEau5/ixHT7NAbFBEDWIxCah1CQUm+gDuqhar48IADs=';

var cm_x = 'data:image/png;base64,' +
	'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAT9JREFUOMu1' +
	'1aFLg0EcxvHv3Y17EYvdZBZMYrLJgiwZBMFifZNNEIwa12RvMgkmg6AWg8O0MOP+BC3TsjR27vVnOhgv7/bevbIH3nDH3ee98HCn' +
	'RASft0TtbGjS4S8nROTHcXtvyNpT6fk5JSI8JspsOdpfllP+kcRx/WG52J/ICBFhAJ2+RfoWERHqfH7/ADoigjoztA4NT7N/3p6I' +
	'ijnpe6JkdvyQc6yPctIiVlwYinqjmZPq3NIqO2kIXoYCrFp2dXFxKD4P9dFlm6rwKnQuvAgPQRfCZXgoWgnPA0LqqFlSdGz5Q6uo' +
	'Y3oa03Md29NQXNcpfwiu65Q/BNffjudYtAofO3q6a8jqoIvwF0O2tIu+AfBpuVp3MLakMXdxMSuObGjJNv2b53PeUAd7OemapRkD' +
	'jhyvXcPN5VTu/NwfKAkh27eh1nMAAAAASUVORK5CYII=';

var cm_dis = 'http://www.geocaching.com/images/gmn/cm_dis.png';

var cm_t = 'data:image/png;base64,' +
	'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAACNJREFUOMtj' +
	'/P//PwMtABMDjcCowaMGjxo8avCowaMGjxSDAYZXAyl/RY8+AAAAAElFTkSuQmCC';

var cm_hvr = 'data:image/png;base64,' +
	'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAUNJREFUOMvt' +
	'lT9LAmEcxz/3R8GcCqlIyMHNq6lyvDfQ1CAI7YIR9AZao6mp8EhocegNFLjrEGTCEV5DTTUcdhAhcQpXei33YDRdnS3hZ/rxGz58' +
	'eR74fSXf9xFUNS0PlIE1YJVw3AFtwChZ1pVYSr7vU9U0BTgC9ojGCbBfsqyeGiyOg6SsFIssZjLER6NQpndF4dm2ua3VAHYBBdiR' +
	'TnO5TeByNptlvVAgNhz+Ou5NvY7T6QBsyyJpRtcjSQGWdV2MZTn4KJZSKaIyn0yKMS8DCSaImkgAxGX+iKl4Kv4vYm+Swo/BAMCT' +
	'gWsAx3UjS1/Hp7YtAwbAU6MRWfw4dhjKheM8tCuVBddxNt76fWLpNDOq+iPhi+dx32xit1oEFXUoDAcAXdMsd00zSmhDvID0rUy3' +
	'vpTpXEhZLyjTs5JlnYvlJ6A1ZyNEKksNAAAAAElFTkSuQmCC';

 // a few global vars for drag and drop support
var dragTarget = null;	// The target object
var dragXoffset = 0;	// How much we've moved the element on the horizontal
var dragYoffset = 0;	// How much we've moved the element on the vertical
LD_addStyle('.dragable { cursor:move; }');

var PMUpgradeOriginalStyleDisplay = $('uxPMUpgrade').style.display;
var advertHidden = false;
var timeout = null;
var Login_Name = Get_Logged_In_User();
var DEBUG = (Login_Name == 'Lil Devil');
Backwards_Compatibility();
Early_Initialization();

window.addEventListener('load', Late_Initialization, false);

function Early_Initialization() {
	// In Greasemonkey 0.8.6 this will run before any page functions run.
	// This will also run early in Opera if "user." is removed from the filename.
	// In older versions of Greasemonkey, and in Opera with "user." in the filename,
	// this function (and everything else in this file) will run after all the page functions.

	LD_Create_Options_Window();

	// provide spacing for certain links
	LD_addStyle('#MapOptions li { white-space:nowrap; margin-right:1em !important; }');

	// create a style to prevent certain elements from printing
	LD_addStyle('@media print {' +
					'.InvisiblePrint { visibility:hidden; } ' +
					'hr { display:none; }' +
				'}');

	// remove a little extra spacing from the Navigation bar
	LD_addStyle('#Navigation .BanManWidget { margin-bottom:0; }');

	// hide the right-most empty cell in the cache list table
	LD_addStyle('#cacheListTable td:last-child, #cacheListTable th:last-child { display:none; }');

	// provide a more prominent hover icon for map markers
	LD_addStyle('img[src$="cm_hvr.png"] { background:url(' + cm_hvr + ') no-repeat left bottom transparent !important;' +
										' height:   0 !important;' +
										' width:    0 !important;' +
										' padding:  0 0 22px 22px !important;' +
										' overflow: hidden !important;' +
										' border:   0 !important;' +
										' }');

	// set the style for the marker labels
	LD_addStyle('div.LabeledMarker_markerLabel { font-weight:bold; font-size:85%; ' +
												'padding:0 1px; white-space:nowrap; }');

	// add some functions to the page
	LD_addScript(Toggle_Cache_States);

	// hide some stuff at the top and bottom of the page
	Toggle_Option_Appearance('Show_Login_Bar');
	LD_addStyle('h2, .BreadcrumbsWidget, .OptionSpacer, #ft { display:none; }');

	// adjust some margins and padding
	document.body.style.padding = '14px';		// originally 1.5em = 19.5px
	$('doc3').style.minWidth = '700px';			// originally 950px
	Get_Parent($('ctl00_HDHomeLink'), 'div').style.width = 'auto';
	Get_Parent($('ctl00_LoginUrl'), 'div').style.width = 'auto';
	$('doc3').style.margin = 'auto';			// originally auto 10px
	$('Content').style.paddingTop = '3px';		// originally 25px
	$('Content').style.paddingRight = '2px';	// originally 1.5em = 19.5px
	$('Content').style.paddingBottom = '0';		// originally 1.5em = 19.5px
	$('lblMapContent').style.marginTop = '7px';	// originally 1.5em = 19.5px
	$('uxSideBar').style.paddingRight = '10px';	// originally part of $('Content').style.paddingRight

	// hide the "map size" links
	firstElementChild(topLinkBar).style.display = 'none';

	// adjust spacing on the right-side info bar
	var filterBox = $('uxPremiumFeatures');
	var refreshMapLink = $('uxMapRefresh');
	$('txtGeocode').style.width = '75%';
	$('uxPocketQueryHomePage').childNodes[1].className = 'NoSpacing';
	filterBox.style.marginTop = '0.5em';
	filterBox.style.padding = '0.4em 0.5em 0.4em 0.6em';
	var hideStuffContainer = Get_Parent($('chkHideFound'), 'p');
	hideStuffContainer.className = 'NoSpacing';
	refreshMapLink.parentNode.style.marginTop = '0.5em';
	$('cacheListBounding').style.margin = '0.5em 0 0 0';
	$('cacheListBounding').style.height = 'auto';

	// set a few items to not show up when printing
	topLinkBar.className += ' HidePrint';
	$('uxZoomToAddress').className = 'HidePrint';
	$('uxPocketQueryHomePage').className = 'HidePrint';
	$('uxSideBarCollapse').className += ' InvisiblePrint';
	refreshMapLink.className += ' HidePrint';
	var chkShowNumbers = $('chkShowNumbers');
	chkShowNumbers.className += ' HidePrint';
	chkShowNumbers.nextSibling.className = 'HidePrint';

	// redefine onclick handlers for a couple of native checkboxes
	$('chkHideFound').setAttribute('onclick', 'Toggle_Cache_States(true);');
	$('chkHideOwned').setAttribute('onclick', 'Toggle_Cache_States(true);');

	// add new checkboxes for hiding ovals and disabled caches
	var PQlink = xPath(
		'.//a[contains(@href, "/pocket/default.aspx")]',
		hideStuffContainer, XPathResult.FIRST_ORDERED_NODE_TYPE);
	if (PQlink) {
		var targetSpan = PQlink.parentNode;

		var newSpan = newElement('label', {	'class' : 'FilterContainer',
											style   : 'margin-bottom:0; font-weight:normal;',
											title   : "Hide caches I haven't found and don't own" },
								newElement('input', {	id      : 'chkHideUnfound',
														'class' : 'Checkbox',
														type    : 'checkbox',
														onclick : 'Toggle_Cache_States(true);' }),
								newElement('img', {		src     : 'http://www.geocaching.com/images/gmn/cm.png',
														width   : 16,
														height  : 16 }),
								newElement('img', {		src     : 'http://www.geocaching.com/images/WptTypes/sm/2.gif',
														style   : 'position:relative; right:14px; bottom:2px; margin-right:-12px;',
														width   : 12,
														height  : 12 }),
								newElement(' Hide Unfound Caches')
							);
		hideStuffContainer.insertBefore(newSpan, targetSpan);
		hideStuffContainer.insertBefore(newElement('br', {}), targetSpan);
		hideStuffContainer.insertBefore(newElement('span', { 'class' : 'Clear' }), targetSpan);

		newSpan = newElement('label', {	'class' : 'FilterContainer',
										style   : 'margin-bottom:0; font-weight:normal;',
										title   : 'Hide caches that are disabled' },
								newElement('input', {	id      : 'chkHideDisabled',
														'class' : 'Checkbox',
														type    : 'checkbox',
														onclick : 'Toggle_Cache_States(true);' }),
								newElement('img', {		id      : 'sampleDisabled',
														src     : ($('chk_Highlight_Disabled').checked ?	cm_x:cm_dis),
														width   : 16,
														height  : 16 }),
								newElement(' Hide Disabled Caches')
							);
		hideStuffContainer.insertBefore(newSpan, targetSpan);
		hideStuffContainer.insertBefore(newElement('br', {}), targetSpan);
		hideStuffContainer.insertBefore(newElement('span', { 'class' : 'Clear' }), targetSpan);

		newSpan = newElement('label', {	'class' : 'FilterContainer',
										style   : 'margin-bottom:0; font-weight:normal;',
										title   : 'Hide the white ovals around the cache icons' },
								newElement('input', {	id      : 'chkHideOvals',
														'class' : 'Checkbox',
														type    : 'checkbox',
														onclick : Toggle_Background_Ovals }),
								newElement('img', {		src     : 'http://www.geocaching.com/images/gmn/cm.png',
														width   : 16,
														height  : 16 }),
								newElement(' Hide Backgound Ovals')
							);
		hideStuffContainer.insertBefore(newSpan, targetSpan);
		hideStuffContainer.insertBefore(newElement('br', {}), targetSpan);
		hideStuffContainer.insertBefore(newElement('span', { 'class' : 'Clear' }), targetSpan);
	}

	// add button to hide the filters
	filterBox.insertBefore(newElement('img', {	id      : 'ToggleFilters',
												src     : MinusButtonGray,
												title   : 'Click to hide filters',
												'class'   : 'HidePrint',
												style   : 'cursor:pointer; float:right; margin-top:2px;',
												onclick : Toggle_Filters_Visibility }),
							filterBox.firstChild);

	// set previous states to the filters
	Init_PM_Filters();
//	alert(unsafeWindow.lgnds);

	// Add Google Map link
	var GoogleMapIcon = 'data:image/gif;base64,' +
		'R0lGODlhEAAQAMZ2AO4YG9geIeMbH0VLUUhOUOQnH0lRWkxSU0hTYU1TWUxWY1FXWExYa1ZcYlFdbXhUVvA0ODxfuFpfYVVhb1Bi' +
		'iUpjm1VlhEVltkVmvFlnf25lZ4RgYdtJS0lquF1tiE1tuk1vwG9wWmJwhFNwtGxxcnp4S1Z4x2h4k3p6WHV5em1+nXh+h36Cg3iE' +
		'l2SFzX+FiWyGy4eIfnmJp4eKi4iLjPRucHGR1JOWl4WYsH6X1KedNfSJIpqcnZCgvomg2J+hooaj3PaKjIyi2Iymy6OkpZSlyZWp' +
		'vZ+sw6ytrpay4qaxx6e316+4yba4uam44K6719fELr6+v7TB5M3Hf8HExLnH5sfHx7HN78DN6LPS87/P7s/Q0u7ZKrrV87rW88jT' +
		'7bjX9c7X7cnZ7frjJ8Hg+tvc3PvU1PrlNMjk/Nrh8fvpXczo/ubn5/zi4ubr9fzxkv31uv3w8PLz8/b5/P788f7+/v//////////' +
		'/////////////////////////////yH5BAEKAH8ALAAAAAAQABAAAAfUgH+Cg4SFhVY8JAQOBgspUYZEDR49Mi0tMioKNIQ8CkUr' +
		'M1RsdVtNWhksgp5HB011sG01HHI+DEhWCUuusHFBAAEacmkVKTQnLzewZhAAAhtysCAEErvRdGc7BQ/RdXMRCgNKM7BqY1wlZbB1' +
		'WBgDCkw/dW9jUCjq6zAfEgRiP3BjdMToBktKBAo0Uhh5MiUElXWwvkTokMAKkgldcjgJU8cNlipCTFxAwEMQCxFgrgCxkcSLiwgj' +
		'FJQcREMBjixk1qAZYqEBEUNRUiwwoIAAiZmDAgEAOw==';
	topLinkBar.appendChild(newElement('li', {},
									newElement('a', {	id		: 'ViewInMap',
														'class'	: 'lnk',
														href	: '#',
														onclick	: Open_Google_Maps },
												newElement('img', { src : GoogleMapIcon }),
												newElement(' '),
												newElement('span', {}, newElement('View in Google Maps'))
												)));

	// Add Google Earth link
	var GoogleEarthIcon = 'data:image/gif;base64,' +
		'R0lGODlhEAAQAOf6AAMHRAsQFggMOQwONRATJBMWNw4WVxUWTBcaQxQcShgaTRcZVhkcRR4eQhwhPhwcYhggYR8iRR4fWBgedCEj' +
		'TB4kYh4kciYmXB8oaygnYCgoXiQpaSssSyouRyUrZSAmliotWiQphygpgystZi4yYiUpri0ugSwuhScvkikxhCgvlzM4TDUyaSw3' +
		'eSw1hDAzhCowqigypSs0my02ky41py80qi84lzs8ajA9gy44qTU/dDk8fzM7njE8pjY7oUFDXDY9sTpEgTdApzY/uDlEnkZIcTlD' +
		'sjhDtjtDsEBKfz9Ik0dJekFNdzxEwztFwUROi0tNgE5WY01Wc0tPpkdSvE5aglpbb1dbiEtV0Fpbh1xcgE9fnWBdfE1YzFBYxU5g' +
		'rFtgl1Fcz2Jjf2VmaF5jpWRllWZnhl5omVhnvF9qomVnsWhyfmBxp2hrsnByiWp1i3F0g2R2pHZ1lXp3jGZ013p5i3p6ln1/i32B' +
		'gnJ8w357qnt6vG156XyCnHyDnYOBmX6DoXeDt31/yn+ErISJjH2Eun2IpneE4IOLnoGFxIaIrnyI0oiKsISQpomNs4GN132N5IqU' +
		'oI+Ss5CSuIyWr4OU3I6R1JWYy5CczpifsZeW5JqksJykt6CiyaOlzqeovaGuwZqv2puv4aiw36Sx5qSz3qu12Ki43K232re7x7C7' +
		'6bq9xLe/wrC85ru62a7B4bHB2ru737q99LjF1sS95rfA/7/J1b/L4cfL1LzP78bQ68vU2c/P8c3P/8/O/87T8dbV9dfY9s/b/9jf' +
		'2NHd/87k787i/9fi9ODh/9Lp+9np/9nr/97p/+Pv/9/z/+Xw/+Dz/+H1/+/v+/Hu//Xx/+X6//Tz//f0//P3//f48+37//32/vb6' +
		'//P8//v5/Pj7//r6//z5//H///f8//r7/fz6//r99vn/8fb+//f+///6//r9//z8///7/vf////7//n///79///9/fv///3+///9' +
		'//7/+f/+////+v7//////////////////////////yH5BAEKAP8ALAAAAAAQABAAAAj+AP8JFKhJkKVLgyYNXPjP0YgdatokktVL' +
		'kR2GiCiZGkXmhQgTe5C5qjPQEDNt4sQ1qzXlxAMoyD79EYhJWKlczqItI/XEAwlGweT8e7QIxxY2oYxVg3aKSQIQnmLJ8SKDCJpD' +
		'kCqJSlYMVpUFErT8AJKjB5UwfOh8ifNKWSswBwZwsCEERhMnWLococEjjypdfW5cUJJCxQckRmIMKVFjgo43mTqVKZQkUJoWLmag' +
		'8BHCQgUBDlbA+efn2S1UZ4JgMABhAwAFBQIIJMRql61js0A1knJlSRYxeAaOWZUqyppImzgBkmTGCsM5XBpQQMAgQgcCdxgO1MMi' +
		'g4YibhgCBgQAOw==';
	topLinkBar.appendChild(newElement('li', {},
									newElement('a', {	id		: 'ViewInEarth',
														'class'	: 'lnk',
														href	: '#',
														onclick	: Open_Google_Earth },
												newElement('img', { src : GoogleEarthIcon }),
												newElement(' '),
												newElement('span', {}, newElement('View in Google Earth'))
												)));

	// add duplicate "Refresh" link
	var dupRefresh = $('uxMapRefresh').cloneNode(true);
	dupRefresh.id += '_dup';
	topLinkBar.appendChild(newElement('li', {}, dupRefresh));

	// add selection for cache labels
	var labelType = LD_getValue('Label_Type', 0, Login_Name) - 0;
	Set_Label_Style(labelType);

	var newSelect = newElement('select', {	id		 : 'labelType',
											style    : 'font-weight:bold; font-family:inherit;' +
														'font-size:inherit; color:inherit;',
											'class'  : 'HidePrint',
											onchange : Change_Label_Type },
								newElement('option', {	value : 0,
														selected : (labelType === 0),
														text     : 'Number' }),
								newElement('option', {	value : 1,
														selected : (labelType == 1),
														text     : 'GC Code' }),
								newElement('option', {	value : 3,
														selected : (labelType == 3),
														text     : 'Code' }),
								newElement('option', {	value : 2,
														selected : (labelType == 2),
														text     : 'Name' })
								);
	chkShowNumbers.nextSibling.textContent = 'Show ';
	chkShowNumbers.parentNode.insertBefore(newSelect, chkShowNumbers.nextSibling.nextSibling);
	var dupLabel = chkShowNumbers.nextSibling.cloneNode(false);
	dupLabel.textContent = ' on Map';
	chkShowNumbers.parentNode.insertBefore(dupLabel, newSelect.nextSibling);

	if (Login_Name) {
		GM_registerMenuCommand('Map Enhancements', LD_Open_Options_Window, '', '', 'E');

		Toggle_Option_Appearance('Show_MyTopo_Link');
		Toggle_Option_Appearance('Show_Self_Link');
		Toggle_Option_Appearance('Show_Beta_Map_Link');
		Toggle_Option_Appearance('Show_Google_Map_Link');
		Toggle_Option_Appearance('Show_Google_Earth_Link');
		Toggle_Option_Appearance('Show_Dup_Refresh_Link');
		Toggle_Background_Ovals();
	}

	window.addEventListener('resize', Adjust_Map_Height, false);

	// support for FastGCUserMenu
	$('hd').addEventListener('DOMNodeInserted', Adjust_Map_Height, false);

	var center = null;
	if (unsafeWindow.map) {
		center = unsafeWindow.map.getCenter();
	}
	Adjust_Map_Height();
	if (center) {
		unsafeWindow.map.setCenter(center);
	}

	// Since this script is now controlling the map height,
	// remove functionality from the page function.
	unsafeWindow.setMapSize = function (b) { return; };

	// getIcon() defines the icon and background graphic for each cache.
	// Insert a hook that will add a red "X" overlay if the cache is disabled.
	unsafeWindow._getIcon = unsafeWindow.getIcon;
	unsafeWindow.getIcon = function (f,e,b,a) {	var ico = unsafeWindow._getIcon(f,e,b,a);
												if (!a) {
													if ($('chk_Highlight_Disabled').checked) {
														ico.transparent = cm_x;
													} else {
														ico.transparent = cm_t;
													}
												}
												return ico;
											};

	// updateSideBarList() is called after hiding My Finds and My Caches
	// so before calling updateSideBarList(), also hide Disabled Caches.
	unsafeWindow._updateSideBarList = unsafeWindow.updateSideBarList;
	unsafeWindow.updateSideBarList = function() {	unsafeWindow.Toggle_Cache_States();
													unsafeWindow._updateSideBarList();
												};

	// setMapLabelDisplay() needs entirely different logic,
	// so it's easiest to just rewrite it here instead of hooking it.
	unsafeWindow.setMapLabelDisplay =
		function (c) {	var mrks = unsafeWindow.mrks;
						for(var a=0,b=mrks.length;a<b;a++){
							if(c){
								if(!mrks[a].isHidden()) {
									mrks[a].showLabel();
								}
							}else{
								mrks[a].hideLabel();
							}
						}
						Hide_Columns();
					};

	// createMarker() is called for each cache that is retrieved from the server.
	// insert a hook to change the label text
	unsafeWindow._createMarker = unsafeWindow.createMarker;
	unsafeWindow.createMarker = function (e) {	var m = unsafeWindow._createMarker(e);
												var labelType = $('labelType').value-0;
												switch (labelType) {
													case 1 :
														m.labelText_ = m.waypointId;
														break;
													case 2 :
														m.labelText_ = m.waypointTitle.trim();
														break;
													case 3 :
														m.labelText_ = m.waypointId.substring(2);
														break;
												}
												return m;
											};

	// hacker fun - uncomment the following 2 lines to show what is returned by the AJAX call
//	unsafeWindow._csae = unsafeWindow.csae;
//	unsafeWindow.csae = function(f,d,a) { window.alert(a); unsafeWindow._csae(f,d,a); };
}

function Late_Initialization() {
	// This function will run after all the page functions.

	// Enable continuous smooth zooming for select browsers
	Toggle_Smooth_Zooming();

	// Enable the Google Earth Plugin - cool to play with, but not ready for prime time
//	unsafeWindow.map.addMapType(unsafeWindow.G_SATELLITE_3D_MAP);

	// The "new Maps Beta" link is not initialized properly. Fix it.
	var betaMaps = $('lnkViewMapBeta');
	var parts = document.location.toString().split('?');
	if ((betaMaps.href.indexOf('?') === -1) && (parts.length > 1)) {
		betaMaps.href += '?' + parts[1].replace(/&mt=(.*?)(?=&|$)/, '');
		// remove any &mt= parameter because it screws up the new map
	}

	// for some reason the table height is a little short at this point, so resize it again
	Adjust_List_Height();

	// If not logged in, or not a Premium Member, all the checkboxes get disabled by page script.
	// Re-enable the ones that this script created.
	if (Login_Name) {
		$('chkHideUnfound').removeAttribute('disabled');
	}
	$('chkHideDisabled').removeAttribute('disabled');
	$('chkHideOvals').removeAttribute('disabled');
	$('statusbar').style.display = 'none';
}

function Toggle_Filters_Visibility() {
	var theButton = $('ToggleFilters');
	var filterBox = $('filterLegend');
	var PMUpgrade = $('uxPMUpgrade');
	if (filterBox.style.display == 'none') {	// hidden - show it
		filterBox.style.display = 'block';
		PMUpgrade.style.display = PMUpgradeOriginalStyleDisplay;
		theButton.src = MinusButtonGray;
		theButton.title= 'Click to hide filters';
	} else {									// visible - hide it
		filterBox.style.display = 'none';
		PMUpgrade.style.display = 'none';
		theButton.src = PlusButtonGray;
		theButton.title= 'Click to show filters';
	}
	Adjust_List_Height();
}

function Init_PM_Filters() {
	var PMfilters = $('uxSideBar').getElementsByTagName('input');
	var states = Read_Filter_States();
	for (var i=0, len=PMfilters.length; i < len; i++) {
		var thisID = PMfilters[i].id;
		if (/^chk(Legend|Hide|Show)/.test(thisID)) { // chkLegend
			if (states.hasOwnProperty(thisID)) {
				PMfilters[i].checked = states[thisID];
			}
			PMfilters[i].addEventListener('click', Handle_Filter_Click, false);
		}
	}
}

function Handle_Filter_Click() {
	Update_Filter_States(this.id, this.checked);
}

function Read_Filter_States() {
	var filtersStates = {},
		str = LD_getValue('PM_Filters', '', Login_Name);
	if (str) {
		str.split(',').forEach(function(x) {var arr2 = x.split(':');
											filtersStates[arr2[0]] = (arr2[1] == 'true');
											});
	}
	return filtersStates;
}

function Write_Filter_States(filtersStates) {
	var f, arr=[];
	for (f in filtersStates) {
		if (filtersStates.hasOwnProperty(f)) {
			arr.push(f + ':' + filtersStates[f]);
		}
	}
	LD_setValue('PM_Filters', arr.sort().join(','), Login_Name);
}

function Update_Filter_States(key, val) {
	var states = Read_Filter_States();
	states[key] = val;
	Write_Filter_States(states);
}

function Backwards_Compatibility() {
	// adjust for changes from version 1.1 to 1.2
	var compat = {	'chkHideOvals'    : 'Hide_Background_Ovals',
					'chkHideDisabled' : 'Hide_Disabled_Caches',
					'chkHideUnfound'  : 'Hide_Unfound_Caches'
				},
		states = Read_Filter_States(),
		b, c;
	for (c in compat) {
		b = LD_getValue(compat[c], null, Login_Name);
		if (typeof b == 'boolean') {
			states[c] = b;
		}
		LD_deleteValue(compat[c], Login_Name);
		LD_deleteValue(compat[c]);
	}
	Write_Filter_States(states);
}

function Adjust_Map_Height() {
	// if the left navbar is too tall, hide the advertisement so the page won't have any scrollbars
	if (!advertHidden) {
		var leftNavBar = $('Navigation');
		if ((leftNavBar.offsetTop + leftNavBar.offsetHeight + 15) > window.innerHeight) {
			advertHidden = true;
			LD_addStyle('.BanManWidget { display:none; }');
		}
	}

	var newHeight = window.innerHeight - Get_Offsets(mapContainer).top - 16;
	mapContainer.style.height = newHeight + 'px';
	if (unsafeWindow.map) {
		unsafeWindow.map.checkResize();
	}
	Adjust_List_Height();
}

function Adjust_List_Height() {
	var list = $('cacheListBounding');
	var newHeight = window.innerHeight - Get_Offsets(list).top - 21;
	list.style.maxHeight = newHeight + 'px';
}

function Set_Label_Style(labelType) {
	if (labelType == 2) {	// name
		LD_addStyle('div.LabeledMarker_markerLabel { font-weight:normal; }', 'LD_Marker_Style');
	} else {
		LD_removeStyle('LD_Marker_Style');
	}
}

function Change_Label_Type() {
	var labelType = $('labelType').value-0;
	LD_setValue('Label_Type', labelType, Login_Name);
	Set_Label_Style(labelType);

	var markerLabels = xPath(
		'.//div[contains(@class, "LabeledMarker_markerLabel")]',
		$('map'), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

	for (var i=0, len=markerLabels.snapshotLength; i<len; i++) {
		var thisLabel = markerLabels.snapshotItem(i),
			cacheNumber = i.toString(), GCcode = 'error', cacheName = 'error', noGCcode = 'error';
		var m = thisLabel.title.match(/^(.*)\((\w+)\)$/);
		if (m) {
			cacheName = m[1].trim();
			GCcode    = m[2];
			noGCcode  = m[2].substring(2);
		}
		switch (labelType) {
			case 1 :	// GC code
				thisLabel.innerHTML = GCcode;
				break;
			case 2 :	// name
				thisLabel.innerHTML = cacheName;
				break;
			case 3 :	// no GC code
				thisLabel.innerHTML = noGCcode;
				break;
			default :	// number
				thisLabel.innerHTML = cacheNumber;
		}
	}
	Hide_Columns();
}

function Hide_Columns() {
	if ($('chkShowNumbers').checked && (Number($('labelType').value) === 0)) {
		LD_removeStyle('Hide_Columns_Style');
	} else {
		LD_addStyle('#cacheListTable td[cellType="cacheIndex"],' +
					'#cacheListTable th[cellType="cacheIndex"] { display:none; }', 'Hide_Columns_Style');
	}
}

function Handle_Highlight_Disabled_Option() {
	var newState = this.checked;
	LD_setValue('Highlight_Disabled', newState, Login_Name);
	var oldOverlay = cm_x,
		newOverlay = cm_t,
		newSample  = cm_dis;
	if (newState) {
		oldOverlay = cm_t;
		newOverlay = cm_x;
		newSample  = cm_x;
	}
	$('sampleDisabled').src = newSample;
	var XPath = xPath(
		'.//img[contains(@src, "' + oldOverlay + '")]',
		$('map'), XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE);

	for (var i=0, len=XPath.snapshotLength; i<len; i++) {
		XPath.snapshotItem(i).src = newOverlay;
	}
}

function Handle_Option_Click() {
	var valName = this.id.substring(4);
	LD_setValue(valName, this.checked, Login_Name);
	switch (valName) {
		case 'Show_Login_Bar'			: Toggle_Option_Appearance(valName);	break;
		case 'Show_Link_Bar'			: Toggle_Option_Appearance(valName);	break;
		case 'Show_MyTopo_Link'			: Toggle_Option_Appearance(valName);	break;
		case 'Show_Self_Link'			: Toggle_Option_Appearance(valName);	break;
		case 'Show_Beta_Map_Link'		: Toggle_Option_Appearance(valName);	break;
		case 'Show_Google_Map_Link'		: Toggle_Option_Appearance(valName);	break;
		case 'Show_Google_Earth_Link'	: Toggle_Option_Appearance(valName);	break;
		case 'Show_Dup_Refresh_Link'	: Toggle_Option_Appearance(valName);	break;
		case 'Smooth_Zooming'			: Toggle_Smooth_Zooming();				return; // don't adjust map
		default							: Toggle_Option_Appearance(valName);
	}
	Adjust_Map_Height();
}

function Toggle_Option_Appearance(valueName) {
	var show = LD_getValue(valueName, true, Login_Name),
		obj;
	switch (valueName) {
		case 'Show_Login_Bar'			: obj = $('hd');							break;
		case 'Show_Link_Bar'			: obj = $('MapOptions');					break;
		case 'Show_MyTopo_Link'			: obj = $('lnkOrderMyTopo').parentNode;		break;
		case 'Show_Self_Link'			: obj = $('lp').parentNode;					break;
		case 'Show_Beta_Map_Link'		: obj = $('lnkViewMapBeta').parentNode;		break;
		case 'Show_Google_Map_Link'		: obj = $('ViewInMap').parentNode;			break;
		case 'Show_Google_Earth_Link'	: obj = $('ViewInEarth').parentNode;		break;
		case 'Show_Dup_Refresh_Link'	: obj = $('uxMapRefresh_dup').parentNode;	break;
	}
	if (obj) {
		obj.style.display = show ? '' : 'none';
	}
}

function Toggle_Smooth_Zooming() {
	if (LD_getValue('Smooth_Zooming', true, Login_Name)) {
		unsafeWindow.map.enableContinuousZoom();
	} else {
		unsafeWindow.map.disableContinuousZoom();
	}
}

function Toggle_Background_Ovals() {
	var newState = $('chkHideOvals').checked;
	Update_Filter_States('chkHideOvals', newState);
	if (newState) {
		LD_addStyle('#map img[src$="/gmn/cm.png"] { visibility:hidden; }', 'Hide_Background_Oval_Style');
	} else {
		LD_removeStyle('Hide_Background_Oval_Style');
	}
}

function Toggle_Cache_States(upd) {	// this function will be injected into the page
//	window.alert(lgnds[0]);
	var chkHideFound    = $('chkHideFound').checked;
	var chkHideOwned    = $('chkHideOwned').checked;
	var chkHideUnfound  = $('chkHideUnfound').checked;
	var chkHideDisabled = $('chkHideDisabled').checked;
	var i, len = mrks.length;
	for (i=0; i<len; i++) {
		var thisMark = mrks[i];
		if ((chkHideFound && thisMark.found) ||
			(chkHideOwned && thisMark.owned) ||
			(chkHideUnfound && !thisMark.found && !thisMark.owned) ||
			(chkHideDisabled && !thisMark.isAvailable)) {
			thisMark.hide();
		} else if (lgnds.byTypeID(thisMark.wptTypeId).visible) {
			thisMark.show();
		}
	}
	if (upd===true) {
		updateSideBarList();
	}
}

function Get_Google_Link() {
	// find the "Powered by Google" image/link and return it's url
	var PbGlink;
	var poweredBy = xPath(
		'.//a/img[contains(@src, "/mapfiles/poweredby.png")]',
		mapContainer, XPathResult.FIRST_ORDERED_NODE_TYPE);
	if (poweredBy) {
		PbGlink = poweredBy.parentNode.href;
	} else {
		LD_log('"Powered By Google" image/link not found.');
	}
	return PbGlink;
}

function Open_Google_Earth() {
	var viewLink = $('ViewInEarth');
	var glink = Get_Google_Link();
	if (glink) {
		viewLink.href = 'http://maps.google.com/maps?q=' + Get_URL_Parameter('ll', glink) +
						'&spn=' + Get_URL_Parameter('spn', glink) + '&output=kml';
	} else {
		viewLink.href = '#';
		return false;
	}
}

function Open_Google_Maps() {
	var viewLink = $('ViewInMap');
	var glink = Get_Google_Link();
	if (glink) {
		viewLink.target = '_blank';
		viewLink.href = glink;
	} else {
		viewLink.target = '';
		viewLink.href = '#';
		return false;
	}
}

 // drag and drop support adapted from http://www.hunlock.com/blogs/Javascript_Drag_and_Drop
function moveHandler(e){
	if (!e) { e = window.event; }
	dragTarget.parentNode.style.left = e.clientX - dragXoffset + 'px';
	dragTarget.parentNode.style.top  = e.clientY - dragYoffset + 'px';
	e.preventDefault();
}

function dragEnd(e) {
	document.removeEventListener('mousemove', moveHandler, false);
	document.removeEventListener('mouseup', dragEnd, false);
	dragTarget.style.cursor = '';
	LD_setValue('Options_X_Position', parseInt(dragTarget.parentNode.style.left, 10), Login_Name);
	LD_setValue('Options_Y_Position', parseInt(dragTarget.parentNode.style.top,  10), Login_Name);
}

function dragStart(e){
	var dragCursor = '-moz-grabbing';
	if (!e) { e = window.event; dragCursor = ''; }
	if (e.button > 0) { return; }
	dragTarget = e.target || e.srcElement;
	if (dragTarget.className.match(/(^|\s)dragable(\s|$)/)) {
		dragTarget.style.cursor = dragCursor;
		dragXoffset = e.clientX - parseInt(dragTarget.parentNode.style.left, 10);
		dragYoffset = e.clientY - parseInt(dragTarget.parentNode.style.top,  10);

		document.addEventListener('mousemove', moveHandler, false);
		document.addEventListener('mouseup', dragEnd, false);
		e.preventDefault();
	}
}

function LD_Open_Options_Window() {
	var optionsWindow = $('LD_options');
	optionsWindow.style.display = '';
	var Xpos = LD_getValue('Options_X_Position', -9999, Login_Name);
	var Ypos = LD_getValue('Options_Y_Position', -9999, Login_Name);

	// set position to center if top edge is off the top of the screen,
	// or if half the box is off any other edge of the screen
	if ((Ypos < 0) ||
		(Xpos + (optionsWindow.offsetWidth  / 2) < 0) ||
		(Ypos + (optionsWindow.offsetHeight / 2) > window.innerHeight) ||
		(Xpos + (optionsWindow.offsetWidth  / 2) > window.innerWidth) ) {
			Xpos = (window.innerWidth  - optionsWindow.offsetWidth)  / 2;
			Ypos = (window.innerHeight - optionsWindow.offsetHeight) / 2;
	}

	optionsWindow.style.left = Xpos + 'px';
	optionsWindow.style.top  = Ypos + 'px';
}

function LD_Close_Options_Window() {
	$('LD_options').style.display = 'none';
}

function LD_Create_Options_Window() {
	LD_addStyle('#LD_options {   position:fixed; background-color:white; z-index:999;' +
								'padding-bottom:1em; border:1px solid gray;' +
								'border-radius:1.2em; -moz-border-radius:1.2em; -webkit-border-radius:1.2em;' +
								'-moz-box-shadow:20px 20px 10px rgba(0,0,0,0.5); }' +
				'#LD_options .title { font-weight:bold; padding:0.7em 1em 0.3em;' +
								'border-radius:1em 1em 0 0; -moz-border-radius:1em 1em 0 0; -webkit-border-radius:1em 1em 0 0;' +
								'background-color:gray; color:white; white-space:nowrap; }' +
				'#LD_options label { font-weight:normal; display:block; }');

	var optionsWindow = newElement('div', {    id : 'LD_options',
											style : 'display:none;' });
	optionsWindow.appendChild(newElement('div', {	'class' : 'title dragable',
													onmousedown : dragStart },
											newElement('Geocaching Google Map Enhancement Options')));
	var optionsTable = optionsWindow.appendChild(newElement('table',
														{	style : 'margin-top:1em; margin-bottom:1em;',
															align : 'center' }));
	var optionsCell  = optionsTable.insertRow(-1).insertCell(-1);
	optionsCell.style.textAlign = 'left';
	optionsCell.appendChild(newElement('label', { title	: 'Disable this to make more room for the map.'},
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Login_Bar',
															checked : LD_getValue('Show_Login_Bar', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Show title/login bar')));
	optionsCell.appendChild(newElement('label', {	style	: 'margin-top:1em;',
													title	: 'Disable this to make more room for the map.' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Link_Bar',
															checked : LD_getValue('Show_Link_Bar', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Show link bar')));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_MyTopo_Link',
															checked : LD_getValue('Show_MyTopo_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Show MyTopo.com link')));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Self_Link',
															checked : LD_getValue('Show_Self_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement('span', { innerHTML : ' Show &ldquo;Link to this page&rdquo; link' } )));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Beta_Map_Link',
															checked : LD_getValue('Show_Beta_Map_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement('span', { innerHTML : ' Show Beta Maps link' } )));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Google_Map_Link',
															checked : LD_getValue('Show_Google_Map_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Show Google Maps link')));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Google_Earth_Link',
															checked : LD_getValue('Show_Google_Earth_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Show Google Earth link')));
	optionsCell.appendChild(newElement('label', { style : 'margin-left:1em;' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Show_Dup_Refresh_Link',
															checked : LD_getValue('Show_Dup_Refresh_Link', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement('span', { innerHTML : ' Show Duplicate &ldquo;Refresh Map Now&rdquo; link' } )));
	optionsCell.appendChild(newElement('label', {	style	: 'margin-top:1em;',
													title	: 'Enable smooth, continuous zoom animation in Firefox and IE.' },
									newElement('input', {	type    : 'checkbox',
															id		: 'chk_Smooth_Zooming',
															checked : LD_getValue('Smooth_Zooming', true, Login_Name),
															onclick : Handle_Option_Click }),
									newElement(' Enable smooth zooming')));
	optionsCell.appendChild(newElement('label', {	style	: 'margin-top:1em;',
													title	: 'Show a red X over disabled caches in addition to the normal gray background.' },
									newElement('input', {	type    : 'checkbox',
															id      : 'chk_Highlight_Disabled',
															checked : LD_getValue('Highlight_Disabled', true, Login_Name),
															onclick : Handle_Highlight_Disabled_Option }),
									newElement(' Make disabled caches more distinctive')));

	optionsWindow.appendChild(newElement('button', { onclick : LD_Close_Options_Window }, newElement('Close')));
	document.body.appendChild(optionsWindow);
}

 // standard functions
function Add_Prototypes() {
	String.prototype.trim = function() {
		if (!this.length) { return ''; }

		// remove leading and trailing spaces and non-breaking spaces
		var s = this.replace(/^[\s\xA0]*/i, '');
		return     s.replace(/[\s\xA0]*$/i, '');
	};

	String.prototype.parseHTMLentities = function() {
		var d = document.createElement('div');
		d.innerHTML = this;
		return d.textContent;
	};

	String.prototype.repeat = function(len) {
		return Array(len + 1).join(this);
	};

	String.prototype.zeroPad = function(len) {
		return '0'.repeat(len - this.length) + this;
	};

	Number.prototype.zeroPad = function(len) {
		return this.toString().zeroPad(len);
	};
}

function Get_Logged_In_User() {
	var loginLogoutLink = $('ctl00_LoginUrl') || $('ctl00_ContentLogin_uxLoginStatus_uxLoginURL');
	if (!loginLogoutLink) { return; }	// print-friendly page?
	var loginNameLink = loginLogoutLink.parentNode.getElementsByTagName('a')[0];

	// if logged in, loginNameLink will be the link to the username
	// if not logged in, loginNameLink will be the same as loginLogoutLink
	if (loginLogoutLink != loginNameLink) {
		return loginNameLink.textContent.trim();
	}
	return '';
}

function Get_URL_Parameter(fieldName, theUrl) {
	var parts = (theUrl || document.location).toString().split(/[?#]/);
	var queryString = parts[1];

	var re = new RegExp('(^|&)' + fieldName + '=(.*?)(&|$)', 'i');
	if (queryString.match(re)) {
		return RegExp.$2;
	}
	return '';
}

	// offsetHeight and offsetWidth: total size of object
	// clientHeight and clientWidth: total size of content area in view
	// scrollHeight and scrollWidth: total size of content, some may not be visible due to scrollbars
function Get_Offsets(obj) {
	var top = 0, left = 0, width = obj.offsetWidth, height = obj.offsetHeight;
	do {
		top  += obj.offsetTop;
		left += obj.offsetLeft;
	} while ((obj = obj.offsetParent));
	return {top    : top,
			left   : left,
			bottom : top + height,
			right  : left + width };
}

function Get_Parent(thisNode) {
	if (!thisNode || !thisNode.parentNode) { return thisNode; }
	if (arguments.length < 2) { return thisNode.parentNode; }

	for (var i=1; i < arguments.length; i++) {
		var nodeNameToLookFor = arguments[i].toLowerCase();
		do {
			thisNode = thisNode.parentNode;
		} while (thisNode && (thisNode.nodeName.toLowerCase() !== nodeNameToLookFor));
	}
	return thisNode;
}

function firstElementChild(p) {
	if (typeof(p.firstElementChild) != 'undefined') {
		return p.firstElementChild;
	} else {
		var child = p.firstChild;
		while (child && child.nodeType !== 1) {
			child = child.nextSibling;
		}
		return child;
	}
}

function nextElementSibling(p) {
	if (typeof(p.nextElementSibling) != 'undefined') {
		return p.nextElementSibling;
	} else {
		var sibling = p.nextSibling;
		while (sibling && sibling.nodeType !== 1) {
			sibling = sibling.nextSibling;
		}
		return sibling;
	}
}

function previousElementSibling(p) {
	if (typeof(p.previousElementSibling) != 'undefined') {
		return p.previousElementSibling;
	} else {
		var sibling = p.previousSibling;
		while (sibling && sibling.nodeType !== 1) {
			sibling = sibling.previousSibling;
		}
		return sibling;
	}
}

function xPath(expr, context, typ) {
	var result = document.evaluate(	(expr || '//body'),
									(context || document),
									null,
									(typ || XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE),
									null);
	switch (typ) {
		case XPathResult.NUMBER_TYPE: return result.numberValue;
		case XPathResult.STRING_TYPE: return result.stringValue;
		case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
		case XPathResult.ANY_UNORDERED_NODE_TYPE:
		case XPathResult.FIRST_ORDERED_NODE_TYPE: return result.singleNodeValue;
		default: return result;
	}
}

function newElement() {
	if(arguments.length == 1) {
		return document.createTextNode(arguments[0]);
	} else {
		var newNode = document.createElement(arguments[0]),
				 newProperties = arguments[1];
		for (var prop in newProperties) {
			if ((prop.indexOf('on') === 0) && (typeof(newProperties[prop]) == 'function')) {
				newNode.addEventListener(prop.substring(2), newProperties[prop], false);
			} else if (',innerHTML,textContent'.indexOf(','+prop) != -1) {
				newNode[prop] = newProperties[prop];
			} else if ((',checked,disabled,selected'.indexOf(','+prop) != -1) && !newProperties[prop]) {
				// value is false, which browsers do not support, so don't set the property at all
			} else if (/\&/.test(newProperties[prop])) {
				newNode.setAttribute(prop, newProperties[prop].parseHTMLentities());
			} else {
				newNode.setAttribute(prop, newProperties[prop]);
			}
		}
		for(var i=2, len=arguments.length; i<len; ++i) {
			newNode.appendChild(arguments[i]);
		}
		return newNode;
	}
}

function $() {
	if (arguments.length==1) {
		return document.getElementById(arguments[0]);
	}
	var elements = [];
	for (var i = 0; i < arguments.length; i++) {
		var e = arguments[i];
		if (typeof e == 'string') {
			e = document.getElementById(e);
		}
		elements.push(e);
	}
	return elements;
}

function encodeName(str) {
	str = str.replace(/^\s+/,'');		// remove leading spaces
	str = str.replace(/\s+$/,'');		// remove trailing spaces
	str = str.replace(/\s+/g,' ');		// replace interior spaces with single space
	return encodeURIComponent(str).replace(/%20/g, '+');
}

function decodeName(str) {
	str += '';
	return decodeURIComponent(str.replace(/\+/g, ' '));
}

function LD_setCookie(key, val, life) {
	if (!key) { return; }
	if (!life) { life = 31536000; }
	var expires = new Date().getTime() + (1000 * life);
	document.cookie = escape(key) + '=' + escape(val) +
		';expires=' + new Date(expires).toGMTString() + ';path=/';
}

function LD_getCookie(key) {
	var cookieJar = document.cookie.split('; ');
	for (var i=0, len=cookieJar.length; i<len; i++ ) {
		var oneCookie = cookieJar[i].split('=');
		if (oneCookie[0] == escape(key)) {
			return unescape(oneCookie[1]);
		}
	}
	return null;
}

function LD_setValue(key, val, username) {
	if (username !== undefined) {
		if (username) {		// if username is supplied, it must not be blank (i.e. not logged in)
			key += '_' + encodeName(username);
		} else {
			return;
		}
	}
	if ((typeof(GM_setValue) != 'undefined') &&
		(GM_setValue.toString().indexOf('not supported') < 0) &&
		!window.opera) {	// don't use Opera compatibility script because it probably uses cookies
			GM_setValue(key, val);
			return;
	}
	val = (typeof(val)).toString().substring(0,1) + val;
	try {
		localStorage.setItem(key, val);
	} catch (err) {
		// if we get here, either localStorage doesn't exist, or we got a Security Error using it
		LD_setCookie(key, val);
	}
}

function LD_getValue(key, defVal, username) {
	if (username !== undefined) {
		if (username) {		// if username is supplied, it must not be blank (i.e. not logged in)
			key += '_' + encodeName(username);
		} else {
			return;
		}
	}
	if ((typeof(GM_setValue) != 'undefined') &&
		(GM_setValue.toString().indexOf('not supported') < 0) &&
		!window.opera) {	// don't use Opera compatibility script because it probably uses cookies
			return GM_getValue(key, defVal);
	}
	var val;
	try {
		val = localStorage.getItem(key);
	} catch (err) {
		val = LD_getCookie(key);
	}
	if (typeof(val) != 'string' || val.length === 0) {
		return defVal;
	}
	var type = val.substr(0,1);
		 val = val.substr(1);
	switch (type) {
		case 'b':
			return (val == 'true');
		case 'n':
			return Number(val);
		default:
			return val;
	}
}

function LD_deleteValue(key, username) {
	if (username !== undefined) {
		if (username) {		// if username is supplied, it must not be blank (i.e. not logged in)
			key += '_' + encodeName(username);
		} else {
			return;
		}
	}
	if ((typeof(GM_deleteValue) != 'undefined') && !window.opera) {
		GM_deleteValue(key);
	} else {
		try {
			localStorage.removeItem(key);
		} catch (err) {
			LD_setCookie(key, '', -1);
		}
	}
}

function LD_removeStyle(theID) {
	var styleSheet = document.getElementById(theID);
	if (styleSheet) {
		styleSheet.parentNode.removeChild(styleSheet);
	}
}

function LD_addStyle(css, theID) {
	var head = document.getElementsByTagName('head');
	if (!head) { return; }
	var styleSheet = document.createElement('style');
	styleSheet.type = 'text/css';
	try {
		styleSheet.innerHTML = css;
	} catch(err) {
		styleSheet.innerText = css;
	}
	if (theID) {
		LD_removeStyle(theID);	// no duplicate IDs
		styleSheet.id = theID;
	}
	head[0].appendChild(styleSheet);
}

function LD_addScript(source) {
	var head = document.getElementsByTagName('head');
	if (!head) { return; }

	// Create a script node holding the source code.
	var script = document.createElement('script');
	script.setAttribute('type', 'text/javascript');
	script.textContent = source.toString();

	// Insert the script node into the page.
	document.body.appendChild(script);
}

function LD_xmlhttpRequest(request) {
	if ((typeof(GM_xmlhttpRequest) != 'undefined') && !window.opera) {
		GM_xmlhttpRequest(request);
	} else {
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.onreadystatechange = function() {
			var responseState = {	responseXML		: '',
									responseText	: '',
									readyState		: xmlhttp.readyState,
									responseHeaders	: '',
									status			: 0,
									statusText		: ''
								};
			if (xmlhttp.readyState == 4) {
				responseState = {	responseXML		: xmlhttp.responseXML,
									responseText	: xmlhttp.responseText,
									readyState		: xmlhttp.readyState,
									responseHeaders	: xmlhttp.getAllResponseHeaders(),
									status			: xmlhttp.status,
									statusText		: xmlhttp.statusText
								};
			}

			if (request['onreadystatechange']) {
				request['onreadystatechange'](responseState);
			}
			if (xmlhttp.readyState == 4) {
				if (request['onload'] && xmlhttp.status >= 200 && xmlhttp.status < 300) {
					request['onload'](responseState);
				}
				if (request['onerror'] && (xmlhttp.status < 200 || xmlhttp.status >= 300)) {
					request['onerror'](responseState);
				}
			}
		};
		try {
			//cannot do cross domain
			xmlhttp.open(request.method, request.url);
		} catch(e) {
			if(request['onerror']) {
				//simulate a real error
				request['onerror']({responseXML		: '',
									responseText	: '',
									readyState		: 4,
									responseHeaders	: '',
									status			: 403,
									statusText		: 'Forbidden'
									});
			}
			return;
		}
		if (request.headers) {
			for (var prop in request.headers) {
				xmlhttp.setRequestHeader(prop, request.headers[prop]);
			}
		}
		xmlhttp.send((typeof(request.data) != 'undefined') ? request.data : null);
	}
}

function LD_log(str) {
	if ((typeof(GM_log) != 'undefined') && !window.opera) {
		GM_log(str);
		return;
	}
	try {
		console.log(str);
	} catch (err) {}
}

function Debug_Log(str) {
	if (DEBUG) {
		var now = new Date();
		GM_log(now.toLocaleTimeString().slice(0, -3) + '.' + now.getMilliseconds().zeroPad(3) + ': ' + str);
	}
}

function Check_for_Update(scriptName, scriptVersion) {
	try {
		var checkURL = 'http://www.lildevil.org/greasemonkey/current-versions.txt';
		if (window.opera) {
			// Opera doesn't support cross-domain xmlhttpRequests so use a URL on geocaching.com
			checkURL = 'http://www.geocaching.com/seek/log.aspx?LUID=606117a5-b2d0-4450-8fa1-f7faae43e4be';
		}

		// show version number on http://www.lildevil.org/greasemonkey/versions
		var versObj = document.getElementById(scriptName);
		if (versObj) {
			versObj.innerHTML = scriptVersion;
		}

		// avoid a flood of dialogs e.g. when opening a browser with multiple tabs open
		var now = new Date().getTime();
		var DOSpreventionTime = 2 * 60 * 1000;	// two minutes
		var abbrev = scriptName.replace(/[^A-Z]/g, '');
		var lastStart = LD_getValue(abbrev + '_Update_Start', null);
		LD_setValue(abbrev + '_Update_Start', now.toString());
		if (lastStart && (now - lastStart) < DOSpreventionTime) { return; }

		// time to check yet?
		var oneDay = 24 * 60 * 60 * 1000;
		var lastChecked = LD_getValue(abbrev + '_Update_Last', null);
		var checkDays = LD_getValue(abbrev + '_Update_Days', 1);
		if (lastChecked && (now - lastChecked) < (oneDay * checkDays)) { return; }

		LD_xmlhttpRequest({
			method: 'GET',
			url: checkURL,
			headers: { 'User-Agent' : scriptName + ' v' + scriptVersion + ' auto updater' },
			onload: function(result) {
				var matches,
					regex = new RegExp('[\\s\\>]' + scriptName +
										'\\s+v([\\d\\.]+)\\s+(\\d+)\\s+(.+?)[\\<\\s]', 'i');
				if (!(matches = regex.exec(result.responseText))) {
					LD_log(scriptName + ': Updater: response unrecognized');
					return;
				}

				var theOtherVersion = matches[1];
				LD_setValue(abbrev + '_Update_Days', +matches[2]);
				var theOtherURL = matches[3];

				if (theOtherVersion.replace(/\./g, '') <= scriptVersion.replace(/\./g, '')) { return; } // no updates or older version
				if (theOtherURL.indexOf('http') !== 0) { theOtherURL = 'http://' + theOtherURL; }

				if (window.confirm(	'The Greasemonkey script "' + scriptName +
									'" has been updated.\n\n' +
									'The new version is ' + theOtherVersion +
									'\nYou are currently using version ' + scriptVersion +
									'\n\nClick OK for instructions on how to upgrade.')) {
					document.location = theOtherURL;	// open in same window to avoid popup blockers
				}
			}
		});
		LD_setValue(abbrev + '_Update_Last', new Date().getTime().toString());
	}
	catch (err) { }
}
})();