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