GIF89a; %PDF-1.5 %���� ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµù Õ5sLOšuY Donat Was Here
DonatShell
Server IP : 134.29.175.74  /  Your IP : 216.73.216.160
Web Server : nginx/1.10.2
System : Windows NT CST-WEBSERVER 10.0 build 19045 (Windows 10) i586
User : Administrator ( 0)
PHP Version : 7.1.0
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  C:/nginx/html/download/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : C:/nginx/html/download/site.js
// js/site.js

// _Initialize_Accesskey(by) ............. Initialize the accesskeys on the page.
// _Initialize_Site() .................... Add submit eventListener for LoadingPleaseWait to all forms.
// AccesskeysCreate() .................... Create accesskeys.
// AccesskeysDisable() ................... Disable accesskeys.
// AccesskeysKeyDown(evt) ................ Handle keyup events.
// console.stub .......................... Avoid `console` errors in browsers that lack a console.
// BackspaceTrap() ....................... To possibly override backspace keypress.
// ClearSelectedText() ................... Clear any text selection
// ColorSelect(e) ........................ Color the select options.
// ColorSelectAttach() ................... Add 'change' EventListener 'ColorSelect(this);' to all select elements.
// ElementBounds(eId) .................... Return left, top, right, bottom, width, height, border, margin, and padding of an element.
// elementLeftTop(e) ..................... Returns left and top of an element.
// errordialog(msg, url, linenumber) ..... Display javascript errors at the top of the browser window.
// eventHide(UTRu) ....................... Hide this event content.
// eventHideAll(userId, turn) ............ Hide all event content for this turn.
// eventShow(UTRu) ....................... Show this event content.
// eventShowAll(userId, turn) ............ Show all event content for this turn.
// eventTurnHide(userId, turn) ........... Hide all turn Events.
// eventTurnShow(userId,turn) ............ Show turn Events based on their current status.
// EPPZScrollTo.scrollVerticalToElementById(id, pad)
// Scroll to element with animation (slow)
// formSubmit(e, formTask, formAction) ... Submit the form while disabling the button.
// getE(id,p,t,by) ....................... Set e and eId global values.
// formSubmit_GetForm(e) ................. Return the form elemnt.
// GetMouse_mxmy(evt) .................... Set mx and my to the mouse position.
// getValue(id, p, t, by) ................ Set e and eId values and return e.value with cleanup.
// Help(helpTip) ......................... Control help button onClick, onMouseDown, onMouseOut, and onMouseOver.
// HelpDisplay(helpName) ................. Display help.
// HelpNotFound() ........................ Show help not written if empty or 404 error.
// htmlSafe(str) ......................... Return an HTML safe string.
// IncrementCounter(thisFunction) ........ Increment the function counter.
// IsNumeric(n) .......................... Returns true if n is numeric, else false.
// LoadingPleaseWait() ................... Show Loading... and hide pageContent when a form is submitted.
// moveDisplay(menuType, mXY) ............ Display the mXY menu.
// menuDrag() ............................ Allows player to drag the MapMenu to another location.
// menuHide(by) .......................... Hide the XY menu.
// Refresh_LastAccessOnce(sound) ......... Refresh the playergame lastAccess time and play sound.
// Refresh_LastAccessAuto(sound, by) ..... Refresh the playergame lastAccess time and play sound automatically every SoundTimer seconds.
// ScreenExit(e, action, name) ........... Exit play and submit form (Also set name or 'task' value).
// SeeHide_Events(see_hide) .............. Show or hide Events.
// SeeHide_Fleets(see_hide) .............. Show or hide fleets and ships.
// SeeHide_Map(see_hide) ................. Show or hide map.
// SeeHide_Settings(see_hide) ............ Show or hide game settings.
// SeeHide_Score(see_hide) ............... Show or hide score.
// SeeHide_Techs(see_hide) ............... Show or hide techs.
// SetGameSetting(e) ..................... Set database, $_SESSION, and javascript values.
// SoundManage(e) ........................ Manage sound settings.
// SoundPlay(soundfile, by) .............. Play the soundfile.
// SoundPlayByName(sound, by) ............ Play the sound by name.
// SoundPlayByNameAuto(sound, by) ........ Play sound by name automatically every SoundTimer seconds.
// SoundPlayByNameAutoReturn() ........... Callback to re-call SoundPlayByNameAuto().
// SoundReset() .......................... Reset javascript sound setting to default values.
// SoundSave() ........................... Save sound setting.
// SoundSet(e) ........................... Change a sound setting.
// starNoteDisplay(XY) ................... Rate or make notes on star.
// StarNoteSave(e) ....................... Rate or make notes on star.
// starValue() ........................... Calculate and return the value of all planets at a star.
// ttDisplay(evt) ........................ Display map tooltip.
// ttDisplay_Events() .................... Display game Events at XY.
// ttDisplay_Fleets() .................... Display hex location.
// ttDisplay_InSpace() ................... Display ships at XY.
// ttDisplay_Location() .................. Display star name and hex location if at a star.
// ttDisplay_Notes() ..................... Display Notes about XY.
// ttDisplay_Planets() ................... Display known planets at XY.
// ttDisplay_Planets_Contents(pn,uId) .... Return planet contents. This function is only called by ttDisplay_Planets().
// ttDisplay_Production() ................ Display units produced at XY ( in space and on planets ).
// ttDisplay_onClick() ................... Display the onClick task.
// ttDisplay_TKO(TKO_XY) ................. Display warning if TKO needs cover here.
// ttEnemyHasSpaceCombatUnits(eId, XY) ... Returns true if the enemy has space combat units (warships or orbital bases).
// ttLostContact(pp) ..................... Return true if the player has 'Lost Contact' with this planet in the past, else false.
// ttPd(pn) .............................. Return planet description for console DEBUGging.
// ttPlanetIsOriginal() .................. Returns true if the planet has original TYPE HAB NM, else false.
// ttPlanetIsPlayer() .................... Returns true if the planet TYPE HAB NM has not been changee by production, else false.
// ttTR(turn) ............................ Returns TR.

// Sound is played by: PHP: audioPlay() and refreshLastAccessSound()
//              Javascript: by Refresh_LastAccessOnce(), Refresh_LastAccessAuto(), SoundPlay(), SoundPlayByName(), SoundPlayByNameAuto(), and SoundPlayByNameAutoReturn().

// BEGIN variable settings.
// Use DEBUG = DEBUG_OFF; to stop debugging.
// Use DEBUG = DEBUG_ON; to start debugging.
//     DEBUG_ON is defined by pageFooter_site_js.phpinc as the value of $debuggingAvailable which is set in application.phpinc line 22.
//     If $debuggingAvailable is false then DEBUG_ON will also be false as debugging is not available.
let DEBUG_OFF = false;
let DEBUG_doc_on = false; // DEBUG set of document onclick, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, and onselectstart.
let DEBUG_SeeEventsFleetsTechs = false;
let DOC = {};
		DOC.onclick = null;				// Remember document.onclick function name.
		DOC.onmousedown = null;		// Remember document.onmousedown function name.
		DOC.onmousemove = null;		// Remember document.onmousemove function name.
		DOC.onmouseout = null;		// Remember document.onmouseout function name.
		DOC.onmouseover = null;		// Remember document.onmouseover function name.
		DOC.onmouseup = null;			// Remember document.onmouseup function name.
		DOC.onselectstart = null;	// Remember document.onselectstart function name.
let Accesskeys = {};
let evt; // The current event.
let form; // Form object.
let movesToTextTimer = false;
let mx; // mouse x pixel position.
let my; // mouse y pixel position.
let Save_mxmy = {}; // Array to save previous mx, my.;
//let SeeHide_EventsLoaded = false;
//let SeeHide_PlanetsLoaded = false;
let SeeHide_ScoreLoaded = false;
let SeeHide_SettingsLoaded = false;
let SoundLastPlayedSound = '';			// The name of the last sound played.
let SoundLastPlayedTime = 0;				// The getTime() a sound was last played.
let SoundPlayByNameAutoBy;					// Save SoundPlayByNameAuto() by for use by SoundPlayByNameAuto();
let SoundPlayByNameAutoRefresh; 		// Save SoundPlayByNameAuto() refresh for use by SoundPlayByNameAuto();
let SoundPlayByNameAutoSetTimeout;	// The setTimeout call for use by SoundPlayByNameAuto() to call clearTimeout(SoundPlayByNameAutoSetTimeout) and stop auto calls.
let SoundPlayByNameAutoSound;				// Save SoundPlayByNameAuto() sound for use by SoundPlayByNameAuto();
let ttContent_Planets; // Store tooltip planet text.
let SoundPlayByNameAutoReturnCount = 0; // Count SoundPlayByNameAutoReturn() calls.
// END variable settings.

// _DEBUG_loaded_or_off()
// Returns true if DEBUG_ON is defined and (DEBUG_ON is false or DEBUG_ON is true and debugging is initalized).
function _DEBUG_loaded_or_off() {
	return ( typeof DEBUG_ON !== 'undefined' ) && ( !DEBUG_ON || ( DEBUG_ON && _Initialize_debug.initalized ) );
} // END _DEBUG_loaded_or_off.

// _Initialize(initFunction)
// Call all the InitializeFunctions that were defined.
// InitializeFunctions is  a comma seperated list of function names.
// InitializeFunctions is defined in the js/site_js.phpinc file from the php $GLOBALS['_PAGE']['onload'] variable.
// $GLOBALS['_PAGE']['onload'] is defined in the common/pageHeader.phpinc file.
// First preset site function are added. Then functions listed in the php pageHeader[] function.
function _Initialize(initFunction='') {
	let DEBUG = true; // Cannot trust that DEBUG_ON is defined at this point.
	if ( !DEBUG ) {
		console.log(`${PC}_Initialize[${initFunction}]`,CH);
	}
	//if ( document.getElementById('pageHeader') ) { document.getElementById('pageHeader').style.display = 'block'; }
	if ( initFunction === '' ) { // Was _Initialize called without a function name?
		// Yes, call _Initialize[] for each of the InitializeFunctions.
		if ( DEBUG ) { console.group(`${PC}_Initialize[]${PC} InitializeFunctions=${InitializeFunctions}`,CG,CL); }//Collapsed
		_Initialize_Page();
		InitializeFunctions.forEach((InitializeFunction, index) => { // Loop thru the InitializeFunctions.
			if ( DEBUG ) { console.log(`${PC}_Initialize+'[${InitializeFunction}]'`,CC); }
			_Initialize(InitializeFunction); // Call _Initialize with the function name.
		}); // Loop thru the InitializeFunctions.
	} else { // Was _Initialize called without a function name?
		// No, Check if the initFunction exists.
		if ( DEBUG ) { console.group(`${PC}_Initialize[${initFunction}]`,CG); }//Collapsed
		if ( window[initFunction] ) { // Does the initFunction function exist?
			// Yes, Call the specified initFunction.
			if ( DEBUG ) { console.log(PC+initFunction+'[]',CC); }
			window[initFunction](); // Run the initFunction function.
		} else { // Does the function exist?
			// No, Try again in 1 second. Attempts are limited to 5 tries.
			if ( DEBUG ) { console.log(`${PC}_Initialize.initFunction=${_Initialize.initFunction}`,CI); }
			_Initialize.initFunction++;
			if ( isNaN(_Initialize.initFunction) ) { _Initialize.initFunction = 1; if ( DEBUG ) { console.log(`${PC}_Initialize.initFunction=${_Initialize.initFunction}`,CI); } }
			console.log(`${PC}${initFunction} is not defined. _Initialize.initFunction=${_Initialize.initFunction}`,CF);
			if ( _Initialize.initFunction < 5 ) {
				setTimeout(function(){ _Initialize(initFunction); },1000); // Try again in 1 second.
			}
		} // Does the function exist?
	} // Was _Initialize called without a function name?
	if ( DEBUG ) { console.groupEnd(); }
} // END _Initialize.

// _Initialize_Accesskey(by)
// Initialize the accesskeys on the page.
function _Initialize_Accesskey(by) {
	IncrementCounter(_Initialize_Accesskey);
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.groupCollapsed(PC+'_Initialize_Accesskey['+_Initialize_Accesskey.counter+'] by='+by,CG); } else { console.log(PC+'_Initialize_Accesskey['+_Initialize_Accesskey.counter+'] by='+by,CH); }
	// Add keydown and keyup listeners.
	document.addEventListener('keydown',AccesskeysKeyDown);
	document.addEventListener('keyup',AccesskeysKeyDown);
	//document.keydown = AccesskeysKeyDown;
	//document.keyup = AccesskeysKeyDown;
	// Capture when the mouse leaves the body. This is needed because keydown can no longer trap the esc key until the body is clicked.
	// This would not need to be done if there was no event listener for document.onclick.
	let Body = document.getElementsByTagName('body');
	//console.log('Body'+Body);
	Body[0].addEventListener("mouseleave",function() { AccesskeySpecialFunction = {}; },0); // Remove 'or hit esc' if the mouse leaves the body.
	if ( DEBUG ) { console.log(PC+'AccesskeysCreate[]',CC); }
	AccesskeysCreate('_Initialize_Accesskey 126');
	if ( DEBUG ) { console.groupEnd(); }
} // END _Initialize_Accesskey.

// _Initialize_Page(by)
// Initialize the accesskeys on the page.
function _Initialize_Page() {
	let DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.groupCollapsed(PC+'_Initialize_Page[]',CG); } else { console.log(PC+'_Initialize_Page[]',CH); }
	if ( document.getElementById('pageContent') ) {
		if ( DEBUG ) { console.log(PC+'Display pageContent.',CL); }
		document.getElementById('pageContent').style.display = 'block';
	}
	if ( document.getElementById('pageFooter') ) {
		if ( DEBUG ) { console.log(PC+'Display pageFooter.',CL); }
		document.getElementById('pageFooter').style.display = 'block';
		if ( document.getElementById('showfonts') ) {
			if ( DEBUG ) { console.log('window.location.href.indexOf("admin")='+window.location.href.indexOf("admin")); }
			if( window.location.href.indexOf("admin") === -1 ) {
				if ( DEBUG ) { console.log(PC+'Hide showfonts.',CL); }
				document.getElementById('showfonts').style.visibility = 'hidden';
			} else {
				if ( DEBUG ) { console.log(PC+'Leave showfonts displayed.',CL); }
			}
		}
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END _Initialize_Page.

// _Initialize_Site()
// Add submit eventListener for LoadingPleaseWait to all forms.
let _Initialize_Site_Done = false;
function _Initialize_Site() {
	IncrementCounter(_Initialize_Site);
	let DEBUG = false; // Cannot trust that DEBUG_ON is defined at this point.
	if ( DEBUG ) { console.groupCollapsed(PC+'_Initialize_Site['+_Initialize_Site.counter+'] site.js',CG); } else { console.log(PC+'_Initialize_Site['+_Initialize_Site.counter+'] site.js',CH); }//
	// Wait for the pageFooter element, the UseCanvasMap js variable, and either DEBUG_ON is off or the _Initialize_debug.initalized variable is set.
	let error = false;
	//if ( !document.getElementById('pageFooter') ) { error = 'The pageFooter has not loaded'; }
	//if ( !error && typeof UseCanvasMap === 'undefined' ) { error = 'The canvas is not ready'; }
	//if ( !error && typeof _Initialize_debug.initalized === 'undefined' ) { error = 'DEBUG is not ready'; }
	if ( !error ) {
		// Add LoadingPleaseWait event listener to all form submit actions.
		for ( let i=0; i<document.forms.length; i++ ) {
			document.forms[i].addEventListener('submit',LoadingPleaseWait);
		}
		if ( DEBUG ) { console.log(PC+'_Initialize_Accesskey[]',CC); }
		_Initialize_Accesskey('_Initialize_Site 121'); // Get accesskey items.
		if ( DEBUG && !UseCanvasMap ) { console.log(PC+'Not using canvas map. UseCanvasMap='+UseCanvasMap,CI); }
		_Initialize_Site_Done = true;
	} else {
		if ( _Initialize_Site.counter < 10 ) {
			if ( DEBUG ) { console.log(PC+'setTimeout[_Initialize_Site,100] '+error,CC); }
			setTimeout(_Initialize_Site,100);
		}
	}
	/** / // No longer using roboto fonts. Switched to Arial on 2021.01.04.
	if ( DEBUG ) { console.log(`${PC}document.fonts.onloadingdone function set.`,CD); }
	/** /
	document.fonts.onloadingdone = function (fontFaceSetEvent) {
		console.log(`${PC}document.fonts.onloadingdone == ${fontFaceSetEvent.fontfaces.length} font faces loaded.`,CA);
		//console.log(`${PC}robotothin=${document.fonts.check('1em robotothin')}`,CB);
		//console.log(`${PC}robotolight=${document.fonts.check('1em robotolight')}`,CB);
		console.log(`${PC}robotoregular=${document.fonts.check('1em robotoregular')}`,CB);
		console.log(`${PC}robotomedium=${document.fonts.check('1em robotomedium')}`,CB);
		console.log(`${PC}robotobold=${document.fonts.check('1em robotobold')}`,CB);
		console.log(`${PC}robotoblack=${document.fonts.check('1em robotoblack')}`,CB);
		if ( document.fonts.check('1em robotoregular') ) {
			document.body.style.fontFamily = "robotoregular, Helvetica, Verdana, Geneva, Arial, sans-serif";
		}
		//_Initialize_Canvas();
		//document.body.style.fontFamily = "robotoregular, Arial, sans-serif";//'Helvetica', 'Verdana', 'Geneva', 'Arial', sans-serif;
		/** /
	};
	/**/
	if ( DEBUG ) { console.groupEnd(); }
} // END _Initialize_Site.

// AccesskeySpecial(27,keyFunction)
// Set AccesskeySpecialFunction to trap keypress.
let AccesskeySpecialFunction = {};
function AccesskeySpecial(keyCode=false,keyFunction='') {
	if ( keyCode !== false ) {
		if ( keyFunction !== '') {
			AccesskeySpecialFunction[keyCode] = keyFunction;
		} else {
			delete AccesskeySpecialFunction[keyCode];
		}
	}
} // END AccesskeySpecial.

// AccesskeysCreate(by)
// Create accesskeys.
function AccesskeysCreate(by) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) {
		console.groupCollapsed(`${PC}AccesskeysCreate[] by=${by}`,CG);
	} else {
		//console.log(`${PC}AccesskeysCreate[] by=${by}`,CH);
	}
	Accesskeys = {};
	let AccesskeyElements = document.getElementsByClassName('accesskey');
	if ( DEBUG ) { console.log(PC+'AccesskeyElements.length='+AccesskeyElements.length,CL); }
	let AccesskeyElement;
	let AccesskeyFound;
	let ClassNames;
	for ( i=0; i<AccesskeyElements.length; i++ ) { // Loop thru Accesskeys.
		AccesskeyElement = AccesskeyElements[i];
		//console.log(AccesskeyElement.nodeName+' '+AccesskeyElement.type+' '+AccesskeyElement.id+' '+AccesskeyElement.className);
		if ( !AccesskeyElement.disabled ) {
			ClassNames = AccesskeyElement.className.split(' ');
			AccesskeyFound = false;
			for ( j=0; j<ClassNames.length; j++ ) {
				if ( AccesskeyFound ) {
					if ( typeof Accesskeys[ClassNames[j]] === 'undefined' ) { Accesskeys[ClassNames[j]] = []; }
					Accesskeys[ClassNames[j]].push(AccesskeyElement.id);
					break;
				}
				if ( ClassNames[j] === 'accesskey') { AccesskeyFound = true; }
			}
		} else {
			if ( DEBUG ) { console.log(PC+AccesskeyElement.id+' is disabled.',CF); }
		}
	} // Loop thru Accesskeys.
	if ( DEBUG ) { console.log(PC+'Accesskeys='+JSON.stringify(Accesskeys),CI); }
	if ( DEBUG ) { console.groupEnd(); }
}

// AccesskeysDisable()
// Disable accesskeys.
function AccesskeysDisable(by) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log(`${PC}AccesskeysDisable[] by=${by}`,CG); } else { console.log(`${PC}AccesskeysDisable[] by=${by}`,CH); }
	Accesskeys = {};
}

// AccesskeysKeyDown(evt)
// Handle keyup events.
let ProperAccesskey = false;
let AccesskeysKeyDown = function(evt) {
	let DEBUG = DEBUG_OFF;
	let DEBUG_ProperAccesskey_DoNotClickButton = false;
	let Return;
	if ( !evt.key ) { console.log(`${PC}evt.key is not available.`,CE); return; }
	if ( !evt.key.toLowerCase ) { console.log(`${PC}evt.key.toLowerCase is not available.`,CE); return; }
	let AccesskeyStroke = evt.key.toLowerCase();
	if ( DEBUG ) { console.group(PC+'AccesskeysKeyDown[] key='+AccesskeyStroke+' id='+evt.target.id+' type='+evt.type+' keyCode='+evt.keyCode+' alt='+evt.altKey+' ctrl='+evt.ctrlKey+' shift='+evt.shiftKey,CG); }
	//console.log(JSON.stringify(evt));
	// Find out if this is a proper accesskey.
	ProperAccesskey = true;
	let AccesskeyElement;
	if ( evt.altKey ) {
		ProperAccesskey = false;
		if ( DEBUG ) { console.log(PC+'alt key. ProperAccesskey=false.',CF); }
	} else {
		if ( evt.ctrlKey ) {
			ProperAccesskey = false;
			if ( DEBUG ) { console.log(PC+'ctrl key. ProperAccesskey=false.',CF); }
		} else {
			if ( evt.shiftKey ) {
				ProperAccesskey = false;
				if ( DEBUG ) { console.log(PC+'shift key. ProperAccesskey=false.',CF); }
			} else {
				if ( ( evt.keyCode < 48 || evt.keyCode > 57 ) && ( evt.keyCode < 65 || evt.keyCode > 90 ) ) {
					ProperAccesskey = false;
					if ( DEBUG ) { console.log(PC+'not a-z0-9. ProperAccesskey=false.',CF); }
				} else { // AccesskeyStroke is not a-z0-9.
					if ( typeof Accesskeys[AccesskeyStroke] === 'undefined' ) { ProperAccesskey = false; if ( DEBUG ) { console.log(PC+'Accesskeys['+AccesskeyStroke+'] undefined. ProperAccesskey=false.',CF); } } else {
						// Valid accesskey.
						console.log(`${PC}Accesskeys[${AccesskeyStroke}=${Accesskeys[AccesskeyStroke]}`,CD);
						for ( let aki=0; aki<Accesskeys[AccesskeyStroke].length; aki++ ) { // Loop thru Accesskeys.
							AccesskeyElement = document.getElementById(Accesskeys[AccesskeyStroke][aki]);
							if ( AccesskeyElement.disabled ) {
								ProperAccesskey = false;
								if ( DEBUG ) { console.log(PC+'Accesskeys['+AccesskeyStroke+'] disabled. ProperAccesskey=false.',CF); }
								break;
							} // AccesskeyElement.disabled.
						} // Loop thru Accesskeys.
					} // Accesskeys[AccesskeyStroke] === 'undefined'.
				} // AccesskeyStroke is not a-z0-9.
			} // shiftKey.
		} // ctrlKey.
	} // altKey.
	if ( !ProperAccesskey ) { // Is this not a proper accesskey.
		if ( DEBUG ) { console.log(PC+'ProperAccesskey=false.',CF); }
		// Check for AccesskeySpecialFunction.
		if ( DEBUG ) { console.log(`${PC}evt.keyCode=${evt.keyCode}`,CL); }
		if ( AccesskeySpecialFunction[evt.keyCode] !== 'undefined' ) {
			if ( DEBUG ) { console.log(`${PC}eval[${AccesskeySpecialFunction[evt.keyCode]}]`,CD); }
			eval(AccesskeySpecialFunction[evt.keyCode]);
		} else {
			if ( DEBUG ) { console.log(`${PC}AccesskeySpecialFunction[${[evt.keyCode]} is not set.`,CF); }
		}
		Return = true;
	} else { // Is this not a proper accesskey.
		// This is a proper accesskey.
		if ( DEBUG ) { console.log(PC+'ProperAccesskey=true. stopPropagation()',CT); }
		event.stopPropagation();
		Return = false;
		if ( evt.type === 'keydown' ) { // Is this a keydown?
			if ( DEBUG ) { console.log(`${PC}AccesskeyElement.type=${AccesskeyElement.type}`,CI); }
			AccesskeyElement.blur();
			switch ( AccesskeyElement.type ) { // switch AccesskeyElement.type.
				case 'button':
				case 'submit':
					if ( !DEBUG_ProperAccesskey_DoNotClickButton ) {
						AccesskeyElement.click();
					} else {
						console.log(PC+AccesskeyStroke+' accesskey for '+AccesskeyElement.id+' skipped because DEBUG_ProperAccesskey_DoNotClickButton is true.',CF);
					}
					break;
				case 'checkbox':
						AccesskeyElement.click();
					break; 
				case 'number':
					AccesskeyElement.focus();
					break;
				case 'radio':
					AccesskeyElement.click();
					break;
				case 'select-one':
					AccesskeyElement.focus();
					AccesskeyElement.click();
					event.stopPropagation();
					break;
				case 'text':
					AccesskeyElement.focus();
					break;
				default:
					console.log(`${PC}AccesskeyElement.type ${AccesskeyElement.type} not acted on.`,CF);
			} // switch AccesskeyElement.type.
		} // Is this a keydown?
	} // Is this not a proper accesskey.
	if ( DEBUG ) { console.log(`${PC}Return=${Return}`,CD); }
	if ( DEBUG ) { console.groupEnd(); }
	return Return;
}; // END AccesskeysKeyDown.

// Avoid `console` errors in browsers that lack a console.
(function() {
	let method;
	let noop = function noop() {};
	let methods = [
	'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
	'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
	'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
	'timeStamp', 'trace', 'warn'
	];
	let length = methods.length;
	let console = (window.console = window.console || {});
	while ( length-- ) {
		method = methods[length];
		// Only stub undefined methods.
		if ( !console[method] ) {
			console[method] = noop;
		}
	}
}()); // END Avoid `console` errors in browsers that lack a console.

/** /
// BackspaceTrap()
// To possibly override backspace keypress.
// Some browsers use the backspace to go back a page. This function stops that behavior.
// All backspaces are trapped if the element triggering the backspace is [object HTMLBodyElement].
let BackspaceTrap = function(event) {
	let DEBUG = false;
	if ( DEBUG ) { console.groupCollapsed(PC+'BackspaceTrap[event.type='+event.type+']',CG); }
 let keynum;
 if ( window.event ) { // eg. IE.
  keynum = window.event.keyCode;
 } else if ( event.which ) { // eg. Firefox.
  keynum = event.which;
 }
 //console.info(' typeof event.target='+(typeof event.target)+' event.target.id='+event.target.id+' Object.prototype.toString.call='+Object.prototype.toString.call(event.target));
 //if ( keynum == 8 &&  event.target.id == '' ) {} // Is this a backspace code 8 and target has no id?
 if ( keynum === 8 && Object.prototype.toString.call(event.target) === '[object HTMLBodyElement]' ) { // Is this a backspace code 8 and target has no id?
  console.log(PC+'backspace trapped.',CW);
  event.stopPropagation();
	if ( DEBUG ) { console.groupEnd(); }
  return false; // Trap the backspace.
 } else {
	 if ( DEBUG ) { console.log(PC+'keynum='+keynum+' passed thru.',CI); }
	 if ( event.type === 'keydown' ) {
		 if ( DEBUG ) { console.groupEnd(); }
		 //return AccesskeysKeyDown(event); // Check AccesskeysKeyDown to allow the keydown.
		 return true; // Allow the keypress.
	 } else {
		 if ( DEBUG ) { console.groupEnd(); }
		 return true; // Allow the keypress.
	 }
 } // Is this a backspace code 8 and target has no id?
};
document.onkeydown = BackspaceTrap; // IE, Firefox, Safari
//document.onkeypress = BackspaceTrap; // Only Opera needs the backspace nullifying in onkeypress
// END BackspaceTrap.
/**/

// ClearSelectedText()
// Clear any text selection
function ClearSelectedText() {
	if (window.getSelection) {
		if (window.getSelection().empty) {  // Chrome
			window.getSelection().empty();
		} else if (window.getSelection().removeAllRanges) {  // Firefox
			window.getSelection().removeAllRanges();
		}
	} else if (document.selection) {  // IE?
		document.selection.empty();
	}	
} // END ClearSelectedText.
// ColorSelect(e)
// Color the select options.
// Selected option backgroundColor is set to #af3.
// The originally selected option backgroundColor is set to #aaf.
let ColorSelect_OriginalOption = {}; // Remember the originally selected option.
function ColorSelect(e) {
	let DEBUG = DEBUG_OFF;
	if ( e && e.id ) {
		if ( DEBUG ) { console.groupCollapsed(PC+'ColorSelect[e.id='+e.id+']',CG); }
		if ( typeof ColorSelect_OriginalOption[e.id] === 'undefined' ) {
			ColorSelect_OriginalOption[e.id] = e.options[e.selectedIndex].value;
			if ( DEBUG ) { console.log(PC+'ColorSelect_OriginalOption['+e.id+'] set to '+ColorSelect_OriginalOption[e.id],CS); }
		}
		if ( DEBUG ) { console.log(TB+'ColorSelect_OriginalOption['+e.id+']='+ColorSelect_OriginalOption[e.id]); }
		for ( let o=0; o<e.options.length; o++ ) {
			if ( DEBUG ) { console.log(TB+'e.options['+o+'].disabled='+e.options[o].disabled); }
			if ( !e.options[o].disabled ) {
				if ( e.options[o].value !== ColorSelect_OriginalOption[e.id] ) {
					e.options[o].style.backgroundColor = '#fff';
				} else {
					e.options[o].style.backgroundColor = '#aaf';
				}
				if ( DEBUG ) { console.log(TB+'e.options['+o+'].style.backgroundColor='+e.options[o].style.backgroundColor); }
			}
		}
		e.options[e.selectedIndex].style.backgroundColor = '#af3';
		for ( o=0; o<e.options.length; o++ ) {
			if ( DEBUG ) { console.log(TB+'e.options['+o+'].style.backgroundColor='+e.options[o].style.backgroundColor); }
		}
		if ( DEBUG ) { console.groupEnd(); }
	} else {
		console.log(PC+TB+'ColorSelect[e] e is undefined.',CE);
	}
} // END ColorSelect.

// ColorSelectAttach()
// Add 'change' EventListener 'ColorSelect(this);' to all select elements.
function ColorSelectAttach() {
	let selects = document.getElementsByTagName('select'); // Get all selects.
	console.log('selects.length='+selects.length);
	for ( let i=0; i<selects.length; i++ ) { // Loop thru selects.
		selects[i].addEventListener('change',function(){ColorSelect(this);}); // Add change EventListener.
		ColorSelect(selects[i]);
	} // Loop thru selects.
} // END ColorSelectAttach.

// ElementBounds(elementOrId)
// Return left, top, right, bottom, width, height, border, margin, and padding of an element.
// elementOrId = element.id or element to check.
// { left, top, right, bottom, width, height, border{ left, top, right, bottom }, margin{left, top, right, bottom }, padding{ left, top, right, bottom } }.
function ElementBounds(elementOrId,by) {
	let DEBUG = DEBUG_OFF;
	let e;
	let eId;
	// Get element and element.id.
	if ( typeof elementOrId === 'string' ) {
		// elementOrId is the element.id.
		eId = elementOrId;
		e = document.getElementById(eId);
		if ( DEBUG ) { console.groupCollapsed(`${PC}ElementBounds[eId=${eId}] by=${by}`,CG); }//
	} else if ( typeof elementOrId === 'object' ) {
		// eId is the element.
		e = elementOrId;
		if ( e.id ) {
			eId = e.id;
			if ( DEBUG ) { console.groupCollapsed(`${PC}ElementBounds[eId=${eId}] by=${by}`,CG); }//
		} else {
			eId = '';
			if ( DEBUG ) { console.groupCollapsed(`${PC}ElementBounds[tagName=${eId.tagName}] by=${by}`,CG); }//
		}
	} else {
		e = false;
		eId = '';
		if ( DEBUG ) { console.groupCollapsed(`${PC}ElementBounds[???] eId='' by=${by}`,CG); }//
		if ( DEBUG ) { console.log(`${PC}eId is not a string or object.`,CE); }
	}
	let left = 0;
	let top = 0;
	let right = 0;
	let bottom = 0;
	let width = 0;
	let height = 0;
	let border = { left:0, top:0, right:0, bottom:0 };
	let margin = { left:0, top:0, right:0, bottom:0 };
	let padding = { left:0, top:0, right:0, bottom:0 };
	if ( e ) {
		width = parseFloat(e.offsetWidth);
		height = parseFloat(e.offsetHeight);
		let style = e.currentStyle || window.getComputedStyle(e);
		border.left    = parseFloat(style.borderLeftWidth.replace('px',''));
		border.top     = parseFloat(style.borderTopWidth.replace('px',''));
		border.right   = parseFloat(style.borderRightWidth.replace('px',''));
		border.bottom  = parseFloat(style.borderBottomWidth.replace('px',''));
		margin.left    = parseFloat(style.marginLeft.replace('px',''));
		margin.top     = parseFloat(style.marginTop.replace('px',''));
		margin.right   = parseFloat(style.marginRight.replace('px',''));
		margin.bottom  = parseFloat(style.marginBottom.replace('px',''));
		padding.left   = parseFloat(style.paddingLeft.replace('px',''));
		padding.top    = parseFloat(style.paddingTop.replace('px',''));
		padding.right  = parseFloat(style.paddingRight.replace('px',''));
		padding.bottom = parseFloat(style.paddingBottom.replace('px',''));
		// Get the left and top by walking up the parent elements.
		let eWalk = e;
		while( eWalk ) { // && eWalk.tagName && eWalk.tagName !== "BODY" && eWalk.id !== 'sectionId_1'
			left += parseFloat(eWalk.offsetLeft);
			top += parseFloat(eWalk.offsetTop);
			if ( DEBUG ) { console.log(`${PC}${TB}eWalk.id=${eWalk.id} tagName=${eWalk.tagName} left=${left} top=${top}`,CL); }
			eWalk = eWalk.offsetParent;
		}
		right = left + width;
		bottom = top + height;
	} else {
		if ( DEBUG ) { console.log(`${PC}No element with id=${eId} found.`,CE); }
	}
	let Return = { left:left, top:top, right:right, bottom:bottom, width:width, height:height, border:{ left:border.left, top:border.top, right:border.right, bottom:border.bottom }, margin:{ left:margin.left, top:margin.top, right:margin.right, bottom:margin.bottom }, padding:{ left:padding.left, top:padding.top, right:padding.right, bottom:padding.bottom } }
	if ( DEBUG ) { console.groupEnd(); }
	if ( DEBUG ) { console.log(`${PC}ElementBounds=${JSON.stringify(Return)}`,CI); }
	return Return;
} // END ElementBounds.

// elementLeftTop(element, by)
// Returns left and top of an element.
// element = element to check.
function elementLeftTop(element,by) {
  let DEBUG = DEBUG_OFF;
 if ( DEBUG ) { console.log('elementLeftTop[element.id='+element.id+' by='+by+']'); }
  let left = 0;
  let top = 0;
 while( element && element.tagName && element.tagName !== "BODY" ) {
  let thisLeft = element.offsetLeft;
  left += thisLeft;
  let thisTop = element.offsetTop;
  top += thisTop;
  if ( DEBUG ) { console.log('element.tagName='+element.tagName+' element.id='+element.id+' element.style.position='+element.style.position+' thisLeft='+thisLeft+' thisTop='+thisTop+' left='+left+' top='+top); }
  element = element.offsetParent;
  }
  return { left: left, top: top };
} // END elementLeftTop.

/** /
// errordialog(msg, url, linenumber)
// Display javascript errors at the top of the browser window.
function errordialog(msg, url, linenumber) {
 let dialog = document.createElement("div");
 dialog.className = 'errordialog';
 dialog.innerHTML = '&nbsp;<b class="error">JavaScript error:</b> ' + msg;
 if ( typeof url !== 'undefined' ) dialog.innerHTML += ' at ' + url + ':' + linenumber;
 dialog.innerHTML += '.';
 document.body.appendChild(dialog);
 return true;
} // END errordialog.
window.onerror=function(msg, url, linenumber){
 return errordialog(msg, url, linenumber)
} // END Attach onerror.
/**/

// eventHide(UTRu)
// Hide this event content.
function eventHide(UTRu) {
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('eventHide['+UTRu+']'); }
 TRstatus[UTRu] = false;
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowShow[eventShow_'+UTRu+'];'); }
 rowShow('eventShow_'+UTRu);
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowHide[eventContent_'+UTRu+'];'); }
 rowHide('eventContent_'+UTRu);
}

// eventHideAll(userId, turn)
// Hide all event content for this turn.
function eventHideAll(userId, turn) {
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('eventHideAll['+userId+', '+turn+']'); }
 document.getElementById('allEventsShow_'+userId+'_'+turn).style.display = 'inline'; // See all shown.
 document.getElementById('allEventsHide_'+userId+'_'+turn).style.display = 'none'; // Hide all hidden.
 for ( let i=0; i<TurnRoundIds[userId][turn].length; i++ ) {
  let UTRu = userId+'_'+turn+'_'+TurnRoundIds[userId][turn][i];
  eventHide(UTRu);
 }
}

// eventShow(UTRu)
// Show this event content.
function eventShow(UTRu) {
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('eventShow['+UTRu+']'); }
 TRstatus[UTRu] = true;
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowHide[eventShow_'+UTRu+'];'); }
 rowHide('eventShow_'+UTRu);
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowShow[eventContent_'+UTRu+'];'); }
 rowShow('eventContent_'+UTRu);
}

// eventShowAll(userId, turn)
// Show all event content for this turn.
function eventShowAll(userId, turn) {
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('eventShowAll['+userId+', '+turn+']'); }
 document.getElementById('allEventsShow_'+userId+'_'+turn).style.display = 'none'; // See all hidden.
 document.getElementById('allEventsHide_'+userId+'_'+turn).style.display = 'inline'; // Hide all shown.
 for ( let i=0; i<TurnRoundIds[userId][turn].length; i++ ) {
  let UTRu = userId+'_'+turn+'_'+TurnRoundIds[userId][turn][i];
  eventShow(UTRu);
 }
}

// eventTurnHide(userId, turn)
// Hide all turn Events.
function eventTurnHide(userId, turn) {
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('eventTurnHide['+userId+', '+turn+']'); }
 if ( DEBUG_SeeEventsFleetsTechs ) { console.log('TurnRoundIds['+userId+']['+turn+']='+TurnRoundIds[userId][turn]); }
 for ( let i=0; i<TurnRoundIds[userId][turn].length; i++ ) {
  let TRu = turn+'_'+TurnRoundIds[userId][turn][i];
  let UTRu = userId+'_'+turn+'_'+TurnRoundIds[userId][turn][i];
  if ( DEBUG_SeeEventsFleetsTechs ) { console.log('TRstatus['+userId+']['+TRu+']='+TRstatus[userId][TRu]); }
  if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowHide[eventShow_'+UTRu+'];'); }
  rowHide('eventShow_'+UTRu);
  if ( DEBUG_SeeEventsFleetsTechs ) { console.log('rowHide[eventContent_'+UTRu+'];'); }
  rowHide('eventContent_'+UTRu);
 }
}

// eventTurnShow(userId,turn)
// Show turn Events based on their current status.
function eventTurnShow(userId, turn) {
 console.log('eventTurnShow['+turn+']');
 console.log('TurnRoundIds['+userId+', '+turn+']='+TurnRoundIds[userId][turn]);
 for ( let i=0; i<TurnRoundIds[userId][turn].length; i++ ) {
  let TRu = turn+'_'+TurnRoundIds[userId][turn][i];
  let UTRu = userId+'_'+turn+'_'+TurnRoundIds[userId][turn][i];
  console.log('TRstatus['+userId+']['+TRu+']='+TRstatus[userId][TRu]);
  if ( TRstatus[UTRu] ) {
   console.log('rowHide[eventShow_'+UTRu+'];');
   rowHide('eventShow_'+UTRu);
   console.log('rowShow[eventContent_'+UTRu+'];');
   rowShow('eventContent_'+UTRu);
  } else {
   console.log('rowShow[eventShow_'+UTRu+'];');
   rowShow('eventShow_'+UTRu);
   console.log('rowHide[eventContent_'+UTRu+'];');
   rowHide('eventContent_'+UTRu);
  }
 }
}

// EPPZScrollTo.scrollVerticalToElementById(id, pad)
// Scroll to element with animation (slow)
//  id = The id of the element to scroll to.
// pad = Top padding to apply above element.
var EPPZScrollTo = {
	/* Helpers. */
	// EPPZScrollTo.documentVerticalScrollPosition
	// Returns scroll top.
	documentVerticalScrollPosition: function() 	{
		if (self.pageYOffset) { return self.pageYOffset; } // Firefox, Chrome, Opera, Safari.
		if (document.documentElement && document.documentElement.scrollTop) { return document.documentElement.scrollTop; } // Internet Explorer 6 (standards mode).
		if (document.body.scrollTop) { return document.body.scrollTop; } // Internet Explorer 6, 7 and 8.
		return 0; // None of the above.
	},
	// EPPZScrollTo.viewportHeight()
	// Returns the viewport height.
	viewportHeight: function() { return (document.compatMode === "CSS1Compat") ? document.documentElement.clientHeight : document.body.clientHeight; },
	// EPPZScrollTo.documentHeight()
	// Returns the document height.
	documentHeight: function() 	{ return (document.height !== undefined) ? document.height : document.body.offsetHeight; },
	// EPPZScrollTo.documentMaximumScrollPosition()
	// Returns the maximum scroll position.
	documentMaximumScrollPosition: function() { return this.documentHeight() - this.viewportHeight(); },
	// EPPZScrollTo.elementVerticalClientPositionById(id)
	// Returns the top position of the element.
	elementVerticalClientPositionById: function(id) {
		var element = document.getElementById(id);
		var rectangle = element.getBoundingClientRect();
		return rectangle.top;
	},
	/* Animation tick. */
	// EPPZScrollTo.scrollVerticalTickToPosition(currentPosition, targetPosition)
	// Scroll from currentPosition to targetPosition
	scrollVerticalTickToPosition: function(currentPosition, targetPosition) {
		var filter = 0.2;
		var fps = 30;
		var difference = parseFloat(targetPosition) - parseFloat(currentPosition);
		// Snap, then stop if arrived.
		var arrived = (Math.abs(difference) <= 0.5);
		if (arrived) {
			// Apply target.
			scrollTo(0.0, targetPosition);
			return;
		}
		// Filtered position.
		currentPosition = (parseFloat(currentPosition) * (1.0 - filter)) + (parseFloat(targetPosition) * filter);
		// Apply target.
		scrollTo(0.0, Math.round(currentPosition));
		// Schedule next tick.
		setTimeout("EPPZScrollTo.scrollVerticalTickToPosition("+currentPosition+", "+targetPosition+")", (1000 / fps));
	},
	/* For public use.
	* @param id  The id of the element to scroll to.
	* @param pad Top padding to apply above element.
	*/
	scrollVerticalToElementById: function(id, pad) {
		var element = document.getElementById(id);
		if (element === null) {
			console.log('Cannot find element with id \''+id+'\'.');
			return;
		}
		var targetPosition = this.documentVerticalScrollPosition() + this.elementVerticalClientPositionById(id) - pad;
		var currentPosition = this.documentVerticalScrollPosition();
		// Clamp.
		var maximumScrollPosition = this.documentMaximumScrollPosition();
		if (targetPosition > maximumScrollPosition) { targetPosition = maximumScrollPosition; }
		// Start animation.
		this.scrollVerticalTickToPosition(currentPosition, targetPosition);
	}
};

// formSubmit(e, formTask, formAction)
// Submit the form while disabling the button.
// Disable any "button" or "submit" that uses this function. This is to help stop multiple form submissions.
// Because the button is disabled the value is not sent when the form is submitted. This function fixes that.
// A hidden form element is created with the name and value of the button.
// Additionally, you can set a new value or even create many name/value pairs. See examples below.
//          e = the element calling formSubmit.
//   formTask = Create task or other hidden form elements.
//  undefined = Create a hidden element with the name and value of the e element.
//     string = Create a hidden element with the name 'task' and the value of formTask.
//      array = Create hidden elements for each associative array pair. The index is the name and the value is the value.
//              If e is of type button or submit and the array does not contain the button name a hidden element with the name and value of the e element is also created.
// formAction = The new form action. If formAction is undefined the form action is unchanged.
//
// 1) i.e. Call w/o formTask.
// formSubmit(this) or formSubmit(this,'','/new_form_action/')
// <input type="submit" name="task" value="ClickMe" onClick="formSubmit(this);">
// Will disable submit button and create a hidden element with the name "task" and the value "ClickMe" then submit the form.
//
// 2) i.e. Call with formTask value.
// formSubmit(this,'County select') or formSubmit(this,'County select','/new_form_action/')
// <input type="radio" name="county" value="McLeod" onChange="formSubmit(this,'County select');">McLeod
// Will create a hidden element with the name "task" and the value "County select" then submit the form.
//
// 3) i.e. Call with formTask as an array of name/value pairs.
// formSubmit(this,{'name1':'value1','name2':'value2'}) or formSubmit(this,{'name1':'value1','name2':'value2'},'/new_form_action/')
// <input type="submit" name="task" value="ClickMe" onClick="formSubmit(this,{'name1':'value1','name2':'value2'});">
// Will disable submit button and create 3 hidden elements with the names "name1", "name2", and "task" with the values "value1", "value2", and "ClickMe" then submit the form.
function formSubmit(e, formTask, formAction, DEBUG_formSubmit=false) {
	var DEBUG = DEBUG_OFF;
	if ( DEBUG_formSubmit ) { DEBUG = DEBUG_ON; }
	let index;
	if ( DEBUG ) {
		let eText = 'e=undefined';
		if ( e ) {
			if ( e.id ) {
				eText = 'e.id='+e.id;
			} else {
				eText = 'e.tagName='+e.tagName;
			}
		}
		let formTaskText;
		if ( typeof formTask  === 'object' && formTask !== null ) {
			formTaskText = JSON.stringify(formTask);
		} else {
			formTaskText = formTask;
		}
		if ( DEBUG ) { console.group(`${PC}formSubmit[${eText}, formTask=${JSON.stringify(formTask)} formAction=${formAction} DEBUG_formSubmit=${DEBUG_formSubmit}`,CG); }//Collapsed
	}
	let defaultName = 'task'; // Set default form element name.
	let formSubmit_return = false;
	ttHide(); // Hide any tooltip.
	UpdateAutoOff(); // Turn off the updateAuto timer.
	//if ( document.getElementById('pageContent') ) document.getElementById('pageContent').style.visibility = 'hidden'; // Hide the map.
	// Show page loading.
	//if ( !DEBUG ) { LoadingPleaseWait(); }
	e.className = "wait"; // Change the class of the button to wait.
	if ( e ) { // Is e defined?
		if ( DEBUG ) { console.log(`${PC}e.name=${e.name} e.type=${e.type} e.value=${e.value}`,CL); }
		if ( e.type === 'button' || e.type === 'submit' ) { // Is e a button?
			if ( DEBUG ) { console.log(`${PC}Button disabled.`,CW); }
			e.disabled = true; // Disable the button.
		} // Is e a button?
		form = formSubmit_GetForm(e); // Get form.
		if ( DEBUG ) { console.log(`${PC}Original form.name=${form.name} form.action=${form.action} form.target=${form.target}`,CI); }
		if ( typeof formAction !== 'undefined' ) {
			form.action = formAction; // If formAction then change the form action.
			if ( DEBUG ) { console.log(`${PC}form.action changed to ${form.action}`,CD); }
		}
		let el;
		if ( typeof formTask === 'undefined' ) { // Is formTask undefined or empty?  || formTask === ''
			if ( DEBUG ) { console.log(`${PC}typeof formTask === 'undefined'`,CI); }
			// formTask is undefined, so set task to e.value.
			if ( e.name ) { // Does e have a name?
				// Yes, named button with no formTask, so create a hidden element with e.name and the e.value.
				el = document.createElement("input"); // Create the input element.
				el.type = "hidden"; // Set element type to hidden.
				el.name = e.name; // Set element name to e.name.
				el.value = e.value; // Set element value to e.value.
				if ( DEBUG ) { console.log(`${PC}Create element type==${el.type} name=${el.name} value=${el.value}`,CD); }
				form.appendChild(el); // Add the element to the form.
			} else { // Does e have a name?
				// No, create a hidden element with the name 'task' and the e.value.
				el = document.createElement("input"); // Create the input element.
				el.type = "hidden"; // Set element type to hidden.
				el.name = defaultName; // Set element name to e.name.
				el.value = e.value; // Set element value to e.value.
				if ( DEBUG ) { console.log(`${PC}Create element type==${el.type} name=${el.name} value=${el.value}`,CD); }
				form.appendChild(el); // Add the element to the form.
			} // Does e have a name?
		//  // Is formTask undefined or empty?
		} else if ( typeof formTask === 'string' ) { // Is formTask a string?
			// formTask is a string so create task with formTask value.
			if ( DEBUG ) { console.log(`${PC}typeof formTask === 'string'`,CI); }
			el = document.createElement("input"); // Create the input element.
			el.type = "hidden"; // Set element type to hidden.
			el.name = defaultName; // Set element name to task.
			el.value = formTask; // Set element value to formTask value.
			if ( DEBUG ) { console.log(`${PC}Create element type==${el.type} name=${el.name} value=${el.value}`,CD); }
			form.appendChild(el); // Add the element to the form.
		 // Is formTask a string?
		} else if ( typeof formTask === 'object' && formTask !== null ) { // Is formTask as object?
			// formTask is an object so create elements for each array item.
			if ( DEBUG ) { console.log(`${PC}typeof formTask === 'object'`,CI); }
			if ( DEBUG ) { console.log(`${PC}formTask=${JSON.stringify(formTask)}`,CL); }
			if ( e.type === 'button' || e.type === 'submit' || e.type === 'click' ) { eFound = false; } else { eFound = true; } // Setup for addition of hidden elements if e is a button, submit, or click.
			if ( DEBUG ) {
				if ( eFound ) {
					console.log(`${PC}eFound=${eFound}`,CT);
				} else {
					console.log(`${PC}eFound=${eFound}`,CF);
				}
			}
			for ( index in formTask ) {if(formTask.hasOwnProperty(index)) { // Loop thru formTask object.
				if ( DEBUG ) { console.log(`${PC}formTask index=${index}`,CL); }
				el = document.createElement("input"); // Create the input element.
				el.type = "hidden"; // Set element type to hidden.
				el.name = index; // Set element name to formTask index.
				if ( el.name === e.name ) { eFound = true; }
				el.value = formTask[index]; // Set element value to formTask[index] value.
				if ( DEBUG ) { console.log(`${PC}Create element type==${el.type} name=${el.name} value=${el.value}`,CD); }
				form.appendChild(el); // Add the element to the form.
			}} // Loop thru formTask object.
			if ( !eFound && ( e.type === 'button' || e.type === 'submit' ) ) { // The button name was not in the array?
				if ( DEBUG ) { console.log(`${PC}The button name was not in the array, check for button name to add.`,CL); }
				if ( e.name ) {
					if ( DEBUG ) { console.log(`${PC}The buttonhad a name, so add a hidden element for button.`,CI); }
					el = document.createElement("input"); // Create the input element.
					el.type = "hidden"; // Set element type to hidden.
					el.name = e.name; // Set element name to e.name.
					el.value = e.value; // Set element value to e.value.
					if ( DEBUG ) { console.log(`${PC}Create element type==${el.type} name=${el.name} value=${el.value}`,CD); }
					form.appendChild(el); // Add the element to the form.
				} else {
					if ( DEBUG ) { console.log(`${PC}The button did not have a name, so do not add element.`,CW); }
				}
			} // The button name was not in the array?
		 // Is formTask as object?
		} else if ( !e.name && ( typeof formTask === 'undefined' || formTask === '' ) ) {
		// Unnamed button with no formTask or formAction. Simply submit the form.
		} else {
			if ( typeof(e) === 'object' ) { ename = ' e.name='+e.name; } else { ename = ''; }
			if ( typeof(formAction) === 'string' ) { aname = ' formAction='+formAction; } else { aname = ''; }
			alert('formSubmit() is not programmed for typeof(e)='+typeof(e)+ename+' typeof(formTask)='+typeof(formTask)+' typeof(formAction)='+typeof(formAction)+aname)+'. 850';
			//if ( DEBUG ) { console.groupEnd(); }
			//return false;
		}
		if ( !DEBUG ) {
			LoadingPleaseWait();
			formSubmit_return = true;
			//if ( DEBUG ) { console.groupEnd(); }
			form.submit();
			//return true;
		} else {
			// DEBUG is true.
			console.log(`${PC}form.action=${form.action} form.target=${form.target}`,CL);
			//let myform = document.forms[2];
			//console.dir(myform);
			//console.groupEnd();
			LoadingPleaseWait();
			//setTimeout(function(){ LoadingPleaseWait(); }, 5000);
			alert('Submit form '+form.name+' in 5 seconds. 868');
			setTimeout(function(){ form.submit(); }, 5000);
			//return false;
		}
	} else { // Is e defined?
		if ( DEBUG ) {
			alert('e is undefined. 874');
		}
		//if ( DEBUG ) { console.groupEnd(); }
		//return false;
	} // Is e defined?
	if ( DEBUG ) { console.groupEnd(); }
	if ( DEBUG ) { console.log(`${PC}formSubmit_return=${formSubmit_return}`,CL); }
	return formSubmit_return;
} // END formSubmit.

// getE(id,p,t,by)
// Set e and eId global values.
// id = id prefix.
//  p = planet number.
//  t = techunitId. (May be planet number for planet only elements)
// Returns true if the element id_t_p exists, else returns false.
function getE(id,p,t,by) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('getE['+id+','+p+','+t+','+by+']'); }
	eId = id;
	if ( p !== undefined && p !== '' ) { eId += '_'+p; }
	if ( t !== undefined && t !== '' ) { eId += '_'+t; }
	if ( DEBUG ) { console.log('eId='+eId); }
	if ( document.getElementById(eId) ) {
		if ( DEBUG ) { console.log('eId='+eId); }
		e = document.getElementById(eId);
		if ( DEBUG ) { console.log('e.id='+e.id); }
		return e;
	} else {
		return false;
	}
} // END getE.

// formSubmit_GetForm(e)
// Return the form elemnt.
// e = Form element to get form from.
// If e is not a form element return last form in document or create a new one..
function formSubmit_GetForm(e) {
	var DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.group(`${PC}formSubmit_GetForm[e.id=${e.id}]`,CG); }//Collapsed
	let Return = null;
	if ( DEBUG ) { console.log(`${PC}e.form=${e.form} e.form.name=${e.form.name}`,CL); }
	if ( e.form && e.form.name ) {
		Return = e.form;
	} else {
		if ( document.forms.length ) {
			Return = document.forms[document.forms.length-1];
		} else {
			let my_form=document.createElement('FORM');
			my_form.name='added_form';
			my_form.method='POST';
			my_form.action='/Play/';
			document.body.appendChild(my_form);
			Return= document.forms[0];
		}
	}
	if ( DEBUG ) { console.groupEnd(); }
	return Return;
} // END formSubmit_GetForm.

// getValue(id, p, t, by)
// Set e and eId values and return e.value with cleanup.
// id = id prefix.
//  p = planet number.
//  t = techunitId.
// Returns false if the element id_t_p does not exist.
function getValue(id, p, t, by) {
	// console.log('getValue[id='+id+', p='+p+', t='+t+', by='+by+']');
	let DEBUG = DEBUG_OFF;
	let getValueE = getE(id,p,t,'getValue['+id+','+p+','+t+','+by+']');
	if ( getValueE ) {
		let eValue = getValueE.value; // Get value.
		thisValue = parseInt(eValue); // Get quantity.
		if ( isNaN(thisValue) || thisValue < 0 ) { thisValue = ''; } // Cleanup bad number.
		if ( eValue !== thisValue ) {
			getValueE.value = thisValue;
			if ( DEBUG ) { console.log('The value has non-numeric character: eValue='+eValue+' thisValue = '+thisValue+' in getValue['+id+','+p+','+t+','+by+']'); }
		}
		if ( thisValue === '' ) { thisValue = 0; }
		if ( thisValue === 0 ) { getValueE.value = ''; } // Clear zero from element.
		return thisValue;
	} else {
		if ( DEBUG ) { console.log('document.getElementById('+getValueE.id+') is undefined in getValue['+id+','+p+','+t+','+by+']'); }
		return false;
	}
} // END getValue.

// Help(evt)
// Control help button onClick, onMouseDown, onMouseOut, and onMouseOver.
function Help(evt) {
	var DEBUG = DEBUG_OFF;
	evt = evt || window.event;
	if ( DEBUG ) { console.log(`${PC}evt.target=${evt.target} evt.type=${evt.type}`,CL); }
	if ( evt.type !== 'button' ) { // Is this a click or mouseover?
		// Yes, Show the tooltip or setup and open the help dialog.
		if ( DEBUG ) { console.group(`${PC}Help[evt.target.id=${evt.target.id}] evt.type=${evt.type}`,CG); }//Collapsed
		switch ( evt.type ) {
			case 'click': // Setup and open the help dialog.
				ttHide();
				document.getElementById('help_title').innerHTML = evt.target.alt;
				let helpName = evt.target.id.substr(5);
				let URL = HTTP_ROOT + '/help/?' + helpName;
				if ( DEBUG ) { console.log(`${PC}helpName=${helpName} URL=${URL}`,CL); }
				document.getElementById('form_HelpDialog_action').value = URL;
				HelpDisplay(evt,helpName);
			break;
			case 'mouseover': // Show the tooltip.
				ttShow(evt.target.getAttribute('data-tt')+'.');
			break;
		}
	} else { // Is this a click or mouseover?
		// No, The Window button was clicked.
		if ( DEBUG ) { console.group(`${PC}Help[] evt.type=${evt.type}`,CG); }//Collapsed
		let URL = document.getElementById('form_HelpDialog_action').value;
		let Title = URL.replace("_",' ');
		if ( DEBUG ) { console.log(`${PC}Open ${URL} in a new window.`,CD); }
		window.open(URL, 'Help', 'location=yes,height=570,width=520,scrollbars=yes,status=yes');
		menuHide();
	} // Is this a click or mouseover?
	if ( DEBUG ) { console.groupEnd(); }
} // END help.

// HelpDisplay(evt, helpName)
// Display the help dialog.
function HelpDisplay(evt, helpName) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('HelpDisplay[helpName='+helpName+']'); }
	//if ( typeof event !== 'undefined' ) { evt = event; } else { evt = window.event; }
	//evt = evt || window.event;
	GetMouse_mxmy(evt);
	if ( DEBUG ) { console.log('mx='+mx+' my='+my); }
	MenuIdBegin = 'dbHelpDialog';
	eMapMenu = document.getElementById(MenuIdBegin);
	if ( ( typeof MenuShownLast !== 'undefined' ) && MenuShownLast ) { menuHide('HelpDisplay:829'); } // Hide the last menu.
	MenuShownLast = MenuIdBegin;
	// Load help contents.
	let URI = HTTP_ROOT+'/help/'+helpName+'.php';
	document.getElementById('help_contents').innerHTML = '';
	UpdateInclude(URI, 'help_contents', evt.target.alt, "HelpNotFound('"+helpName+"');");
	let targetLeft = mx;
	let targetTop = my;
	if ( DEBUG ) { console.log('targetLeft='+targetLeft+' targetTop='+targetTop); }
	// Get windowScrollLeft and windowScrollTop.
	let windowScrollLeft = ttAllGet? ttTrueBody.scrollLeft : window.pageXOffset;
	let windowScrollTop = ttAllGet? ttTrueBody.scrollTop : window.pageYOffset;
	if ( DEBUG ) { console.log('windowScrollLeft='+windowScrollLeft+' windowScrollTop='+windowScrollTop); }
	let root = document.compatMode === 'BackCompat'? document.body : document.documentElement;
	let isHorizScroll = root.scrollWidth>root.clientWidth;
	let isVertScroll = root.scrollHeight>root.clientHeight;
	if ( DEBUG ) { console.log('isHorizScroll='+isHorizScroll+' isVertScroll='+isVertScroll); }
	document.body.style.overflow = 'hidden';
	let scrollbarSize = document.body.clientWidth;
	document.body.style.overflow = 'scroll';
	scrollbarSize -= document.body.clientWidth;
	if ( !scrollbarSize ) { scrollbarSize = document.body.offsetWidth - document.body.clientWidth; }
	document.body.style.overflow = '';
	if ( DEBUG ) { console.log('scrollbarSize='+scrollbarSize); }
	// Get winWidth and winHeight.
	let winWidth = 630;
	let winHeight = 460;
	let winSizeChanged = false;
	if (document.body && document.body.offsetWidth) {
		winWidth = document.body.offsetWidth;
		winHeight = document.body.offsetHeight;
	}
	if (document.compatMode === 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth ) {
		winWidth = document.documentElement.offsetWidth;
		winHeight = document.documentElement.offsetHeight;
	}
	if (window.innerWidth && window.innerHeight) {
		winWidth = window.innerWidth;
		winHeight = window.innerHeight;
	}
	if ( DEBUG ) { console.log('winWidth='+winWidth+' winHeight='+winHeight); }
	if ( isHorizScroll ) {
	winHeight -= scrollbarSize;
	winSizeChanged = true;
	if ( DEBUG ) { console.log('winHeight changed to '+winHeight+' because of horizontal scroll bar'); }
	}
	if ( isVertScroll ) {
	winWidth -= scrollbarSize;
		if ( DEBUG ) { console.log('winWidth changed to '+winWidth+' because of vertical scroll bar'); }
		winSizeChanged = true;
	}
	if ( winSizeChanged && DEBUG ) { console.log('winWidth='+winWidth+' winHeight='+winHeight); }
	// Set starting locationLeft and locationTop.
	let locationLeft = mx;
	let locationTop = my;
	if ( DEBUG ) { console.log('Starting position: locationLeft='+locationLeft+' locationTop='+locationTop); }
	// Display so menuWidth and menuHeight can be determined.
	eMapMenu.style.left = locationLeft+'px';
	eMapMenu.style.top = locationTop+'px';
	eMapMenu.style.display = 'block';
	//eMapMenu.style.visibility = 'visible';
	// Get menuWidth and menuHeight.
	let menuWidth = eMapMenu.offsetWidth + 2;
	let menuHeight = eMapMenu.offsetHeight + 2;
	if ( DEBUG ) { console.log('menuWidth='+menuWidth); }
	if ( DEBUG ) { console.log('menuHeight='+menuHeight); }
	// Center the menu on mouseX and place Y above mouse.
	locationLeft -= Math.round(menuWidth / 2);
	locationTop -= menuHeight;
	if ( DEBUG ) { console.log('Centered position: locationLeft='+locationLeft+' locationTop='+locationTop); }
	// Make sure window is not too far left.
	let Xchanged = false;
	if ( locationLeft < windowScrollLeft ) {
		Xchanged = true;
		locationLeft = windowScrollLeft;
		if ( DEBUG ) { console.log('Menu is too far left.'); }
		if ( DEBUG ) { console.log('Moved to left: locationLeft='+locationLeft); }
	}
	// Make sure window is not too high.
	let Ychanged = false;
	if ( locationTop < windowScrollTop ) {
		Ychanged = true;
		locationTop = windowScrollTop;
		if ( DEBUG ) { console.log('Menu is too high.'); }
		if ( DEBUG ) { console.log('Moved to top: locationTop='+locationTop); }
	}
	if ( !Xchanged ) {
		// Make sure window is not too far right.
		let menuRight = locationLeft + menuWidth - windowScrollLeft; // Get right side of menu.
		if ( DEBUG ) { console.log('menuRight='+menuRight); }
		if ( menuRight > winWidth) {
			locationLeft = locationLeft - menuRight + winWidth;
			if ( DEBUG ) { console.log('Menu is too far right.'); }
			if ( DEBUG ) { console.log('Moved to left: locationLeft='+locationLeft); }
		}
	}
	if ( !Ychanged ) {
		// Make sure window is not too low. This should never happen as you did click on it.
		let menuBottom = locationTop + menuHeight - windowScrollTop; // Get bottom of menu.
		if ( DEBUG ) { console.log('menuBottom='+menuBottom); }
		if ( menuBottom > winHeight) {
			locationTop = locationTop - menuBottom + winHeight;
			if ( DEBUG ) { console.log('Menu is too low.'); }
			if ( DEBUG ) { console.log('Moved to top: locationTop='+locationTop); }
		}
	}
	eMapMenu.style.left = locationLeft+'px';
	eMapMenu.style.top = locationTop+'px';
} // END helpDisplay.

// HelpNotFound(btnId)
// Show help not written if empty or 404 error.
// A 404 error will leave the Loading text in the help_contents td.
function HelpNotFound(btnId) {
	let HelpContents = document.getElementById('help_contents').innerHTML;
	console.log('btnId='+btnId);
	if ( HelpContents === '' || HelpContents.substring(0,30) === '<em class="info bold">Loading ' ) {
		document.getElementById('help_contents').innerHTML = '<b class="warntext">Sorry, this help content has not yet been written.</b>';
		document.getElementById('help_'+btnId).disabled = true;
	}
} // END HelpNotFound.

// htmlSafe(str)
// Return an HTML safe string.
function htmlSafe(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/(?:\r\n|\r|\n)/g, '<br>').replace(/"/g, '&quot;');
} // END htmlSafe.

// IncrementCounter(thisFunction)
// Increment the function counter. Used to count the number of times a function is called.
function IncrementCounter(thisFunction) {
	if (typeof thisFunction.counter === 'undefined' ) { thisFunction.counter = 0; }
	thisFunction.counter++;
	return thisFunction.counter;
} // END IncrementCounter.

// IsInViewport(e)
// Return tru if the element is in the viewport else false.
// e = The element to check.
var IsInViewport = function (e) {
	var bounding = e.getBoundingClientRect();
	return (
	bounding.top >= 0 &&
	bounding.left >= 0 &&
	bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
	bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
	);
};

// IsNumeric(n)
// Returns true if n is numeric, else false.
function IsNumeric(value) {
  return !isNaN(parseFloat(value)) && isFinite(value);
} // END isNumeric.

// 
// 
function IsUndefined(value) {
	//return value === undefined;
	console.log( typeof value );
	return ( value === 'undefined' || value === null );
} // END IsUndefined.

// LoadingPleaseWait()
// Show Loading... and hide pageContent when a form is submitted.
function LoadingPleaseWait() {
	var DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}LoadingPleaseWait[]`,CG); }//Collapsed
	//return;
	/** /
	if ( document.getElementById('pageHeader') ) {
		document.getElementById('pageHeader').style.display = 'none';
		if ( DEBUG ) { console.log(`${PC}pageHeader hidden.`,CD); }
	} else {
		if ( DEBUG ) { console.log(`${PC}pageHeader not found.`,CW); }
	}
	if ( document.getElementById('pageContent') ) {
		document.getElementById('pageContent').style.display = 'none';
		if ( DEBUG ) { console.log(`${PC}pageContent hidden.`,CD); }
	} else {
		if ( DEBUG ) { console.log(`${PC}pageContent not found.`,CW); }
	}
	if ( document.getElementById('pageFooter') ) {
		document.getElementById('pageFooter').style.display = 'none';
		if ( DEBUG ) { console.log(`${PC}pageFooter hidden.`,CD); }
	} else {
		if ( DEBUG ) { console.log(`${PC}pageFooter not found.`,CW); }
	}
	/**/
	// Hide dbMessageContents
	if ( document.getElementById('dbMessageContents') ) { document.getElementById('dbMessageContents').style.display = 'none'; }
	// Hide ttSpendingContents.
	if ( document.getElementById('ttSpendingContents') ) { document.getElementById('ttSpendingContents').style.display = 'none'; }
	let elements;
	// Hide all main.
	elements = document.getElementsByTagName('main');
	for ( let i=0; i<elements.length; i++ ) {
		elements[i].style.display = 'none';
	}
	// Hide all section.
	elements = document.getElementsByTagName('section');
	for ( let i=0; i<elements.length; i++ ) {
		elements[i].style.display = 'none';
	}
	// Hide all divs.
	elementss = document.getElementsByTagName('div');
	for ( let i=0; i<elements.length; i++ ) {
		elements[i].style.display = 'none';
	}
	// Hide all footer.
	elements = document.getElementsByTagName('footer');
	for ( let i=0; i<elements.length; i++ ) {
		elements[i].style.display = 'none';
	}
	if ( document.getElementById('pageContentLoading') ) {
		document.getElementById('pageContentLoading').style.display = 'block';
		if ( DEBUG ) { console.log(`${PC}pageContentLoading shown.`,CD); }
	} else {
		if ( DEBUG ) { console.log(`${PC}pageContentLoading not found.`,CW); }
	}
	//if ( DEBUG ) { alert('pageContentLoading'); }
	if ( DEBUG ) { console.groupEnd(); }
} // END LoadingPleaseWait.

// moveDisplay(menuType, mXY, by)
// Display the movement dialog.
// menuType = the click event.
// mXY = The XY of the menu to display. If undefined it will be determined by XY.
function moveDisplay(menuType, mXY, by) {
	let DEBUG = DEBUG_OFF;
	if ( typeof mXY === 'undefined' ) { mXY = ''; }
	if ( DEBUG ) { console.groupCollapsed(`${PC}moveDisplay[menuType=${menuType} mXY=${mXY} XY=${XY}] by=${by}`,CG); }
	let hXY;
	let StarNote_div_Bounds;
	let StarNote_tbl_Bounds;
	if ( typeof mXY !== 'undefined' && mXY !== '' ) {
		MenuIdBegin = menuType + '_' + mXY;
		hXY = GetHexCenterCoords(mXY);
		if ( menuType === 'MapMenu' ) { MapMenu = document.getElementById('MapMenu_'+mXY); }
		//console.log('mx='+mx+' my='+my);
		//console.log('hXY.X='+hXY.X+' hXY.Y='+hXY.Y);
	} else {
		MenuIdBegin = menuType;
		hXY = GetHexCenterCoords(XY);
		//console.log('mx='+mx+' my='+my);
		//console.log('hXY.X='+hXY.X+' hXY.Y='+hXY.Y);
	}
	if ( DEBUG ) { console.log(PC+'hXY='+JSON.stringify(hXY),CL); }
	// Get map image top left.
	let canvasTopLeft = elementLeftTop(document.getElementById('canvasMapBase'),'moveDisplay_434'); // The top and left of the canvas.
	if ( DEBUG ) { console.log(PC+'canvasTopLeft='+JSON.stringify(canvasTopLeft),CL); }
	mx = hXY.X + canvasTopLeft.left;// + ( PadHoriz * sideC ); // Adjust the mouseX for mapImage_e X and image padding.
	my = hXY.Y + canvasTopLeft.top; // Adjust the mouseY for mapImage_e Y.
	if ( DEBUG ) { console.log('mx='+mx+' my='+my); }
	if ( DEBUG ) { console.log('MenuIdBegin='+MenuIdBegin+' mx='+mx+' my='+my); }
	if ( DEBUG ) { console.log('MenuOnClickEnabled='+MenuOnClickEnabled); }
	if ( MenuOnClickEnabled ) { // Is the menu onclick enabled?
		if ( movesToTextTimer ) { // Remove movement tooltip and redraw movement lines.
			if ( DEBUG ) { console.info(PC+'clearTimeout[movesToTextTimer='+movesToTextTimer+'];',CD); }
			clearTimeout(movesToTextTimer);
			MovementTooltipHide('site.js moveDisplay 1202 movesToTextTimer');
		}
		if ( false ) {
			// Yes, it must be a drag.
			if ( DEBUG ) { console.info('e.id='+e.id); }
			menuDrag.startMoving(evt);
		} else { // Is this a MapMenuHeader element.
			// No, show the menu.
			let eMapMenu;
			ttHide(); // Hide tooltip.
			// Get mapMenu,
			if ( document.getElementById(MenuIdBegin) ) {
				eMapMenu = document.getElementById(MenuIdBegin);
				eMapMenu.style.opacity = 1;
			} else {
				eMapMenu = false;
			}
			if ( MenuShownLast ) { menuHide('moveDisplay 860'); } // Hide the last menu.
			MenuShownLast = MenuIdBegin;
			if ( eMapMenu ) { // Is there a map menu?
				if ( DEBUG ) { console.log('eMapMenu.id='+eMapMenu.id); }
				let targetLeft = mx;
				let targetTop = my;
				if ( DEBUG ) { console.log('targetLeft='+targetLeft+' targetTop='+targetTop); }
				// Get windowScrollLeft and windowScrollTop.
				let windowScrollLeft = ttAllGet? ttTrueBody.scrollLeft : window.pageXOffset;
				let windowScrollTop = ttAllGet? ttTrueBody.scrollTop : window.pageYOffset;
				if ( DEBUG ) { console.log('windowScrollLeft='+windowScrollLeft+' windowScrollTop='+windowScrollTop); }
				let root = document.compatMode === 'BackCompat'? document.body : document.documentElement;
				let isHorizScroll = root.scrollWidth>root.clientWidth;
				let isVertScroll = root.scrollHeight>root.clientHeight;
				if ( DEBUG ) { console.log('isHorizScroll='+isHorizScroll+' isVertScroll='+isVertScroll); }
				document.body.style.overflow = 'hidden';
				let scrollbarSize = document.body.clientWidth;
				document.body.style.overflow = 'scroll';
				scrollbarSize -= document.body.clientWidth;
				if ( !scrollbarSize ) { scrollbarSize = document.body.offsetWidth - document.body.clientWidth; }
				document.body.style.overflow = '';
				if ( DEBUG ) { console.log('scrollbarSize='+scrollbarSize); }
				// Get winWidth and winHeight.
				let winWidth = 630;
				let winHeight = 460;
				let winSizeChanged = false;
				if (document.body && document.body.offsetWidth) {
					winWidth = document.body.offsetWidth;
					winHeight = document.body.offsetHeight;
				}
				if (document.compatMode === 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth ) {
					winWidth = document.documentElement.offsetWidth;
					winHeight = document.documentElement.offsetHeight;
				}
				if (window.innerWidth && window.innerHeight) {
					winWidth = window.innerWidth;
					winHeight = window.innerHeight;
				}
				if ( DEBUG ) { console.log('winWidth='+winWidth+' winHeight='+winHeight); }
				if ( isHorizScroll ) {
					winHeight -= scrollbarSize;
					winSizeChanged = true;
				if ( DEBUG ) { console.log('winHeight changed to '+winHeight+' because of horizontal scroll bar'); }
				}
				if ( isVertScroll ) {
					winWidth -= scrollbarSize;
					if ( DEBUG ) { console.log('winWidth changed to '+winWidth+' because of vertical scroll bar'); }
					winSizeChanged = true;
				}
				if ( winSizeChanged && DEBUG ) { console.log('winWidth='+winWidth+' winHeight='+winHeight); }
				// Set starting locationLeft and locationTop.
				let locationLeft = mx;
				let locationTop = my;
				if ( DEBUG ) { console.log('Starting position: locationLeft='+locationLeft+' locationTop='+locationTop); }
				// Display so menuWidth and menuHeight can be determined.
				eMapMenu.style.left = locationLeft+'px';
				eMapMenu.style.top = locationTop+'px';
				eMapMenu.style.display = 'block';
				StarNote_div_Bounds = ElementBounds(MenuIdBegin,'moveDisplay 1181'); if ( DEBUG ) { console.log(`${PC}StarNote_div_Bounds=${JSON.stringify(StarNote_div_Bounds)}`,CL); }
				if ( menuType === 'dbStarNoteContents' && document.getElementById('tbl_StarNote')) {
					StarNote_tbl_Bounds = ElementBounds('tbl_StarNote','moveDisplay 1183'); if ( DEBUG ) { console.log(`${PC}StarNote_tbl_Bounds=${JSON.stringify(StarNote_tbl_Bounds)}`,CL); }
					eMapMenu.style.height = StarNote_tbl_Bounds.height+'px';
					document.getElementById('tbl_StarNote').style.top = StarNote_div_Bounds.top+'px';
				} else {
					if ( DEBUG ) { console.log(`tbl_StarNote not found.`,CF); }
				}
				StarNote_div_Bounds = ElementBounds(MenuIdBegin,'moveDisplay 1189'); if ( DEBUG ) { console.log(`${PC}StarNote_div_Bounds=${JSON.stringify(StarNote_div_Bounds)}`,CI); }
				//eMapMenu.style.visibility = 'visible';
				// Get menuWidth and menuHeight.
				let menuWidth = eMapMenu.offsetWidth;// + 2;
				let menuHeight = eMapMenu.offsetHeight;// + 2;
				if ( DEBUG ) { console.log(`${PC}menuWidth=${menuWidth} menuWidth=${menuWidth}`,CL); }
				// Center the menu on mouseX and place Y above mouse.
				locationLeft -= Math.round(menuWidth / 2);
				locationTop -= menuHeight;
				if ( DEBUG ) { console.log(`${PC}Centered position: locationLeft=${locationLeft} locationTop=${locationTop}`,CL); }
				// Make sure window is not too far left.
				let Xchanged = false;
				if ( locationLeft < windowScrollLeft ) {
					Xchanged = true;
					locationLeft = windowScrollLeft;
					if ( DEBUG ) { console.log('Menu is too far left.'); }
					if ( DEBUG ) { console.log('Moved to left: locationLeft='+locationLeft); }
				}
				// Make sure window is not too high.
				let Ychanged = false;
				if ( locationTop < windowScrollTop ) {
					Ychanged = true;
					locationTop = windowScrollTop;
					if ( DEBUG ) { console.log('Menu is too high.'); }
					if ( DEBUG ) { console.log('Moved to top: locationTop='+locationTop); }
				}
				if ( !Xchanged ) {
					// Make sure window is not too far right.
					let menuRight = locationLeft + menuWidth - windowScrollLeft; // Get right side of menu.
					if ( DEBUG ) { console.log('menuRight='+menuRight); }
					if ( menuRight > winWidth) {
						locationLeft = locationLeft - menuRight + winWidth;
						if ( DEBUG ) { console.log('Menu is too far right.'); }
						if ( DEBUG ) { console.log('Moved to left: locationLeft='+locationLeft); }
					}
				}
				if ( !Ychanged ) {
					// Make sure window is not too low. This should never happen as you did click on it.
					let menuBottom = locationTop + menuHeight - windowScrollTop; // Get bottom of menu.
					if ( DEBUG ) { console.log('menuBottom='+menuBottom); }
					if ( menuBottom > winHeight) {
						locationTop = locationTop - menuBottom + winHeight;
						if ( DEBUG ) { console.log('Menu is too low.'); }
						if ( DEBUG ) { console.log('Moved to top: locationTop='+locationTop); }
					}
				}
				eMapMenu.style.left = locationLeft+'px';
				eMapMenu.style.top = locationTop+'px';
				StarNote_div_Bounds = ElementBounds(MenuIdBegin,'moveDisplay 1237'); if ( DEBUG ) { console.log(`${PC}StarNote_div_Bounds=${JSON.stringify(StarNote_div_Bounds)}`,CI); }
				if ( menuType === 'dbStarNoteContents' && document.getElementById('tbl_StarNote')) {
					StarNote_tbl_Bounds = ElementBounds('tbl_StarNote','moveDisplay 1239'); if ( DEBUG ) { console.log(`${PC}StarNote_tbl_Bounds=${JSON.stringify(StarNote_tbl_Bounds)}`,CI); }
					eMapMenu.style.height = StarNote_tbl_Bounds.height+'px';
					document.getElementById('tbl_StarNote').style.top = '0px';
					StarNote_tbl_Bounds = ElementBounds('tbl_StarNote','moveDisplay 1242'); if ( DEBUG ) { console.log(`${PC}StarNote_tbl_Bounds=${JSON.stringify(StarNote_tbl_Bounds)}`,CL); }
				}
			} else { // Is there a map menu?
				console.log('In call to moveDisplay['+menuType+','+mXY+'] element '+MenuIdBegin+' does not exist.');
			} // Is there a map menu?
		} // Is this a MapMenuHeader element.
	} else { // Is the menu onclick enabled?
		//if ( DEBUG ) {  }
		console.info('moveDisplay disabled.');
	} // Is the menu onclick enabled?
	if ( DEBUG ) { console.groupEnd(); }
	return false;
} // END moveDisplay

// menuDrag()
// Allows player to drag the MapMenu to another location.
let menuDrag = function() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('menuDrag[]'); }
	return {
		startMoving : function(evt) { // The function that sets up the div coordinates to make it move. Executed on the onmousedown event on the div.
			if ( DEBUG ) { console.info('menuDrag.startMoving[] Getting target...'); }
			evt = evt || window.event;
			if ( DEBUG ) { console.log('evt='+evt); }
			let eTarget = evt.target;
			if ( DEBUG ) { console.log('eTarget.id='+eTarget.id); }
			let XYsplit = eTarget.id.split('_'); // Split the target id to get XY.
			if ( DEBUG ) { console.log('XYsplit='+XYsplit); }
			if ( XYsplit[0] !== 'MapMenuHeader' ) { menuDrag.stopMoving(); }
			XY = XYsplit[1]; // Set XY.
			let eMapMenu = document.getElementById(MenuIdBegin); // Set element to move.
			if ( DEBUG ) { console.info('startMoving('+eMapMenu.id+')'); }
			GetMouse_mxmy(evt); // Set mx and my to the mouse position.
			divTop = eMapMenu.style.top; // We need the initial position of the div so that we can determine its final position on dragging.
			divLeft = eMapMenu.style.left; // We need the initial position of the div so that we can determine its final position on dragging.
			divTop = divTop.replace('px',''); // Remove px so that we can perform calculations on divTop.
			divLeft = divLeft.replace('px',''); // Remove px so that we can perform calculations on divLeft.
			let diffX = mx - divLeft, // We keep this value so that we can calculate the final position of the element.
			diffY = my - divTop; // We keep this value so that we can calculate the final position of the element.
			DOC.onmousemove = 'menuDrag.startMoving onmousemove function';
			document.onmousemove = function(evt) { // Set onmousemove control to this function.
				evt = evt || window.event;
				GetMouse_mxmy(evt);
				aX = mx - diffX; // The current x-coordinate of the element.
				aY = my - diffY; // The current y-coordinate of the element.
				menuDrag.move(MenuIdBegin,aX,aY); // Function to actually move the element.
			};
			if ( DEBUG || DEBUG_doc_on ) { console.log(`${PC}onmousemove set to ${DOC.onmousemove}${PC} @menuDrag startMoving 1286`,CI,CL); }
		},
		stopMoving : function() { // This function gets executed when the user leaves the div alone.
			if ( DEBUG ) { console.info('menuDrag.stopMoving[]'); }
			if ( typeof CanvasMouseMoveListener !== 'undefined' ) {
				DOC.onmousemove = 'CanvasMouseMoveListener';
				document.onmousemove = CanvasMouseMoveListener; // Set onmousemove control to CanvasMouseMoveListener.
			} else {
				DOC.onmousemove = 'ttControl';
				document.onmousemove = ttControl; // Set onmousemove control to ttControl.
			}
			if ( DEBUG || DEBUG_doc_on ) { console.log(`${PC}onmousemove set to ${DOC.onmousemove}${PC} @menuDrag.stopMoving 1297`,CI,CL); }
			DOC.onmouseup = 'null';
			document.onmouseup = null; // Stop mouseup. function(){}
			if ( DEBUG || DEBUG_doc_on ) { console.log(`${PC}onmouseup set to ${DOC.onmouseup}${PC} @menuDrag.stopMoving 1300`,CI,CL); }
			//e = false; // Make sure a new click starts with a fresh element.
		},
		move : function(divid,xpos,ypos){ // Function to assign the style rules to the element.
			if ( DEBUG ) { console.info('menuDrag.move['+divid+','+xpos+','+ypos+']'); }
			let eMapMenu = document.getElementById(divid);
			eMapMenu.style.left = xpos + 'px';
			eMapMenu.style.top = ypos + 'px';
			DOC.onmouseup = 'menuDrag.stopMoving';
			document.onmouseup = menuDrag.stopMoving; // Set the onmouseup to menuDrag.stopMoving.
			if ( DEBUG || DEBUG_doc_on ) { console.log(`${PC}onmouseup set to ${DOC.onmouseup}${PC} @menuDrag.move 1310`,CI,CL); }
		}
	};
}(); // END menuDrag.

// menuHide(by)
// Hide the XY menu.
function menuHide(by) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('menuHide() MenuShownLast='+MenuShownLast+' by='+by); }
	if ( document.getElementById(MenuShownLast) ) {
		document.getElementById(MenuShownLast).style.display = 'none';
	} else {
		console.log('In call to menuHide() element '+MenuShownLast+' does not exist. by='+by);
	}
	ttHide();
	MenuShownLast = false;
	return false;
} // END menuHide.

// GetMouse_mxmy(evt)
// Set mx and my to the mouse position.
function GetMouse_mxmy(evt) {
	let DEBUG = DEBUG_OFF;
	evt = evt || window.event; // Get the event.
	if ( DEBUG ) { console.groupCollapsed(`${PC}GetMouse_mxmy[evt.target=${evt.target}]`,CG); }//
	if ( DEBUG  && evt.composedTarget) { console.log(`${PC}composedTarget.id=${evt.composedTarget.id}`,CL); }
	if ( DEBUG && evt.explicitOriginalTarget ) { console.log(`${PC}currentTarget.id=${evt.currentTarget.id}`,CL); }
	if ( DEBUG && evt.explicitOriginalTarget ) { console.log(`${PC}explicitOriginalTarget.id=${evt.explicitOriginalTarget.id}`,CL); }
	if ( DEBUG && evt.relatedTarget ) { console.log(`${PC}relatedTarget.id=${evt.relatedTarget.id}`,CL); }
	if ( DEBUG ) { console.log(`${PC}screenX=${evt.screenX} screenY=${evt.screenY}`,CL); }
	if ( DEBUG ) { console.log(`${PC}clientX=${evt.clientX} clientY=${evt.clientY}`,CL); }
	if ( DEBUG ) { console.log(`${PC}pageX=${evt.pageX} pageY=${evt.pageY}`,CL); }
	if ( DEBUG ) { console.log(`${PC}document.documentElement.scrollLeft=${document.documentElement.scrollLeft} document.documentElement.scrollTop=${document.documentElement.scrollTop}`,CL); }
	let New_mxmy = true;
	if ( DEBUG ) { console.log(`${PC}document.body.scrollLeft=${document.body.scrollLeft} document.body.scrollTop=${document.body.scrollTop}`,CL); }
	if ( /** /typeof evt !== 'undefined' && typeof evt.target !== 'undefined' &&/**/ evt.pageX ) { // Does the event have a target.id?
		mx = evt.pageX;
		my = evt.pageY;
		//if (evt.pageX) { mx = evt.pageX; } else if (evt.clientX) { mx = evt.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); } else { mx = 0; }
		//if (evt.pageY) { my = evt.pageY; } else if (evt.clientY) { my = evt.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); } else { my = 0; }
		Save_mxmy.mx = mx;
		Save_mxmy.my = my;
	} else { // Does the event have a target.id?
		mx = Save_mxmy.mx;
		my = Save_mxmy.my;
		New_mxmy = false;
	} // Does the event have a target.id?
	if ( DEBUG ) { console.groupEnd(); }
	if ( DEBUG ) {
		if ( New_mxmy ) {
			if ( DEBUG ) { console.log(`${PC}GetMouse_mxmy[] mx=${mx} my=${my}${PC} New mx my`,CS,CI); }
		} else {
			if ( DEBUG ) { console.log(`${PC}GetMouse_mxmy[] mx=${mx} my=${my}${PC} Old mx my`,CS,CW); }
		}
	}
} // END GetMouse_mxmy.

// Refresh_LastAccessAuto(sound, by)
// Refresh the playergame lastAccess time and play sound automatically every SoundTimer seconds.
function Refresh_LastAccessAuto(sound, by) {
	//console.log('Refresh_LastAccessOnce[sound='+sound+', by='+by+']');
	SoundPlayByNameAuto(sound, true, 'Refresh_LastAccessAuto['+by+']');
} // END Refresh_LastAccessAuto.

// Refresh_LastAccessOnce(sound, by)
// Refresh the playergame lastAccess time and play sound if given.
function Refresh_LastAccessOnce(sound, by) {
	//console.log('Refresh_LastAccessOnce[sound='+sound+', by='+by+']');
	if ( typeof sound === 'undefined' ) { sound = 'false'; }
	let URI = HTTP_ROOT+'/Play/common/Refresh_LastAccess.php?sound='+sound+'&by=Refresh_LastAccessOnce[sound='+sound+',by='+by+']';
	UpdateInclude(URI);
	if ( ( typeof sound !== 'undefined' ) && sound ) {
		SoundPlayByName(sound, 'Refresh_LastAccessOnce '+by);
		//SoundLastPlayedSound ='';
	}
} // END Refresh_LastAccessOnce.

// ScreenExit(e, action, name)
// Exit play and submit form (Also set name or 'task' value).
// Needs: <div id="divScreenExit" class="hide"></div> in body of page.
//      e = The object calling the function.
// action = Replacement form action.
//   name = The form element name. If unset use the calling element name. If the calling element has no name use 'task'.
function ScreenExit(e, action, name) {
	let DEBUG = DEBUG_OFF;
	let value;
	if ( e.value !== '' ) {
			value = e.value;
			if ( DEBUG ) { console.log('ScreenExit[value \''+value+'\',\''+action+'\',\''+name+'\']'); }
	} else {
		if ( e.name !== '' ) {
			value = e.name;
			if ( DEBUG ) { console.log('ScreenExit[name \''+value+'\',\''+action+'\',\''+name+'\']'); }
		} else {
			value = e.innerText;
			if ( DEBUG ) { console.log('ScreenExit[innerText \''+value+'\',\''+action+'\',\''+name+'\']'); }
		}
	}
	/** / This is now done in application.phpinc
	value = value.replace('B̲','B'); // Replace underlined B̲ with B.
	value = value.replace('D̲','D'); // Replace underlined D̲ with D.
	value = value.replace('d̲̲','d'); // Replace underlined d̲̲ with d.
	value = value.replace('N̲','N'); // Replace underlined N̲ with N.
	value = value.replace('o̲','o'); // Replace underlined o̲ with o.
	value = value.replace('x̲','x'); // Replace underlined x̲ with x.
	value = value.replace('Y̲','Y'); // Replace underlined Y̲ with Y.
	/**/
	if ( DEBUG ) { console.log('value='+value); }
 ttHide(); // Hide any tooltip.
  UpdateAutoOff(); // Turn off the updateAuto timer.
 // Show page loading.
 LoadingPleaseWait();
 e.className = "wait"; // Change the class of the button to wait.
 // Get the form.
 if ( DEBUG ) { console.log('document.forms='+document.forms); }
 if ( document.forms ) {
  // Document has forms.
  form = e.form; // Get form.
  if ( DEBUG ) { console.log('form.name='+form.name); }
 } else {
  if ( DEBUG ) { console.error('Document has no forms'); }
 }
 if ( DEBUG ) { console.log('form='+form.name); }
 // Get element name. If name is not set and calling e has no name, set name to 'task';
 if ( DEBUG ) { console.log('name='+name); }
 if ( typeof name === 'undefined' || name === '' ) {
  if ( DEBUG ) { console.log('name is undefined or empty'); }
  if ( typeof e.name === 'undefined' || e.name === '' ) {
   if ( DEBUG ) { console.log('e.name is undefined or empty Set name=task'); }
   name = 'task';
  } else {
   name = e.name;
  }
 }
 if ( DEBUG ) { console.log('name='+name); }
 if ( DEBUG ) { console.log('value='+value); }
 // Create name form element and set value.
 let fe = document.createElement("input"); // Create the input element.
 fe.type = 'hidden'; // Set element type to hidden.
 fe.name = name; // Set element name.
 fe.value = value; // Set element value to formTask value.
 form.appendChild(fe); // Add the element to the form.
 if ( DEBUG ) { console.log('Created element type='+fe.type+' name='+fe.name+' value='+fe.value); }
 // Change the form action if set.
 //if ( action != undefined ) {
 if ( typeof action !== 'undefined' && action !== '' ) {
  form.action = HTTP_ROOT+action; // Change the form action.
  if ( DEBUG ) { console.log('Changed the form action to: '+form.action); }
 }
 URLget('/Play/common/ScreenExit.php','divScreenExit'); // Exit the screen.
 if ( DEBUG ) {
  if ( document.getElementById('divScreenExit') ) { document.getElementById('divScreenExit').style.display = 'block'; }
  alert('ScreenExit() is submitting '+form.name);
 }
 //setTimeout(ScreenExit__Submit,100); // Submit the form in 100 milliseconds.
 setTimeout(function() { form.submit(); },100); // Submit the form in 100 milliseconds.
} // END ScreenExit.

// SeeHide_Events(see_hide)
// Show or hide Events.
function SeeHide_Events(see_hide) {
	let DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Events[see_hide=${see_hide}] SeeHide_Events.Loaded=${SeeHide_Events.Loaded}`,CG); }//Collapsed
	if ( !SeeHide_Events.Loaded ) {
		if ( see_hide ) { see_hide = 1; } else { see_hide = 0; }
		let URI = HTTP_ROOT+'/Play/common/See/get_Events.php?GetEvents=1';
		let elementId = 'div_See_Events';
		let preloadText = '<br><br><span class="bold info">Loading Events. Please wait ...</span><br><br><br>';
		let jsReturnCode = 'SeeHide_Events_Loaded('+see_hide+');'
		if ( DEBUG ) { console.log(`${PC}UpdateInclude('${HTTP_PROTOCOL}://${SERVER_NAME}${URI}&DEBUG=true','${elementId}','${preloadText}','${jsReturnCode}');`,CC); }
		UpdateInclude(URI,elementId,preloadText,jsReturnCode);
	}
	if ( see_hide ) {
		divShow('div_See_Events');
		document.getElementById('hid_sEvent').value = 1; // Set hid_sEvent form element value to 1.
		// Set the btn_See_Events button to hide Events.
		document.getElementById('btn_See_Events').setAttribute('class','current small');
		document.getElementById('btn_See_Events').onmouseover = function() { ttShow('Hide the list of Events.'); };
		ttShow('Hide the list of Events.');
		document.getElementById('btn_See_Events').onclick = function() { SeeHide_Events(false); };
	} else {
		divHide('div_See_Events');
		document.getElementById('hid_sEvent').value = 0; // Set hid_sEvent form element value to 0.
		// Set the btn_See_Events button to see Events.
		document.getElementById('btn_See_Events').setAttribute('class','small');
		document.getElementById('btn_See_Events').onmouseover = function() { ttShow('Show your events.'); };
		ttShow('Show your events.');
		document.getElementById('btn_See_Events').onclick = function() { SeeHide_Events(true); };
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Events.
SeeHide_Events.Loaded = false;

// function SeeHide_Events_Loaded(see_hide)
// Ensure events were loaded.
function SeeHide_Events_Loaded(see_hide) {
	var DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Events_Loaded[see_hide=${see_hide}]`,CG); }//Collapsed
	if ( document.getElementById('div_See_Events').innerHTML.search('Loading Events. Please wait ...') === -1 ) { // Did the events get loaded?
		if ( DEBUG ) { console.log(`${PC}Events loaded OK.`,CT); }
		SeeHide_Events.Loaded = true;
	} else {
		if ( DEBUG ) { console.log(`${PC}Events did not load. Re-calling SeeHide_Events[${see_hide}].`,CT); }
		setTimeout(function(){ SeeHide_Events(see_hide); }, 500);
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Events_Loaded.

// SeeHide_Fleets(see_hide)
// Show or hide fleets and ships.
// The div_See_Fleets is always written so it only eed to be shown or hidden.
function SeeHide_Fleets(see_hide) {
 console.log('SeeHide_Fleets['+see_hide+']');
 if ( see_hide ) {
  divShow('div_See_Fleets');
  document.getElementById('hid_sFleet').value = 1; // Set hid_sFleet form element value to 1.
  // Set the btn_See_Fleets button to hide Fleets.
  document.getElementById('btn_See_Fleets').setAttribute('class','current small');
  document.getElementById('btn_See_Fleets').onmouseover = function() { ttShow('Hide your fleets.'); };
  ttShow('Hide your fleets.');
  document.getElementById('btn_See_Fleets').onclick = function() { SeeHide_Fleets(false); };
 } else {
  divHide('div_See_Fleets');
  document.getElementById('hid_sFleet').value = 0; // Set hid_sFleet form element value to 0.
  // Set the btn_See_Fleets button to see Fleets.
  document.getElementById('btn_See_Fleets').setAttribute('class','small');
  document.getElementById('btn_See_Fleets').onmouseover = function() { ttShow('Show a list of fleets.'); };
  ttShow('See your fleets.');
  document.getElementById('btn_See_Fleets').onclick = function() { SeeHide_Fleets(true); };
 }
} // END SeeHide_Fleets.

// SeeHide_Map(see_hide)
// Show or hide map.
// The mapWrapper is always written so it only eed to be shown or hidden.
function SeeHide_Map(see_hide) {
 console.log('SeeHide_Map['+see_hide+']');
 if ( see_hide ) {
  divShow('mapWrapper');
	document.getElementById('sel_See_Map').style.visibility = 'visible';
	divShow('mapResize');
  document.getElementById('hid_sMap').value = 1; // Set hid_sMap form element value to 1.
  // Set the btn_See_Map button to hide Map.
  document.getElementById('btn_See_Map').setAttribute('class','current small');
  document.getElementById('btn_See_Map').onmouseover = function() { ttShow('Hide the game map.'); };
  ttShow('Hide the game map.');
  document.getElementById('btn_See_Map').onclick = function() { SeeHide_Map(false); };
 } else {
  divHide('mapWrapper');
	document.getElementById('sel_See_Map').style.visibility = 'hidden';
	divHide('mapResize');
  document.getElementById('hid_sMap').value = 0; // Set hid_sMap form element value to 0.
  // Set the btn_See_Map button to see Map.
  document.getElementById('btn_See_Map').setAttribute('class','small');
  document.getElementById('btn_See_Map').onmouseover = function() { ttShow('Show the game map.'); };
  ttShow('Show the game map.');
  document.getElementById('btn_See_Map').onclick = function() { SeeHide_Map(true); };
 }
} // END SeeHide_Map.

// SeeHide_Planets(see_hide)
// Show or hide Planets.
function SeeHide_Planets(see_hide) {
	let DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Planets[see_hide=${see_hide}]`,CG); }//Collapsed
	if ( !SeeHide_Planets.Loaded ) {
		if ( see_hide ) { see_hide = 1; } else { see_hide = 0; }
		let URI = HTTP_ROOT+'/Play/common/See/get_Planets.php?GetPlanets=1';
		let elementId = 'div_See_Planets';
		let preloadText = '<br><br><span class="bold info">Loading Planets. Please wait ...</span><br><br><br>';
		let jsReturnCode = 'SeeHide_Planets_Loaded('+see_hide+');'
		if ( DEBUG ) { console.log(`${PC}UpdateInclude('${HTTP_PROTOCOL}://${SERVER_NAME}${URI}&DEBUG=true','${elementId}','${preloadText}','${jsReturnCode}');`,CC); }
		UpdateInclude(URI,elementId,preloadText,jsReturnCode);
	}
	if ( see_hide ) {
		divShow('div_See_Planets');
		document.getElementById('hid_sEvent').value = 1; // Set hid_sEvent form element value to 1.
		// Set the btn_See_Planets button to hide Planets.
		document.getElementById('btn_See_Planets').setAttribute('class','current small');
		document.getElementById('btn_See_Planets').onmouseover = function() { ttShow('Hide your planets.'); };
		ttShow('Hide your planets.');
		document.getElementById('btn_See_Planets').onclick = function() { SeeHide_Planets(false); };
	} else {
		divHide('div_See_Planets');
		document.getElementById('hid_sEvent').value = 0; // Set hid_sEvent form element value to 0.
		// Set the btn_See_Planets button to see Planets.
		document.getElementById('btn_See_Planets').setAttribute('class','small');
		document.getElementById('btn_See_Planets').onmouseover = function() { ttShow('Show your planets.'); };
		ttShow('Show your planets.');
		document.getElementById('btn_See_Planets').onclick = function() { SeeHide_Planets(true); };
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Planets.
SeeHide_Planets.Loaded = false;

// function SeeHide_Planets_Loaded(see_hide)
// Ensure events were loaded.
function SeeHide_Planets_Loaded(see_hide) {
	var DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Planets_Loaded[see_hide=${see_hide}]`,CG); }//Collapsed
	if ( document.getElementById('div_See_Planets').innerHTML.search('Loading Planets. Please wait ...') === -1 ) { // Did the events get loaded?
		if ( DEBUG ) { console.log(`${PC}Planets loaded OK.`,CT); }
		SeeHide_Planets.Loaded = true;
	} else {
		if ( DEBUG ) { console.log(`${PC}Planets did not load. Re-calling SeeHide_Planets[${see_hide}].`,CT); }
		setTimeout(function(){ SeeHide_Planets(see_hide); }, 500);
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Planets_Loaded.

// SeeHide_Settings(see_hide)
// Show or hide Settings.
function SeeHide_Settings(see_hide) {
	let DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Settings[see_hide=${see_hide}]`,CG); }//Collapsed
	if ( !SeeHide_Settings.Loaded ) {
		if ( see_hide ) { see_hide = 1; } else { see_hide = 0; }
		let URI = HTTP_ROOT+'/Play/common/See/get_Settings.php?GetSettings=1';
		let elementId = 'div_See_Settings';
		let preloadText = '<br><br><span class="bold info">Loading Settings. Please wait ...</span><br><br><br>';
		let jsReturnCode = 'SeeHide_Settings_Loaded('+see_hide+');'
		if ( DEBUG ) { console.log(`${PC}UpdateInclude('${HTTP_PROTOCOL}://${SERVER_NAME}${URI}&DEBUG=true','${elementId}','${preloadText}','${jsReturnCode}');`,CC); }
		UpdateInclude(URI,elementId,preloadText,jsReturnCode);
	}
	if ( see_hide ) {
		divShow('div_See_Settings');
		document.getElementById('hid_sEvent').value = 1; // Set hid_sEvent form element value to 1.
		// Set the btn_See_Settings button to hide Settings.
		document.getElementById('btn_See_Settings').setAttribute('class','current small');
		document.getElementById('btn_See_Settings').onmouseover = function() { ttShow('Hide the list of Settings.'); };
		ttShow('Show the game settings and your bank.');
		document.getElementById('btn_See_Settings').onclick = function() { SeeHide_Settings(false); };
	} else {
		divHide('div_See_Settings');
		document.getElementById('hid_sEvent').value = 0; // Set hid_sEvent form element value to 0.
		// Set the btn_See_Settings button to see Settings.
		document.getElementById('btn_See_Settings').setAttribute('class','small');
		document.getElementById('btn_See_Settings').onmouseover = function() { ttShow('Show the game settings and your bank.'); };
		ttShow('Show the game settings and your bank.');
		document.getElementById('btn_See_Settings').onclick = function() { SeeHide_Settings(true); };
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Settings.
SeeHide_Settings.Loaded = false;

// SeeHide_Settings_Loaded(see_hide)
// Ensure settings were loaded.
function SeeHide_Settings_Loaded(see_hide) {
	var DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SeeHide_Settings_Loaded[see_hide=${see_hide}]`,CG); }//Collapsed
	if ( document.getElementById('div_See_Settings').innerHTML.search('Loading Settings. Please wait ...') === -1 ) { // Did the events get loaded?
		if ( DEBUG ) { console.log(`${PC}Settings loaded OK.`,CT); }
		SeeHide_Settings.Loaded = true;
	} else {
		if ( DEBUG ) { console.log(`${PC}Settings did not load. Re-calling SeeHide_Settings[${see_hide}].`,CT); }
		setTimeout(function(){ SeeHide_Settings(see_hide); }, 500);
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SeeHide_Settings_Loaded.

// SeeHide_Score(see_hide)
// Show or hide score.
// The div_See_Score is always written so it only eed to be shown or hidden.
function SeeHide_Score(see_hide) {
 console.log('SeeHide_Score['+see_hide+']');
 if ( see_hide ) {
  divShow('div_See_Score');
  document.getElementById('hid_sScore').value = 1; // Set hid_sScore form element value to 1.
  // Set the btn_See_Score button to hide score.
  document.getElementById('btn_See_Score').setAttribute('class','current small');
  document.getElementById('btn_See_Score').onmouseover = function() { ttShow('Hide the scores.'); };
  ttShow('Hide the scores.');
  document.getElementById('btn_See_Score').onclick = function() { SeeHide_Score(false); };
 } else {
  divHide('div_See_Score');
  document.getElementById('hid_sScore').value = 0; // Set hid_sScore form element value to 0.
  // Set the btn_See_Score button to see score.
  document.getElementById('btn_See_Score').setAttribute('class','small');
  document.getElementById('btn_See_Score').onmouseover = function() { ttShow('Show the scores.'); };
  ttShow('Show the scores.');
  document.getElementById('btn_See_Score').onclick = function() { SeeHide_Score(true); };
 }
} // END SeeHide_Score.

// SeeHide_Techs(see_hide)
// Show or hide techs.
// The div_See_Techs is always written so it only eed to be shown or hidden.
function SeeHide_Techs(see_hide) {
 console.log('SeeHide_Techs['+see_hide+']');
 if ( see_hide ) {
  divShow('div_See_Techs');
  document.getElementById('hid_sTech').value = 1; // Set hid_sTech form element value to 1.
  // Set the btn_See_Techs button to hide Techs.
  document.getElementById('btn_See_Techs').setAttribute('class','current small');
  document.getElementById('btn_See_Techs').onmouseover = function() { ttShow('Hide your technologies.'); };
  ttShow('Hide your technologies.');
  document.getElementById('btn_See_Techs').onclick = function() { SeeHide_Techs(false); };
 } else {
  divHide('div_See_Techs');
  document.getElementById('hid_sTech').value = 0; // Set hid_sTech form element value to 0.
  // Set the btn_See_Techs button to see Techs.
  document.getElementById('btn_See_Techs').setAttribute('class','small');
  document.getElementById('btn_See_Techs').onmouseover = function() { ttShow('Show your technologies.'); };
  ttShow('Show your technologies.');
  document.getElementById('btn_See_Techs').onclick = function() { SeeHide_Techs(true); };
 }
} // END SeeHide_Techs.

// SetGameSetting(e)
// Set database, $_SESSION, and javascript values.
function SetGameSetting(e) {
	var DEBUG = DEBUG_ON;
	if ( DEBUG ) { console.group(`${PC}SetGameSetting[e.id=${e.id}]`,CG); }//Collapsed
	ttHide();
	let IdParts = e.id.split('_');
	let radioName = IdParts[1];
	let gameIndex = radioName.replace('Game','game');
	let task = `Set${radioName}`;
	if ( DEBUG ) { console.log(`${PC}IdParts=${IdParts} radioName=${radioName} gameIndex=${gameIndex}`,CL); }
	// Get radio value.
	let value = 0;
	let radios = document.getElementsByName(radioName);
	for (let i = 0, length = radios.length; i < length; i++) { // Loop thru radio buttons.
		if (radios[i].checked) { // Is this button checked?
			value = radios[i].value;
			break;
		} // Is this button checked?
	} // Loop thru radio buttons.
	if ( GameInfo[gameIndex] != value ) { // Has the setting changed?
		// Yes, update the playergame table, $_SESSION variable, and GameInfo javascript values.
		if ( DEBUG ) { console.log(`${PC}${radioName} value has changed.`,CF); }
		GameInfo[gameIndex] = value; // Set GameInfo value.
		if ( DEBUG ) { console.log(`${PC}value=${value} GameInfo[${gameIndex}]=${GameInfo[gameIndex]}`,CL); }
		e.className = e.className.replace(' action',''); // Remove action from className.
		let URI = `${HTTP_ROOT}/Play/common/Set_Settings.php?task=${task}&userId=${userId}&gameId=${GameId}&field=${gameIndex}&value=${value}`;
		if ( DEBUG ) { console.log(`${PC}URI=${URI}`,CL); }
		if ( DEBUG ) { console.log(`${PC}UpdateInclude(${HTTP_PROTOCOL}://${SERVER_NAME}${URI}&DEBUG=true);`,CL); }
		UpdateInclude(URI,false);
	} else {
		// No, do nothing.
		if ( DEBUG ) { console.log(`${PC}${radioName} value is the current one.`,CT); }
		if ( DEBUG ) { console.log(`${PC}No changes were made`,CL); }
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SetGameSetting.

// SoundManage(e)
// Manage sound settings.
function SoundManage() {
 console.log('SoundManage[]');
 if ( document.getElementById('menu_SoundSetting') ) {
  if ( document.getElementById('menu_SoundSetting').style.display !== 'block' ) {
   document.getElementById('menu_SoundSetting').style.display = 'block';
  } else {
   document.getElementById('menu_SoundSetting').style.display = 'none';
  }
 }
} // END SoundManage.

// SoundPlay(sound, by)
// Play the soundfile.
// If the sound is different from the last sound played:
//    play it immediately.
// else
//    play it when getTime() > ( SoundLastPlayedTime + SoundTimer * 1000 ).
let SoundLoaded;
function SoundPlay(soundfile, by) {
	let DEBUG = DEBUG_OFF;
	let DEBUG_SOUND = DEBUG_OFF;
	SoundLoaded = false;
	//if(userId===3){DEBUG=true;}
	if ( DEBUG ) { console.group(PC+'SoundPlay[soundfile='+soundfile+'] by='+by,CG); }
	if ( document.getElementById("spn_PlaySound") ) {
		let PlaySound = false;
		document.getElementById("spn_PlaySound").innerHTML = '';
		if ( typeof soundfile !== 'undefined' && soundfile !== '' ) {
			let SoundVolume = soundfile.substr(soundfile.length-5,1);
			let SoundFile = soundfile.replace(".wav",'').replace(".mp3",'');
			if ( DEBUG ) { console.log('SoundMute='+SoundMute+' SoundVolume='+SoundVolume+' SoundFile='+SoundFile); }
			// Get current time.
			let date = new Date();
			let currentTime = date.getTime();
			if ( SoundFile === SoundLastPlayedSound ) { // Is this sound the last one played?
				// Yes, check time.
				let playtime = SoundLastPlayedTime + SoundTimer * 1000 - 10;
				let difftime = currentTime - playtime;
				if ( DEBUG ) { console.log('currentTime-playtime='+difftime+' ( currentTime > playtime )='+( currentTime > playtime )); }
				if ( currentTime > playtime ) { // Is it past the timer?
					PlaySound = 1403;
				}
			} else { // Is this sound the last one played?
				// No, play the sound.
				SoundLastPlayedSound = SoundFile;
				SoundLastPlayedTime = currentTime;
				PlaySound = 1409;
			} // Is this sound the last one played?
			if ( DEBUG ) { console.log('PlaySound='+PlaySound+' SoundCount='+SoundCount+' !SoundMute='+!SoundMute+' SoundVolume='+SoundVolume); }
			if ( PlaySound && SoundCount && !SoundMute && SoundVolume !== '0' && SoundVolume !== 0 ) { // Should the sound be played?
				// Create the audio element text.
				let audio = "<audio id=\"soundtoplay\" onloadeddata=\"SoundLoaded = true;\" autoplay>\n\t<source src=\""+SoundFile+".mp3\" type=\"audio/mpeg\"><source src=\""+SoundFile+".wav\" type=\"audio/wav\">\n\t\n\t<img src=\""+HTTP_ROOT+"/images/sound_mute.png\" alt=\"Your browser does not support the audio element.\" title=\"Your browser does not support the audio element.\">\n</audio>";//SoundPlay("+soundfile+")";
				//if ( DEBUG ) { console.info(PC+'audio.play['+SoundFile+']',CI); }
				// Put the text in spn_PlaySound.
				document.getElementById("spn_PlaySound").innerHTML = audio;
				// Define StartPlayback function for starting automatic playback.
				let StartPlayback = function() {
					//let StartPlaybackWorked = document.querySelector('#soundtoplay').play();
					//return StartPlaybackWorked;
					return document.querySelector('#soundtoplay').play();
				};
				// In browsers that don’t yet support this functionality, playPromise won’t be defined.
				if ( audio !== undefined ) { // Is audio defined?
					if ( DEBUG ) { console.log('Attempt to play sound. autoplay='+document.getElementById('soundtoplay').autoplay); }
					// Yes, try starting automatic playback.
					let StartPlaybackWorked = null;
					StartPlaybackWorked = StartPlayback().then(function() {
						// Automatic playback started!
						SoundLastPlayedTime = currentTime;
						if ( DEBUG_SOUND || DEBUG ) { console.info(PC+TB+SoundFile+' played by='+by+'.',CI); }
						if ( DEBUG_SOUND || DEBUG ) { console.info(PC+TB+'SoundLoaded='+SoundLoaded+'.',CI); }
						SoundPlayButtonClicked(); // Hide the Sound play button.
					}).catch(function(error) {
						// Automatic playback failed.
						// Show a UI element to let the user manually start playback.
						console.log('catch function triggered. StartPlaybackWorked='+StartPlaybackWorked+' SoundLoaded='+SoundLoaded);
						SoundLastPlayedTime = 0;
						if ( document.getElementById('btn_SoundPlay') ) { document.getElementById('btn_SoundPlay').style.display = 'inline'; }
						if ( ( DEBUG_SOUND || DEBUG ) && SoundLoaded ) { console.info(PC+TB+SoundFile+' did not play by='+by+'.',CW); }
					});
					//console.log('StartPlaybackWorked='+StartPlaybackWorked);
				} else {

				} // Is audio defined?
			} // Should the sound be played?
			SoundCount++;
		}
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SoundPlay.

// SoundPlayByName(sound, by)
// Play the sound by name. Calls SoundPlay().
function SoundPlayByName(sound, by) {
	let DEBUGByName = DEBUG_OFF;
	if ( DEBUGByName ) { console.log('SoundPlayByName[sound='+sound+']'); }
	if ( typeof sound !== 'undefined' && sound ) {
		switch ( sound.charAt(0).toUpperCase() + sound.slice(1) ) {
			case 'Attention':
				SoundPlay(SoundAttention, 'SoundPlayByName '+by);
			break;
			case 'Available':
				SoundPlay(SoundAvailable, 'SoundPlayByName '+by);
			break;
			case 'Error':
				SoundPlay(SoundError, 'SoundPlayByName '+by);
			break;
			case 'Notice':
				SoundPlay(SoundNotice, 'SoundPlayByName '+by);
			break;
			case 'Ping':
				SoundPlay(SoundPing, 'SoundPlayByName '+by);
			break;
			case 'Warning':
				SoundPlay(SoundWarning, 'SoundPlayByName '+by);
			break;
		}
	} else {
		//SoundPlay();
	}
} // END SoundPlayByName.

// SoundPlayByNameAuto(sound, refresh, by)
// Play sound by name automatically every SoundTimer seconds.
function SoundPlayByNameAuto(sound, refresh, by) {
	let DEBUG = DEBUG_OFF;
	if ( ( typeof refresh === 'undefined' ) || !refresh ) {
		refreshText = 'false';
	} else {
		refreshText = 'true';
	}
	if ( DEBUG ) { console.log('SoundPlayByNameAuto[sound='+sound+', refresh='+refreshText+', by='+by+'] SoundTimer='+SoundTimer); }
	if ( ( typeof sound !== 'undefined' ) && sound ) {
		SoundPlayByNameAutoSound = sound;
	} else {
		SoundPlayByNameAutoSound = false;
	}
	SoundPlayByNameAutoRefresh = refresh;
	// Remember the by.
	if ( typeof by !== 'undefined' ) {
		SoundPlayByNameAutoBy = by.split('|'); SoundPlayByNameAutoBy = SoundPlayByNameAutoBy[0];
	} else {
		SoundPlayByNameAutoBy = 'undefined';
	}
	SoundPlayByName(sound, 'SoundPlayByNameAuto '+by);
	let timer = 5000; // Re-call SoundPlayByNameAuto every 5 seconds. The sound will still only be played every SoundTimer seconds.
	let URI = HTTP_ROOT+'/Play/common/Refresh_LastAccess.php?refresh='+refreshText+'&by=SoundPlayByNameAuto[sound='+sound+',refresh='+refreshText+',by='+by+']SoundTimer='+SoundTimer;
	UpdateInclude(URI);
	//console.log(PC+'SoundPlayByNameAuto() clearInterval('+SoundPlayByNameAutoSetTimeout+'); AutoURI='+AutoURI,CE);
	clearInterval(SoundPlayByNameAutoSetTimeout);
	if ( DEBUG ) { console.log("SoundPlayByNameAutoSetTimeout = window.setTimeout(SoundPlayByNameAutoReturn,"+timer+");"); }
	SoundPlayByNameAutoSetTimeout = window.setTimeout(SoundPlayByNameAutoReturn,timer);
} // END SoundPlayByNameAuto.

// SoundPlayByNameAutoReturn()
// Callback to re-call SoundPlayByNameAuto().
function SoundPlayByNameAutoReturn() {
	//console.log('SoundPlayByNameAutoReturn[sound='+SoundPlayByNameAutoSound+', refresh='+SoundPlayByNameAutoRefresh+', by='+SoundPlayByNameAutoBy+'] SoundTimer='+SoundTimer);
	SoundPlayByNameAutoReturnCount++;
	SoundPlayByNameAuto(SoundPlayByNameAutoSound, SoundPlayByNameAutoRefresh, SoundPlayByNameAutoBy+'|count='+SoundPlayByNameAutoReturnCount);
} // END SoundPlayByNameAutoReturn.

// SoundPlayButtonClicked(e)
// Hide the 'Sound play' button after it has been clicked or a sound has been played.
function SoundPlayButtonClicked(e) {
	let DEBUG = DEBUG_OFF;
	if ( typeof e !== 'undefined' ) {
		if ( DEBUG ) { console.group(PC+'SoundPlayButtonClicked[e.id='+e.id+']',CG); }
		ttHide();
	} else {
		if ( DEBUG ) { console.group(PC+'SoundPlayButtonClicked[]',CG); }
	}
	if ( typeof e !== 'undefined' ) {
		e.style.display = 'none';
	} else {
		if ( document.getElementById('btn_SoundPlay') ) {
			if ( DEBUG ) { console.log(PC+'btn_SoundPlay exists.',CI); }
			e = document.getElementById('btn_SoundPlay');
			e.style.display='none';
		} else {
			if ( DEBUG ) { console.log(PC+'btn_SoundPlay does not exist.',CW); }
		}
	}
	if ( DEBUG ) { console.log(PC+'GameInfo.SoundPlayButton='+GameInfo.SoundPlayButton,CI); }
	if ( document.getElementById('btn_SoundPlay') || GameInfo.SoundPlayButton ) {
		GameInfo.SoundPlayButton = 0;
		let URI = `${HTTP_ROOT}/Play/common/Set_Settings.php?task=SetSESSION&field1=game&field2=SoundPlayButton&value=false`;
		if ( DEBUG ) { console.log(PC+'UpdateInclude('+HTTP_PROTOCOL+'://'+SERVER_NAME+URI+'&DEBUG=true);',CD); }
		UpdateInclude(URI);
	} else {
		if ( DEBUG ) { console.log(PC+'SoundPlayButton=0',CL); }
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END SoundPlayButtonClicked.

// SoundReset();
// Reset javascript sound setting to default values.
function SoundReset() {
 let DEBUG = DEBUG_OFF;
 let soundId;
 soundId = 'id_Mute';
 if ( document.getElementById(soundId) ) {
  let sounds = [ 'Mute', 'Attention', 'Available', 'Error', 'Notice', 'Ping', 'Warning' ];
  let soundDiv;
  let soundFile;
  let soundRadioId;
  let soundSetting;
  for ( let i=0; i<sounds.length; i++ ) {
   let sound = sounds[i];
   if ( DEBUG ) { console.log('sound='+sound); }
   switch ( sound ) {
    case 'Mute':
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundMute = document.getElementById(soundId).innerHTML;
     soundRadioId = 'id_Sound_'+sound+'_'+soundMute;
     if ( DEBUG ) { console.log('soundMute='+soundMute+' soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
     let SoundMuteImage;
     let SoundMuteTooltip;
     if ( SoundMute ) {
      SoundMuteImage = HTTP_ROOT+'/images/sound_mute.png';
      SoundMuteTooltip = 'Manage sound (currently: Mute all).';
     } else {
      SoundMuteImage = HTTP_ROOT+'/images/sound_on.png';
      SoundMuteTooltip = 'Manage sound (currently: All on).';
     }
     if ( document.getElementById('id_Sound_Mute_Setting') ) {
      document.getElementById('id_Sound_Mute_Setting').innerHTML = '<abbr class="nodot" onMouseOver="ttShow(\''+SoundMuteTooltip+'\')" onMouseOut="ttHide();"><img src="'+SoundMuteImage+'" width="24" height="21" alt="Manage sound."></abbr>';
     }
    break;
    case 'Attention':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundAttention = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
    case 'Available':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundAvailable = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
    case 'Error':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundError = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
    case 'Notice':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundNotice = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
    case 'Ping':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundPing = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
    case 'Warning':
     soundId = 'id_'+sound;
     if ( DEBUG ) { console.log('soundId='+soundId); }
     soundDiv = document.getElementById(soundId).innerHTML;
     soundDiv = soundDiv.split('|');
     soundSetting = soundDiv[0];
     soundFile = soundDiv[1];
     if ( DEBUG ) { console.log('soundDiv='+soundDiv+' soundSetting='+soundSetting+' soundFile='+soundFile); }
     SoundWarning = soundFile;
     soundRadioId = 'id_Sound_'+sound+'_'+soundSetting;
     if ( DEBUG ) { console.log('soundRadioId='+soundRadioId); }
     document.getElementById(soundRadioId).checked = true;
    break;
   }
  }
 } else {
  setTimeout(SoundReset,500);
 }
} // END SoundReset.

// SoundSave()
// Save sound setting.
function SoundSave(task) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('SoundSave[task='+task+']'); }
	let sounds = [ 'Mute', 'Attention', 'Available', 'Error', 'Notice', 'Ping', 'Warning' ];
	let query;
	let URI;
	switch ( task ) { // switch task.
		case 'Reset':
			document.getElementById('menu_SoundSetting_Return').innerHTML = task;
			if ( DEBUG ) { console.log('task='+task); }
			query = 'task='+task;
			URI = HTTP_ROOT+'/site/menu_SoundSettingSave.php?'+query;
			if ( DEBUG ) { console.log("UpdateInclude("+URI+", 'menu_SoundSetting_Return );"); }
			UpdateInclude(URI,'menu_SoundSetting_Return');
			SoundReset();
		break;
		case 'Save':
			document.getElementById('menu_SoundSetting_Return').innerHTML = task;
			let i;
			let sound;
			let setting;
			let radioName;
			let radios;
			let r;
			for ( i=0; i<sounds.length; i++ ) { // Loop thru radio buttons.
				sound = sounds[i];
				setting = '';
				radioName = 'Sound_'+sound;
				if ( DEBUG ) { console.log('radioName='+radioName); }
				radios = document.getElementsByName(radioName);
				for ( r=0; r<radios.length; r++) { // Loop thru radios.
					if (radios[r].checked) {
						setting = radios[r].value;
						break;
					}
				} // Loop thru radios.
				if ( DEBUG ) { console.log('task='+task+' sound='+sound+' setting='+setting); }
				query = 'task='+task+'&sound='+sound+'&setting='+setting;
				URI = HTTP_ROOT+'/site/menu_SoundSettingSave.php?'+query;
				if ( DEBUG ) { console.log("UpdateInclude("+URI+", 'menu_SoundSetting_Return );"); }
				UpdateInclude(URI,'menu_SoundSetting_Return');
			} // Loop thru radio buttons.
			// Get Timer.
			let thisSelect = document.getElementById('id_Sound_Timer');
			if ( DEBUG ) { console.log('thisSelect='+thisSelect); }
			let Timer = parseInt(thisSelect.options[thisSelect.selectedIndex].value);
			if ( DEBUG ) { console.log('Timer='+Timer); }
			query = 'task='+task+'&sound=Timer&setting='+Timer;
			URI = HTTP_ROOT+'/site/menu_SoundSettingSave.php?'+query;
			if ( DEBUG ) { console.log("UpdateInclude("+URI+", 'menu_SoundSetting_Return );"); }
			UpdateInclude(URI,'menu_SoundSetting_Return');
		break;
	} // switch task.
} // END SoundSave.

// SoundSet(e)
// Change a sound setting.
function SoundSet(e) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('SoundSet[e.id='+e.id+']'); }
	let temp = e.id.split('_');
	if ( DEBUG ) { console.log('temp='+temp); }
	let sound = temp[2];
	let setting;
	if ( typeof temp[3] !== 'undefined' ) {
		setting = temp[3];
	}
	if ( DEBUG ) { console.log('sound='+sound+' setting='+setting); }
	switch ( sound ) { // switch sound.
		case 'Mute':
			SoundMute = parseInt(setting);
			let SoundMuteImage;
			let SoundMuteTooltip;
			if ( SoundMute === 1 ) {
				SoundMuteImage = HTTP_ROOT+'/images/sound_mute.png';
				SoundMuteTooltip = 'Manage sound (currently: Mute all).';
			} else {
				SoundMuteImage = HTTP_ROOT+'/images/sound_on.png';
				SoundMuteTooltip = 'Manage sound (currently: All on).';
			}
			if ( document.getElementById('id_Sound_Mute_Setting') ) {
				document.getElementById('id_Sound_Mute_Setting').innerHTML = '<abbr class="nodot" onMouseOver="ttShow(\''+SoundMuteTooltip+'\')" onMouseOut="ttHide();"><img src="'+SoundMuteImage+'" width="24" height="21" alt="Manage sound."></abbr>';
			}
		break;
		case 'Attention':
			SoundAttention = '/sounds/attention'+setting+'.wav';
			SoundPlay(SoundAttention, 'SoundSet');
		break;
		case 'Available':
			SoundAvailable = '/sounds/available'+setting+'.wav';
			SoundPlay(SoundAvailable, 'SoundSet');
		break;
		case 'Error':
			SoundError = '/sounds/error'+setting+'.wav';
			SoundPlay(SoundError, 'SoundSet');
		break;
		case 'Notice':
			SoundNotice = '/sounds/notice'+setting+'.wav';
			SoundPlay(SoundNotice, 'SoundSet');
		break;
		case 'Ping':
			SoundPing = '/sounds/ping'+setting+'.wav';
			SoundPlay(SoundPing, 'SoundSet');
		break;
		case 'Warning':
			SoundWarning = '/sounds/warning'+setting+'.wav';
			SoundPlay(SoundWarning, 'SoundSet');
		break;
		case 'Timer':
			let thisSelect = document.getElementById('id_Sound_Timer');
			setting = parseInt(thisSelect.options[thisSelect.selectedIndex].value);
			if ( DEBUG ) { console.log('setting='+setting); }
			SoundTimer = setting;
		break;
	} // switch sound.
	let task = 'Set';
	document.getElementById('menu_SoundSetting_Return').innerHTML = task;
	let query = 'task='+task+'&sound='+sound+'&setting='+setting;
	let URI = HTTP_ROOT+'/site/menu_SoundSettingSave.php?'+query;
	if ( DEBUG ) { console.log("UpdateInclude("+URI+", 'menu_SoundSetting_Return );"); }
	UpdateInclude(URI,'menu_SoundSetting_Return');
} // END SoundSet.

// starNoteDisplay(XY)
// Rate or make notes on star.
function starNoteDisplay(XY) {
 let DEBUG = DEBUG_OFF;
 if ( DEBUG ) { console.log('starNoteDisplay[XY='+XY+']'); }
	// Check for id_StarNote_XY_Event.
	let StarNote_XY;
	if ( document.getElementById('id_StarNote_XY_Event') ) { // Does id_StarNote_XY_Event exist?
		// Yes, check if XY is the same as id_StarNote_XY_Event.
		StarNote_XY = document.getElementById('id_StarNote_XY_Event').value;
		if ( DEBUG ) { console.log ('StarNote_XY='+StarNote_XY+' XY='+XY); }
		if ( XY === StarNote_XY ) { // Is this the Event XY?
			// Yes, display the id_StarNote_Event div and give the id_noteText_Event focus.
			ttHide();
			document.getElementById('id_StarNote_Event').style.display = 'block';
			document.getElementById('id_noteText_Event').focus();
			if ( DEBUG ) { console.log(PC+'This is an event star note.',CF); }
			return;
		} else { // Is this the Event XY?
			if ( DEBUG ) { console.log(PC+'This is an map star note.',CT); }
		} // Is this the Event XY?
	} else {
		StarNote_XY = '';
		if ( DEBUG ) { console.log(PC+'This is an map star note.',CT); }
	} // Does id_StarNote_XY_Event exist?
 let radios;
 let i;
 if ( typeof Mapstar[XY] !== 'undefined' ) {
  // Clear notePlayer radio checked.
  radios = document.getElementsByName('notePlayer');
  for ( i = 0; i<radios.length; i++ ) {
   radios[i].checked = false;
  }
  // Clear noteRating radio checked.
  radios = document.getElementsByName('noteRating');
  for ( i = 0; i<radios.length; i++ ) {
   radios[i].checked = false;
  }
  // Load dbStarNoteContents form values with playerstarnote.
  document.getElementById('id_StarNote_XY').value = XY;
  let locationText = Startype[Mapstar[XY].startypeId].ColorSpan+' '+Mapstar[XY].Name+'<span class="small"> ['+XY+']</span>';
  if ( DEBUG ) { console.log('locationText='+locationText); }
  document.getElementById('id_StarNote_locationText').innerHTML = locationText;
  let note = Mapstar[XY].NoteText.replace(/<br>/g,"\n");
  let player = Mapstar[XY].NotePlayer;
  let rating = Mapstar[XY].NoteRating;
  if ( DEBUG ) { console.log('XY='+XY+' note='+note+' player='+player+' rating='+rating); }
  if (document.getElementById('id_noteText')) { document.getElementById('id_noteText').value = note; }
  if ( player > 0 ) {
    if (document.getElementById('id_notePlayer'+player)) { document.getElementById('id_notePlayer'+player).checked = true; }
  } else {
    if(document.getElementById('id_notePlayer'+player)) { document.getElementById('id_notePlayer'+userId).checked = true; }
  }
  if ( rating >= 0 ) {
   if (document.getElementById('id_noteRating'+rating)) { document.getElementById('id_noteRating'+rating).checked = true; }
  } else {
     if (document.getElementById('id_playerstarNoRating')) { document.getElementById('id_playerstarNoRating').checked = true; }
  }
	if ( DEBUG ) { console.log(`${PC}moveDisplay[dbStarNoteContents','']`,CC); }
  moveDisplay('dbStarNoteContents','');
  if (document.getElementById('id_noteText') ) { document.getElementById('id_noteText').focus(); }
 } else {
  console.info('Mapstar['+XY+'] is undefined');
 }
} // END starNoteDisplay.

// StarNoteSave(e)
// Save star note.
function StarNoteSave(e) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log(PC+'StarNoteSave[e.id='+e.id+' e.value='+e.value+']',CG); }
	ttHide();
	// Check if this in an Event button.
	let IsEvent = '';
	let StarNote_XY;
	if ( e.id.indexOf('_Event') === -1 ) { // Is this a map button?
		// Yes, check if this is .
		XY = document.getElementById('id_StarNote_XY').value;
		if ( DEBUG ) { console.log(PC+'This is an map star note.',CT); }
	} else { // Is this a map button?
		XY = document.getElementById('id_StarNote_XY_Event').value;
		if ( DEBUG ) { console.log(PC+'This is an event star note.',CS); }
		IsEvent = '_Event';
	} // Is this a map button?
	if ( DEBUG ) { console.log(PC+'XY='+XY+' IsEvent='+IsEvent,CL); }
	// Get and save the star note.
	let i = 0;
	let note = '';
	let query = '';
	if ( DEBUG ) { console.log(PC+'e.value='+e.value,CL); }
	switch ( e.value.replace('S̲','S').replace('C̲','C') ) { // Switch on e.value.
		case 'Save': {
			// Get playerstarnote from form values.
			note = htmlSafe(document.getElementById('id_noteText'+IsEvent).value.trim());
			if ( DEBUG ) { console.log(PC+'note='+note,CL); }
			let radios = document.getElementsByName('notePlayer'+IsEvent);
			if ( DEBUG ) { console.log('note='+note+' radios.length='+radios.length); }
			let player = 0;
			for ( i = 0; i<radios.length; i++) { // Loop thru notePlayer radio buttons.
				if ( DEBUG ) { console.log('radios['+i+'].name='+radios[i].name); }
				if (radios[i].checked) {
					player = radios[i].value;
					break; // Only one radio can be logically checked, don't check the rest.
				}
			} // Loop thru notePlayer radio buttons.
			if ( DEBUG ) { console.log('player='+player); }
			radios = document.getElementsByName('noteRating'+IsEvent);
			let rating = -1;
			for ( i = 0; i<radios.length; i++) { // Loop thru noteRating radio buttons.
				if ( DEBUG ) { console.log('radios['+i+'].name='+radios[i].name); }
				if (radios[i].checked) {
					rating = parseInt(radios[i].value);
					break; // only one radio can be logically checked, don't check the rest.
				}
			} // Loop thru noteRating radio buttons.
			if ( DEBUG ) { console.log('rating='+rating); }
			// Save in mapstar.
			Mapstar[XY].NoteText = note;
			Mapstar[XY].NotePlayer = player;
			Mapstar[XY].NoteRating = rating;
			Mapstar[XY].NoteTurn = playerTurn;
			let ratingSpan;
			switch ( rating ) {
				case -1:
					ratingSpan = '';
					break;
				case 0:
					ratingSpan = ' <span style="background-color:'+MapColors.StarNameExploredBackground0+'; color:'+MapColors.StarNameExploredColor0+';">*</span>';
					break;
				case 1:
					ratingSpan = ' <span style="background-color:'+MapColors.StarNameExploredBackground1+'; color:'+MapColors.StarNameExploredColor1+';">**</span>';
					break;
				case 2:
					ratingSpan = ' <span style="background-color:'+MapColors.StarNameExploredBackground2+'; color:'+MapColors.StarNameExploredColor2+';">***</span>';
					break;
			}
			if ( DEBUG ) { console.log('XY='+XY+' note='+note+' ratingSpan='+ratingSpan); }
			if ( document.getElementById('tt_Note_'+XY+IsEvent) ) { document.getElementById('tt_Note_'+XY+IsEvent).innerHTML = note; }
			if ( player === 0 ) { player = ''; }
			if ( document.getElementById('tt_Player_'+XY+IsEvent) ) { document.getElementById('tt_Player_'+XY+IsEvent).innerHTML = player; }
			if ( document.getElementById('tt_Rating_'+XY+IsEvent) ) { document.getElementById('tt_Rating_'+XY+IsEvent).innerHTML = ratingSpan; }
			if ( document.getElementById('tt_Notes_'+XY+IsEvent) ) {
				if ( note ) { // Is there a note?
					document.getElementById('tt_Notes_'+XY+IsEvent).style.display = 'table'; // Display the tt_Notes table.
				} else {
					document.getElementById('tt_Notes_'+XY+IsEvent).style.display = 'none'; // else hide it.
				} // Is there a note?
			}
			// Send note info to the server.
			if ( DEBUG ) { console.info('UPDATE the star note.'); }
			query = 'XY='+XY+'&note='+encodeURIComponent(note)+'&player='+player+'&rating='+rating;
			if ( document.getElementById('canvasMapBase') ) { DrawMapFinish('StarNoteSave 2611'); }
			console.log('Mapstar['+XY+']='+JSON.stringify(Mapstar[XY]));
			if ( document.getElementById('id_noteText'+IsEvent) ) { console.log('id_noteText cleared'); document.getElementById('id_noteText'+IsEvent).value = note; }
			break;
		}
		case 'Clear':
			if ( document.getElementById('tt_Note_'+XY+IsEvent) ) { document.getElementById('tt_Note_'+XY+IsEvent).innerHTML = ''; }
			if ( document.getElementById('tt_Player_'+XY+IsEvent) ) { document.getElementById('tt_Player_'+XY+IsEvent).innerHTML = ''; }
			if ( document.getElementById('tt_Rating_'+XY+IsEvent) ) { document.getElementById('tt_Rating_'+XY+IsEvent).innerHTML = ''; }
			if ( document.getElementById('tt_Notes_'+XY+IsEvent) ) { document.getElementById('tt_Notes_'+XY+IsEvent).style.display = 'none'; }
			query = 'XY='+XY;
			Mapstar[XY].NoteText = '';
			Mapstar[XY].NotePlayer = 0;
			Mapstar[XY].NoteRating = -1;
			console.log('Mapstar['+XY+']='+JSON.stringify(Mapstar[XY]));
			if ( document.getElementById('canvasMapBase') ) { DrawMapFinish('StarNoteSave 2626'); }
			if ( document.getElementById('id_noteText'+IsEvent) ) { console.log('id_noteText cleared'); document.getElementById('id_noteText'+IsEvent).value = ''; }
			break;
	} // Switch on e.value.
	let URI = HTTP_ROOT+'/Play/common/StarNotesSave.php?'+query;
	if ( DEBUG ) { console.log('UpdateInclude('+HTTP_PROTOCOL+'://'+SERVER_NAME+URI+'&DEBUG=true);'); }
	UpdateInclude(URI,'div_jsReturnData');
	if ( !document.getElementById('id_StarNote_Event') ) {
		menuHide('StarNoteSave:1950');
	} else {
		/** //**/if ( e.value === 'Close' ) { document.getElementById('id_StarNote_Event').style.display = 'none'; }
	}
	if ( IsEvent !== '' ) { // Is this an event star note?
		// Update star note screen text.
		if ( Mapstar[XY].NoteText !== '' || Mapstar[XY].NotePlayer !== 0 || Mapstar[XY].NoteRating !== -1 ) { // Is there a star note?
			// Yes, update to current text.
			document.getElementById('span_current_star_note').className = '';
			document.getElementById('span_current_star_note').innerHTML = 'This is the current star note.';
			document.getElementById('td_current_star_note').innerHTML = '';
		} else { // Is there a star note?
			// No, update to no note.
			document.getElementById('span_current_star_note').className = 'info';
			document.getElementById('span_current_star_note').innerHTML = 'There is currently no star note.';
			document.getElementById('td_current_star_note').innerHTML = '';
		} // Is there a star note?
	} // Is this an event star note?
} // END StarNoteSave.

// starValue()
// Calculate and return the value of all planets at a star.
function starValue() {
 let valueOfStar = 0;
 if ( PPXY[XY] ) { // Does this location have planets?
  for ( let i=0; i<PPXY[XY].length; i++ ) { // Loop thru planets.
   let pn = PPXY[XY][i];
   if ( PP[pn].OW === userId || PP[pn].PO === userId ) {
    valueOfStar += PP[pn].Vnow;
   } else {
    valueOfStar += PP[pn].Vgame;
   }
  } // Loop thru planets.
 } // Does this location have planets?
 return valueOfStar;
} // END starValue.

// ttDisplay(evt)
// Display map tooltip.
function ttDisplay(evt,DEBUG=false) {
	// DEBUG = DEBUG_ON;
	evt = evt || window.event; // Get the event.
	if ( DEBUG ) { console.groupCollapsed(`${PC}ttDisplay[evt.tagret.id=${evt.target.id}] XY=${XY} GameId=${GameId}`,CG); }
	if ( GameId ) {
		ttinnerHTML = '';
		if ( typeof PlayerEntryPoint === 'undefined' ) { PlayerEntryPoint = 0; }
		if ( MapEntrypointXYs.indexOf(XY) !== -1 ) {
			let EntryPointNumber = MapEntrypointXYs.indexOf(XY) + 1;
			if ( DEBUG ) { console.log('['+XY+'] entry '+EntryPointNumber+' PlayerEntryPoint='+PlayerEntryPoint); }
			if ( EntryPointNumber === PlayerEntryPoint ) { ttinnerHTML += 'This is your starting entry point.<br>'; }
		}
		ttinnerHTML = ttDisplay_onClick() + ttDisplay_Location();
		if ( typeof Mapstar[XY] !== 'undefined' ) {
			ttinnerHTML += ttDisplay_Planets();
		}
		ttinnerHTML += ttDisplay_InSpace() + ttDisplay_Fleets();
		if ( typeof Mapstar[XY] !== 'undefined' ) {
			//console.log(PC+'Mapstar['+XY+']='+JSON.stringify(Mapstar[XY]),CI);
			ttinnerHTML += ttDisplay_Production() + ttDisplay_Events() + ttDisplay_Notes() + ttDisplay_TKO(XY);
		}
		if ( ttinnerHTML !== '[0000]') {
			if ( DEBUG ) { console.log(`${PC}ttShow[ttinnerHTML=${ttinnerHTML}]`,CC); }
			ttShow(ttinnerHTML,'',DEBUG);
			if ( DEBUG ) { console.log(`${PC}ttControl[evt,DEBUG]`,CC); }
			ttControl(evt,DEBUG);
		} else {
			if ( DEBUG ) { console.log(`${PC}ttShow skipped because ttinnerHTML=${ttinnerHTML}`,CW); }
		}
	}
	if ( DEBUG ) { console.groupEnd(); }
} // END ttDisplay.

// ttDisplay_Events()
// Display game Events at XY.
function ttDisplay_Events() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_Events[] XY='+XY+''); }
	if ( DEBUG ) { console.log('playerScreen='+playerScreen); }
	ttContent_Events = '';
	if ( typeof Events !== 'undefined' ) {
		let uId;
		let uText;
		for ( let u=0; u<UserIdsEvent.length; u++ ) { // Loop thtu UserIdsEvent.
			uId = UserIdsEvent[u];
			if ( typeof Events[uId] !== 'undefined' ) {
				if ( typeof Events[uId][XY] !== 'undefined' ) { // Are there Events?
					if ( UserIdsEvent.length > 1 ) {
						uText = ' <span class="normal">for</span> '+PlayerColorNames[uId];
					} else {
						uText = '';
					}
					ttContent_Events += '<table class="ttTable">';
					ttContent_Events += "\n\t"+'<thead><tr><th class="left" colspan="2">Events'+uText+'</th></tr><tr><td class="center">T#</td><td class="left">Event</td></tr></thead>';
					ttContent_Events += "\n\t"+'<tbody>';
					let eventColorAction;
					let eventsToShow = Math.min(Events[uId][XY].length,ttMaxEventsToShow);
					let TRnow;
					let TRprevious = 0;
					let TRtext;
					for ( let i=0; i<eventsToShow; i++ ) {
						if ( Events[uId][XY][i].hidden === 0 ) {
							switch ( Events[uId][XY][i].action ) {
								// Class attention.
								case 'Take Over':
									eventColorAction = '<span class="attention">'+Events[uId][XY][i].action+'</span>';
								break;
								// Class errortext.
								case 'Fleet Destroyed':
								case 'Fleet Lost':
								case 'Lost Contact':
								case 'Out of Range Losses':
									eventColorAction = '<span class="errortext">'+Events[uId][XY][i].action+'</span>';
								break;
								// Class info.
								case 'Explore':
									eventColorAction = '<span class="info">'+Events[uId][XY][i].action+'</span>';
								break;
								// Class warntext.
								case 'Adjust Defense':
								case 'Bombardment':
								case 'Combat':
								case 'Initial Defense':
								case 'Movement Losses':
									eventColorAction = '<span class="warntext">'+Events[uId][XY][i].action+'</span>';
								break;
								//case '':
								default:
									eventColorAction = Events[uId][XY][i].action;
								break;
							}
							TRnow = Events[uId][XY][i].TR;//ttTR(Events[uId][XY][i].Turn);
							let eventResult = Events[uId][XY][i].result;
							if ( TRnow !== TRprevious ) {
								TRprevious = TRnow;
								TRtext = TRnow;
							} else {
								TRtext = '';
							}
							ttContent_Events += "\n\t"+'<tr><td class="right">'+TRtext+'</td><td>'+eventColorAction+eventResult+'</td></tr>';
						}
					}
					let moreEvents = Events[uId][XY].length - eventsToShow;
					let moreEventsPlural;
					if ( moreEvents ) {
						if ( moreEvents > 1 ) { moreEventsPlural = 's'; } else { moreEventsPlural = ''; }
						ttContent_Events += "\n\t"+'<tr><td></td><td class="info">'+moreEvents+' more event'+moreEventsPlural+' ...</td></tr>';
					}
					ttContent_Events += "\n\t"+'</tbody>';
					ttContent_Events += "\n"+'</table>';
				} // Are there Events?
			}
		} // Loop thtu UserIdsEvent.
	}
	return ttContent_Events;
} // END ttDisplay_Events.

// ttDisplay_Fleets()
// Display fleets at XY.
function ttDisplay_Fleets() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_Fleets[] XY='+XY+''); }
	//if ( DEBUG ) console.log('playerScreen='+playerScreen);
	let ttContent_Fleets = '';
	let ttContent_FleetsPlayer = '';
	let ttContent_FleetsEnemy = '';
	let ttContent_Fleets_Previous = ''; // Used to stop multiple ? fleets from same player.
	let tuId;
	let FleetCount = 0;
	if ( typeof PlayerfleetByXY !== 'undefined' ) { // Are there fleets?
		if ( typeof PlayerfleetByXY[XY] !== 'undefined' && PlayerfleetByXY[XY].length ) { // Are there fleets at this XY?
			if ( DEBUG ) { console.info('PlayerfleetByXY['+XY+']='+PlayerfleetByXY[XY]); }
			let PlayerFleetCountAtThisXY = 0;
			let PlayerFleetsWithoutDestinationAtThisXY = 0;
			for ( let FleetIndex=0; FleetIndex<PlayerfleetByXY[XY].length; FleetIndex++ ) { // Loop thru fleets at this XY.
				let UN = PlayerfleetByXY[XY][FleetIndex];
				let fleetName = PF[UN].Name;
				FleetCount++;
				if ( DEBUG ) { console.log(PlayerNames[PF[UN].userId]+' fleet '+PF[UN].Name+' fleetName='+fleetName); }
				if ( DEBUG ) { clog('271 PF['+fleetName+']',PF[UN],''); }
				// Add fleet name.
				if ( PF[UN].userId === userId ) { // Is this a player fleet?
					// Yes, Player fleet.
					PlayerFleetCountAtThisXY++;
					if ( PF[UN].dXY === '' ) { PlayerFleetsWithoutDestinationAtThisXY++; }
					if ( DEBUG ) { console.log('PF['+fleetName+'].userId == '+userId); }
					ttContent_FleetsPlayer += "\n\t"+'<tr><td class="right">';
					ttContent_FleetsPlayer += PF[UN].Name+'&#41;</td>';
					// Add contents.
					if ( DEBUG ) { console.log("typeof PFU["+fleetName+"] !== 'undefined'=",(typeof PFU[UN] !== 'undefined')); }
					let fleetContents = [];
					if ( typeof PFU[UN] !== 'undefined' ) { // Does this fleet have units?
						if ( DEBUG ) { console.log('TechunitIds='+TechunitIds+' TechunitIds.length='+TechunitIds.length); }
						for ( t=0; t<TechunitIds.length; t++ ) {
							tuId = TechunitIds[t];
							if ( DEBUG ) { console.log("typeof PFU['+fleetName+']['+tuId+'] !== 'undefined'=",(typeof PFU[UN][tuId] !== 'undefined')); }
							if ( typeof PFU[UN][tuId] !== 'undefined' ) { // Loop thru fleet units.
								//console.log('tuId='+tuId);
								if ( DEBUG ) { console.log('PFU['+fleetName+']['+tuId+']='+PFU[UN][tuId]); }
								if ( PFU[UN][tuId] > 0 ) {
									let unitText = PFU[UN][tuId]+' '+Techunit[tuId].Name;
									if ( PFU[UN][tuId] > 1 ) { unitText += 's'; }
									fleetContents.push(unitText);
								}
							} // Loop thru fleet units.
						}
						if ( fleetContents.length === 0 ) { fleetContents = ['<span class="warntext">empty</span>']; }
					} else {
						fleetContents = ['<span class="warntext">empty</span>'];
					} // Does this fleet have units?
					ttContent_FleetsPlayer += '<td>';
					if ( fleetContents.length ) {
						let prefix = '';
						let fleetContentsEnd = fleetContents.length - 1;
						if ( fleetContentsEnd === 0 ) { fleetContentsEnd = -1; }
						for ( let c=0; c<fleetContents.length; c++ ) {
							ttContent_FleetsPlayer += prefix;
							if ( c === fleetContentsEnd ) { ttContent_FleetsPlayer += 'and '; }
							ttContent_FleetsPlayer += fleetContents[c];
							if ( fleetContentsEnd > 1 ) { prefix = ', '; } else { prefix = ' '; }
						}
					}
					ttContent_FleetsPlayer += '</td><td>';
					// Add destination.
					if ( PF[UN].dTurns > 0 ) {
						if ( PF[UN].dNote.indexOf('warp') === -1 ) { // Is this regular movement?
							ttContent_FleetsPlayer += 'moves to '+PF[UN].dName+' in '+PF[UN].dTurns+' turn';
							if ( PF[UN].dTurns > 1 ) { ttContent_FleetsPlayer += 's'; }
						} else { // Is this regular movement?
							ttContent_FleetsPlayer += '<span class="warntext">warps</span> to '+PF[UN].dName;
						} // Is this regular movement?
					} else {
						// Check for Run.
						if ( PF[UN].Action.indexOf('Run') === -1 ) { // Is the fleet not running.
							if ( PF[UN].dTurns === 0 ) { ttContent_FleetsPlayer += '<span class="info">Just arrived</span>'; }
						} else { // Is the fleet not running.
							// The fleet is running.
							let runXY = PF[UN].Action.substr(3);
							ttContent_FleetsPlayer += '<span class="info">Run from combat at ';
							if ( typeof Mapstar[runXY] !== 'undefined' ) {
								ttContent_FleetsPlayer += Mapstar[runXY].Name;
							} else {
								ttContent_FleetsPlayer += runXY;
							}
							ttContent_FleetsPlayer += '.</span>';
						} // Is the fleet not running.
					}
					// Add Bomb.
					if ( DEBUG ) { console.log('PF['+UN+'].Bomb='+PF[UN].Bomb); }
					if ( PF[UN].Bomb === 1 ) { ttContent_FleetsPlayer += ' <span class="isOn">Bomb</span>'; }
					ttContent_FleetsPlayer += '</td></tr>';
				} else { // Is this a player fleet?
					// No, Enemy fleet.
					if ( DEBUG ) { console.info('PF['+fleetName+'].userId != '+userId+' IsPlayerFleetHere='+IsPlayerFleetHere(XY,'ttDisplay_Fleets')+' IsPlayerHere='+IsPlayerHere(XY,'ttDisplay_Fleets')); }
					if ( ( typeof Mapstar[XY] !== 'undefined' ) && ( IsPlayerFleetHere(XY,'ttDisplay_Fleets') || IsPlayerHere(XY,'ttDisplay_Fleets') ) ) { // Is this a star hex and is the player here?
						if ( PlayerStatus[PF[UN].userId].Turn <= playerTurn || PF[UN].dTurns === -1 ) { // Is the player NOT in the future or was the fleet already here?
							// Count playerfleetunit total ships.
							let playerfleetunitTotal = 0;
							if ( typeof PFU[UN] !== 'undefined' ) { // Does this fleet have units?
								for ( t=0; t<TechunitIds.length; t++ ) { // Loop thru ships.
									tuId = TechunitIds[t];
									if ( typeof PFU[UN][tuId] !== 'undefined' ) { // Loop thru fleet units.
										//console.log('tuId='+tuId);
										//console.log('PFU['+fleetName+']['+tuId+']='+PFU[UN][tuId]);
										if ( PFU[UN][tuId] > 0 ) {
											playerfleetunitTotal += PFU[UN][tuId];
										}
									} // Loop thru fleet units.
								} // Loop thru ships.
							} // Does this fleet have units?
							//playerfleetunitTotal = 1 // UNCOMMENT to force view of empty enemy fleets.
							if ( playerfleetunitTotal ) { // Does the enemy fleet have units?
								ttContent_Fleets_Current = "\n\t"+'<tr><td>';
								ttContent_Fleets_Current += '<span style="color:'+PlayerColors[PF[UN].userId].Color+'; background-color:'+PlayerColors[PF[UN].userId].Background+';">?&#41;</td><td colspan="2"style="color:'+PlayerColors[PF[UN].userId].Color+'; background-color:'+PlayerColors[PF[UN].userId].Background+';">'+PlayerNames[PF[UN].userId]+'</span>';
								if ( PF[UN].dTurns > 0 ) { ttContent_Fleets_Current += ' <span class="info">Glancing thru outer edge of the system.</span>'; }
								ttContent_Fleets_Current += '</td></tr>';
								if ( ttContent_Fleets_Current !== ttContent_Fleets_Previous ) { ttContent_FleetsEnemy += ttContent_Fleets_Current; }
								ttContent_Fleets_Previous = ttContent_Fleets_Current;
							} // Does the enemy fleet have units?
						} // Is the player NOT in the future or was the fleet already here?
					} // Is this a star hex and is the player here?
				} // Is this a player fleet?
			} // Loop thru fleets at this XY.
			if ( DEBUG ) { console.log('ttContent_FleetsPlayer='+ttContent_FleetsPlayer+' ttContent_FleetsEnemy='+ttContent_FleetsEnemy); }
			if ( ttContent_FleetsPlayer || ttContent_FleetsEnemy ) {
				ttContent_Fleets += '<table class="ttTable">';
				ttContent_Fleets += "\n\t"+'<thead><tr><th class="left" colspan="3">Fleets</th></tr></thead>';
				ttContent_Fleets += "\n\t"+'<tbody>';
				ttContent_Fleets += ttContent_FleetsPlayer + ttContent_FleetsEnemy;
				if ( !Mapstar[XY] && PlayerFleetCountAtThisXY && !PlayerFleetsWithoutDestinationAtThisXY && Techlimit.movement && Techlimit.movement.change && Techlimit.movement.change === 'star' ) {
					ttContent_Fleets += "\n\t"+'<tr><td colspan="3" class="warntext">Fleet';
					if ( PlayerFleetCountAtThisXY > 1 ) { ttContent_Fleets += 's'; }
					ttContent_Fleets += ' cannot change destination.</td></tr>';
				}
				ttContent_Fleets += "\n\t"+'</tbody>';
				ttContent_Fleets += "\n"+'</table>';//+Techlimit.movement.change;
			}
		} else { // Are there fleets at this XY?
			if ( DEBUG ) { console.info('There are no fleets at '+XY+'.'); }
		} // Are there fleets at this XY?
	} else {
		if ( DEBUG ) { console.info('There are no fleets.'); }
	} // Are there fleets?
	if ( DEBUG ) { console.log('ttContent_Fleets='+ttContent_Fleets); }
	return ttContent_Fleets;
} // END ttDisplay_Fleets.

// ttDisplay_InSpace()
// Display ships and orbital bases at XY.
function ttDisplay_InSpace() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_InSpace[] XY='+XY+''); }
	//if ( DEBUG ) console.log('playerScreen='+playerScreen);
	let ttContent_InSpace = '';
	if ( playerorbitalbaseXYs.indexOf(XY) !== -1 || playershipXYs.indexOf(XY) !== -1 ) { // Are there ships or orbital bases here?
		let UXY;
		let tuId;
		let unitText;
		let InSpaceContents = [];
		let t;
		if ( playershipXYs.indexOf(XY) !== -1 ) { // Are there ships here?
			UXY = userId+'_'+XY;
			for ( t=0; t<TechunitIds.length; t++ ) { // Loop thru tech units.
				tuId = TechunitIds[t];
				if ( typeof PSU[UXY] !== 'undefined' && typeof PSU[UXY][tuId] !== 'undefined' && PSU[UXY][tuId] > 0 ) { // Is this unit here?
					unitText = PSU[UXY][tuId] + ' ' + Techunit[tuId].Name;
					if ( PSU[UXY][tuId] > 1 ) { unitText += 's'; }
					InSpaceContents.push(unitText);
				} // Is this unit here?
			} // Loop thru tech units.
		} // Are there ships here?
		if ( playerorbitalbaseXYs.indexOf(XY) !== -1 ) { // Are there orbital bases here?
			UXY = userId+'_'+XY;
			for ( t=0; t<TechunitIds.length; t++ ) { // Loop thru tech units.
				tuId = TechunitIds[t];
				if ( ( typeof POU[UXY] !== 'undefined' ) && ( typeof POU[UXY][tuId] !== 'undefined' ) && POU[UXY][tuId] > 0 ) { // Is this unit here?
					unitText = POU[UXY][tuId] + ' ' + Techunit[tuId].Name;
					if ( POU[UXY][tuId] > 1 ) { unitText += 's'; }
					InSpaceContents.push(unitText);
				} // Is this unit here?
			} // Loop thru tech units.
		} // Are there orbital bases here? 0525
		//console.log('playerAction='+playerAction+' document.getElementById(\'id_CTsInSpace_'+XY+'\')='+document.getElementById('id_CTsInSpace_'+XY)+' XY='+XY);
		if ( playerAction === 'Production' && document.getElementById('id_CTsInSpace_'+XY) && document.getElementById('id_CTsInSpace_'+XY).value > 0 ) { // Are there CTs placed inspace?
			unitText = document.getElementById('id_CTsInSpace_'+XY).value+' '+Techunit[POPtransport_tuId].Name;
			if ( document.getElementById('id_CTsInSpace_'+XY).value > 1 ) { unitText += 's'; }
			InSpaceContents.push('<span class="production">'+unitText+' placed in space.</span>');
		} // Are there CTs placed inspace?
		if ( InSpaceContents.length ) {
			let InSpaceContent = '';
			let prefix = '';
			let InSpaceContentsEnd = InSpaceContents.length - 1;
			if ( InSpaceContentsEnd === 0 ) { InSpaceContentsEnd = -1; }
			for ( let c=0; c<InSpaceContents.length; c++ ) {
				InSpaceContent += prefix;
				if ( c === InSpaceContentsEnd ) { InSpaceContent += 'and '; }
				InSpaceContent += InSpaceContents[c];
				if ( InSpaceContentsEnd > 1 ) { prefix = ', '; } else { prefix = ' '; }
			}
			ttContent_InSpace += '<table class="ttTable">';
			ttContent_InSpace += "\n\t"+'<thead><tr><th class="left">InSpace</th></tr></thead>';
			ttContent_InSpace += "\n\t"+'<tbody>';
			ttContent_InSpace += "\n\t"+'<tr><td>'+InSpaceContent+'</td></tr>';
			ttContent_InSpace += "\n\t"+'</tbody>';
			ttContent_InSpace += "\n"+'</table>';
		}
	} // Are there ships or orbital bases here?
	if ( DEBUG ) { console.log('ttContent_InSpace='+ttContent_InSpace); }
	return ttContent_InSpace;
} // END ttDisplay_InSpace.

// ttDisplay_Location()
// Display star name if at star and/or hex location.
function ttDisplay_Location() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_Location[] XY='+XY+''); }
	if ( DEBUG ) { console.log('playerScreen='+playerScreen); }
	ttContent_Location = '';
	if ( typeof Mapstar[XY] !== 'undefined' ) { // Is this a star?
		// At star.
		let DustText = '';
		if ( MapDustHexXYs.indexOf(XY) !== -1 ) { DustText = ' in dust cloud'; }
		ttContent_Location += '<b>'+Startype[Mapstar[XY].startypeId].ColorSpan+' '+Mapstar[XY].Name+'</b> ['+XY;
		if ( XY === homeSystem ) { ttContent_Location += ' home system'; }
		ttContent_Location +=']'+DustText;
	} else { // Is this a star?
		if ( MapDustHexXYs.indexOf(XY) !== -1 ) { // Is this a dust hex?
			ttContent_Location += '['+XY+'] dust cloud'; // Dust hex.
		} else { // Is this a dust hex?
			ttContent_Location += '['+XY+']'; // Space hex.
		} // Is this a dust hex
	} // Is this a star?
	let starValueAtLocation = starValue();
	if ( starValueAtLocation ) { ttContent_Location += ' = '+starValueAtLocation; }
	if ( DEBUG ) { console.log('ttContent_Location='+ttContent_Location); }
	return ttContent_Location;
} // END ttDisplay_Location.

// ttDisplay_Notes()
// Display Notes about XY.
function ttDisplay_Notes() {
	let DEBUG_Notes = DEBUG_OFF;
	if ( DEBUG_Notes ) { console.log('ttDisplay_Notes[] XY='+XY+''); }
	if ( DEBUG_Notes ) { console.log('playerScreen='+playerScreen); }
	ttContent_Notes = '';
	if ( Mapstar[XY] && Mapstar[XY].NoteText !== '' ) { // Is this a star?
		ttContent_Notes += '<table class="ttTable">';
		ttContent_Notes += "\n\t"+'<thead><tr><th class="left">Notes</th><td>TR: '+ttTR(Mapstar[XY].NoteTurn)+'</tr></thead>';
		ttContent_Notes += "\n\t"+'<tbody>';
		if ( Mapstar[XY].NotePlayer !==0 && Mapstar[XY].NotePlayer !== userId ) {
			ttContent_Notes += "\n\t"+'<tr><td colspan="2">'+PlayerColorNames[Mapstar[XY].NotePlayer]+'</td></tr>';
		}
		ttContent_Notes += "\n\t"+'<tr><td colspan="2">'+Mapstar[XY].NoteText+'</td></tr>';
		ttContent_Notes += "\n\t"+'</tbody>';
		ttContent_Notes += "\n"+'</table>';
	}
	return ttContent_Notes;
} // END ttDisplay_Notes.

// ttDisplay_Planets()
// Display known planets at XY.
function ttDisplay_Planets() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.group(PC+'ttDisplay_Planets[] XY=['+XY+'] playerScreen=['+playerScreen+']',CG); }
	ttContent_Planets = '';
	let ProductionChangeClass;
	if ( typeof Mapstar[XY] !== 'undefined' ) { // Is this a star?
		if ( DEBUG ) { console.info(PC+Mapstar[XY].Name+' Mapstar['+XY+'].PNs='+Mapstar[XY].PNs+' .Turn='+Mapstar[XY].Turn,CI); }
		if ( Mapstar[XY].Turn !== -1 ) { // Has this star been explored?
			// This star has been explored.
			if ( DEBUG ) { console.info(PC+'This star has been explored.',CI); }
			// Show planet info if:
			//	1. There are planets ( typeof Mapstar[XY].PNs !== 'undefined' ) && Mapstar[XY].PNs.length > 0.
			//	2. There are PP[pn] entries ( typeof PP[Mapstar[XY].PNs[0]] !== 'undefined' ).
			//	3. The playerAction is not 'Spend Initial RPs' or the player previewed their home system ( playerAction != 'Spend Initial RPs' || GameInfo.previewHome ).
			if ( ( typeof Mapstar[XY].PNs !== 'undefined' ) && Mapstar[XY].PNs.length > 0 && ( typeof PP[Mapstar[XY].PNs[0]] !== 'undefined' ) && ( playerAction !== 'Spend Initial RPs' || GameInfo.previewHome ) ) { // Does this star have viewable planets?
				// This star has viewable planets.
				if ( DEBUG ) { console.info(PC+'This star has viewable planets. Mapstar['+XY+'].PNs='+Mapstar[XY].PNs,CI); }
				let p; // Index for looping thru planets.
				let pn = Mapstar[XY].PNs[0];
				// Get EnemyHasSpaceCombatUnits.
				// Display last explored if not current.
				if ( PP[pn].Turn !== playerTurn ) {
					ttContent_Planets += '<br><span class="info">Last explored on turn '+ttTR(PP[pn].Turn)+'.</span>';
				}
				ttContent_Planets += '<table class="ttTable">';
				ttContent_Planets += "\n\t"+'<thead><tr><th class="left" colspan="6">Planets</th></tr></thead>';
				ttContent_Planets += "\n\t"+'<tbody>';
				let EnemyHasSpaceCombatUnits = false;
				for ( p=0; p<Mapstar[XY].PNs.length; p++ ) { // Loop thru planets.
					pn = Mapstar[XY].PNs[p];
					if ( PP[pn].OW !== userId && ttEnemyHasSpaceCombatUnits(PP[pn].OW, PP[pn].XY) ) { EnemyHasSpaceCombatUnits = true; }
				}
				if ( DEBUG ) { console.log(PC+'EnemyHasSpaceCombatUnits='+EnemyHasSpaceCombatUnits,CL); }
				if ( DEBUG ) { console.log(PC+'Mapstar['+XY+'].PNs='+Mapstar[XY].PNs,CI); }
				for ( p=0; p<Mapstar[XY].PNs.length; p++ ) { // Loop thru planets.
					let LostContact = false;
					pn = Mapstar[XY].PNs[p];
					let pd = p+1;
					let pp = PP[pn];
					if ( DEBUG ) { console.groupCollapsed(PC+ttPd(pn)+' pn='+pn+' PP['+pn+'].OW='+pp.OW+' .LC='+pp.LC,CG); }
					if ( DEBUG ) { console.log(PC+'PP['+pn+']='+JSON.stringify(pp),CI); }
					ttContent_Planets += "\n\t"+'<tr>'; // BEGIN tr.
					let playerIsHere = IsPlayerHere(XY,'ttDisplay_Planets 2570'); // Check if player is here.
					if ( DEBUG ) { console.log(PC+'playerIsHere='+playerIsHere,CL); }
					// Show planet number.
					ttContent_Planets += '<td class="narrow right"';
					if ( playerIsHere && pp.OW !== -1 ) { // Is the player here and the planet owned by someone? // && pp.LC === 0 
						ttContent_Planets += ' style="background-color:'+PlayerColors[pp.OW].Background+'; color:'+PlayerColors[pp.OW].Color+';"'; // Show planet number in owner color.
					} // Is the player here and the planet owned by someone?
					ttContent_Planets += '>'+pd+'&#41;'+'</td>';
					if ( playerIsHere ) { // Is the player here?
						if ( DEBUG ) { console.info(PC+'The player is here at planet '+pn+'.',CI); }
						if ( pp.OW === userId ) { // Is this planet owned by the player?
							// This planet is owned by the user.
							if ( DEBUG ) { console.info(PC+'The planet is owned by the player.',CI); }
							if ( pp.LC === 0 && pp.Note.indexOf('LostContact') === -1 && pp.Note.indexOf('TakeOver') === -1 ) { // Is this planet currently owned by the user?
								// This planet is currently owned by the user. Show playerplanet info with content.
								if ( DEBUG ) { console.info(PC+'The player has not Lost Contact with the planet.',CI); }
								if ( DEBUG ) { console.info(PC+'Display planet contents.',CD); }
								ttContent_Planets += '<td class="narrow">';
								ProductionChangeClass = '';
								if ( ttPlanetIsOriginal(pn) ) { // Is the planet original?
									if ( DEBUG ) { console.info(PC+'The planet is original.',CI); }
									if ( ttPlanetIsPlayer(pn) ) { // Is the planet unchanged by production?
										if ( DEBUG ) { console.info(PC+'The planet is unchanged by production.',CI); }
										// Show player.
										if ( DEBUG ) { console.info(PC+'Show player planet contents.',CD); }
										ttContent_Planets += Planettype[pp.TYPEp].Code + pp.HABp + PlanetNMsuffix[pp.NMp];
									} else { // Is the planet unchanged by production?
										if ( DEBUG ) { console.info(PC+'The planet was changed by production.',CI); }
										// Show player changed and set to show production.
										if ( DEBUG ) { console.info(PC+'Show player planet changed by production.',CD); }
										ttContent_Planets += '<span class="planetChange">';
										ttContent_Planets += Planettype[pp.TYPEp].Code + pp.HABp + PlanetNMsuffix[pp.NMp];
										ttContent_Planets += '</span> ';
										ProductionChangeClass = 'production';
									} // Is the planet unchanged by production?
								} else { // Is the planet original?
									if ( DEBUG ) { console.info(PC+'The planet is NOT original.',CI); }
									// Show original changed.
									ttContent_Planets += '<span class="planetChange">';
									ttContent_Planets += Planettype[pp.TYPEo].Code + pp.HABo + PlanetNMsuffix[pp.NMo];
									ttContent_Planets += '</span> ';
									if ( ttPlanetIsPlayer(pn) ) { // Is the planet unchanged by production?
										if ( DEBUG ) { console.info(PC+'The planet is unchanged by production.',CI); }
										// Show player.
										if ( DEBUG ) { console.info(PC+'Show player planet contents.',CD); }
										ttContent_Planets += Planettype[pp.TYPEp].Code + pp.HABp + PlanetNMsuffix[pp.NMp];
									} else { // Is the planet unchanged by production?
										if ( DEBUG ) { console.info(PC+'The planet was changed by production.',CI); }
										// Show player changed and set to show production.
										if ( DEBUG ) { console.info(PC+'Show player planet changed by production.',CD); }
										ttContent_Planets += '<span class="planetChange">';
										ttContent_Planets += Planettype[pp.TYPEp].Code + pp.HABp + PlanetNMsuffix[pp.NMp];
										ttContent_Planets += '</span> ';
										ProductionChangeClass = 'production';
									} // Is the planet unchanged by production?
								}
								let VAL;
								if ( DEBUG ) { console.info(PC+'ProductionChangeClass="'+ProductionChangeClass+'" PP['+pn+'].VALp='+pp.VALp+' PP['+pn+'].VALr='+pp.VALr,CI); }
								if ( ProductionChangeClass === '' ) {
									VAL = pp.VALp;
								} else {
									// Show production.
									ttContent_Planets += '<span class="production">';
									ttContent_Planets += Planettype[pp.TYPEr].Code + pp.HABr + PlanetNMsuffix[pp.NMr];
									ttContent_Planets += '</span> ';
									VAL = pp.VALr;
								}
								ttContent_Planets += '</td>';
								// Add planet contents and value.
								ttContent_Planets += '<td>'+ttDisplay_Planets_Contents(pn)+'</td>';
								//ttContent_Planets += '<td class="narrow'+ProductionChangeClass+'">=</td>';
								ttContent_Planets += '<td class="narrow'+ProductionChangeClass+'">v'+VAL+'</td>';
							} else {
								// This planet was previously owned by the user. Show 'Lost Contact'.
								if ( DEBUG ) { console.info(PC+'The user has Lost Contact with the planet.',CI); }
								ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEr].Code+pp.HABr+PlanetNMsuffix[pp.NMr]+'</td>';
								ttContent_Planets += '<td class="errortext">Lost Contact</td>';
								//ttContent_Planets += '<td class="narrow">=</td>';
								ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
							}
						} else if ( pp.OW !== -1 ) { // Is this planet owned by another player?
							// This planet is owned by another user.
							if ( DEBUG ) { console.info(PC+'This planet is owned by another player.',CI); }
							if ( pp.LC === 0 && pp.Note.indexOf('Lost Contact') === -1 ) { // Does the owner still have contact?
								if ( DEBUG ) { console.info(PC+'This owner still has contact.',CI); }
								// This planet is currently owned by another user. Show playerplanet info with GBs or full content.
								ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEr].Code+pp.HABr;
								if ( pp.NMr ) { ttContent_Planets += PlanetNMsuffix[pp.NMr]; }
								ttContent_Planets += '</td>';
								if ( EnemyHasSpaceCombatUnits ) {
									if ( DEBUG ) { console.info(PC+'The enemy has space combat units.',CI); }
									if ( DEBUG ) { console.info(PC+'Do not show playerplanet info.',CD); }
									ttContent_Planets += '<td></td>';
								} else {
									if ( DEBUG ) { console.info(PC+'The enemy does NOT have space combat units.',CI); }
									if ( DEBUG ) { console.info(PC+'Show playerplanet info with GBs or full content.',CD); }
									ttContent_Planets += '<td>'+ttDisplay_Planets_Contents(pn,pp.OW)+'</td>';
								}
								//ttContent_Planets += '<td class="narrow">=</td>';
								ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
							} else { // Does the owner still have contact?
								if ( DEBUG ) { console.info(PC+'This owner does NOT have contact.',CI); }
								if ( DEBUG ) { console.info(PC+'Show prod info.',CD); }
								ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEr].Code+pp.HABr+PlanetNMsuffix[pp.NMr]+'</td>';
								// Check if player is the TKO player.
								if ( pp.Note.indexOf('LostContact:userId='+userId) !== -1 || pp.Note.indexOf('TakeOver:userId='+userId) !== -1 || pp.Note.indexOf('Production:userId='+userId) !== -1 ) { // Is the player the TKO player?
									if ( DEBUG ) { console.info(PC+'The player is the TKO player.',CI); }
									if ( DEBUG ) { console.info(PC+'Show planet contents with TKO or PRD.',CD); }
									// Show the contents.
									ttContent_Planets += '<td>'+ttDisplay_Planets_Contents(pn,pp.OW);
									if ( pp.Note.indexOf('LostContact:userId='+userId) !== -1 || pp.Note.indexOf('TakeOver:userId='+userId) !== -1 ) {
										ttContent_Planets += ' <span class="info">TKO</span>';
									}
									if ( pp.Note.indexOf('Production:userId='+userId) !== -1 ) {
										ttContent_Planets += ' <span class="production">PRD</span>';
									}
									ttContent_Planets += '</td>';
								} else { // Is the player the TKO player?
									if ( DEBUG ) { console.info(PC+'The player NOT the TKO player.',CI); }
									if ( DEBUG ) { console.info(PC+'Show no contents.',CD); }
									ttContent_Planets += '<td></td>';
								} // Is the player the TKO player?
								//ttContent_Planets += '<td class="narrow">=</td>';
								ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
							} // Does the owner still have contact?
						} else { // Is this planet owned by another player?
							// This planet is not owned by any player.
							if ( DEBUG ) { console.info(PC+'This planet is not owned by any player.',CI); }
							if ( pp.Note.indexOf('Obliterated') !== -1 ) { // Is there an obliterated note?
								// There is an obliterated note. Show playerplanet info.
								if ( DEBUG ) { console.log(TB+'There is an obliterated note. Show playerplanet info without content.'); }
								ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEr].Code+pp.HABr+PlanetNMsuffix[pp.NMr]+'</td>';
								ttContent_Planets += '<td>';
								if ( playerAction === 'Production' && pp.COLnext ) {
									ttContent_Planets += ttDisplay_Planets_Contents(pn,pp.OW);
								}							
								if ( pp.Note !== '' ) { ttContent_Planets += ' '+pp.Note; }
								ttContent_Planets += '</td>';
							} else { // Is there an obliterated note?
								// There is not an obliterated note. Show playerplanet info without content.
								if ( DEBUG ) { console.log(TB+"There is not an obliterated note. Show gameplanet info with COLnext or 'Lost Contact'."); }
								ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEp].Code+pp.HABp+PlanetNMsuffix[pp.NMp]+'</td>';
								LostContact = ttLostContact(pp);
								ttContent_Planets += '';
								if ( LostContact ) { // Did the player lose contact?
									ttContent_Planets += '<td class="errortext">Lost Contact</td>';
								} else {
									if ( turnType === 'Production' && pp.COLnext > 0 ) {
										ttContent_Planets += '<td class="production">'+pp.COLnext+' COL</td>';
									} else {
										ttContent_Planets += '<td></td>';
									}
								}
								//ttContent_Planets += '<td class="narrow">=</td>';
								ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
							} // Is there an obliterated note?
						} // Is this planet owned by another player?
					} else { // Is the player here?
						if ( DEBUG ) { console.info(PC+'The player is NOT here.',CI); }
						if ( DEBUG ) { console.info(PC+'Show production[r] planet info.',CD); }
						ttContent_Planets += '<td class="narrow">'+Planettype[pp.TYPEr].Code+pp.HABr+PlanetNMsuffix[pp.NMr]+'</td>';
						// See if the player has 'Lost Contact' with the planet.
						LostContact = ttLostContact(pp);
						ttContent_Planets += '';
						if ( LostContact ) { // Did the player lose contact?
							ttContent_Planets += '<td class="errortext">Lost Contact</td>';
							//ttContent_Planets += '<td></td>';
							//ttContent_Planets += '<td></td>';
						} else {
							ttContent_Planets += '<td></td>';
							//ttContent_Planets += '<td class="narrow">=</td>';
							//ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
						}
						ttContent_Planets += '<td class="narrow">v'+pp.VALp+'</td>';
					} // Is the player here?
					// Add DEBUG info.
					/** /
					if ( DEBUG ) {
						ttContent_Planets += '<td>';
						if ( pp.LC === 0 ) {
							if ( pp.OW !== -1 ) {
								ttContent_Planets += 'OW='+pp.OW+' '+PlayerColorNames[pp.OW]+' #'+pn;
							} else {
								ttContent_Planets += 'NO #'+pn;
							}
						} else {
							ttContent_Planets += 'LC='+pp.OW+' '+PlayerColorNames[pp.OW]+' #'+pn;
						}
						ttContent_Planets += '</td>';
					}
					/**/
					ttContent_Planets += '</tr>'; // END tr.
					if ( DEBUG ) { console.groupEnd(); }
				} // Loop thru planets.
				ttContent_Planets += "\n\t"+'</tbody>';
				ttContent_Planets += "\n"+'</table>';
			} else { // This star has viewable planets.
				if ( DEBUG ) { console.info(PC+'This star does NOT have viewable planets.',CI); }
				if ( playerTurn > 0 ) { // Is this a normal turn?
					// This star has no planets.
					if ( DEBUG ) { console.info(PC+'This is a normal turn.',CI); }
					if ( DEBUG ) { console.info(PC+'Show no planets.',CD); }
					ttContent_Planets += '<table class="ttTable"><tbody><tr><td>No planets.</td></tr></tbody></table>';
				} else { // Is this a normal turn?
					if ( DEBUG ) { console.info(PC+'This is a setup turn.',CI); }
					if ( playerAction === 'Select Techs' ) { //  && playerScreen !== 'SelectTechs'
						ttContent_Planets += '<table class="ttTable"><tbody><tr><td><span style="color:' + PlayerColors[userId].Color + '; background-color;' + PlayerColors[userId].Background + ';">' + Mapstar[XY].Name + '</span> is your home system.</td></tr></tbody></table>';
					} else if ( playerAction === 'Spend Initial RPs' ) {
						ttContent_Planets += '<table class="ttTable"><tbody><tr><td>You did not preview your home system.</td></tr></tbody></table>';
					}
				} // Is this a normal turn?
			} // DOes this star have planets?
		} else { // Has this star been explored?
			// This star has NOT been explored.
			if ( DEBUG ) { console.info(PC+'This star has NOT been explored.',CI); }
			if ( playerTurn > 0 ) { // Is this a normal turn?
				// This star has no planets.
				if ( DEBUG ) { console.info(PC+'This is a normal turn.',CI); }
				if ( DEBUG ) { console.info(PC+'Show unexplored.',CD); }
				ttContent_Planets += '<table class="ttTable"><tbody><tr><td>Unexplored.</td></tr></tbody></table>';
			} else { // Is this a normal turn?
				if ( DEBUG ) { console.info(PC+'This is a setup turn.',CI); }
				if ( DEBUG ) { console.info(PC+'Show nothing.',CD); }
				//ttContent_Planets += '<table class="ttTable"><tbody><tr><td>Unexplored.</td></tr></tbody></table>';
			} // Is this a normal turn?
		} // Has this star been explored?
	} else { // Is this a star?
		if ( DEBUG ) { console.info('Mapstar['+XY+'] is undefined.'); }
	} // Is this a star?
	if ( DEBUG ) {
		console.info(PC+'ttContent_Planets='+"\n"+ttContent_Planets,CI);
		//DEBUG_textarea_Load(ttContent_Planets);
	}
	if ( DEBUG ) { console.groupEnd(); }
	if ( DEBUG ) { console.log(PC+'END ttDisplay_Planets.',CG); }
	return ttContent_Planets;
} // END ttDisplay_Planets.

// ttDisplay_Planets_Contents(pn, uId)
// Return planet contents. This function is only called by ttDisplay_Planets().
//  pn = planet number.
// uId = If set show contents in uId's color else set uId to userId and use userId's color.
// If enemy warships or OBs are present return nothing; else if only GBs are present show only GBs; else show all POPCOL and units.
function ttDisplay_Planets_Contents(pn, uId) {
	let DEBUG = DEBUG_OFF;
	if ( typeof uId === 'undefined' ) { uId = userId; }
	if ( DEBUG ) { console.group(PC+'ttDisplay_Planets_Contents[pn='+pn+', uId='+uId+']',CG); }
	let planetContent = '';
	let unitQty = 0;
	let tuId = 0;
	if ( DEBUG && ( typeof PP[pn] !== 'undefined' ) ) { console.log(PC+'PP['+pn+']='+JSON.stringify(PP[pn]),CI); }
	if ( DEBUG ) { console.log(PC+"( typeof PP["+pn+"] !== 'undefined' )="+( typeof PP[pn] !== 'undefined' ),CL); }
	if ( DEBUG ) { console.log(PC+'IsPlayerHere(PP['+pn+'].XY)='+IsPlayerHere(PP[pn].XY,'ttDisplay_Planets_Contents'),CL); }
	//function dfdf() {
		if ( PP[pn].Note.indexOf('Lost Contact|'+userId) !== -1 || PP[pn].Note.indexOf('TakeOver:userId='+userId) !== -1 || PP[pn].Note.indexOf('Production:userId='+userId) !== -1 ) { // Is the player the TKO player?
		PlanetIsCoveredBySpaceCombatUnits = false;
	} else {
		PlanetIsCoveredBySpaceCombatUnits = ttEnemyHasSpaceCombatUnits(PP[pn].OW);
	}
	//}
	if ( PP[pn].Note.indexOf('Lost Contact|'+userId) !== -1 || PP[pn].Note.indexOf('TakeOver:userId='+userId) !== -1 || PP[pn].Note.indexOf('Production:userId='+userId) !== -1 ) { // Is the player the TKO player?
		PlanetIsCoveredBySpaceCombatUnits = false;
	} else {
		PlanetIsCoveredBySpaceCombatUnits = ttEnemyHasSpaceCombatUnits(PP[pn].OW);
	}
	let enemyHasGB = false;
	let planetContents = [];
	if ( uId !== userId ) { // Should we check for GBs?
		// Yes, check for GBs.
		for ( tuId in PPU[pn] ) {if(PPU[pn].hasOwnProperty(tuId)) { // Loop thru Techunit.
			if ( DEBUG ) { console.log(PC+Techunit[tuId].Name+ ' PPU['+pn+']['+tuId+']='+PPU[pn][tuId]+' Techunit['+tuId+'].Att='+Techunit[tuId].Att,CL); }
			if ( ( Techunit[tuId].Att > 0 ) && PPU[pn][tuId] ) { // Are there GBs?
				enemyHasGB = true; // Set true again.
				break; // Exit loop since GB found.
			} // Are there GBs?
		}} // Loop thru Techunit.
	} // Should we check for GBs?
	let enemyHasPlanetCover = PlanetIsCoveredBySpaceCombatUnits || enemyHasGB;
	if ( typeof PP[pn] !== 'undefined' && IsPlayerHere(PP[pn].XY,'ttDisplay_Planets_Contents') ) { // Does player have PP data and is player here?
		{
			// BEGIN Add POPCOL.
			if ( DEBUG ) { console.groupCollapsed(PC+'BEGIN Add POPCOL',CG); }
			if ( DEBUG ) { console.log('enemyHasPlanetCover='+enemyHasPlanetCover+' enemyHasGB='+enemyHasGB+' PlanetIsCoveredBySpaceCombatUnits='+PlanetIsCoveredBySpaceCombatUnits); }
			if ( !enemyHasPlanetCover ) { // Are their no GB or should GBs not be checked for?
				// Yes, GBs do not matter, show contents.
				let POPCOL = PP[pn].POP+PP[pn].COL;
				let POPCOLnext = PP[pn].POPnext+PP[pn].COLnext;
				if ( DEBUG ) { console.log(PC+'POPCOL='+POPCOL+' POPCOLnext='+POPCOLnext,CL); }
				let POPCOLclass = '';
				let POPCOLtext;
				if ( POPCOLnext !== 0 ) { // Is there POPCOL next turn?
					if ( playerAction !== 'Production' || PP[pn].LC ) { // Is this a production turn or planet is TKO or PRD?
						// Yes, this is not a production turn or planet is TKO or PRD.
						if ( DEBUG ) { console.log(PC+'Not a production turn or planet is taken over.',CI); }
						// Add POPCOL.
						let POPCOLspan = '';
						if ( POPCOLnext === PP[pn].HABp ) {
							POPCOLclass = '<span class="info">';
							POPCOLspan = '</span>';
						}
						POPCOLtext = POPCOLclass+POPCOLnext+' ';
						if ( PP[pn].POPnext ) {
							POPCOLtext += 'POP';
							if ( PP[pn].COLnext ) {
								POPCOLtext +='+';
							}
						} else if ( PP[pn].COLnext ) {
							POPCOLtext += 'COL';
						}
						POPCOLtext += POPCOLspan;
						planetContents.push(POPCOLtext);
					} else { // Is this a production turn or planet is TKO or PRD?
						// No, This is a production turn and the planet is not TKO or PRD.
						if ( DEBUG ) { console.log(PC+'A production turn and planet is not taken over.',CI); }
						// Production. Show POPnext + COLnext as POPCOL.
						if ( POPCOLnext === PP[pn].HABr && PP[pn].HABp === PP[pn].HABr ) {
							POPCOLclass = 'info';
						} else {
							if ( POPCOL === POPCOLnext ) {
								POPCOLclass = '';
							} else {
								POPCOLclass = 'production';
							}
						}
						POPCOLtext = ' <span class="'+POPCOLclass+'">'+POPCOLnext+' ';
						if ( PP[pn].POPnext ) {
							POPCOLtext += 'POP';
							if ( PP[pn].COLnext ) {
								POPCOLtext +='+';
							}
						} else if ( PP[pn].COLnext ) {
							POPCOLtext += 'COL';
						}
						POPCOLtext += '</span>';
						planetContents.push(POPCOLtext);
					} // Is this a production turn or planet is TKO or PRD?
					if ( DEBUG ) { console.log(PC+'POPCOLtext='+POPCOLtext,CL); }
				} else { // Is there POPCOL next turn?
					if ( DEBUG ) { console.log(PC+'There is no POP or COL next turn.',CF); }
				} // Is there POPCOL next turn?
			} else { // Are their no GB or should GBs not be checked for?
				if ( DEBUG ) { console.log('Space combat units or GBs hide planet contents.'); }
			} // Are their no GB or should GBs not be checked for?
			if ( DEBUG ) { console.groupEnd(); }
			if ( DEBUG ) { console.log(PC+'END Add POPCOL',CG); }
			// END Add POPCOL.
		}
		{
			// BEGIN Add units.
			if ( DEBUG ) { console.groupCollapsed(PC+'BEGIN Add units',CG); }
			if ( DEBUG ) { console.log(PC+'PPU['+pn+']='+JSON.stringify(PPU[pn])+' PPR['+pn+']='+JSON.stringify(PPR[pn]),CI); }
			if ( ( typeof PPU[pn] !== 'undefined' ) || ( typeof PPR[pn] !== 'undefined' ) ) { // Does this planet have units?
				for ( let tuIndex=0; tuIndex<TechunitIds.length; tuIndex++ ) {
					tuId = TechunitIds[tuIndex];
					let UnitClassBegin = '';
					let UnitClassEnd = '';
					
					if ( DEBUG ) { console.log(PC+'!enemyHasPlanetCover='+!enemyHasPlanetCover+' Techunit['+tuId+'].Att='+Techunit[tuId].Att,CL); }
					if ( ( !enemyHasPlanetCover || Techunit[tuId].Att > 0 ) ) { // Is the enemy planet not protected or is this a GB unit?
						// Yes, planet contents can be displayed.
						if ( DEBUG ) { console.log(PC+'This unit ('+Techunit[tuId].Name+') can be displayed.',CT); }
						let unitText = '';
						if ( Techunit[tuId].Special.text.search('RP=') !== -1 ) { // Is this an IU or RU unit?
							if ( PPU[pn][tuId] === 200 ) {
								UnitClassBegin = '<span class="info">';
								UnitClassEnd = '</span>';
							}
						} // Is this an IU or RU unit?
						if ( DEBUG ) { console.log(PC+'playerAction='+playerAction+' (typeof PPR['+pn+']['+tuId+']===\'undefined\')='+( typeof PPR[pn][tuId] === 'undefined' ),CL); }
						if ( playerAction !== 'Production' || ( typeof PPR[pn][tuId] === 'undefined' ) ) { // Is this not a production turn or PPR for this unit is undefined?
							// Yes, this not a production turn or PPR for this unit is undefined.
							if ( DEBUG ) { console.log(PC+'This not a production turn or PPR for this unit is undefined.',CL); }
							if ( PPU[pn][tuId] ) {
								unitText = UnitClassBegin+PPU[pn][tuId]+' '+Techunit[tuId].Name;
								if ( PPU[pn][tuId] > 1 ) { unitText += 's'; }
							}
							unitText += UnitClassEnd;
						} else { // Is this not a production turn or PPR for this unit is undefined?
							// No, this is a production turn and PPR for this unit is defined.
							if ( DEBUG ) { console.log(PC+'This is a production turn and PPR for this unit is defined.',CL); }
							if ( PPU[pn][tuId] || PPR[pn][tuId] ) {
								//console.log(PC+'PPU='+PPU[pn][tuId]+' PPR '+JSON.stringify(PPR[pn][tuId]),CI);
								unitQty = 0;
								if ( PPU[pn][tuId] > 0 ) { unitQty += PPU[pn][tuId]; }
								if ( PPR[pn][tuId] > 0 ) { unitQty += PPR[pn][tuId]; }
								unitText = '<span class="production">'+unitQty+' '+Techunit[tuId].Name;
								if ( unitQty > 1 ) { unitText += 's'; }
								unitText += '</span>';
							}
						} // Is this not a production turn or PPR for this unit is undefined?
						if ( unitText) { planetContents.push(unitText); }
					} else { // Is the enemy planet not protected or is this a GB unit?
						if ( DEBUG ) { console.log(PC+'The enemy planet is protected and this unit ('+Techunit[tuId].Name+') is not a GB unit.',CF); }
					} // Is the enemy planet not protected or is this a GB unit?
				}
			} // Does this planet have units?
			if ( DEBUG ) { console.groupEnd(); }
			if ( DEBUG ) { console.log(PC+'END Add units',CG); }
			// END Add units.
		}
		// Display planetContents.
		if ( planetContents.length ) {
			let prefix = '';
			let planetContentsEnd = planetContents.length - 1;
			if ( planetContentsEnd === 0 ) { planetContentsEnd = -1; }
			for ( let c=0; c<planetContents.length; c++ ) {
				planetContent += prefix;
				if ( c === planetContentsEnd ) { planetContent += 'and '; }
				planetContent += planetContents[c];
				if ( planetContentsEnd > 1 ) { prefix = ', '; } else { prefix = ' '; }
			}
		}
		if ( DEBUG ) { console.log('userId='+userId+' uId='+uId); }
		if ( userId !== uId && uId > 0 ) {
			planetContent = '<span style="color:'+PlayerColors[uId].Color+'; background-color:'+PlayerColors[uId].Background+';">'+planetContent+'</span>';
		}
	} else { // Does player have PP data and is player here?
		if ( DEBUG ) { console.log('Player does not have data for planet #'+pn+' or is not here.'); }
	} // Does player have PP data and is player here?
	//if ( planetContent === '' ) { planetContent = ' &nbsp; '; }
	if ( DEBUG ) { console.log('planetContent='+planetContent); }
	if ( DEBUG ) { console.groupEnd(); }
	return planetContent;
} // END ttDisplay_Planets_Contents.

// ttDisplay_Production()
// Display in space units produced at XY.
function ttDisplay_Production() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_Production[] XY='+XY+''); }
	//if ( DEBUG ) console.log('playerScreen='+playerScreen);
	let ttContent_Production = '';
	let totalCost = 0;
	let InSpaceProductionContent = '';
	let InSpaceProductionCTs = 0;
	let PlanetProductionContent = '';
	let prefix;
	if ( playerAction === 'Production' ) { // Is this a production screen?
		// In space production.
		let InSpaceProduction = [];
		let t;
		let tuId;
		let unitText;
		if ( typeof PSR[XY] !== 'undefined' ) { // Was there ship or OB production here?
			for ( t=0; t<TechunitIds.length; t++ ) { // Loop thru tech units.
				tuId = TechunitIds[t];
				if ( typeof PSR[XY][tuId] !== 'undefined' && PSR[XY][tuId] > 0 ) { // Was this unit built?
					unitText = PSR[XY][tuId] + ' ' + Techunit[tuId].Name + Techunit[tuId].NameSuffix;
					if ( PSR[XY][tuId] > 1 ) { unitText += 's'; }
					totalCost += Techunit[tuId].Cost * PSR[XY][tuId];
					InSpaceProduction.push(unitText);
				} // Was this unit built?
			} // Loop thru tech units.
			// id_CTsInSpace_2416
			thisQty = getValue('id_CTsInSpace',XY);
			if ( DEBUG ) { console.log('thisQty='+thisQty); }
			if ( thisQty ) {
				//InSpaceProductionCTs += thisQty;
				InSpaceProductionCTs = thisQty+' '+Techunit[POPtransport_tuId].Name;
				if ( thisQty > 1 ) { InSpaceProductionCTs += 's'; }
				InSpaceProductionCTs += ' in space here';
			}
		} // Was there ship or OB production here?
		if ( InSpaceProduction.length ) {
			prefix = '';
			let InSpaceProductionEnd = InSpaceProduction.length - 1;
			if ( InSpaceProductionEnd === 0 ) { InSpaceProductionEnd = -1; }
			for ( let c=0; c<InSpaceProduction.length; c++ ) {
				InSpaceProductionContent += prefix;
				if ( c === InSpaceProductionEnd ) { InSpaceProductionContent += 'and '; }
				InSpaceProductionContent += InSpaceProduction[c];
				if ( InSpaceProductionEnd > 1 ) { prefix = ', '; } else { prefix = ' '; }
			}
		}
		// Planet production.
		let PlanetProduction = [];
		let pn;
		if ( typeof PPXY[XY] !== 'undefined' ) { // Are there planets here?
			prefix = '';
			for ( let p=0; p<PPXY[XY].length; p++ ) { // Loop thru planets at star.
				PlanetProduction[p] = '';
				pn = PPXY[XY][p];
				for ( t=0; t<TechunitIds.length; t++ ) { // Loop thru tech units.
					tuId = TechunitIds[t];
					if ( typeof PPR[pn] !== 'undefined' && typeof PPR[pn][tuId] !== 'undefined' && PPR[pn][tuId] > 0 ) { // Was this unit built?
						unitText = PPR[pn][tuId] + ' ' + Techunit[tuId].Name;
						if ( Techunit[tuId].NameSuffix ) {
							if ( Techunit[tuId].Name !== 'AM' ) { unitText += Techunit[tuId].NameSuffix; }
						} else {
							if ( PPR[pn][tuId] > 1 ) { unitText += 's'; }
						}
						if ( Techunit[tuId].Cost ) {
							totalCost += Techunit[tuId].Cost * PPR[pn][tuId];
						} else if (Techunit[tuId].CostSpecial.code && Techunit[tuId].CostSpecial.code === 'RP' ) { // Does this unit have a CostSpecial.code?
							// Yes, use it.
							// Currently only AM has a special cost and it is planetHAB_Begin.
							totalCost += PP[pn].HAB;
						}
						PlanetProduction[p] += prefix+unitText;
						prefix = ', ';
					} else {
						//PlanetProduction[p] = '';
					} // Was this unit built?
				} // Loop thru tech units.
			} // Loop thru planets at star.
			prefix = '';
			for ( p=0; p<PPXY[XY].length; p++ ) { // Loop thru planets at star.
				let pd = p+1;
				pn = Mapstar[XY].PNs[p];
				if ( PlanetProduction[p] !== '' ) {
					//if ( PP[pn].OW == userId ) { // Is this planet owned by the user?
					if ( PP[pn].OW > 0 ) { // Is this planet owned by the user?
						PlanetProductionContent += prefix+'<span style="color:'+PlayerColors[PP[pn].OW].Color+'; background-color:'+PlayerColors[PP[pn].OW].Background+';">'+pd+'&#41;</span> ';
					} else {
						PlanetProductionContent += prefix+pd+'&#41; ';
					}
					ProductionChangeClass = '';
					if ( ttPlanetIsOriginal(pn) ) {
						if ( ttPlanetIsPlayer(pn) ) {
							PlanetProductionContent += Planettype[PP[pn].TYPEp].Code + PP[pn].HABp + PlanetNMsuffix[PP[pn].NMp];
						} else {
							// Show player changed and set to show production.
							PlanetProductionContent += '<span class="planetChange">';
							PlanetProductionContent += Planettype[PP[pn].TYPEp].Code + PP[pn].HABp + PlanetNMsuffix[PP[pn].NMp];
							PlanetProductionContent += '</span> ';
							ProductionChangeClass = 'production';
						}
					} else {
						// Show original changed.
						PlanetProductionContent += '<span class="planetChange">';
						PlanetProductionContent += Planettype[PP[pn].TYPEo].Code + PP[pn].HABo + PlanetNMsuffix[PP[pn].NMo];
						PlanetProductionContent += '</span> ';
						if ( ttPlanetIsPlayer(pn) ) {
							// Show player.
							PlanetProductionContent += Planettype[PP[pn].TYPEp].Code + PP[pn].HABp + PlanetNMsuffix[PP[pn].NMp];
						} else {
							// Show player changed and set to show production.
							PlanetProductionContent += '<span class="planetChange">';
							PlanetProductionContent += Planettype[PP[pn].TYPEp].Code + PP[pn].HABp + PlanetNMsuffix[PP[pn].NMp];
							PlanetProductionContent += '</span> ';
							ProductionChangeClass = 'production';
						}
					}					
					if ( ProductionChangeClass === 'production' ) {
						// Show production.
						ttContent_Planets += '<span class="production">';
						ttContent_Planets += Planettype[PP[pn].TYPEr].Code + PP[pn].HABr + PlanetNMsuffix[PP[pn].NMr];
						ttContent_Planets += '</span> ';
						VAL = PP[pn].VALr;
					}
					PlanetProductionContent += ' <span class="production">'+PlanetProduction[p]+'</span>';
					prefix = '<br>';
				}
			}
		} // Are there planets here?
	} // Is this a production screen?
	if ( InSpaceProductionContent || InSpaceProductionCTs || PlanetProductionContent ) {
		ttContent_Production += '<table class="ttTable">';
		ttContent_Production += "\n\t"+'<thead><tr><th class="production">Production <span class="normal">('+totalCost+' RP';
		if ( totalCost > 1 ) { ttContent_Production += 's'; }
		ttContent_Production += ')</span></th></tr></thead>';
		ttContent_Production += "\n\t"+'<tbody>';
		if ( InSpaceProductionContent ) { ttContent_Production += "\n\t"+'<tr><td class="production">'+InSpaceProductionContent+'</td></tr>'; }
		if ( InSpaceProductionCTs ) { ttContent_Production += "\n\t"+'<tr><td class="production">'+InSpaceProductionCTs+'</td></tr>'; }
		if ( PlanetProductionContent ) { ttContent_Production += "\n\t"+'<tr><td class="xproduction">'+PlanetProductionContent+'</td></tr>'; }
		ttContent_Production += "\n\t"+'</tbody>';
		ttContent_Production += "\n"+'</table>';
	}
	if ( DEBUG ) { console.log('ttContent_Production='+ttContent_Production); }
	return ttContent_Production;
} // END ttDisplay_Production.

// ttDisplay_onClick()
// Display the onClick task.
function ttDisplay_onClick() {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_onClick[] XY='+XY+' playerScreen='+playerScreen); }
	ttContent_onClick = '';
	switch ( playerScreen ) {
		case 'Movement':
			CanvasClickFunction[XY] = false;
			if ( typeof PlayerfleetByXY !== 'undefined' ) { // Are there fleets?
				if ( typeof PlayerfleetByXY[XY] !== 'undefined' ) { // Are there fleets here?
					if ( DEBUG ) { console.log('PlayerfleetByXY['+XY+']='+PlayerfleetByXY[XY]); }
					fleetCanMove = false;
					if ( typeof Mapstar[XY] !== 'undefined' || Techlimit.movement.change !== 'star' ) { // Is this is a star or does the player have USC?
						fleetCanMove = true;
						if ( DEBUG ) { console.log('This is a star or player has USC. fleetCanMove='+fleetCanMove); }
					} else { // Is this is a star or does the player have USC?
						// Check if fleet is running or stuck in space.
						if ( DEBUG ) { console.log('PlayerfleetByXY['+XY+']='+PlayerfleetByXY[XY]); }
						for ( let FleetIndex=0; FleetIndex<PlayerfleetByXY[XY].length; FleetIndex++ ) { // Loop thru PlayerfleetByXY[XY]s.
							let UN = PlayerfleetByXY[XY][FleetIndex];
							let fleetName = PF[UN].Name;
							if ( PF[UN].userId === userId && ( PF[UN].Action.substring(0,3) === 'Run' || ( PF[UN].dTurns <= 0 && Techlimit.movement.change === 'star' ) ) ) {
								fleetCanMove = true;
								if ( DEBUG ) { console.log('Fleet is running. fleetCanMove='+fleetCanMove); }
							}
						} // Loop thru PlayerfleetByXY[XY]s.
					} // Is this is a star or does the player have USC?
					if ( fleetCanMove ) { // Can this fleet move?
						if ( typeof Mapstar[XY] !== 'undefined' ) {
							ttContent_onClick = '<span class="action">Click on star to perform movement.</span>'+"\n";
							if ( DEBUG ) { console.log('ttContent_onClick='+ttContent_onClick); }
						} else {
							ttContent_onClick = '<span class="action">Click on hex to perform movement.</span>'+"\n";
							if ( DEBUG ) { console.log('ttContent_onClick='+ttContent_onClick); }
						}
						CanvasClickFunction[XY] = function() { moveDisplay('MapMenu',XY, 'ttDisplay_onClick 3448'); };
					} else { // Can this fleet move?
						if ( DEBUG ) { console.log('Fleet cannot move. fleetCanMove='+fleetCanMove); }
					} // Can this fleet move?
				} else { // Are there fleets here?
					if ( DEBUG ) { console.info('There are no fleets at '+XY+'.'); }
				} // Are there fleets here?
			} else { // Are there fleets?
				if ( DEBUG ) { console.info('There are no fleets.'); }
			} // Are there fleets?
			// Assign onClick for ships.
			if ( typeof playershipXYs !== 'undefined' && playershipXYs.indexOf(XY) !== -1 ) { // Are there ships here?
				ttContent_onClick = '<span class="action">Click on star to perform movement.</span>'+"\n";
				if ( DEBUG ) { console.log('ttContent_onClick='+ttContent_onClick); }
				CanvasClickFunction[XY] = function() { moveDisplay('MapMenu',XY, 'ttDisplay_onClick 33462'); };
			} // Are there ships here?
			if ( !CanvasClickFunction[XY] && typeof Mapstar[XY] !== 'undefined' ) {
				ttContent_onClick = '<span class="action">Click to make note.</span>'+"\n";
				CanvasClickFunction[XY] = function() { starNoteDisplay(XY); };
			}
		break;
		case 'Production':
			if ( DEBUG ) { console.log('( typeof productionXYs != \'undefined\' )='+( typeof productionXYs !== 'undefined' )); }
			if ( typeof productionXYs !== 'undefined' ) { // Are productionXYs defined?
				if ( DEBUG ) { console.log('productionXYs='+productionXYs); }
				if ( typeof Mapstar[XY] !== 'undefined' && productionXYs.indexOf(XY) !== -1 ) { // Is this XY a star in productionXYs?
					// Add onclick for production at XY.
					if ( document.getElementById('btnSpend_'+XY) ) {
						ttContent_onClick = '<span class="action">Click on star to spend.</span>'+"\n";
						CanvasClickFunction[XY] = function() { document.getElementById('btnSpend_'+XY).click(); };
					} else {
						if ( typeof Mapstar[XY] !== 'undefined' && playerTurn > 0 ) { ttContent_onClick = '<span class="action">Click to make note.</span>'+"\n"; }
						CanvasClickFunction[XY] = function() { starNoteDisplay(XY); };
					}
				} else { // Is this XY a star in productionXYs?
					// Add onclick for note.
					if ( typeof Mapstar[XY] !== 'undefined' && playerTurn > 0 ) { ttContent_onClick = '<span class="action">Click to make note.</span>'+"\n"; }
					CanvasClickFunction[XY] = function() { starNoteDisplay(XY); };
				} // Is this XY a star in productionXYs?
			} else { // Are productionXYs defined?
				if ( DEBUG ) { console.log('( typeof ProductionSelectNewHome != \'undefined\' )='+( typeof ProductionSelectNewHome !== 'undefined' )); }
				if ( ( typeof ProductionSelectNewHome !== 'undefined' ) && ProductionSelectNewHome && starXY.indexOf(XY) !== -1 ) {
					if ( DEBUG ) { console.log(XY+' is a possible new home system.'); }
					// XY is a possible new home system.
					ttContent_onClick = '<span class="action">Click to select '+Mapstar[XY].Name+' to be your new home system.</span>'+"\n";
					CanvasClickFunction[XY] = function() { HomeSystemSelect(this,XY); };
				} else {
					// Add onclick for note.
					if ( typeof Mapstar[XY] !== 'undefined' && playerTurn > 0 ) { ttContent_onClick = '<span class="action">Click to make note.</span>'+"\n"; }
					CanvasClickFunction[XY] = function() { starNoteDisplay(XY); };
				}
			} // Are productionXYs defined?
		break;
		case 'ReselectHome':
		case 'SelectHome':
			if ( typeof Mapstar[XY] !== 'undefined' ) {
				let sXY = Mapstar[XY].XY;
				ttContent_onClick = '<span class="action">Select '+Mapstar[XY].Name+' as your home system.</span>'+"\n";
				CanvasClickFunction[XY] = function() { SelectHome(XY); };
			}
		break;
		case 'SelectTechs':
			if ( typeof Mapstar[XY] !== 'undefined' && !GameInfo.previewHome ) {
				if ( XY !== homeSystem ) {
					ttContent_onClick = '<span class="info">'+Mapstar[XY].Name+' is not your home system.</span>'+"\n";
				} else {
					ttContent_onClick = '<span class="attention">Click to preview your home system.<br>This costs 4 points and may not be cancelled.</span>'+"\n";
					CanvasClickFunction[XY] = function() { formSubmit('mapWrapper',{'subtask':'Preview Home'}); };
				}
			}
		break;
		default:
			if ( DEBUG ) { console.log(PC+TB+'default (typeof Mapstar['+XY+'] !== \'undefined\')='+(typeof Mapstar[XY] !== 'undefined')+' playerAction='+playerAction,CI); }
			if ( typeof Mapstar[XY] !== 'undefined' && playerAction !== 'Configure Game' && playerAction !== 'Select Techs' && playerAction !== 'Spend Initial RPs' ) {
				ttContent_onClick = '<span class="action">Click to make note.</span>'+"\n";
				CanvasClickFunction[XY] = function() { starNoteDisplay(XY); };
			}
		break;
	}
	if ( DEBUG ) { console.log('ttContent_onClick='+ttContent_onClick); }
	return ttContent_onClick;
} // END ttDisplay_onClick.

// ttDisplay_TKO(TKO_XY)
// Display warning if TKO needs cover here.
function ttDisplay_TKO(TKO_XY) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.log('ttDisplay_TKO[TKO_XY='+TKO_XY+']'); }
	ttContent_TKO = '';
	if ( ( typeof TKOsThatWereAbandoned !== 'undefined' ) && TKOsThatWereAbandoned.indexOf(TKO_XY) !== -1 ) { // Is there a TKO abandoned here?
		ttContent_TKO = '<div><span class="warntext">Take over at </span>'+Startype[Mapstar[TKO_XY].startypeId].ColorSpan+' '+Mapstar[TKO_XY].Name+'<span class="small"> ['+TKO_XY+']</span><span class="warntext"> will be abandoned.</span></div>';
	} // Is there a TKO abandoned here?
	return ttContent_TKO;
} // END ttDisplay_TKO.

// ttEnemyHasSpaceCombatUnits(eId)
// Returns true if the enemy has space combat units (warships or orbital bases).
function ttEnemyHasSpaceCombatUnits(eId) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.groupCollapsed(PC+'ttEnemyHasSpaceCombatUnits[eId='+eId+', XY='+XY+']',CG); }
	let EnemyHasSpaceCombatUnits = false;
	if ( DEBUG ) { console.log(PC+'PlayerStatus['+eId+'].Turn='+PlayerStatus[eId].Turn+' playerTurn='+playerTurn,CL); }
	if ( eId !== userId ) {
		// Check enemy fleets for warships.
		let tuId;
		FleetLoop:
		for ( let UN in PF ) {if(PF.hasOwnProperty(UN)) { // Loop thru fleets.
			if ( DEBUG ) { console.log(PC+'PF['+UN+'].userId='+PF[UN].userId+' PF['+UN+'].XY='+PF[UN].XY,CL); }
			if ( eId === PF[UN].userId && PF[UN].XY === XY ) { // Does this fleet belong to the enemy we are looking for and is it at the correct XY?
				if ( DEBUG ) { console.log(PC+'PF['+UN+']='+JSON.stringify(PF[UN]),CL); }
				if ( PlayerStatus[eId].Turn <= playerTurn || PF[UN].dTurns === -1 ) { // Is the player NOT in the future or was the fleet already here?
					for ( tuId in PFU[UN] ) {if(PFU[UN].hasOwnProperty(tuId)) { // Loop thru fleet units.
						if ( DEBUG ) { console.log('UN='+UN+' tuId='+tuId+'='+PFU[UN][tuId]);}
						if ( PFU[UN][tuId] > 0 && Techunit[tuId].Type ==='warship' ) {
							EnemyHasSpaceCombatUnits = true;
							break FleetLoop; // Warship found, loop can stop.
						}
				} // Is the player NOT in the future or was the fleet already here?
				}} // Loop thru fleet units.
			} // Does this fleet belong to the enemy we are looking for and is it at the correct XY?
		}} // Loop thru fleets.
		let UXY;
		if ( !EnemyHasSpaceCombatUnits ) { // Have enemy warships not been found?
			// Check enemy ships for warships.
			UXY = eId+'_'+XY;
			if ( typeof PSU[UXY] !== 'undefined' ) { // Are there enemy ships?
				for ( tuId in PSU[UXY] ) {if(PSU[UXY].hasOwnProperty(tuId)) { // Loop thru enemy ships.
					if ( DEBUG ) { console.log('UXY='+UXY+' tuId='+tuId+'='+PSU[UXY][tuId]); }
					if ( PSU[UXY][tuId] > 0 && Techunit[tuId].Type ==='warship' ) {
						EnemyHasSpaceCombatUnits = true;
						break; // Warship found, loop can stop.
					}
				}} // Loop thru enemy ships.
			} // Are there enemy ships?
		} // Have enemy warships not been found?
		if ( !EnemyHasSpaceCombatUnits ) { // Have enemy warships not been found?
			// Check for enemy orbital bases.
			UXY = eId+'_'+XY;
			if ( typeof POU[UXY] !== 'undefined' ) { // Are there enemy orbital bases?
				for ( tuId in POU[UXY] ) {if(POU[UXY].hasOwnProperty(tuId)) { // Loop thru enemy orbital bases.
					if ( DEBUG ) { console.log('UXY='+UXY+' tuId='+tuId+'='+POU[UXY][tuId]); }
					if ( POU[UXY][tuId] > 0 && Techunit[tuId].Type ==='orbital' ) {
						EnemyHasSpaceCombatUnits = true;
						break; // Orbital base found, loop can stop.
					}
				}} // Loop thru enemy orbital bases.
			} // Are there enemy orbital bases?
		} // Have enemy warships not been found?
	} else {
		if ( DEBUG ) { console.log('This is the player.'); }
	}
	if ( DEBUG ) { console.log('EnemyHasSpaceCombatUnits='+EnemyHasSpaceCombatUnits); }
	if ( DEBUG ) { console.groupEnd(); }
	return EnemyHasSpaceCombatUnits;
} // END ttEnemyHasSpaceCombatUnits.

// ttLostContact(pp)
// Return true if the player has 'Lost Contact' with this planet in the past, else false.
// Will also return false if there is a 'Contact Restored', 'Explore' or 'Take Over' event before any 'Lost Contact' is found.
function ttLostContact(pp) {
	let DEBUG = DEBUG_OFF;
	if ( DEBUG ) { console.group(PC+'ttLostContact[pp='+pp+']',CG); }
	LostContact = false;
	if ( ( typeof Events[userId] !== 'undefined' ) && ( typeof Events[userId][pp.XY] !== 'undefined' ) ) { // Does the player have events here?
		if ( DEBUG ) { console.info(PC+'The player has events here.',CI); }
		for ( let ei=0; ei<Events[userId][pp.XY].length; ei++ ) { // Loop thru events.
			if ( DEBUG ) { console.log(PC+TB+'Events[userId]['+pp.XY+']['+ei+'].action='+Events[userId][pp.XY][ei].action,CL); }
			if ( Events[userId][pp.XY][ei].action === 'Contact Restored' || Events[userId][pp.XY][ei].action === 'Explore' || Events[userId][pp.XY][ei].action === 'Take Over' ) { break; }
			if ( Events[userId][pp.XY][ei].action === 'Lost Contact' ) { LostContact = true; break; }
		} // Loop thru events.
	} else {
		if ( DEBUG ) { console.info(PC+'The player does NOT have events here.',CI); }
	}
	if ( DEBUG ) { console.info(PC+'LostContact='+LostContact,CI); }
	if ( LostContact !== 0 && pp.OW === userId && pp.LC !== 0 ) {
		if ( DEBUG ) { console.info(PC+'The player skipped \'Lost Contact\'.',CI); }
		LostContact = true;
	}
	if ( DEBUG ) { console.groupEnd(); }
	return LostContact;
} // END ttLostContact.

// ttPd(pn)
// Return planet description for console DEBUGging.
function ttPd(pn) {
	if ( typeof PP[pn] !== 'undefined' ) {
		//console.log('Mapstar['+XY+'].PNs.indexOf('+pn+')'+Mapstar[XY].PNs.indexOf(pn));
		return (Mapstar[XY].PNs.indexOf(pn)+1)+'☽ '+Planettype[PP[pn].TYPEp].Code + PP[pn].HABp + PlanetNMsuffix[PP[pn].NMp];
	} else {
		return '';
	}
} // END ttPd.

// ttPlanetIsOriginal()
// Returns true if the planet has original TYPE HAB NM, else false.
// pn = planet number.
function ttPlanetIsOriginal(pn) {
	//console.log('ttPlanetIsOriginal PP[pn].TYPEo+PP[pn].HABo+PP[pn].NMo='+PP[pn].TYPEo+PP[pn].HABo+PP[pn].NMo+' PP[pn].TYPEp+PP[pn].HABp+PP[pn].NMp='+PP[pn].TYPEp+PP[pn].HABp+PP[pn].NMp+' is '+(PP[pn].TYPEo+PP[pn].HABo+PP[pn].NMo === PP[pn].TYPEp+PP[pn].HABp+PP[pn].NMp));
	if ( PP[pn].TYPEo+PP[pn].HABo+PP[pn].NMo === PP[pn].TYPEp+PP[pn].HABp+PP[pn].NMp ) {
		return true;
	} else {
		return false;
	}
} // END ttPlanetIsOriginal.

// ttPlanetIsPlayer()
// Returns true if the planet TYPE HAB NM has not been changee by production, else false.
// pn = planet number.
function ttPlanetIsPlayer(pn) {
	//console.log('PP['+pn+'].TYPEp='+PP[pn].TYPEp+' PP['+pn+'].HABp='+PP[pn].HABp+' PP['+pn+'].NMp='+PP[pn].NMp);
	//console.log('PP['+pn+'].TYPEr='+PP[pn].TYPEr+' PP['+pn+'].HABr='+PP[pn].HABr+' PP['+pn+'].NMr='+PP[pn].NMr);
	if ( typeof PP[pn].NMr === 'undefined' ) { PP[pn].NMr = PP[pn].NMp; }
	if ( PP[pn].TYPEp+PP[pn].HABp+PP[pn].NMp === PP[pn].TYPEr+PP[pn].HABr+PP[pn].NMr ) {
		return true;
	} else {
		return false;
	}
} // END ttPlanetIsPlayer.

// ttTR(turn)
// Returns TR.
function ttTR(turn) {
	let turnNumber = parseInt( turn / ( GameInfo.productionYearInterval * 2 + 1 ) + 0.0001 ) + 1;
	let TR = turnNumber;
	let Tround = turn % ( GameInfo.productionYearInterval * 2 + 1 );
	if ( Tround !== 0 ) {
		TR += ( parseInt( ( Tround - 1 ) / 2 ) + 1 ) / 10;
	} else {
		TR += '.0';
	}
	//console.log('turn='+turn+' turnNumber='+turnNumber+' TR='+TR+' Tround='+Tround);
	return TR;
} // END ttTR.

Anon7 - 2022
AnonSec Team