MZ - Player Weekly Friendlies Tracker

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

目前為 2024-12-27 提交的版本,檢視 最新版本

// ==UserScript==
// @name         MZ - Player Weekly Friendlies Tracker
// @namespace    douglaskampl
// @version      4.5
// @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();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址