Apache1990 / Exploration Assistant

// ==UserScript==
// @name      Exploration Assistant
// @version    1.5.3.3.1
// @author    Apache1990
// @namespace  Apache1990Userscript
// @updateURL https://openuserjs.org/meta/Apache1990/Exploration_Assistant.meta.js
// @downloadURL https://openuserjs.org/install/Apache1990/Exploration_Assistant.user.js
// @copyright 2023, Apache1990 (https://openuserjs.org/users/Apache1990)
// @license MIT
// @include    /^https:\/\/[0-z]*\.atmoburn\.com\/fleet.php\?\S*fleet=[0-9]\S*/
// @include    /^https:\/\/[0-z]*\.atmoburn\.com\/fleet\/[0-9]\S*/
// @grant      unsafeWindow
// ==/UserScript==

function byId(ele) {
  //getElementById shortcut function (Atmoburn uses this too, but part of this script runs before that copy has loaded)
  return document.getElementById(ele);
}

function goToNextExplorer(fromIgnoreOrLoad = false) {
  let nextFleetIgnored = true;

  if (byId("coordinatorListing")) {
    //Find next non-ignored explorer fleet page
    for (var nextFleet = 0; nextFleet < (byId("coordinatorListing").querySelectorAll("div a").length); nextFleet++) {
      //If there is no cookie set to ignore the fleet, break out of the loop
      if (!asstCheckLocalStorage(byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href.match(/(?<=fleet\/)[0-9]*/))) {
        if (byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href.match(/(?<=fleet\/)[0-9]*/) !== null) {
          nextFleetIgnored = false;
          break;
        }
      }
    }

    if (nextFleet == byId("coordinatorListing").querySelectorAll("div a").length)
      nextFleet--;

    console.log("nextFleet: " + nextFleet);
  }

  if (byId("coordinatorListing") && /(?<=fleet\/)[0-9]*/.test(byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href))
    console.log("Next fleet ID: " + byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href.match(/(?<=fleet\/)[0-9]*/) + " nextFleetIgnored: " + nextFleetIgnored + " fromIgnoreOrLoad: " + fromIgnoreOrLoad);

  //Load next non-ignored explorer fleet page
  if (byId("coordinatorListing") && /fleet\//.test(byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href) && (!nextFleetIgnored || fromIgnoreOrLoad)) {
    console.log("Attempting to navigate fleet: " + byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href);
    window.location.assign(byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href.replace(/#/g, ''));
  }
  else {
    console.log("Attempting refresh");
    window.location.reload(); //Reload the page if there is no valid next explorer (to reload the Exploration Coordinator list)
  }
}

function explorationClicked() {
  //Function sends fleet to first unexplored planet in local targets, alerts about wormholes, and opens starmap if system is fully explored

  //If parking mode is active and fleet is at system entrance, Parks the fleet and proceeds to next fleet
  if (asstCheckLocalStorage("asstParkingModeActive")) {
    if (byId("positionRight").querySelectorAll("div")[1]) {
      if (/0, 40, 10 local/.test(byId("positionRight").querySelectorAll("div")[1].innerText)) {
        parkClicked();
        return;
      }
    }
  }

  let assistanceSuccess = false;
  let wormholeDetected = false;
  if (byId('mLocalTargets').length > 0) { //Don't run this if there are no local targets.
    for (let spob of byId("mLocalTargets").querySelectorAll('optgroup')) {
      if (spob && /Wormholes/.test(spob.label)) {
        wormholeDetected = true;
        nextButton.innerHTML = "Ne<u>x</u>t (WORMHOLE DETECTED)";
        if (!confirm("Wormhole detected in-system! Continue?"))
          return;
        break;
      }
    }
    for (let spob of byId("mLocalTargets").querySelectorAll('option')) {
      if (spob && (/\(unexplored\)/.test(spob.innerHTML) || /\(empire explored\)/.test(spob.innerHTML))) {
        assistanceSuccess = true;
        byId("nextButton").style.visibility = "hidden";
        byId("ignoreButton").style.visibility = "hidden";
        spob.selected = true;
        verifyMission(byId("mLocalTargets").value);
        launchMission();

        break;
      }
    }
  }
  if (!assistanceSuccess) {
    //If no unexplored planets, bring up starmap (also check fleet range, ask if we should ignore when fuel is critical)
    let currentFleetRange = byId("fleetRange").innerHTML;
    if (currentFleetRange == "")
      return;
    if (currentFleetRange.replace(/,/g, '') > parseInt(asstSettings.criticalFuelLevel)) {
      window.location.assign(byId("positionRight").querySelectorAll("span ~ a")[0].href);
      setTimeout(pointClosestSystem, 2000);
    }
    else {
      if (!confirm("Current fleet range " + currentFleetRange + "; continue exploring? (Fleet ignored " + asstSettings.ignoreDuration + "h on cancel)"))
        ignoreClicked(true);
      else {
        window.location.assign(byId("positionRight").querySelectorAll("span ~ a")[0].href);
        setTimeout(pointClosestSystem, 2000);
      }
    }
  }
}

function entrancePark() {
  //Sends fleet to system entrance if not yet there, otherwise does nothing.
  if (byId('mLocalTargets').length > 0) { //Don't try to do anything if there are no local targets
    let spob = byId("mLocalTargets").querySelectorAll('option')[1];
    if (/Entry/.test(spob.innerHTML) && !/0, 40, 10 local/.test(byId("positionRight").querySelectorAll("div")[1].innerText)) {
      byId("nextButton").style.visibility = "hidden";
      byId("parkButton").style.visibility = "hidden";
      spob.selected = true;
      byId("mObjective").querySelectorAll('option')[0].selected = true;
      verifyMission(byId("mLocalTargets").value);
      launchMission(true);
    }
    else if (/0, 40, 10 local/.test(byId("positionRight").querySelectorAll("div")[1].innerText)) {
      byId("nextButton").style.visibility = "hidden";
      byId("parkButton").style.visibility = "hidden";
      goToNextExplorer(true);
    }
  }
}

function parkClicked() {
  if (byId("ignoreButton")) { //Don't try if ignore button doesn't exist (how did you get here?)
    //Function handles fleet 'parking' (moves to system entrance, with short 1 hour ignore added)
    let asstCurrentFleet = window.location.href.match(/(?<=fleet\/)[0-9]*|(?<=fleet=)[0-9]*/);
    asstSetLocalStorage(asstCurrentFleet, 1, 1);
    byId("ignoreButton").innerHTML = "Remove igno<u>r</u>e?";

    entrancePark();
  }
}

function ignoreClicked(fromRangeCheck = false) {
  //Function handles fleet ignoring/unignoring
  if (byId("ignoreButton")) { //Don't try if ignore/unignore button doesn't exist (how did you get here?)
    if (!(/Remove/.test(byId("ignoreButton").innerHTML))) { //Adds to ignore list if not presently on it
      if (!fromRangeCheck) //Only pops up confirm window if called directly (failing a fleet range check already pops up an earlier confirm)
        var confirmIgnore = window.confirm("Ignore current fleet for " + asstSettings.ignoreDuration + " hours?");
      if (confirmIgnore || fromRangeCheck) {
        let asstCurrentFleet = window.location.href.match(/(?<=fleet\/)[0-9]*|(?<=fleet=)[0-9]*/);
        asstSetLocalStorage(asstCurrentFleet, 1, 1 * asstSettings.ignoreDuration);
        byId("ignoreButton").innerHTML = "Remove igno<u>r</u>e?";

        entrancePark();
      }
    }
    else { //Removes from ignore list if on it
      if (window.confirm("Remove fleet from ignore list?")) {
        let asstCurrentFleet = window.location.href.match(/(?<=fleet\/)[0-9]*|(?<=fleet=)[0-9]*/);
        asstSetLocalStorage(asstCurrentFleet, 1, 0);
        byId("ignoreButton").innerHTML = "Igno<u>r</u>e " + asstSettings.ignoreDuration + "h";
        byId("nextButton").style.visibility = "";
        byId("parkButton").style.visibility = "";
      }
    }
  }
}

function parkingModeClicked() {
  //Activates parking mode for 2 hours if not yet active, otherwise ends it immediately
  if (byId("parkingModeButton")) { //Don't try if the parking mode button doesn't exist (how did you get here?)
    if (!asstCheckLocalStorage("asstParkingModeActive")) {
      asstBlinkEffect("#003300", "#008800", "parkingModeButton");
      asstSetLocalStorage("asstParkingModeActive", 1, 2);
    }
    else {
      asstSetLocalStorage("asstParkingModeActive", 1, 0);
      if (blinkInterval)
        clearInterval(blinkInterval);
      byId("parkingModeButton").style = "";
    }
  }
}

function explorationAssistant() {
  //Function creates the new buttons used by the userscript, other prepwork
  if (/Explorer/.test(byId("pageSubContainer").childNodes[0].innerHTML)) { //This checks that the fleet is an Explorer, probably unneeded as this function won't be setup in initiation if not
    console.log("Initiating assistant...");
    let isFleetIgnored = asstCheckLocalStorage(window.location.href.match(/(?<=fleet\/)[0-9]*|(?<=fleet=)[0-9]*/));

    if (!asstCheckLocalStorage("asstSettings")) {
      asstSettings = {
        criticalFuelLevel: 40000000,
        cautionFuelLevel: 80000000,
        ignoreDuration: 48
      };
      asstSetLocalStorage("asstSettings", JSON.stringify(asstSettings), null);
    }
    else
      asstSettings = asstGetLocalStorage("asstSettings");

    var navigateFleetAnyway = false;

    if (!byId("nextButton")) {
      let nextButton = document.createElement('button');
      nextButton.innerHTML = "Ne<u>x</u>t";
      nextButton.id = "nextButton";
      nextButton.className = "submit greenbutton darkbutton";
      nextButton.style.visibility = isFleetIgnored ? "hidden" : ""; //Hide if fleet ignored
      nextButton.addEventListener("click", explorationClicked);
      nextButton.title = "Explores next unexplored planet in system, if none remain, opens Starmap.";
      byId("missionSelectionLocal").append(nextButton);
    }

    if (!byId("parkButton")) {
      let parkButton = document.createElement('button');
      parkButton.innerHTML = "<u>P</u>ark";
      parkButton.id = "parkButton";
      parkButton.className = "submit darkbutton";
      parkButton.style.visibility = isFleetIgnored ? "hidden" : ""; //Hide if fleet ignored
      parkButton.addEventListener("click", parkClicked);
      parkButton.title = "Sends fleet to system entrance (if not already there), then ignores for 1 hour.";
      byId("missionSelectionLocal").append(parkButton);
    }

    if (!byId("ignoreButton")) {
      let ignoreButton = document.createElement('button');
      ignoreButton.innerHTML = isFleetIgnored ? "Remove igno<u>r</u>e?" : "Igno<u>r</u>e " + asstSettings.ignoreDuration + "h";
      ignoreButton.id = "ignoreButton";
      ignoreButton.className = "darkbutton dangerbutton";
      ignoreButton.addEventListener("click", ignoreClicked);
      ignoreButton.title = "Sends fleet to system entrance (if not already there), then ignores for " + asstSettings.ignoreDuration + " hours.";
      byId("missionSelectionLocal").append(ignoreButton);
    }

    if (!byId("settingsButton")) {
      let settingsButton = document.createElement('button');
      settingsButton.innerHTML = "Settings";
      settingsButton.id = "settingsButton";
      settingsButton.className = "submit darkbutton";
      settingsButton.addEventListener("click", asstSettingsClicked);
      settingsButton.title = "Allows customization of fuel warning levels, and duration of fleet ignore.";
      byId("missionSelectionLocal").append(settingsButton);
    }

    if (!byId("parkingModeButton")) {
      let parkingModeButton = document.createElement('button');
      parkingModeButton.innerHTML = "Parking Mode";
      parkingModeButton.id = "parkingModeButton";
      parkingModeButton.className = "submit darkbutton";
      parkingModeButton.addEventListener("click", parkingModeClicked);
      parkingModeButton.title = "When enabled, changes function of Next button. If fleet is at system entrance, it is Parked; if at a planet, continues to explore as normal. Lasts 2 hours, or until disabled.";
      byId("missionSelectionLocal").append(parkingModeButton);
    }

    //Pulses Parking Mode button if the mode is active
    if (asstCheckLocalStorage("asstParkingModeActive")) {
      asstBlinkEffect("#003300", "#008800", "parkingModeButton");
    }

    if (isFleetIgnored && !navigateFleetAnyway && !readyKeyboardShortcuts) {
      if (!window.confirm("This fleet is currently ignored, navigate anyway?")) {
        goToNextExplorer(true);
      }
      navigateFleetAnyway = true;
    }

    //Colors fleet range depending on remaining fuel, based on caution and critical fuel levels
    //Critical will ask if you want to place the fleet on ignore before bringing up starmap for a new system
    let currentFleetRange = byId("fleetRange").innerHTML.replace(/,/g, '');
    if (!isNaN(parseInt(currentFleetRange))) {
      if (currentFleetRange <= parseInt(asstSettings.criticalFuelLevel))
        byId("fleetRange").parentElement.style.color = "red";
      else if (currentFleetRange <= parseInt(asstSettings.cautionFuelLevel))
        byId("fleetRange").parentElement.style.color = "yellow";
    }
  }

  readyKeyboardShortcuts = true;
}

function asstBlinkEffect(color1, color2, fieldName) {
  //Modified version of Atmoburn blinkEffect() function that works on buttons.
  var blinkField = document.getElementById(fieldName);
  blinkField.style.transition = "background-color 0.5s";
  var isBlinked = false;

  function blinkToggle() {
    if (!isBlinked) {
      isBlinked = true;
      blinkField.style.background = color1;
    }
    else {
      isBlinked = false;
      blinkField.style.background = color2;
    }
  }
  if (blinkInterval) clearInterval(blinkInterval);
  blinkInterval = setInterval(blinkToggle, 1000);
}

function asstSettingsClicked() {
  //Displays Exploration Assistant settings window (using Atmoburn genericPopup() function)
  let settingsForm = "Ignore Duration: <input id='asstIgnoreSetting' onclick='this.select();' onkeyup='asstSettingsSave()' class='darkinput' type='text' size='3' value='" + asstSettings.ignoreDuration + "' /><br />" +
    "Caution Fuel Level: <input id='asstCautionSetting' onclick='this.select();' onkeyup='asstSettingsSave();' class='darkinput' type='text' size='10' value='" + asstSettings.cautionFuelLevel + "' /><br />" +
    "Critical Fuel Level: <input id='asstCriticalSetting' onclick='this.select();' onkeyup='asstSettingsSave();' class='darkinput' type='text' size='10' value='" + asstSettings.criticalFuelLevel + "' /><br />" +
    "<span class='smalltext'>Invalid inputs will be saved as default settings.</span>";

  genericPopup({
    content: settingsForm
  });
}

function asstSettingsSave() {
  //Saves Exploration Assistant custom settings
  let ignoreInput = byId("asstIgnoreSetting").value.replace(/[^0-9]+/g, '');
  let cautionInput = byId("asstCautionSetting").value.replace(/[^0-9]+/g, '');
  let criticalInput = byId("asstCriticalSetting").value.replace(/[^0-9]+/g, '');

  if (isNaN(parseInt(ignoreInput)))
    ignoreInput = 48;
  if (isNaN(parseInt(cautionInput)))
    cautionInput = 80000000;
  if (isNaN(parseInt(criticalInput)))
    criticalInput = 40000000;

  asstSettings.ignoreDuration = ignoreInput;
  asstSettings.cautionFuelLevel = cautionInput;
  asstSettings.criticalFuelLevel = criticalInput;
  byId("asstIgnoreSetting").value = asstSettings.ignoreDuration;
  byId("asstCautionSetting").value = asstSettings.cautionFuelLevel;
  byId("asstCriticalSetting").value = asstSettings.criticalFuelLevel;

  let isFleetIgnored = asstCheckLocalStorage(window.location.href.match(/(?<=fleet\/)[0-9]*|(?<=fleet=)[0-9]*/));
  ignoreButton.innerHTML = isFleetIgnored ? "Remove igno<u>r</u>e?" : "Igno<u>r</u>e " + asstSettings.ignoreDuration + "h";
  //Colors fleet range depending on remaining fuel, based on caution and critical fuel levels
  //Critical will ask if you want to place the fleet on ignore before bringing up starmap for a new system
  let currentFleetRange = byId("fleetRange").innerHTML.replace(/,/g, '');
  if (currentFleetRange <= parseInt(asstSettings.criticalFuelLevel))
    byId("fleetRange").parentElement.style.color = "red";
  else if (currentFleetRange <= parseInt(asstSettings.cautionFuelLevel))
    byId("fleetRange").parentElement.style.color = "yellow";
  else
    byId("fleetRange").parentElement.style.color = "";

  asstSetLocalStorage("asstSettings", JSON.stringify(asstSettings), null);
}

//Return all fleet missions
function getSystemsBeingExplored() {
  var missions = [];
  for (var ID in mapWindow.mapMeta.f) {
    if (mapWindow.mapMeta.f[ID][13] !== '') {
      missions.push(mapWindow.mapMeta.f[ID][13]);
    }
  }
  return missions;
}

//Pass list of missions and target system, return true if there is a mission targeting system coordinates.
function isTargetInMissions(missions, target) {
  for (var mission in missions) {
    if (missions[mission][3] === target[3] && missions[mission][4] === target[4] && missions[mission][5] === target[5]) {
      return true;
    }
  }
  return false;
}

//Find closest system, then draw a line to it (tells the starmap that your current fleet is on an assault mission to said system, creating a semi-persistent line until it refreshes the fleet data
function pointClosestSystem() {
  let closestSys = 0,
    forceRender;

  function acquireClosest() {
    if (mapWindow && typeof mapWindow.mapMeta !== 'undefined' && mapWindow.mapMeta && mapWindow.mapMeta.f[fleetID]) { //Does the map window exist? Is mapMeta a thing? Does your fleet exist in mapMeta yet?
      let distance = Infinity,
        thisDistance, myFleet = mapWindow.mapMeta.f[fleetID];
      let missions = getSystemsBeingExplored();
      for (var ID in mapWindow.mapMeta.s) {
        thisDistance = mapWindow.getDistance(myFleet, mapWindow.mapMeta.s[ID]);
        if (thisDistance < distance && typeof mapWindow.mapMeta.s[ID][8] !== 'undefined' && mapWindow.mapMeta.s[ID][8] == 0 && !isTargetInMissions(missions, mapWindow.mapMeta.s[ID])) {
          distance = thisDistance;
          closestSys = ID;
        }
      }

      clearInterval(acqInt);
      forceRender = setInterval(renderFunction, 1000); //Keep the line being redrawn every second until fleet window is changed
      renderFunction();
    }
  }

  function renderFunction() {
    if (mapWindow && typeof mapWindow.mapMeta !== 'undefined' && !mapWindow.isRendering && mapWindow.mapMeta && mapWindow.mapMeta.f[fleetID] && closestSys && mapWindow.mapMeta.s[closestSys]) { //Added way too many checks here to stop this from throwing errors if something doesn't exist for no good reason
      try {
        mapWindow.updateRenderItem(mapWindow.mapMeta.f[fleetID], 13, [-99, 0, "g", mapWindow.mapMeta.s[closestSys][3], mapWindow.mapMeta.s[closestSys][4], mapWindow.mapMeta.s[closestSys][5], -1, "assault"]);
        mapWindow.render();
        console.log("Exploration Assistant force render. Fleet #" + fleetID + " object: " + typeof mapWindow.mapMeta.f[fleetID] + " [" + mapWindow.mapMeta.f[fleetID].join() + "], Nearest system coords: " + mapWindow.mapMeta.s[closestSys][3] + ", " + mapWindow.mapMeta.s[closestSys][4] + ", " + mapWindow.mapMeta.s[closestSys][5] + "");
      }
      catch (error) {
        console.log("Exploration Assistant force render failed, reason: " + error + " ... Fleet #" + fleetID + " object: " + typeof mapWindow.mapMeta.f[fleetID] + " [" + mapWindow.mapMeta.f[fleetID].join() + "], Nearest system coords: " + mapWindow.mapMeta.s[closestSys][3] + ", " + mapWindow.mapMeta.s[closestSys][4] + ", " + mapWindow.mapMeta.s[closestSys][5] + "");
        clearInterval(forceRender);
        alert(error + "...See console log for details. Also yell at Apache1990, this should never appear under any circumstances now.");
      }
    }
    else {
      console.log("Exploration Assistant force render delayed (mapWindow open: " + (!mapWindow.closed) + ", isRendering: " + mapWindow.isRendering + ")");
    }

    if (mapWindow.closed) {
      clearInterval(forceRender);
      console.log("Map window closed, shutting down pointing function.");
    }
  }

  let acqInt = setInterval(acquireClosest, 250);
}

//Set of functions that handle Local Storage (setting, reading, and checking for the existence of)
function asstSetLocalStorage(key, value, exhours) {
  if (exhours === null) {
    localStorage.setItem(key, value);
  }
  else {
    const expires = new Date();
    expires.setTime(expires.getTime() + (exhours * 60 * 60 * 1000));
    const item = {
      value: value,
      expires: expires.toUTCString()
    };
    localStorage.setItem(key, JSON.stringify(item));
  }
}

function asstGetLocalStorage(key) {
  const itemString = localStorage.getItem(key);
  if (itemString) {
    const item = JSON.parse(itemString);
    if (item.expires) {
      const expires = new Date(item.expires);
      if (expires > new Date()) {
        return item.value;
      }
      else {
        localStorage.removeItem(key);
      }
    }
    else {
      return item;
    }
  }
  return "";
}

function asstCheckLocalStorage(key) {
  return asstGetLocalStorage(key) !== "";
}

//Only do any of this if the selected fleet is an Explorer
if (/Explorer/.test(byId("pageSubContainer").childNodes[0].innerHTML)) {
  //Eval all script functions so they remain available after the pageload
  unsafeWindow.eval("var asstSettings;");
  unsafeWindow.eval("var readyKeyboardShortcuts = false;");
  unsafeWindow.eval(byId.toString());
  unsafeWindow.eval(explorationAssistant.toString());
  unsafeWindow.eval(goToNextExplorer.toString());
  unsafeWindow.eval(asstSetLocalStorage.toString());
  unsafeWindow.eval(asstGetLocalStorage.toString());
  unsafeWindow.eval(asstCheckLocalStorage.toString());
  unsafeWindow.eval(explorationClicked.toString());
  unsafeWindow.eval(entrancePark.toString());
  unsafeWindow.eval(asstSettingsClicked.toString());
  unsafeWindow.eval(asstSettingsSave.toString());
  unsafeWindow.eval(asstBlinkEffect.toString());
  unsafeWindow.eval(parkClicked.toString());
  unsafeWindow.eval(parkingModeClicked.toString());
  unsafeWindow.eval(ignoreClicked.toString());
  unsafeWindow.eval(getSystemsBeingExplored.toString());
  unsafeWindow.eval(isTargetInMissions.toString());
  unsafeWindow.eval(pointClosestSystem.toString());
  unsafeWindow.eval("explorationAssistant();");
  //Adds call to explorationAssistant() to the end of Atmoburn's setInterfaceNoMission()
  let newFleetData = unsafeWindow.setInterfaceNoMission.toString().slice(0, -1) + "\n\texplorationAssistant();\n}";
  unsafeWindow.eval(newFleetData);
  //Adds call to goToNextExplorer() to return function of Atmoburn's launchMission()
  let newLaunchMission = unsafeWindow.launchMission.toString().slice(0, 23) + "ignoreOrLoad = false" + unsafeWindow.launchMission.toString().slice(23, -25) + "\n\t\tgoToNextExplorer(ignoreOrLoad);\n" + unsafeWindow.launchMission.toString().slice(-25);
  unsafeWindow.eval(newLaunchMission);

  //Update exploration coordinator
  if (byId("coordinatorListing")) {
    for (var nextFleet = 0; nextFleet < byId("coordinatorListing").querySelectorAll("div a").length; nextFleet++) {
      //If there is a cookie set to ignore the fleet, mark red
      if (asstCheckLocalStorage(byId("coordinatorListing").querySelectorAll("div a")[nextFleet].href.match(/(?<=fleet\/)[0-9]*/))) byId("coordinatorListing").querySelectorAll("div a")[nextFleet].classList.add("dangerbutton");
    }
  }

  //Keyboard shortcuts section
  document.onkeydown = function (e) {
    e = e || window.event;
    var keycode = e.which || e.keyCode;
    var ctrlPressed = e.ctrlKey || e.metaKey; //Is Ctrl key pressed?

    if (!ctrlPressed && document.activeElement.tagName == "BODY" && unsafeWindow.readyKeyboardShortcuts) { //skip if Ctrl key is pressed, activeElement used to skip if user has a text input active, don't do anything before page is loaded
      if (keycode == 88) { //'88' is the keycode for "x"
        e.preventDefault();
        if (byId("mLocalTargets").offsetParent != null && byId("nextButton") && byId("nextButton").style.visibility != "hidden") //skip if local navigation is hidden or nextButton hidden
          unsafeWindow.explorationClicked();
        else
          unsafeWindow.goToNextExplorer();
      }
      else if (keycode == 82) { //'82' is the keycode for "r"
        e.preventDefault();
        if (byId("mLocalTargets").offsetParent != null && byId("ignoreButton") && byId("ignoreButton").style.visibility != "hidden") //skip if local navigation is hidden or nextButton hidden
          unsafeWindow.ignoreClicked();
      }
      else if (keycode == 80) { //'80' is the keycode for "p"
        e.preventDefault();
        if (byId("mLocalTargets").offsetParent != null && byId("parkButton") && byId("parkButton").style.visibility != "hidden") //skip if local navigation is hidden or nextButton hidden
          unsafeWindow.parkClicked();
      }
    }
  };

}