dreifachpunkt. / GeoGuessr Liked Maps List Enhanced

// ==UserScript==
// @name GeoGuessr Liked Maps List Enhanced
// @namespace   dreifachpunkt.
// @description Display all your liked maps in a simple table, ordered by the amount of games. Based on Kommu's enhanced map list: openuserjs.org/scripts/kommu/GeoGuessr_Maps_List_Enhanced
// @version 0.0.3
// @include https://www.geoguessr.com/*
// @run-at document-start
// @license MIT
// ==/UserScript==

/*jshint esversion: 6 */

//--------------------------------------------------------------------------------------------------------

var sortOption = "games"; //available: games, likes, locations, avgscore, creator, name

//--------------------------------------------------------------------------------------------------------

var howMuch = 500;
var oldHref = document.location.href;

window.onload = (event) => {
  mapsBehavior();

  var bodyList = document.querySelector("body"),
    observer = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {
        if (oldHref != document.location.href) {
          oldHref = document.location.href;
          mapsBehavior();
        }
      });
    });

  var config = {
    childList: true,
    subtree: true,
  };

  observer.observe(bodyList, config);
};

function mapsBehavior() {
  if (
    !location.pathname.startsWith("/me/likes") ||
    location.pathname.endsWith("#")
  ) {
    return false;
  }

  var style =
    "table.custom-table {\n" +
    "    border: 1px solid #e2e2e2;\n" +
    "    width: 100%;\n" +
    "    border-collapse: collapse;\n" +
    "    margin-bottom: 40px;\n" +
    "        font-size: 12px;\n" +
    "}\n" +
    "\n" +
    "table.custom-table td, table.custom-table th {\n" +
    "    padding: 5px 10px;\n" +
    "    border: 1px solid #e2e2e2;\n" +
    "}\n" +
    "\n" +
    "table.custom-table th {\n" +
    "    text-transform: uppercase;\n" +
    "    font-size: 10px;\n" +
    "    color: #FCFAF9;\n" +
    "}\n" +
    "\n" +
    "table.custom-table td:nth-child(3) {\n" +
    "    font-size: 10px;\n" +
    "    width: 300px!important;\n" +
    "    max-width: 500px;\n" +
    "    word-break: break-word;\n" +
    "}\n" +
    ".table-filter input {\n" +
    "    width: 100%;\n" +
    "}\n" +
    ".map-name.visible {\n" +
    "    display: table-row;\n" +
    "}\n" +
    ".map-name {\n" +
    "    display: none;\n" +
    "}\n" +
    "table.custom-table td:nth-child(1),\n" +
    "table.custom-table td:nth-child(4),\n" +
    "table.custom-table td:nth-child(5),\n" +
    "table.custom-table td:nth-child(6),\n" +
    "table.custom-table td:nth-child(7) {\n" +
    "    text-align: center;\n" +
    "}";
  var table =
    '<h1 data-qa="title" class="title title--medium" style="color:white">Liked Maps</h1><style>' +
    style +
    '</style><div class="table-filter"><input id="filter-map-name" type="text" placeholder="Filter by map name"></div><table class="custom-table"><tr bgcolor="282828"><th>Author' +
    (sortOption === "creator" ? " ⬇️" : "") +
    "</th><th>Name" +
    (sortOption === "name" ? " ⬇️" : "") +
    "</th><th>Description</th><th>Locations" +
    (sortOption === "locations" ? " ⬇️" : "") +
    "</th><th>Likes" +
    (sortOption === "likes" ? " ⬇️" : "") +
    "</th><th>Games" +
    (sortOption === "games" ? " ⬇️" : "") +
    "</th><th>Avg Score" +
    (sortOption === "avgscore" ? " ⬇️" : "") +
    "</th></tr>";
  var current = null;
  var page = 0;
  var fulldata = [];
  var no_data_found = false;
  while ((current === null || current % howMuch === 0) && !no_data_found) {
    if (current === null) {
      current = 0;
    }
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open(
      "GET",
      "https://www.geoguessr.com/api/v3/likes?page=" +
      page +
      "&count=" +
      howMuch,
      false
    );
    xmlhttp.send();
    if (xmlhttp.status === 200) {
      var data = JSON.parse(xmlhttp.responseText);
      if (data.length > 0) {
        current = current + data.length;
        page++;
        fulldata = fulldata.concat(data);
      }
      else {
        no_data_found = true;
      }
    }
    else {
      alert("An error occured, while getting maps.");
      return false;
    }
  }
  fulldata.sort(function (a, b) {
    return a.name.localeCompare(b.name);
  });

  table += getHtml(fulldata);

  table +=
    '</table><div style="margin-bottom: 15px; color:#ACAAA9; font-size: 12px; float: right">You have liked ' +
    fulldata.length +
    " maps. This table might display " +
    howMuch +
    " maps at most. | by Kommu / dreifachpunkt. | v0.0.3</div><div><br><br><br><hr><br><br></div>";
  var div = document.createElement("div");
  div.innerHTML = table;

  // var elements = document.getElementsByClassName("container__content");
  var elements = document.querySelectorAll("[class^=container_content]");

  elements[0].parentNode.insertBefore(div, elements[0]);

  var rows = document.getElementsByClassName("map-name");
  var filter_map_name = document.getElementById("filter-map-name");
  filter_map_name.addEventListener("input", function (evt) {
    var filter_value = filter_map_name.value.toLowerCase();
    if (filter_value != "" && filter_value.length > 2) {
      for (var row_index = 0; row_index < rows.length; row_index++) {
        if (typeof rows[row_index] != "undefined") {
          var text = rows[row_index].getAttribute("mapname");
          if (text.includes(filter_value)) {
            if (!rows[row_index].classList.contains("visible")) {
              rows[row_index].classList.add("visible");
            }
          }
          else {
            if (rows[row_index].classList.contains("visible")) {
              rows[row_index].classList.remove("visible");
            }
          }
        }
      }
    }
    else {
      for (var row_index_2 = 0; row_index_2 < rows.length; row_index_2++) {
        if (typeof rows[row_index_2] != "undefined") {
          if (!rows[row_index_2].classList.contains("visible")) {
            rows[row_index_2].classList.add("visible");
          }
        }
      }
    }
  });
}

function getHtml(data) {
  var output = "";
  switch (sortOption) {
    case "games":
      data.sort((a, b) => {
        return b.numFinishedGames - a.numFinishedGames;
      });
      break;
    case "likes":
      data.sort((a, b) => {
        return b.likes - a.likes;
      });
      break;
    case "avgscore":
      data.sort((a, b) => {
        return b.averageScore - a.averageScore;
      });
      break;
    case "creator":
      data.sort((a, b) => {
        return a.creator.nick.localeCompare(b.creator.nick);
      });
      break;
    case "locations":
      data.sort((a, b) => {
        var aCoords = a.coordinateCount,
          bCoords = b.coordinateCount;
        if (aCoords.substring(aCoords.length - 1) === "+") {
          aCoords = aCoords.substring(0, aCoords.length - 1);
          if (aCoords.substring(aCoords.length - 1) === "K") {
            aCoords = aCoords.substring(0, aCoords.length - 1).concat("000");
          }
          if (aCoords.substring(aCoords.length - 1) === "M") {
            aCoords = aCoords.substring(0, aCoords.length - 1).concat("000000");
          }
        }
        if (bCoords.substring(bCoords.length - 1) === "+") {
          bCoords = bCoords.substring(0, bCoords.length - 1);
          if (bCoords.substring(bCoords.length - 1) === "K") {
            bCoords = bCoords.substring(0, bCoords.length - 1).concat("000");
          }
          if (bCoords.substring(bCoords.length - 1) === "M") {
            bCoords = bCoords.substring(0, bCoords.length - 1).concat("000000");
          }
        }
        return bCoords - aCoords;
      });
      break;
    case "name":
    default:
      break;
  }

  for (let obj in data) {
    var map = data[obj];
    output +=
      '<tr class="map-name visible" mapname="' +
      map.name.toLowerCase() +
      '">' +
      '<td><a href="https://www.geoguessr.com/user/' +
      map.creator.id +
      ' " style="color:white">' +
      map.creator.nick +
      "</td>" +
      '<td><b><a href="https://www.geoguessr.com/maps/' +
      map.slug +
      ' " style="color:white">' +
      map.name +
      "</a></b></td>" +
      '<td style="width: 200px">' +
      map.description +
      "</td>" +
      "<td>" +
      map.coordinateCount +
      "</td>" +
      "<td>" +
      map.likes +
      "</td>" +
      "<td>" +
      map.numFinishedGames +
      "</td>" +
      "<td>" +
      map.averageScore +
      "<br>(Level " +
      map.difficultyLevel +
      ")</td>" +
      "</tr>";
  }
  return output;
}