// ==UserScript==
// @name MZ - Friendly Matches Played by Players
// @namespace douglaskampl
// @version 4.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;
overflow-y: auto;
}
.friendly-modal.visible {
opacity: 1;
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: 5vh;
}
.friendly-modal-content {
background: linear-gradient(145deg, #2a2a2a, #1a1a1a);
margin: auto;
padding: 0;
border: 1px solid #444;
width: fit-content;
max-width: 80%;
max-height: 90vh;
overflow-y: auto;
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);
position: relative;
}
.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: "Friendly Matches 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();
})();