Greasy Fork

MZ - Friendly Matches Played by Players

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

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

// ==UserScript==
// @name         MZ - Friendly Matches Played by Players
// @namespace    douglaskampl
// @version      3.0
// @description  Gets the amount of friendlies played by each player during the current week
// @author       Douglas Vieira
// @match        https://www.managerzone.com/?p=challenges*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

GM_addStyle(`
  .friendly-modal {
    display: none;
    position: fixed;
    z-index: 9999;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.9);
    backdrop-filter: blur(5px);
    transform: translateZ(0);
    opacity: 0;
    transition: opacity 0.3s ease-out;
  }

  .friendly-modal.visible {
    opacity: 1;
  }

  .friendly-modal-content {
    background: linear-gradient(145deg, #2a2a2a, #1a1a1a);
    margin: 5% auto;
    padding: 0;
    border: 1px solid #444;
    width: fit-content;
    max-width: 80%;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
    transform: translateY(-20px);
    opacity: 0;
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  }

  .friendly-modal.visible .friendly-modal-content {
    transform: translateY(0);
    opacity: 1;
  }

  .friendly-close {
    color: #888;
    float: right;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    padding: 0 10px;
    transition: color 0.2s ease;
  }

  .friendly-close:hover {
    color: #fff;
  }

  .friendly-table {
    border-collapse: collapse;
    margin: 0 auto;
    border: 1px solid #333;
    background: #111;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  }

  .friendly-cell {
    border: 1px solid #333;
    padding: 8px 16px;
    color: #e0e0e0;
    transition: background-color 0.2s ease;
  }

  .friendly-table tr:hover .friendly-cell {
    background-color: #1a1a1a;
  }

  .friendly-header {
    background: linear-gradient(90deg, #d6204e, #b81b42);
    color: white;
    border: 1px solid #333;
    padding: 12px 16px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 14px;
    font-weight: 600;
  }

  .friendly-link {
    color: #e0e0e0;
    text-decoration: none;
    position: relative;
    padding-bottom: 2px;
    transition: color 0.2s ease;
  }

  .friendly-link:hover {
    color: #d6204e;
  }

  .friendly-loading {
    display: none;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100px;
  }

  .friendly-message {
    color: #d6204e;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-size: 16px;
    text-align: center;
    animation: pulse 1.5s ease-in-out infinite;
  }

  .friendly-button {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    color: white;
    background: linear-gradient(145deg, #1e3a8a, #1e40af);
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .friendly-button:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 8px -2px rgba(0, 0, 0, 0.2);
    background: linear-gradient(145deg, #1e40af, #1e3a8a);
  }

  .friendly-button:active {
    transform: translateY(0);
    box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1);
  }

  .friendly-text {
    margin-right: 15px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    color: #1e3a8a;
    font-size: 16px;
    font-weight: 500;
    text-shadow: 2px 2px 4px rgba(255, 165, 0, 0.3);
  }

  @keyframes pulse {
    0% { opacity: 0.6; }
    50% { opacity: 1; }
    100% { opacity: 0.6; }
  }
`);

(function() {
  "use strict";

  const UI_TEXT = {
    BUTTON_TEXT: "Click the circle to see how many matches your players played this week ->",
    LOADING_TEXT: "Loading…",
    NO_MATCHES_TEXT: "No friendly matches have been played this week!",
    PLAYER_COLUMN: "Player",
    GAMES_COLUMN: "Games This Week"
  };

  class FriendlyMatchTracker {
    constructor() {
      this.sport = this.getSport();
      this.sportId = this.sport === "soccer" ? 1 : 2;
      this.challengeTemplateUrl = `https://www.managerzone.com/ajax.php?p=challenge&sub=personal-challenge-template&sport=${this.sport}`;
      this.teamId = null;
      this.appearances = new Map();
      this.initializeUI();
      this.fetchPageData();
    }

    getSport() {
      return new URL(document.querySelector("#shortcut_link_thezone").href).searchParams.get("sport");
    }

    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;
    }

    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 = UI_TEXT.LOADING_TEXT;

      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 = UI_TEXT.BUTTON_TEXT;

          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";
          }, 300);
        }
      });
    }

    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 response = await fetch(`https://www.managerzone.com/xml/manager_data.php?sport_id=1&username=${username}`);
        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 response = await fetch(this.challengeTemplateUrl);
        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 ["d1", "d3", "d4", "d5", "d7"]
        .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 response = await fetch(`https://www.managerzone.com/xml/match_info.php?sport_id=${this.sportId}&match_id=${matchId}`);
          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");
      [UI_TEXT.PLAYER_COLUMN, UI_TEXT.GAMES_COLUMN].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 = UI_TEXT.NO_MATCHES_TEXT;
    }
  }

  new FriendlyMatchTracker();
})();