Greasy Fork

MZ - Player Weekly Friendlies Tracker

Tracks the amount of friendlies played by each player during the current week

目前为 2024-12-26 提交的版本。查看 最新版本

// ==UserScript==
// @name         MZ - Player Weekly Friendlies Tracker
// @namespace    douglaskampl
// @version      4.4
// @description  Tracks the amount of friendlies played by each player during the current week
// @author       Douglas
// @match        https://www.managerzone.com/?p=challenges*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @resource     playerFriendlyTrackerStyles https://u18mz.vercel.app/mz/userscript/other/playerFriendlyTracker.css
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  GM_addStyle(GM_getResourceText("playerFriendlyTrackerStyles"));

  class MZConfig {
    static get ENDPOINTS() {
      return {
        CHALLENGE_TEMPLATE: "https://www.managerzone.com/ajax.php",
        MANAGER_DATA: "https://www.managerzone.com/xml/manager_data.php",
        MATCH_INFO: "https://www.managerzone.com/xml/match_info.php"
      };
    }

    static get MATCH_DAYS() {
      return ["d1", "d3", "d4", "d5", "d7"];
    }

    static get SPORT_IDS() {
      return {
        SOCCER: "1",
        HOCKEY: "2"
      };
    }
  }

  class FriendlyMatchTracker {
    constructor() {
      this.initializeState();
      this.initializeUI();
      this.fetchPageData();
    }

    initializeState() {
      const sportElement = document.querySelector("#shortcut_link_thezone");
      const sportParam = new URL(sportElement.href).searchParams.get("sport");

      this.sport = sportParam;
      this.sportId = sportParam === "soccer" ? MZConfig.SPORT_IDS.SOCCER : MZConfig.SPORT_IDS.HOCKEY;
      this.teamId = null;
      this.appearances = new Map();
    }

    get challengeTemplateUrl() {
      return new URL(MZConfig.ENDPOINTS.CHALLENGE_TEMPLATE);
    }

    initializeUI() {
      this.createModal();
      this.createTable();
      this.createLoadingElements();
      this.addMainButton();
    }

    createModal() {
      const modal = document.createElement("div");
      modal.className = "friendly-modal";

      const content = document.createElement("div");
      content.className = "friendly-modal-content";

      const close = document.createElement("span");
      close.className = "friendly-close";
      close.innerHTML = "×";
      close.onclick = () => this.toggleModal(false);

      content.appendChild(close);
      modal.appendChild(content);
      document.body.appendChild(modal);

      this.modal = modal;
      this.modalContent = content;

      modal.onclick = (e) => {
        if (e.target === modal) {
          this.toggleModal(false);
        }
      };
    }

    createTable() {
      this.table = document.createElement("table");
      this.table.className = "friendly-table";
      this.modalContent.appendChild(this.table);
    }

    createLoadingElements() {
      const loadingDiv = document.createElement("div");
      loadingDiv.className = "friendly-loading";

      const message = document.createElement("p");
      message.className = "friendly-message";
      message.textContent = "Loading…";

      loadingDiv.appendChild(message);
      this.modalContent.appendChild(loadingDiv);

      this.loadingDiv = loadingDiv;
      this.loadingMessage = message;
    }

    addMainButton() {
      const checkExist = setInterval(() => {
        const target = document.getElementById("fss-title-heading");
        if (target) {
          clearInterval(checkExist);

          const container = document.createElement("div");
          container.style.display = "flex";
          container.style.alignItems = "center";
          container.style.marginBottom = "15px";

          const text = document.createElement("span");
          text.className = "friendly-text";
          text.textContent = "Click the circle to see how many matches your players played this week ->";

          const button = document.createElement("button");
          button.className = "friendly-button";
          button.innerHTML = '<svg width="16" height="16" viewBox="0 0 16 16"><path fill="currentColor" d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"/><path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/></svg>';
          button.onclick = () => this.toggleModal(true);

          container.appendChild(text);
          container.appendChild(button);
          target.parentNode.insertBefore(container, target);
        }
      }, 100);
    }

    toggleModal(show) {
      requestAnimationFrame(() => {
        if (show) {
          this.modal.style.display = "block";
          requestAnimationFrame(() => {
            this.modal.classList.add("visible");
            if (this.appearances.size === 0) {
              this.loadingDiv.style.display = "flex";
            }
          });
        } else {
          this.modal.classList.remove("visible");
          setTimeout(() => {
            this.modal.style.display = "none";
            this.loadingDiv.style.display = "none";
          }, 200);
        }
      });
    }

    async fetchPageData() {
      try {
        const response = await fetch(window.location.href);
        const data = await response.text();
        const doc = new DOMParser().parseFromString(data, "text/html");
        const username = doc.getElementById("header-username").textContent;
        await this.fetchManagerData(username);
      } catch (error) {
        console.warn("Error fetching page data:", error);
      }
    }

    async fetchManagerData(username) {
      try {
        const url = new URL(MZConfig.ENDPOINTS.MANAGER_DATA);
        url.searchParams.set("sport_id", this.sportId);
        url.searchParams.set("username", username);

        const response = await fetch(url);
        const xmlDoc = new DOMParser().parseFromString(await response.text(), "text/xml");
        const teamElement = Array.from(xmlDoc.getElementsByTagName("Team"))
          .find(team => team.getAttribute("sport") === this.sport);

        this.teamId = teamElement.getAttribute("teamId");
        await this.fetchChallengeTemplate();
      } catch (error) {
        console.warn("Error fetching manager data:", error);
      }
    }

    async fetchChallengeTemplate() {
      try {
        const url = this.challengeTemplateUrl;
        url.searchParams.set("p", "challenge");
        url.searchParams.set("sub", "personal-challenge-template");
        url.searchParams.set("sport", this.sport);

        const response = await fetch(url);
        const doc = new DOMParser().parseFromString(await response.text(), "text/html");
        const matchesDiv = this.getCurrentWeekMatchesDiv(doc);

        if (!matchesDiv) {
          this.showNoMatchesMessage();
          return;
        }

        const matchIds = this.extractMatchIds(matchesDiv);
        if (matchIds.length === 0) {
          this.showNoMatchesMessage();
          return;
        }

        await this.fetchMatchInfo(matchIds);
      } catch (error) {
        console.warn("Error fetching challenge template:", error);
      }
    }

    getCurrentWeekMatchesDiv(doc) {
      const scheduleDiv = doc.getElementById("friendly_series_schedule");
      if (!scheduleDiv) return null;

      const calendarDiv = scheduleDiv.querySelector(".calendar");
      if (!calendarDiv) return null;

      const calendarForm = calendarDiv.querySelector("#saveMatchTactics");
      return calendarForm?.querySelector("div.flex-nowrap.fss-row.fss-gw-wrapper.fss-has-matches");
    }

    extractMatchIds(matchesDiv) {
      return MZConfig.MATCH_DAYS
        .map(className => matchesDiv.querySelector(`.${className}`))
        .filter(Boolean)
        .flatMap(div =>
          Array.from(div.querySelectorAll("a.score-shown:not(.gray)"))
            .map(link => link.getAttribute("href").split("mid=")[1].split("&")[0])
        );
    }

    async fetchMatchInfo(matchIds) {
      try {
        await Promise.all(matchIds.map(async matchId => {
          const url = new URL(MZConfig.ENDPOINTS.MATCH_INFO);
          url.searchParams.set("sport_id", this.sportId);
          url.searchParams.set("match_id", matchId);

          const response = await fetch(url);
          const xmlDoc = new DOMParser().parseFromString(await response.text(), "text/xml");
          const teamElements = Array.from(xmlDoc.getElementsByTagName("Team"));
          const ourTeamElement = teamElements.find(team => team.getAttribute("id") === this.teamId);
          this.updatePlayerAppearances(ourTeamElement);
        }));
        this.displayPlayerMatches();
      } catch (error) {
        console.warn("Error fetching match info:", error);
      }
    }

    updatePlayerAppearances(ourTeamElement) {
      Array.from(ourTeamElement.getElementsByTagName("Player")).forEach(player => {
        const playerId = player.getAttribute("id");
        const playerName = player.getAttribute("name");
        const playerInfo = this.appearances.get(playerId);

        if (playerInfo) {
          playerInfo.appearances += 1;
        } else {
          this.appearances.set(playerId, { name: playerName, appearances: 1 });
        }
      });
    }

    displayPlayerMatches() {
      this.loadingDiv.style.display = "none";
      this.table.innerHTML = "";
      this.table.appendChild(this.createHeaderRow());

      Array.from(this.appearances)
        .sort((a, b) => b[1].appearances - a[1].appearances)
        .forEach(([playerId, playerInfo]) => {
          this.table.appendChild(this.createPlayerRow(playerId, playerInfo));
        });
    }

    createHeaderRow() {
      const row = document.createElement("tr");
      ["Player", "Friendly Matches This Week"].forEach(text => {
        const th = document.createElement("th");
        th.className = "friendly-header";
        th.textContent = text;
        row.appendChild(th);
      });
      return row;
    }

    createPlayerRow(playerId, playerInfo) {
      const row = document.createElement("tr");

      const nameCell = document.createElement("td");
      nameCell.className = "friendly-cell";

      const link = document.createElement("a");
      link.className = "friendly-link";
      link.href = `https://www.managerzone.com/?p=players&pid=${playerId}`;
      link.textContent = playerInfo.name;
      nameCell.appendChild(link);

      const appearancesCell = document.createElement("td");
      appearancesCell.className = "friendly-cell";
      appearancesCell.textContent = playerInfo.appearances;

      row.appendChild(nameCell);
      row.appendChild(appearancesCell);

      return row;
    }

    showNoMatchesMessage() {
      this.loadingMessage.style.color = "lightgray";
      this.loadingMessage.textContent = "No friendly matches have been played this week!";
    }
  }

  new FriendlyMatchTracker();
})();