MZ - Multiple League Standings in a Single View

Displays all leagues or world leagues from a country or region, grouped by div, in a single view

当前为 2024-12-16 提交的版本,查看 最新版本

// ==UserScript==
// @name          MZ - Multiple League Standings in a Single View
// @namespace     douglaskampl
// @version       3.4
// @description   Displays all leagues or world leagues from a country or region, grouped by div, in a single view
// @author        Douglas
// @match         https://www.managerzone.com/?p=team
// @icon          https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant         GM_addStyle
// @license       MIT
// ==/UserScript==

GM_addStyle(`
    #league-buttons-container {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      margin-top: 20px;
    }

    #league-toggle-button {
      margin: 4px;
      padding: 8px 16px;
      border: none;
      border-radius: 6px;
      background-color: #570987;
      color: #ffffff;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.2s ease;
    }

    #league-toggle-button:hover {
      background-color: #a020f0;
    }

    .league-button {
      margin: 4px;
      padding: 8px 16px;
      border: none;
      border-radius: 6px;
      background-color: navy;
      color: #ffffff;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.2s ease;
      opacity: 0;
      display: none;
    }

    .league-button:hover {
      background-color: #000066;
    }

    #leagues-modal {
      position: fixed;
      z-index: 1000;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.75);
      display: flex;
      justify-content: center;
      align-items: flex-start;
      overflow-y: auto;
      padding: 40px 20px;
    }

    #leagues-modal-content {
      background-color: #ffffff;
      background-image: url('https://s1.ax1x.com/2023/06/25/pCNmCSH.jpg');
      border-radius: 8px;
      box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
      padding: 24px;
      width: 90%;
      max-width: 1200px;
      position: relative;
      max-height: 85vh;
      overflow-y: auto;
    }

    #leagues-modal-title {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
      font-size: 24px;
      color: #1a1a1a;
      margin-bottom: 24px;
      text-align: center;
    }

    #leagues-modal-close-button {
      position: absolute;
      top: 16px;
      right: 16px;
      background: none;
      border: none;
      font-size: 24px;
      cursor: pointer;
      color: #666666;
      padding: 4px 8px;
      transition: color 0.2s ease;
    }

    #leagues-modal-close-button:hover {
      color: #1a1a1a;
    }

    #country-dropdown {
      width: 200px;
      padding: 8px;
      margin-bottom: 16px;
      border: 1px solid #cccccc;
      border-radius: 4px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    }

    #leagues-tabs {
      display: flex;
      gap: 8px;
      margin-bottom: 24px;
      flex-wrap: wrap;
    }

    #leagues-tabs button {
      padding: 8px 16px;
      border: solid black 1px;
      border-radius: 4px;
      background-color: #f0f0f0;
      color: #1a1a1a;
      cursor: pointer;
      transition: all 0.2s ease;
    }

    #leagues-tabs button:hover {
      background-color: #e0e0e0;
    }

    #league-tables-container {
      width: 100%;
    }

    #league-tables-container p {
      font-size: 16px;
      margin: 20px 0;
      font-style: italic;
      color: navy;
      text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
    }

    #league-tables-container .nice_table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 24px;
      background-color: #ffffff;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    }

    #league-tables-container .nice_table th {
      background-color: #f5f5f5;
      padding: 12px;
      text-align: left;
      border-bottom: 2px solid #e0e0e0;
    }

    #league-tables-container .nice_table td {
      padding: 12px;
      border-bottom: 1px solid #e0e0e0;
    }

    #league-tables-container .nice_table tr:hover {
      background-color: #f8f8f8;
    }

    #league-tables-container .nice_table .seriesHeader th {
      color: black;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(-10px); }
      to { opacity: 1; transform: translateY(0); }
    }

    @keyframes fadeOut {
      from { opacity: 1; transform: translateY(0); }
      to { opacity: 0; transform: translateY(10px); }
    }

    .fade-in {
      animation: fadeIn 0.2s ease forwards;
    }

    .fade-out {
      animation: fadeOut 0.2s ease forwards;
    }

     .loader {
      width: 10px;
      height: 10px;
      border: 3px solid #f3f3f3;
      border-top: 3px solid navy;
      border-radius: 50%;
      display: inline-block;
      box-sizing: border-box;
      animation: rotation 1s linear infinite;
      margin-bottom: 10px;
    }

    @keyframes rotation {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }

    .loading-overlay {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(255, 255, 255, 0.9);
      display: none;
      z-index: 10;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }

    .loading-text {
      color: navy;
      font-size: 14px;
      margin-top: 10px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    }
  `);

(function () {
    const TOGGLE_TEXT_UP = "UP 上";
    const TOGGLE_TEXT_DOWN = "DOWN 下";

    const infoAboutTeam = document.getElementById("infoAboutTeam");
    const teamStadiumWrapper = document.getElementById("team-stadium-wrapper");

    const sport = new URL(document.querySelector("#shortcut_link_thezone").href).searchParams.get("sport");
    const teamId = RegExp(/\((\d+)\)/).exec(infoAboutTeam.querySelector("dd").textContent)[1];

    const seniorLeagues = getSeniorLeagues();
    const worldLeagues = { World: getWorldLeaguesObj() };
    const uxxLeagues = getUxxLeagues();

    if (infoAboutTeam && teamStadiumWrapper) {
        initializeInterface();
    }

    function initializeInterface() {
        const mainContainer = document.createElement("div");
        mainContainer.id = "league-buttons-container";

        const toggleBtn = createToggleButton();
        toggleBtn.onclick = toggleLeagueButtons;

        const buttonNames = [
            "Senior Leagues",
            "World Leagues",
            "U18 World Leagues",
            "U21 World Leagues",
            "U23 World Leagues",
            "U18 Leagues",
            "U21 Leagues",
            "U23 Leagues"
        ];

        buttonNames.forEach(name => {
            const btn = document.createElement("button");
            btn.className = "league-button";
            btn.id = `league-button-${name.toLowerCase()}`;
            btn.textContent = name;
            btn.style.display = "none";
            btn.onclick = () => {
                const leaguesModal = createLeaguesModal("leagues-modal", btn);
                document.body.appendChild(leaguesModal);
            };
            mainContainer.appendChild(btn);
        });

        mainContainer.appendChild(toggleBtn);
        teamStadiumWrapper.appendChild(mainContainer);
    }

    function createToggleButton() {
        const btn = document.createElement("button");
        btn.id = "league-toggle-button";
        btn.textContent = TOGGLE_TEXT_DOWN;
        return btn;
    }

    function toggleLeagueButtons() {
        const container = document.getElementById("league-buttons-container");
        const buttons = container.querySelectorAll(".league-button");
        const toggleBtn = document.getElementById("league-toggle-button");

        buttons.forEach(btn => {
            if (btn.style.display === "none") {
                btn.style.display = "block";
                btn.classList.remove("fade-out");
                btn.classList.add("fade-in");
                toggleBtn.textContent = TOGGLE_TEXT_UP;
            } else {
                btn.classList.remove("fade-in");
                btn.classList.add("fade-out");
                setTimeout(() => btn.style.display = "none", 200);
                toggleBtn.textContent = TOGGLE_TEXT_DOWN;
            }
        });
    }

    function createLeaguesModal(modalId, button) {
        const leagueType = getLeagueTypeFromButtonId(button.id);
        const modal = document.createElement("div");
        modal.id = modalId;

        const content = document.createElement("div");
        content.id = "leagues-modal-content";

        const title = document.createElement("h2");
        title.id = "leagues-modal-title";
        title.textContent = button.textContent;
        content.appendChild(title);

        const closeButton = document.createElement("button");
        closeButton.id = "leagues-modal-close-button";
        closeButton.textContent = "×";
        closeButton.onclick = () => modal.remove();
        content.appendChild(closeButton);

        const tablesContainer = document.createElement("div");
        tablesContainer.id = "league-tables-container";

        const countryDropdown = createCountryDropdown(leagueType);
        content.appendChild(countryDropdown);

        const tabContainer = createTabContainer(leagueType, countryDropdown.value, tablesContainer);
        content.appendChild(tabContainer);
        content.appendChild(tablesContainer);

        modal.appendChild(content);

        modal.onclick = (e) => { if (e.target === modal) { modal.remove(); } };

        return modal;
    }

    function createCountryDropdown(leagueType) {
        const dropdown = document.createElement("select");
        dropdown.id = "country-dropdown";

        if (!["world", "u18_world", "u21_world", "u23_world"].includes(leagueType)) {
            const allOption = document.createElement("option");
            allOption.value = "All";
            allOption.text = "All";
            dropdown.appendChild(allOption);
        }

        const countries = getCountries(leagueType);
        countries.forEach(country => {
            const option = document.createElement("option");
            option.value = country;
            option.text = country;
            dropdown.appendChild(option);
        });

        dropdown.onchange = function () {
            const tablesContainer = document.getElementById("league-tables-container");
            const tabs = document.getElementById("leagues-tabs");

            while (tabs.firstChild) tabs.removeChild(tabs.firstChild);
            while (tablesContainer.firstChild) tablesContainer.removeChild(tablesContainer.firstChild);

            const newTabs = createTabContainer(leagueType, this.value, tablesContainer);
            tabs.parentNode.replaceChild(newTabs, tabs);

            if (newTabs.firstChild) { newTabs.firstChild.click(); }
        };

        return dropdown;
    }

    function createTabContainer(leagueType, country, tablesContainer) {
        const container = document.createElement("div");
        container.id = "leagues-tabs";
        const leagues = getLeaguesObjFromLeagueType(leagueType, country);

        Object.keys(leagues).forEach(league => {
            const tab = document.createElement("button");
            tab.textContent = league;

            tab.onclick = async () => {
                while (tablesContainer.firstChild) {
                    tablesContainer.removeChild(tablesContainer.firstChild);
                }

                const modalContent = document.getElementById("leagues-modal-content");

                const loadingOverlay = document.createElement('div');
                loadingOverlay.className = 'loading-overlay';

                const spinner = document.createElement('div');
                spinner.className = 'loader';

                const loadingText = document.createElement('div');
                loadingText.className = 'loading-text';
                loadingText.textContent = 'Loading ロード中…';

                loadingOverlay.appendChild(spinner);
                loadingOverlay.appendChild(loadingText);
                modalContent.appendChild(loadingOverlay);
                loadingOverlay.style.display = 'flex';

                const leagueIds = leagues[league];
                const tables = await Promise.all(leagueIds.map((sid, index) =>
                    fetchLeagueTable(sid, leagueType, index + 1, country)
                ));

                tables.sort((a, b) => a.divisionCount - b.divisionCount);

                tables.forEach(data => {
                    if (data) {
                        const hr = document.createElement("hr");
                        const title = createLeagueTitle(league, data.divisionCount, leagueType, country, league);
                        tablesContainer.appendChild(hr);
                        tablesContainer.appendChild(title);
                        tablesContainer.appendChild(data.table);
                    }
                });

                loadingOverlay.style.display = 'none';
                loadingOverlay.remove();
            };

            container.appendChild(tab);
        });

        return container;
    }

    async function fetchLeagueTable(sid, leagueType, divisionCount, country) {
        try {
            const response = await fetch(
                `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${sid}&tid=${teamId}&sport=${sport}&sub=table`
            );
            const html = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, "text/html");

            const table = doc.querySelector(".nice_table");
            if (!table) return null;

            setUpTableLinks(table);
            setUpHelpButton(table, sid, leagueType);

            return {
                table,
                divisionCount,
                country
            };
        } catch (error) {
            console.error("Error fetching league table:", error);
            return null;
        }
    }

    function setUpTableLinks(table) {
        const rows = table.querySelectorAll('tbody tr');
        rows.forEach(row => {
            const link = row.querySelector('a[href^="/?p=league&type="]');
            if (link) {
                const tid = link.href.match(/tid=(\d+)/);
                if (tid) {
                    link.href = `/?p=team&tid=${tid[1]}`;
                }
            }

            const helpButton = row.querySelector('.help_button');
            if (helpButton) {
                helpButton.style.pointerEvents = 'none';
                helpButton.style.opacity = '0.5';
                helpButton.style.cursor = 'default';
                helpButton.onclick = (e) => {
                    e.preventDefault();
                    return false;
                };
                helpButton.href = 'javascript:void(0);';
            }
        });
    }

    function setUpHelpButton(table, sid, leagueType) {
        const secondRow = table.querySelector("tbody tr:nth-child(2)");
        if (!secondRow) return;

        const helpButton = secondRow.querySelector(".help_button");
        const teamLink = secondRow.querySelector("a[onclick*='purchaseChallenge']");

        if (helpButton && teamLink) {
            const tid = teamLink.getAttribute("onclick").split(",")[2].replace(/[ ';)]/g, "");
            helpButton.removeAttribute("onclick");
            helpButton.onclick = () => handleExtraLeagueData(sid, leagueType, tid);
        }
    }

    function handleExtraLeagueData(sid, leagueType, tid) {
        fetch(
            `https://www.managerzone.com/ajax.php?p=extraLeague&sub=division_runner_ups&type=${leagueType}&sid=${sid}&tid=${tid}&sport=${sport}`
        )
            .then(response => response.text())
            .then(html => {
                const modal = document.createElement("div");
                modal.id = "extra-league-data-modal";
                modal.className = "leagues-modal";

                const content = document.createElement("div");
                content.className = "leagues-modal-content";
                content.innerHTML = html;
                modal.appendChild(content);

                document.body.appendChild(modal);

                modal.onclick = (event) => {
                    if (event.target === modal) modal.remove();
                };
            });
    }

    function createLeagueTitle(selectedLeague, divisionCount, type, country, tabName) {
        const p = document.createElement("p");

        if (!selectedLeague.startsWith("Division")) {
            p.textContent = selectedLeague;
        } else {
            const theDivision = selectedLeague + "." + divisionCount;
            p.textContent = theDivision.replace("Division", "div");
        }

        p.textContent += " " + type.charAt(0).toUpperCase() + type.slice(1);

        if ((type === "senior" || type === "u18" || type === "u21" || type === "u23") && country !== "All") {
            p.textContent += " " + country;
        }

        if (p.textContent.includes("All")) {
            p.textContent = p.textContent.replace("All", "");
        }

        if (p.textContent.includes("_")) {
            p.textContent = p.textContent.replace("_", " ");
        }

        if (type === "senior" && country === "All" && tabName === "Top Division") {
            p.textContent += " " + Object.keys(seniorLeagues)[divisionCount - 1];
        }

        const tabNameEndsWithNumber = /\d$/.test(tabName);
        if (!type.includes("world") && tabNameEndsWithNumber) {
            const tabNameNumber = parseInt(tabName.slice(-1));
            const match = RegExp(/\.\d+/).exec(p.textContent);
            if (match) {
                const number = parseInt(match[0].slice(1));
                const divisor = Math.pow(3, tabNameNumber);
                if (number > divisor) {
                    const replacement = number % divisor || divisor;
                    p.textContent = p.textContent.replace(/\.\d+/, `.${replacement}`);
                }
            }
        }

        return p;
    }

    function getLeagueTypeFromButtonId(id) {
        if (id.includes("senior leagues")) return "senior";
        if (id.includes("u18 world leagues")) return "u18_world";
        if (id.includes("u21 world leagues")) return "u21_world";
        if (id.includes("u23 world leagues")) return "u23_world";
        if (id.includes("u18 leagues")) return "u18";
        if (id.includes("u21 leagues")) return "u21";
        if (id.includes("u23 leagues")) return "u23";
        return "world";
    }

    function getLeaguesObjFromLeagueType(leagueType, country) {
        switch (leagueType) {
            case "senior":
                return country === "All" ? getAllLeagues(seniorLeagues) : seniorLeagues[country];
            case "world":
            case "u18_world":
            case "u21_world":
            case "u23_world":
                return worldLeagues.World;
            case "u18":
            case "u21":
            case "u23":
                return country === "All" ? getAllLeagues(uxxLeagues) : uxxLeagues[country];
            default:
                return {};
        }
    }

    function getAllLeagues(leaguesObj) {
        const allLeagues = {};
        Object.entries(leaguesObj).forEach(([country, leagues]) => {
            Object.entries(leagues).forEach(([league, ids]) => {
                if (!allLeagues[league]) allLeagues[league] = [];
                allLeagues[league].push(...ids);
            });
        });
        return allLeagues;
    }

    function getCountries(leagueType) {
        if (["world", "u18_world", "u21_world", "u23_world"].includes(leagueType)) {
            return ["World"];
        }
        return Object.keys(leagueType === "senior" ? seniorLeagues : uxxLeagues);
    }

    function getSeniorLeagues() {
        const soccerCountries = [
            { name: "Argentina", start: 16096 },
            { name: "Brazil", start: 26187 },
            { name: "China", start: 70847 },
            { name: "Germany", start: 12086 },
            { name: "Italy", start: 10625 },
            { name: "Netherlands", start: 15004 },
            { name: "Portugal", start: 17566 },
            { name: "Spain", start: 10746 },
            { name: "Poland", start: 13181 },
            { name: "Romania", start: 17929 },
            { name: "Sweden", start: 43 },
            { name: "Turkey", start: 20356 }
        ];

        const hockeyCountries = [
            { name: "Brazil", start: 7900 },
            { name: "Sweden", start: 1 },
            { name: "Argentina", start: 2 },
            { name: "Poland", start: 23 },
            { name: "Portugal", start: 24 },
            { name: "Spain", start: 12 },
            { name: "Romania", start: 25 },
            { name: "Turkey", start: 27 },
            { name: "China", start: 13727 }
        ];

        const countries = sport === "soccer" ? soccerCountries : hockeyCountries;
        return countries.reduce((acc, { name, start }) => {
            acc[name] = { "Top Division": [start] };
            return acc;
        }, {});
    }

    function getWorldLeaguesObj() {
        const leagues = {};
        let start = 1;

        for (let i = 0; i <= 4; ++i) {
            const divisionName = i === 0 ? "Top Series" : `Division ${i}`;
            leagues[divisionName] = [];
            const numLeagues = Math.pow(3, i);

            for (let j = 0; j < numLeagues; ++j) {
                leagues[divisionName].push(start++);
            }
        }

        return leagues;
    }

    function getUxxLeagues() {
        const soccerRegions = [
            { name: "Argentina", start: 1 },
            { name: "Brazil", start: 122 },
            { name: "Latin America, USA and Canada", start: 727 },
            { name: "Central Europe", start: 848 },
            { name: "Iberia", start: 969 },
            { name: "Mediterranean", start: 1090 },
            { name: "Northern Europe", start: 1211 },
            { name: "Poland", start: 243 },
            { name: "Romania", start: 364 },
            { name: "Sweden", start: 485 },
            { name: "Turkey", start: 606 },
            { name: "China/Asia/Africa", start: 1332 }
        ];

        const hockeyRegions = [
            { name: "Northern Europe", start: 1 },
            { name: "Southern Europe", start: 122 },
            { name: "Rest of the World", start: 243 }
        ];

        const regions = sport === "soccer" ? soccerRegions : hockeyRegions;

        return regions.reduce((acc, region) => {
            acc[region.name] = {
                "Top Division": [region.start],
                "Division 1": Array.from({ length: 3 }, (_, i) => region.start + i + 1),
                "Division 2": Array.from({ length: 9 }, (_, i) => region.start + i + 4)
            };
            return acc;
        }, {});
    }
})();