MouseHunt - Mapping Helper

Invite players and send SB+ directly from the map interface

当前为 2019-06-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         MouseHunt - Mapping Helper
// @author       Tran Situ (tsitu)
// @namespace    https://greasyfork.org/en/users/232363-tsitu
// @version      1.2
// @description  Invite players and send SB+ directly from the map interface
// @match        http://www.mousehuntgame.com/*
// @match        https://www.mousehuntgame.com/*
// ==/UserScript==

(function() {
  // Valid Queso map variants
  const quesoMaps = [
    "Queso Canyoneer Treasure Map",
    "Queso Geyser Treasure Map",
    "Queso Canyon Grand Tour Treasure Map"
  ];

  // Queso cheese image icons
  const quesoImg = {
    Standard:
      "https://www.mousehuntgame.com/images/items/bait/7e0daa548364166c46c0804e6cb122c6.gif?cv=243",
    "SB+":
      "https://www.mousehuntgame.com/images/items/bait/d3bb758c09c44c926736bbdaf22ee219.gif?cv=243",
    Bland:
      "https://www.mousehuntgame.com/images/items/bait/4752dbfdce202c0d7ad60ce0bacbebae.gif?cv=243",
    Mild:
      "https://www.mousehuntgame.com/images/items/bait/7193159aa90c85ba67cbe02d209e565f.gif?cv=243",
    Medium:
      "https://www.mousehuntgame.com/images/items/bait/be747798c5e6a7747ba117e9c32a8a1f.gif?cv=243",
    Hot:
      "https://www.mousehuntgame.com/images/items/bait/11d1170bc85f37d67e26b0a05902bc3f.gif?cv=243",
    "Flamin'":
      "https://www.mousehuntgame.com/images/items/bait/5a69c1ea617ba622bd1dd227afb69a68.gif?cv=243",
    Wildfire:
      "https://www.mousehuntgame.com/images/items/bait/73891a065f1548e474177165734ce78d.gif?cv=243"
  };

  // Location -> Cheese -> Mouse
  const quesoData = {
    "Queso River": {
      Standard: [
        "Tiny Saboteur",
        "Pump Raider",
        "Croquet Crusher",
        "Queso Extractor"
      ],
      "SB+": ["Sleepy Merchant"],
      Wildfire: ["Queen Quesada"]
    },
    "Prickly Plains": {
      Bland: ["Spice Seer", "Old Spice Collector"],
      Mild: ["Spice Farmer", "Granny Spice"],
      Medium: ["Spice Sovereign", "Spice Finder"],
      Hot: ["Spice Raider", "Spice Reaper"],
      "Flamin'": ["Inferna, The Engulfed"]
    },
    "Cantera Quarry": {
      Bland: ["Chip Chiseler", "Tiny Toppler"],
      Mild: ["Ore Chipper", "Rubble Rummager"],
      Medium: ["Nachore Golem", "Rubble Rouser"],
      Hot: ["Grampa Golem", "Fiery Crusher"],
      "Flamin'": ["Nachous, The Molten"]
    },
    "Cork Collecting": {
      Bland: ["Fuzzy Drake"],
      Mild: ["Cork Defender"],
      Medium: ["Burly Bruiser"],
      Hot: ["Horned Cork Hoarder"],
      "Flamin'": ["Rambunctious Rain Rumbler", "Corky, the Collector"],
      Wildfire: ["Corkataur"]
    },
    "Pressure Building": {
      Mild: ["Steam Sailor"],
      Medium: ["Warming Wyvern"],
      Hot: ["Vaporior"],
      "Flamin'": ["Pyrehyde"],
      Wildfire: ["Emberstone Scaled"]
    },
    "Small Eruption": {
      Mild: ["Sizzle Pup"],
      Medium: ["Sizzle Pup"],
      Hot: ["Sizzle Pup"]
      // Mild: ["Mild Spicekin", "Sizzle Pup"],
      // Medium: ["Sizzle Pup", "Smoldersnap", "Mild Spicekin"],
      // Hot: ["Sizzle Pup", "Ignatia"]
    },
    "Medium Eruption": {
      Medium: ["Bearded Elder"],
      Hot: ["Bearded Elder"],
      "Flamin'": ["Bearded Elder"]
      // Mild: ["Mild Spicekin"],
      // Medium: ["Bearded Elder", "Smoldersnap"],
      // Hot: ["Bearded Elder", "Ignatia"],
      // "Flamin'": ["Bearded Elder", "Bruticus, the Blazing"]
    },
    "Large Eruption": {
      Hot: ["Cinderstorm"],
      "Flamin'": ["Cinderstorm"]
      // Medium: ["Smoldersnap"],
      // Hot: ["Cinderstorm", "Ignatia"],
      // "Flamin'": ["Cinderstorm", "Bruticus, the Blazing"]
    },
    "Epic Eruption": {
      "Flamin'": ["Stormsurge, the Vile Tempest"],
      Wildfire: ["Kalor'ignis of the Geyser"]
      // Hot: ["Ignatia", "Stormsurge, the Vile Tempest"],
      // "Flamin'": ["Stormsurge, the Vile Tempest", "Bruticus, the Blazing"],
    },
    "Any Eruption": {
      Mild: ["Mild Spicekin"],
      Medium: ["Smoldersnap"],
      Hot: ["Ignatia"],
      "Flamin'": ["Bruticus, the Blazing"]
    }
  };

  // Alternate representation: Mouse -> Location -> Cheese
  const quesoMice = {};
  for (let location in quesoData) {
    for (let cheese in quesoData[location]) {
      const arr = quesoData[location][cheese];
      for (let mouse of arr) {
        if (quesoMice[mouse] === undefined) {
          quesoMice[mouse] = [[location, cheese]];
        } else {
          quesoMice[mouse].push([location, cheese]);
        }
      }
    }
  }

  // RH endpoint listener - caches maps (which come in one at a time)
  const originalOpen = XMLHttpRequest.prototype.open;
  XMLHttpRequest.prototype.open = function() {
    this.addEventListener("load", function() {
      if (
        this.responseURL ===
        "https://www.mousehuntgame.com/managers/ajax/users/relichunter.php"
        // && document.querySelector(".treasureMapPopup")
        // TODO: Add ".treasureMapPopup" existence check or not?
        // Caught error is probably from render()
        // hg.showMap() will trigger it
      ) {
        try {
          const map = JSON.parse(this.responseText).treasure_map;
          if (map) {
            const obj = {};
            const condensed = {};

            const condensedGroups = [];
            map.groups.forEach(el => {
              // TODO: Compress goals array for individual mice/items in the future
              const innerObj = {};
              innerObj.name = el.name;
              innerObj.profile_pic = el.profile_pic;
              innerObj.snuid = el.snuid;
              condensedGroups.push(innerObj);
            });
            condensed.groups = condensedGroups;

            condensed.hunters = map.hunters;
            condensed.invited_hunters = map.invited_hunters;
            condensed.is_complete = map.is_complete;
            condensed.is_owner = map.is_owner;
            condensed.is_scavenger_hunt = map.is_scavenger_hunt;
            condensed.is_wanted_poster = map.is_wanted_poster;
            condensed.map_class = map.map_class;
            condensed.map_id = map.map_id;
            condensed.timestamp = Date.now();
            obj[map.name] = condensed;
            // console.log(obj);

            const mapCacheRaw = localStorage.getItem("tsitu-mapping-cache");
            if (mapCacheRaw) {
              const mapCache = JSON.parse(mapCacheRaw);
              mapCache[map.name] = condensed;
              localStorage.setItem(
                "tsitu-mapping-cache",
                JSON.stringify(mapCache)
              );
            } else {
              localStorage.setItem("tsitu-mapping-cache", JSON.stringify(obj));
            }

            render();
          }
        } catch (error) {
          console.log("Server response doesn't contain a valid treasure map");
          console.error(error.stack);
        }
      }
    });
    originalOpen.apply(this, arguments);
  };

  // Renders custom UI elements onto the DOM
  function render() {
    // Clear out existing custom elements
    // Uses static collection instead of live one from getElementsByClassName
    document.querySelectorAll(".tsitu-mapping").forEach(el => el.remove());
    document.querySelectorAll(".tsitu-queso-mapper").forEach(el => el.remove());

    /**
     * Refresh button
     * Iterate thru QRH.maps array for element matching current map and set its hash to empty string
     * This forces a hard refresh via hasCachedMap, which is called in show/showMap
     */
    const refreshSpan = document.createElement("span");
    refreshSpan.className = "tsitu-mapping tsitu-refresh-span";
    const refreshButton = document.createElement("button");
    refreshButton.innerText = "Refresh";
    refreshButton.className = "treasureMapPopup-action-button tsitu-mapping";
    refreshButton.style.cursor = "pointer";
    refreshButton.style.fontSize = "9px";
    refreshButton.style.padding = "2px";
    refreshButton.style.margin = "3px 5px 0px 0px";
    refreshButton.style.textShadow = "none";
    refreshButton.style.display = "inline-block";
    refreshButton.addEventListener("click", function() {
      const mapName = document.querySelector(
        ".treasureMapPopup-header-title.mapName"
      ).textContent;

      user.quests.QuestRelicHunter.maps.forEach(el => {
        if (el.name === mapName) {
          // Reset hash to bust cache
          el.hash = "";
        }
      });

      // Close map dialog and re-open either with current map, default, or overview
      const mapIdEl = document.querySelector("[data-map-id].active");
      const mapId = mapIdEl ? mapIdEl.getAttribute("data-map-id") : -1;
      document.getElementById("jsDialogClose").click();
      mapId === -1
        ? hg.views.TreasureMapView.show()
        : hg.views.TreasureMapView.show(mapId);
    });

    refreshSpan.appendChild(refreshButton);
    document
      .querySelector(
        ".treasureMapPopup-state.viewMap .treasureMapPopup-header-subtitle"
      )
      .insertAdjacentElement("afterend", refreshSpan);

    // Utility handler that opens supply transfer page and selects SB+
    function transferSB(snuid) {
      const newWindow = window.open(
        `https://www.mousehuntgame.com/supplytransfer.php?fid=${snuid}`
      );
      newWindow.addEventListener("load", function() {
        if (newWindow.supplyTransfer1) {
          newWindow.supplyTransfer1.setSelectedItemType("super_brie_cheese");
          newWindow.supplyTransfer1.renderTabMenu();
          newWindow.supplyTransfer1.render();
        }
      });
      return false;
    }

    // Corkboard image click handling
    document.querySelectorAll("[data-message-id]").forEach(msg => {
      const snuid = msg
        .querySelector(".messageBoardView-message-name")
        .href.split("snuid=")[1];
      const img = msg.querySelector(".messageBoardView-message-image");
      img.href = "#";
      img.onclick = function() {
        transferSB(snuid);
      };
    });

    // Hunter container image click handling
    document
      .querySelectorAll(".treasureMapPopup-hunter:not(.empty)")
      .forEach(el => {
        const img = el.querySelector(".treasureMapPopup-hunter-image");
        const snuid = el.getAttribute("data-snuid");
        img.style.cursor = "pointer";
        img.onclick = function() {
          transferSB(snuid);
        };
      });

    // Check for valid Queso Canyon map name
    const mapNameSelector = document.querySelector(
      ".treasureMapPopup-header-title.mapName"
    );
    if (mapNameSelector) {
      const split = mapNameSelector.textContent.split("Rare ");
      const mapName = split.length === 2 ? split[1] : split[0];

      if (quesoMaps.indexOf(mapName) >= 0) {
        // Queso Mapper toggling
        const quesoToggle = document.createElement("input");
        quesoToggle.type = "checkbox";
        quesoToggle.className = "tsitu-mapping";
        quesoToggle.name = "tsitu-queso-toggle";
        quesoToggle.addEventListener("click", function() {
          localStorage.setItem("tsitu-queso-toggle", quesoToggle.checked);
          render();
        });

        const quesoToggleLabel = document.createElement("label");
        quesoToggleLabel.className = "tsitu-mapping";
        quesoToggleLabel.htmlFor = "tsitu-queso-toggle";
        quesoToggleLabel.innerText = "Toggle Queso Mapper functionality";

        const qtChecked = localStorage.getItem("tsitu-queso-toggle") || "false";
        quesoToggle.checked = qtChecked === "true";
        if (quesoToggle.checked) {
          quesoRender();
        }

        const quesoToggleDiv = document.createElement("div");
        quesoToggleDiv.className = "tsitu-queso-mapper";
        if (!quesoToggle.checked) quesoToggleDiv.style.marginBottom = "10px";
        quesoToggleDiv.appendChild(quesoToggle);
        quesoToggleDiv.appendChild(quesoToggleLabel);

        document
          .querySelector(".treasureMapPopup-hunterContainer")
          .insertAdjacentElement("afterend", quesoToggleDiv);
      }
    }

    // Features that require cache checking
    const cacheRaw = localStorage.getItem("tsitu-mapping-cache");
    if (cacheRaw) {
      const cache = JSON.parse(cacheRaw);
      const mapName = document.querySelector(
        ".treasureMapPopup-header-title.mapName"
      ).textContent;

      if (cache[mapName] !== undefined) {
        // Must specify <a> because favorite button <div> also matches the selector
        const mapIdEl = document.querySelector("a[data-map-id].active");
        if (mapIdEl) {
          // Abstract equality comparison because map ID can be number or string
          const mapId = mapIdEl.getAttribute("data-map-id");
          if (mapId == cache[mapName].map_id) {
            // "Last refreshed" timestamp
            const refreshSpan = document.querySelector(".tsitu-refresh-span");
            if (refreshSpan && cache[mapName].timestamp) {
              const timeSpan = document.createElement("span");
              timeSpan.innerText = `(This map was last refreshed on: ${new Date(
                parseInt(cache[mapName].timestamp)
              ).toLocaleString()})`;
              refreshSpan.appendChild(timeSpan);
            }

            // Invite via Hunter ID (only for map captains)
            if (cache[mapName].is_owner) {
              const inputLabel = document.createElement("label");
              inputLabel.innerText = "Hunter ID: ";
              inputLabel.htmlFor = "tsitu-mapping-id-input";

              const inputField = document.createElement("input");
              inputField.setAttribute("type", "number");
              inputField.setAttribute("name", "tsitu-mapping-id-input");
              inputField.setAttribute("data-lpignore", "true"); // Get rid of LastPass Autofill
              inputField.setAttribute("min", 1);
              inputField.setAttribute("max", 9999999);
              inputField.setAttribute("placeholder", "e.g. 1234567");
              inputField.setAttribute("required", true);
              inputField.addEventListener("keyup", function(e) {
                if (e.keyCode === 13) {
                  inviteButton.click(); // 'Enter' pressed
                }
              });

              const overrideStyle =
                "input[name='tsitu-mapping-id-input'] { -webkit-appearance:textfield; -moz-appearance:textfield; appearance:textfield; } input[name='tsitu-mapping-id-input']::-webkit-outer-spin-button, input[name='tsitu-mapping-id-input']::-webkit-inner-spin-button { display:none; -webkit-appearance:none; margin:0; }";
              let stylePresent = false;
              document.querySelectorAll("style").forEach(style => {
                if (style.textContent === overrideStyle) {
                  stylePresent = true;
                }
              });
              if (!stylePresent) {
                const spinOverride = document.createElement("style");
                spinOverride.innerHTML = overrideStyle;
                document.body.appendChild(spinOverride);
              }

              const inviteButton = document.createElement("button");
              inviteButton.innerText = "Invite";
              inviteButton.addEventListener("click", function() {
                const rawText = inputField.value;
                if (rawText.length > 0) {
                  const hunterId = parseInt(rawText);
                  if (typeof hunterId === "number" && !isNaN(hunterId)) {
                    if (hunterId > 0 && hunterId < 9999999) {
                      postReq(
                        "https://www.mousehuntgame.com/managers/ajax/pages/friends.php",
                        `sn=Hitgrab&hg_is_ajax=1&action=community_search_by_id&user_id=${hunterId}&uh=${
                          user.unique_hash
                        }`
                      ).then(res => {
                        let response = null;
                        try {
                          if (res) {
                            response = JSON.parse(res.responseText);
                            // console.log(response);
                            const data = response.friend;
                            if (data.has_invitable_map) {
                              if (
                                confirm(
                                  `Are you sure you'd like to invite this hunter?\n\nName: ${
                                    data.name
                                  }\nTitle: ${data.title_name} (${
                                    data.title_percent
                                  }%)\nLocation: ${
                                    data.environment_name
                                  }\nLast Active: ${
                                    data.last_active_formatted
                                  } ago`
                                )
                              ) {
                                postReq(
                                  "https://www.mousehuntgame.com/managers/ajax/users/relichunter.php",
                                  `sn=Hitgrab&hg_is_ajax=1&action=send_invites&map_id=${mapId}&snuids%5B%5D=${
                                    data.snuid
                                  }&uh=${user.unique_hash}`
                                ).then(res2 => {
                                  let inviteRes = null;
                                  try {
                                    if (res2) {
                                      inviteRes = JSON.parse(res2.responseText);
                                      if (inviteRes.success === 1) {
                                        refreshButton.click();
                                      } else {
                                        alert(
                                          "Map invite unsuccessful - may be because map is full"
                                        );
                                      }
                                    }
                                  } catch (error2) {
                                    alert("Error while inviting hunter to map");
                                    console.error(error2.stack);
                                  }
                                });
                              }
                            } else {
                              if (data.name) {
                                alert(
                                  `${
                                    data.name
                                  } cannot to be invited to a map at this time`
                                );
                              } else {
                                alert("Invalid hunter information");
                              }
                            }
                          }
                        } catch (error) {
                          alert("Error while requesting hunter information");
                          console.error(error.stack);
                        }
                      });
                    }
                  }
                }
              });

              // Invited hunters aka pending invites
              const invitedArr = cache[mapName].invited_hunters;
              const invitedSpan = document.createElement("span");
              invitedSpan.style.marginLeft = "5px";
              invitedSpan.innerText =
                invitedArr.length > 0
                  ? "Pending Invites:"
                  : "Pending Invites: None";

              if (invitedArr.length > 0) {
                let count = 1;
                invitedArr.forEach(snuid => {
                  const link = document.createElement("a");
                  link.innerText = `[${count}]`;
                  link.href = `https://www.mousehuntgame.com/profile.php?snuid=${snuid}`;
                  link.target = "_blank";
                  invitedSpan.appendChild(document.createTextNode("\u00A0"));
                  invitedSpan.appendChild(link);

                  // Prevent text from running past width of dialog (fails when >999)
                  if (count < 100) {
                    if (count === 20) {
                      invitedSpan.appendChild(document.createElement("br"));
                    } else if ((count - 20) % 30 === 0) {
                      invitedSpan.appendChild(document.createElement("br"));
                    }
                  } else {
                    if (count === 108) {
                      invitedSpan.appendChild(document.createElement("br"));
                    } else if ((count - 108) % 24 === 0) {
                      invitedSpan.appendChild(document.createElement("br"));
                    }
                  }
                  count += 1;
                });

                // -- Text debugging --
                // for (let i = 0; i < 1337; i++) {
                // for (let i = 0; i < 80; i++) {
                //   const link = document.createElement("a");
                //   link.innerText = `[${i + 1}]`;
                //   link.href = `#`;
                //   invitedSpan.appendChild(document.createTextNode("\u00A0"));
                //   invitedSpan.appendChild(link);
                //   // Prevent text from running past width of dialog (formatting breaks past 1k but meh)
                //   if (i + 1 < 100) {
                //     if (i + 1 === 20) {
                //       invitedSpan.appendChild(document.createElement("br"));
                //     } else if ((i + 1 - 20) % 30 === 0) {
                //       invitedSpan.appendChild(document.createElement("br"));
                //     }
                //   } else {
                //     if (i + 1 === 108) {
                //       invitedSpan.appendChild(document.createElement("br"));
                //     } else if ((i + 1 - 108) % 24 === 0) {
                //       invitedSpan.appendChild(document.createElement("br"));
                //     }
                //   }
                // }
              }

              const span = document.createElement("span");
              span.className = "tsitu-mapping";
              span.style.display = "inline-block";
              span.style.marginBottom = "10px";
              span.appendChild(inputLabel);
              span.appendChild(inputField);
              span.appendChild(inviteButton);
              span.appendChild(invitedSpan);

              document
                .querySelector(".treasureMapPopup-hunterContainer")
                .insertAdjacentElement("afterend", span);
            }
          }
        }

        // "x caught these mice" image click handling
        const groups = cache[mapName].groups;
        const format = {};

        groups.forEach(el => {
          if (el.profile_pic !== null) {
            format[el.profile_pic] = [el.name, el.snuid];
          }
        });

        document
          .querySelectorAll(".treasureMapPopup-goals-group-header")
          .forEach(group => {
            const text = group.textContent.split(":(")[0] + ":";
            if (text !== "Uncaught mice in other locations:") {
              const img = group.querySelector(
                ".treasureMapPopup-goals-group-header-image"
              );
              if (img) {
                const pic = img.style.backgroundImage
                  .split('url("')[1]
                  .split('")')[0];
                if (format[pic] !== undefined) {
                  if (format[pic][0] === text) {
                    img.style.cursor = "pointer";
                    img.onclick = function() {
                      const snuid = format[pic][1];
                      transferSB(snuid);
                    };
                  }
                }
              }
            }
          });
      }
    }
  }

  // POST to specified endpoint URL with desired form data
  function postReq(url, form) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      xhr.onreadystatechange = function() {
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
          resolve(this);
        }
      };
      xhr.onerror = function() {
        reject(this);
      };
      xhr.send(form);
    });
  }

  // MutationObserver logic for map UI
  // Observers are attached to a *specific* element (will DC if removed from DOM)
  const observerTarget = document.getElementById("overlayPopup");
  if (observerTarget) {
    MutationObserver =
      window.MutationObserver ||
      window.WebKitMutationObserver ||
      window.MozMutationObserver;

    const observer = new MutationObserver(function() {
      // Callback

      // Render if treasure map popup is available
      const mapTab = observerTarget.querySelector("[data-tab=map_mice]");
      const groupLen = document.querySelectorAll(
        ".treasureMapPopup-goals-groups"
      ).length;

      // Prevent conflict with 'Bulk Map Invites'
      const inviteHeader = document.querySelector(
        ".treasureMapPopup-inviteFriend-header"
      );

      if (
        mapTab &&
        mapTab.className.indexOf("active") >= 0 &&
        groupLen > 0 &&
        !inviteHeader
      ) {
        // Disconnect and reconnect later to prevent infinite mutation loop
        observer.disconnect();

        render();

        observer.observe(observerTarget, {
          childList: true,
          subtree: true
        });
      }
    });

    observer.observe(observerTarget, {
      childList: true,
      subtree: true
    });
  }

  // Queso Mapper functionality
  function quesoRender() {
    const mapMice = document.querySelectorAll(
      "div.treasureMapPopup-goals-group-goal.treasureMapPopup-searchIndex.mouse"
    );
    if (mapMice.length > 0) {
      // Generate DOM elements
      const displayDiv = document.createElement("div");
      displayDiv.className = "tsitu-queso-mapper";
      displayDiv.style.fontSize = "14px";
      displayDiv.style.marginBottom = "10px";
      displayDiv.innerText = "Preferred Location & Cheese -> ";

      const cacheSel = localStorage.getItem("tsitu-queso-mapper-sel");
      if (cacheSel) {
        const cache = JSON.parse(cacheSel);
        for (let location in quesoData) {
          const locSel = `${classBuilder(location, "loc")}`;
          if (cache[locSel] !== undefined) {
            const locationSpan = document.createElement("span");
            locationSpan.innerText = `${location}: `;
            let cheeseCount = 0;
            for (let cheese in quesoData[location]) {
              const cheeseSel = `${classBuilder(location, cheese)}`;
              if (cache[locSel].indexOf(cheeseSel) >= 0) {
                const cheeseSpan = document.createElement("span");
                let prependStr = "";
                if (cheeseCount > 0) prependStr = ", ";

                const imgSpan = document.createElement("span");
                imgSpan.setAttribute(
                  "style",
                  `background-image:url('${
                    quesoImg[cheese]
                  }');width:20px;height:20px;display:inline-block;background-size:contain;background-repeat:no-repeat;position:relative;top:4px;`
                );

                let appendStr = "";
                if (cheese !== "Standard" && cheese !== "SB+") {
                  appendStr += " Queso";
                }

                cheeseSpan.innerText = `${prependStr + cheese + appendStr}`;
                locationSpan.append(cheeseSpan);
                locationSpan.append(document.createTextNode("\u00A0"));
                locationSpan.append(imgSpan);
                cheeseCount += 1;
              }
            }
            displayDiv.appendChild(locationSpan);
          }
        }
      } else {
        displayDiv.style.marginTop = "5px";
        displayDiv.innerText = "Preferred Location & Cheese -> N/A";
      }

      const target = document.querySelector(
        ".treasureMapPopup-map-stateContainer.viewGoals"
      );
      if (target) target.insertAdjacentElement("beforebegin", displayDiv);

      mapMice.forEach(el => {
        if (el.className.indexOf("tsitu-queso-mapper-mouse") < 0) {
          function listener() {
            const span = el.querySelector("span");
            if (span) {
              const mouse = span.textContent;
              const mouseData = quesoMice[mouse];
              if (mouseData) {
                const toCache = {};
                for (let arr of mouseData) {
                  const locSel = classBuilder(arr[0], "loc");
                  const cheeseSel = classBuilder(arr[0], arr[1]);
                  if (toCache[locSel] === undefined) {
                    toCache[locSel] = [cheeseSel];
                  } else {
                    toCache[locSel].push(cheeseSel);
                  }
                  localStorage.setItem(
                    "tsitu-queso-mapper-sel",
                    JSON.stringify(toCache)
                  );
                  render();
                }
              }
            }
          }

          el.addEventListener("mouseover", function() {
            listener();
          });

          el.addEventListener("click", function() {
            listener();
          });
        }
        el.classList.add("tsitu-queso-mapper-mouse");
      });
    }

    function classBuilder(location, cheese) {
      let retVal = "";

      switch (location) {
        case "Queso River":
          retVal += "river-";
          break;
        case "Prickly Plains":
          retVal += "plains-";
          break;
        case "Cantera Quarry":
          retVal += "quarry-";
          break;
        case "Cork Collecting":
          retVal += "cork-";
          break;
        case "Pressure Building":
          retVal += "pressure-";
          break;
        case "Small Eruption":
          retVal += "small-";
          break;
        case "Medium Eruption":
          retVal += "medium-";
          break;
        case "Large Eruption":
          retVal += "large-";
          break;
        case "Epic Eruption":
          retVal += "epic-";
          break;
        default:
          retVal += location;
      }

      switch (cheese) {
        case "Standard":
          retVal += "standard";
          break;
        case "SB+":
          retVal += "super";
          break;
        case "Bland":
          retVal += "bland";
          break;
        case "Mild":
          retVal += "mild";
          break;
        case "Medium":
          retVal += "medium";
          break;
        case "Hot":
          retVal += "hot";
          break;
        case "Flamin'":
          retVal += "flamin";
          break;
        case "Wildfire":
          retVal += "wildfire";
          break;
        default:
          retVal += cheese;
      }

      return retVal;
    }
  }
})();