MisatoTremor / AntiGameReborn SpyTableGrouper

// ==UserScript==
// @name         AntiGameReborn SpyTableGrouper
// @namespace    https://openuserjs.org/users/MisatoTremor
// @version      1.2.1
// @description  Groups the AntiGameReborn SpyTable by galaxy
// @author       MisatoTremor
// @license      MIT
// @copyright    2024, MisatoTremor
// @match        https://*.ogame.gameforge.com/game/index.php?page=ingame&component=messages*
// @updateURL    https://openuserjs.org/meta/MisatoTremor/AntiGameReborn_SpyTableGrouper.meta.js
// @downloadURL  https://openuserjs.org/install/MisatoTremor/AntiGameReborn_SpyTableGrouper.user.js
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==
/*jshint esversion: 6 */
(function () {
  "use strict";
  class SpyTableGrouper {
    constructor() {
      this.observerQueue = {};
      this.mutationList = {};
      this.observerTimers = [];
      this.observer = new MutationObserver(
        mutations => {
          (() => {
            let now = performance.now();
            for (let i = 0, len = mutations.length; i < len; i++) {
              let mutation = mutations[i];
              if (mutation.target.querySelector("#agoSpyReportOverview")) {
                this.mutationList.tablereport = now;
              }
            }
          })();
          if (Object.keys(this.mutationList).length > 0) {
            tryUpdate();
          }
        }
      );
      let tryUpdate = () => {
        Object.keys(this.mutationList).forEach(k => {
          if (typeof this.observerQueue[k] === "function") {
            this.observerQueue[k]();
          }
          delete this.mutationList[k];
        });
      };
      this.observer.observe(document.querySelector(".messageContent"), {
        childList: true,
        subtree: true
      });
      this.observeMutation(() => this.groupTable(), "tablereport");
    }

    groupTable() {
      // We use that parseFloat stops parsing at the colon
      const getGalaxy = (row) => parseFloat(row.cells[0].textContent);
      const table = document.querySelector("#agoSpyReportOverview .ago_reports");
      if (table) {
        // Get all the rows of the table
        const tableBody = table.querySelector("tbody");
        const tableRows = Array.from(table.querySelectorAll("tbody tr"));
        // Sort the rows based on their x-coordinate values
        tableRows.sort((row1, row2) => {
          return getGalaxy(row1) - getGalaxy(row2);
        });
        let currentGalaxyId = 0;
        // Iterate over the sorted rows and group them
        tableRows.forEach((row) => {
          const galaxyId = getGalaxy(row);
          if (galaxyId !== currentGalaxyId) {
            currentGalaxyId = galaxyId;
            // If the x-coordinate of the current row is different from the current group,
            // add a class
            row.classList.add("first-report-in-galaxy");
          }
          row.classList.remove("even");
        });
        tableBody.prepend(...tableRows);
      }
    }

    observeMutation(callback, id) {
      this.observerQueue[id] = callback;
    }
  }
  // #endregion

  if (document.readyState !== "loading") {
    unsafeWindow.agr_stg = new SpyTableGrouper();
  }
  else {
    window.addEventListener("DOMContentLoaded", () => {
      unsafeWindow.agr_stg = new SpyTableGrouper();
    });
  }

  // #region
  const css =
    `
/*css*/
#agoSpyReportOverview .ago_reports tr.first-report-in-galaxy td {
    border-top: 5px solid #293748;
}
#agoSpyReportOverview .ago_reports tbody tr:nth-child(even) {
    background-color: #0f141a;
}
/*!css*/
`;

  GM_addStyle(css);
})();