// ==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 // @downloadURL none // ==/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 = ''; 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(); })();