MZ – Show Player Ages on Match Page

Show player ages on match page

// ==UserScript==
// @name         MZ – Show Player Ages on Match Page
// @namespace    douglaskampl
// @version      2.9
// @description  Show player ages on match page
// @author       Douglas
// @match        *://www.managerzone.com/?p=match&sub=result*
// @grant        GM_xmlhttpRequest
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @license      MIT
// ==/UserScript==

const playerTableDivs = document.getElementsByClassName("team-table block");
const matchFactsWrapper = document.getElementById("match-tactic-facts-wrapper");
const ageCounters = [{}, {}];
const loadingMessage = getLoadingMessage();
const sport = new URL(
  document.querySelector("#shortcut_link_thezone").href
).searchParams.get("sport");
let teamNames = [];
let playerValues = [[], []];
let playerSalaries = [[], []];
let teamRanks = [null, null];
let currency = "";
let totalRequests = 0;
let completedRequests = 0;
let totalPlayers;

(function () {
  "use strict";

  if (playerTableDivs) {
    for (const div of playerTableDivs) {
      if (!div.querySelector("table")) {
        return;
      }
    }
  }

  let shouldAppendLoadingMsg = false;
  for (const table of playerTableDivs) {
    if (
      table.querySelector(
        "table.hitlist.soccer.statsLite.marker.tablesorter"
      ) ||
      table.querySelector("table.hitlist.hockey.statsLite.marker.tablesorter")
    ) {
      shouldAppendLoadingMsg = true;
      break;
    }
  }

  if (shouldAppendLoadingMsg) {
    matchFactsWrapper.appendChild(loadingMessage);
  }

  for (let tableIndex = 0; tableIndex < playerTableDivs.length; tableIndex++) {
    const table = playerTableDivs[tableIndex];

    const teamLink = table.querySelector("a");
    const teamName = teamLink.textContent;
    teamNames.push(teamName);

    const teamIdMatch = RegExp(/tid=(\d+)/).exec(teamLink.href);
    if (teamIdMatch) {
      const teamId = teamIdMatch[1];
      totalRequests++;

      GM_xmlhttpRequest({
        method: "GET",
        url: "https://www.managerzone.com/?p=team&tid=" + teamId,
        onload: function (response) {
          const parser = new DOMParser();
          const doc = parser.parseFromString(
            response.responseText,
            "text/html"
          );
          const rankElement = doc.querySelector(
            "#infoAboutTeam dd:nth-child(3) span[title]"
          );

          if (rankElement) {
            teamRanks[tableIndex] = parseInt(rankElement.textContent);
          } else {
            teamRanks[tableIndex] = "N/A";
          }

          completedRequests++;

          if (completedRequests === totalRequests) {
            displayFinalTable();
          }
        },
      });
    }

    const playerRows = Array.from(
      table.querySelector("tbody").querySelectorAll("tr")
    );

    for (const row of playerRows) {
      const link = row.querySelector("a");
      if (!link) {
        console.log("no element found in row: ", row);
      } else {
        const match = link.href.match(/pid=(\d+)/);
        const subIcon = row.querySelector(".fa-arrow-circle-up");

        totalPlayers = sport === "soccer" ? 11 : 21;
        if (match && (playerRows.indexOf(row) < totalPlayers || subIcon)) {
          let playerId = match[1];
          totalRequests++;

          GM_xmlhttpRequest({
            method: "GET",
            url: "https://www.managerzone.com/?p=players&pid=" + playerId,
            onload: function (response) {
              const parser = new DOMParser();
              const doc = parser.parseFromString(
                response.responseText,
                "text/html"
              );

              const ageElement = doc.querySelector(
                "#thePlayers_0 .mainContent .dg_playerview .dg_playerview_info table tr:first-child strong"
              );

              if (ageElement) {
                const age = parseInt(ageElement.textContent);
                if (!isNaN(age)) {
                  ageCounters[tableIndex][age] =
                    (ageCounters[tableIndex][age] || 0) + 1;
                }
              }

              const playerInfoElement = doc.querySelector(
                "#thePlayers_0 .mainContent .dg_playerview .dg_playerview_info"
              );

              let valueElement = playerInfoElement.querySelector(
                "table tr:nth-child(5) span.bold"
              );
              let salaryElement = playerInfoElement.querySelector(
                "table tr:nth-child(6) span.bold"
              );

              if (!valueElement || valueElement.textContent.trim() === "") {
                valueElement = playerInfoElement.querySelector(
                  "table tr:nth-child(6) span.bold"
                );
                salaryElement = playerInfoElement.querySelector(
                  "table tr:nth-child(7) span.bold"
                );
              }

              if (!valueElement || valueElement.textContent.trim() === "") {
                valueElement = playerInfoElement.querySelector(
                  "table tr:nth-child(7) span.bold"
                );
                salaryElement = playerInfoElement.querySelector(
                  "table tr:nth-child(8) span.bold"
                );
              }

              if (valueElement) {
                const valueText = valueElement.textContent;
                const playerValue = parseInt(valueText.replace(/\D/g, ""));
                if (!isNaN(playerValue)) {
                  playerValues[tableIndex].push(playerValue);
                }
                currency = valueText.replace(/\d/g, "").trim();
              }

              if (salaryElement && salaryElement.textContent.trim() !== "") {
                const salaryText = salaryElement.textContent;
                let playerSalary = parseInt(salaryText.replace(/\D/g, ""));
                if (isNaN(playerSalary)) {
                  playerSalary = 0;
                }
                playerSalaries[tableIndex].push(playerSalary);
              } else {
                playerSalaries[tableIndex].push(0);
              }

              completedRequests++;

              if (completedRequests === totalRequests) {
                displayFinalTable();
              }
            },
          });
        }
      }
    }
  }
})();

function displayFinalTable() {
  if (loadingMessage && loadingMessage.parentNode === matchFactsWrapper) {
    matchFactsWrapper.removeChild(loadingMessage);
  }

  const container = document.createElement("div");
  container.style.display = "flex";
  container.style.justifyContent = "center";
  container.style.alignItems = "center";
  container.className = "player-stats-container";

  for (let i = 0; i < 2; i++) {
    const teamContainer = document.createElement("div");
    teamContainer.style.display = "flex";
    teamContainer.style.flexDirection = "column";
    teamContainer.style.alignItems = "center";

    const tableContainer = document.createElement("div");
    tableContainer.style.display = "flex";
    tableContainer.style.flexDirection = "column";
    tableContainer.style.alignItems = "center";

    const table = document.createElement("table");
    table.style.border = "1px solid black";
    table.style.borderCollapse = "collapse";
    table.style.margin = "2px 16px 4px";
    table.style.animation = "fadeIn 1s";

    const caption = document.createElement("caption");
    let teamName = teamNames[i].substring(0, 8);
    if (teamNames[0].substring(0, 8) === teamNames[1].substring(0, 8)) {
      teamName = teamNames[i].substring(teamNames[i].length - 8);
    }
    caption.textContent = teamName;
    caption.style.padding = "4px";
    caption.style.whiteSpace = "nowrap";
    caption.style.overflow = "hidden";
    caption.style.textOverflow = "ellipsis";

    table.appendChild(caption);

    const header = document.createElement("tr");
    const ageHeader = document.createElement("th");
    ageHeader.textContent = "Age";
    ageHeader.style.border = "1px solid black";
    ageHeader.style.padding = "4px";
    header.appendChild(ageHeader);

    const countHeader = document.createElement("th");
    countHeader.textContent = "n";
    countHeader.style.fontStyle = "italic";
    countHeader.style.border = "1px solid black";
    countHeader.style.padding = "4px";
    header.appendChild(countHeader);
    table.appendChild(header);

    const ages = Object.keys(ageCounters[i]).sort((a, b) => b - a);
    for (const age of ages) {
      const row = document.createElement("tr");
      const ageCell = document.createElement("td");
      ageCell.textContent = age;
      ageCell.style.border = "1px solid black";
      ageCell.style.padding = "4px";
      row.appendChild(ageCell);

      const countCell = document.createElement("td");
      countCell.textContent = ageCounters[i][age];
      countCell.style.border = "1px solid black";
      countCell.style.padding = "4px";
      row.appendChild(countCell);
      table.appendChild(row);
    }

    tableContainer.appendChild(table);
    teamContainer.appendChild(tableContainer);

    const avgStatsContainer = document.createElement("div");
    avgStatsContainer.style.display = "flex";
    avgStatsContainer.style.flexDirection = "column";
    avgStatsContainer.style.alignItems = "center";

    let totalAge = 0;
    let totalPlayers = 0;
    for (const age in ageCounters[i]) {
      totalAge += age * ageCounters[i][age];
      totalPlayers += ageCounters[i][age];
    }
    const avgAge = totalAge / totalPlayers;

    const avgAgeLabel = document.createElement("div");
    avgAgeLabel.textContent = "AvgAge: ";
    teamContainer.appendChild(avgAgeLabel);

    const avgAgeElement = document.createElement("div");
    avgAgeElement.textContent = `${avgAge.toFixed(1)}`;
    avgAgeElement.style.marginBottom = "4px";
    teamContainer.appendChild(avgAgeElement);

    const avgPlayerValue =
      playerValues[i].reduce((a, b) => a + b, 0) / playerValues[i].length;

    const avgValueLabel = document.createElement("div");
    avgValueLabel.textContent = "AvgValue: ";
    teamContainer.appendChild(avgValueLabel);

    const avgValueElement = document.createElement("div");
    avgValueElement.textContent = `${Number(
      avgPlayerValue.toFixed(0)
    ).toLocaleString()} ${currency}`;
    avgValueElement.style.marginBottom = "4px";
    teamContainer.appendChild(avgValueElement);

    const avgPlayerSalary =
      playerSalaries[i].reduce((a, b) => a + b, 0) / playerSalaries[i].length;

    const avgSalaryLabel = document.createElement("div");
    const avgSalaryElement = document.createElement("div");
    if (avgPlayerSalary > 0) {
      avgSalaryLabel.textContent = "AvgSalary: ";
      teamContainer.appendChild(avgSalaryLabel);

      avgSalaryElement.textContent = `${Number(
        avgPlayerSalary.toFixed(0)
      ).toLocaleString()} ${currency}`;
      avgSalaryElement.style.marginBottom = "4px";
      teamContainer.appendChild(avgSalaryElement);
    }

    const rank = teamRanks[i];
    const rankElement = document.createElement("p");
    rankElement.textContent = "Rank: " + rank;
    rankElement.style.width = "100%";
    teamContainer.appendChild(rankElement);

    avgStatsContainer.appendChild(avgAgeElement);
    avgStatsContainer.appendChild(avgValueLabel);
    avgStatsContainer.appendChild(avgValueElement);
    avgStatsContainer.appendChild(avgSalaryLabel);
    avgStatsContainer.appendChild(avgSalaryElement);
    avgStatsContainer.appendChild(rankElement);

    teamContainer.appendChild(avgStatsContainer);
    container.appendChild(teamContainer);
  }
  matchFactsWrapper.appendChild(container);
}

function getLoadingMessage() {
  const message = document.createElement("div");
  message.textContent = "Loading…";
  message.style.display = "flex";
  message.style.justifyContent = "center";
  message.style.alignItems = "center";
  message.style.height = "40px";
  message.style.fontFamily = "Montserrat, sans-serif";
  message.style.fontSize = "16px";
  message.style.animation = "fadeIn 1s";
  return message;
}

const style = document.createElement("style");
style.textContent = `
    @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');
    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    .player-stats-container div,
    .player-stats-container table,
    .player-stats-container th,
    .player-stats-container td,
    .player-stats-container p {
      color: black;
      font-family: 'Montserrat', sans-serif;
      font-size: 12px;
    }
    `;
document.head.appendChild(style);