Greasy Fork

Greasy Fork is available in English.

MZ Player Values

Add a table to show squad value in squad summary tab

当前为 2023-10-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         MZ Player Values
// @namespace    http://tampermonkey.net/
// @version      0.10
// @description  Add a table to show squad value in squad summary tab
// @author       z7z
// @license      MIT
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      self
// @match        https://www.managerzone.com/?p=players&sub=alt
// @match        https://www.managerzone.com/?p=players&sub=alt&tid=*
// @match        https://www.managerzone.com/?p=federations&sub=clash*
// @match        https://www.managerzone.com/?p=federations
// @match        https://www.managerzone.com/?p=federations&fid=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// ==/UserScript==
(function () {
    "use strict";

    /* *********************** Styles ********************************** */

    GM_addStyle(`
    .donut {
        width: 1.7em;
        height: 1.7em;
        margin-left: 5px;
        border-radius: 50%;
        text-align: center;
        font-weigth = bold;
        font-size = 1.2em;
        padding: 3px;
        background-color: yellow;
        color: yellow;
    }

    .final-donut {
        border: rgb(213, 232, 44) solid 2px;
        color: inherit;
        padding:0;
    }

    .loading-donut {
        border-bottom-color: rgb(213, 232, 44);
        animation: 1.5s donut-spin infinite linear;
    }

    @keyframes donut-spin {
        to {
            transform: rotate(360deg);
        }
    }
    `);

    /* *********************** Squad Summary ********************************** */

    function formatBigNumber(n, sep = " ") {
        let numberString = n.toString();
        let formattedParts = [];
        for (let i = numberString.length - 1; i >= 0; i -= 3) {
            let part = numberString.substring(Math.max(i - 2, 0), i + 1);
            formattedParts.unshift(part);
        }
        return formattedParts.join(sep);
    }

    function createSquadTable(rows, currency) {
        const table = document.createElement("table");
        table.classList.add("tablesorter", "hitlist", "marker", "hitlist-compact-list-included");
        table.width = "30%";
        table.cellSpacing = "1px";
        table.cellPadding = "3px";
        table.border = "0";
        table.align = "center";

        const titleHeader = document.createElement("th");
        titleHeader.align = "center";
        titleHeader.classList.add("header");
        titleHeader.innerText = "Group";
        const valueHeader = document.createElement("th");
        valueHeader.align = "center";
        valueHeader.classList.add("header");
        valueHeader.innerHTML = "Values";
        const thead = document.createElement("thead");
        thead.appendChild(titleHeader);
        thead.appendChild(valueHeader);
        table.appendChild(thead);

        const tbody = document.createElement("tbody");
        for (const row of rows) {
            const tr = document.createElement("tr");
            const title = document.createElement("td");
            title.innerHTML = `${row.title}`;
            const value = document.createElement("td");
            value.innerText = `${formatBigNumber(row.value)} ${currency}`;
            value.style.textAlign = "end";
            value.width = "e";
            tr.appendChild(title);
            tr.appendChild(value);
            tbody.appendChild(tr);
        }
        table.appendChild(tbody);

        const info = document.createElement("div");
        info.appendChild(table);
        info.style = "margin: 10px 0px";
        return info;
    }

    function getCurrency(doc) {
        const playerNode = doc.getElementById("playerAltViewTable")?.querySelectorAll("tr");
        if (playerNode && playerNode.length > 1) {
            const valueText = playerNode[1].querySelector("td:nth-child(3)")?.innerText;
            const parts = valueText?.split(" ");
            return parts[parts.length - 1];
        }
        return "";
    }

    function getPlayers(doc, currency) {
        const players = [];
        const playerNodes = doc.getElementById("playerAltViewTable")?.querySelectorAll("tr");
        for (const playerNode of [...playerNodes]) {
            const age = playerNode.querySelector("td:nth-child(5)")?.innerText.replace(/\s/g, "");
            if (age) {
                const value = playerNode
                    .querySelector("td:nth-child(3)")
                    ?.innerText.replaceAll(currency, "")
                    .replace(/\s/g, "");
                const shirtNumber = playerNode.querySelector("td:nth-child(0)")?.innerText.replace(/\s/g, "");
                players.push({
                    shirtNumber,
                    age: parseInt(age, 10),
                    value: parseInt(value, 10),
                });
            }
        }
        return players;
    }

    function getTotal(players) {
        const values = players.map((player) => player.value);
        return values.reduce((a, b) => a + b, 0);
    }

    function getTopPlayers(players, count, ageLow = 0, ageHigh = 99) {
        return players
            .filter((player) => player.age <= ageHigh && player.age >= ageLow)
            .sort((a, b) => b.value - a.value)
            .slice(0, count)
            .map((player) => player.value)
            .reduce((a, b) => a + b, 0);
    }

    function createSquadSummary(doc) {
        const currency = getCurrency(doc);
        const rows = [];
        const players = getPlayers(doc, currency);
        if (players) {
            rows.push({
                title: "All",
                value: getTotal(players),
            });
            rows.push({
                title: "Top 16 - All",
                value: getTopPlayers(players, 16),
            });
            rows.push({
                title: "Top 11 - All",
                value: getTopPlayers(players, 11),
            });

            rows.push({
                title: "Top 16 - U23",
                value: getTopPlayers(players, 16, 0, 23),
            });
            rows.push({
                title: "Top 11 - U23",
                value: getTopPlayers(players, 11, 0, 23),
            });
            rows.push({
                title: "Top 16 - U21",
                value: getTopPlayers(players, 16, 0, 21),
            });
            rows.push({
                title: "Top 11 - U21",
                value: getTopPlayers(players, 11, 0, 21),
            });
            rows.push({
                title: "Top 16 - U18",
                value: getTopPlayers(players, 16, 0, 18),
            });
            rows.push({
                title: "Top 11 - U18",
                value: getTopPlayers(players, 11, 0, 18),
            });
        }
        return createSquadTable(rows, currency);
    }

    /* *********************** Clash ********************************** */

    function createModal() {
        const modalContent = document.createElement("div");
        modalContent.style.backgroundColor = "#fefefe";
        modalContent.style.margin = "15% auto";
        modalContent.style.padding = "20px";
        modalContent.style.border = "1px solid #888";
        modalContent.style.width = "15%";

        const divContent = document.createElement("div");
        divContent.id = "squad-display-modal-content";
        modalContent.appendChild(divContent);

        const modal = document.createElement("div");
        modal.style.display = "none";
        modal.style.position = "fixed";
        modal.style.zIndex = "1";
        modal.style.left = "0";
        modal.style.top = "0";
        modal.style.width = "100%";
        modal.style.height = "100%";
        modal.style.overflow = "auto";
        modal.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
        modal.id = "squad-display-modal";
        modal.appendChild(modalContent);
        modal.onclick = () => {
            modal.style.display = "none";
        };
        document.body.appendChild(modal);
    }

    function extractTeamID(link) {
        let regex = /tid=(\d+)/;
        let match = regex.exec(link);
        if (match) {
            return match[1];
        } else {
            return null;
        }
    }

    function displayOnModal(url) {
        const modal = document.getElementById("squad-display-modal");
        const divContent = document.getElementById("squad-display-modal-content");
        divContent.innerHTML = "loading...";
        modal.style.display = "block";
        GM_xmlhttpRequest({
            method: "GET",
            url,
            onload: function (resp) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(resp.responseText, "text/html");
                const content = createSquadSummary(doc);
                divContent.innerHTML = content.innerHTML;
            },
        });
    }

    function getSquadSummaryLink(url) {
        const tid = extractTeamID(url);
        return `https://www.managerzone.com/?p=players&sub=alt&tid=${tid}`;
    }

    function getTopEleven(doc) {
        const currency = getCurrency(doc);
        const rows = [];
        const players = getPlayers(doc, currency);
        return players ? getTopPlayers(players, 11) : 0;
    }

    function calculateRankOfTeams(teams) {
        const finals = [];
        for (const team of teams) {
            const url = getSquadSummaryLink(team.href);
            finals.push({
                target: team,
                url,
                values: 0,
                done: false,
            });
            GM_xmlhttpRequest({
                method: "GET",
                url,
                onload: function (resp) {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(resp.responseText, "text/html");
                    const values = getTopEleven(doc);
                    const fin = finals.find((p) => resp.finalUrl === p.url);
                    fin.values = values;
                    fin.done = true;
                },
            });
        }

        let timeout = 16000;
        const step = 1000;
        let interval = setInterval(() => {
            if (finals.every((a) => a.done)) {
                clearInterval(interval);
                finals.sort((a, b) => b.values - a.values);
                let rank = 0;
                for (const team of finals) {
                    rank++;
                    const target = team.target.parentNode.querySelector("button.donut");
                    target.classList.remove("loading-donut");
                    target.classList.add("final-donut");
                    target.innerText = `${rank}`;
                }
            } else {
                timeout -= step;
                if (timeout < 0) {
                    clearInterval(interval);
                    for (const team of finals) {
                        const target = team.target.parentNode.querySelector("button.donut");
                        target.classList.remove("loading-donut");
                        target.classList.add("final-donut");
                        target.innerText = `-`;
                    }
                }
            }
        }, step);
    }

    function addSquadButton(target) {
        const url = getSquadSummaryLink(target.href);
        const button = document.createElement("button");
        button.classList.add("donut", "final-donut");
        button.innerText = `S`;
        button.style.color = "inherit";
        target.parentNode.appendChild(button);
        button.onclick = () => {
            displayOnModal(url);
        };
    }

    function addRankView(target) {
        const url = getSquadSummaryLink(target.href);
        const rank = document.createElement("button");
        rank.innerText = "_";
        rank.classList.add("donut", "loading-donut");
        target.parentNode.appendChild(rank);
    }

    function addSquadButtonsToClashPage() {
        const teams = document.querySelectorAll("a.team-name");
        for (const team of teams) {
            addRankView(team);
            addSquadButton(team);
        }
        calculateRankOfTeams(teams);
    }

    /* *********************** Sort ********************************** */

    function fetchTopEleven(context, tid) {
        const url = `https://www.managerzone.com/?p=players&sub=alt&tid=${tid}`;
        GM_xmlhttpRequest({
            method: "GET",
            url,
            context,
            onload: function (resp) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(resp.responseText, "text/html");
                const team = resp.context.teams.find((t) => t.username === resp.context.username);
                const currency = getCurrency(doc);
                team.values = getTopEleven(doc);
                const value = document.createElement("div");
                value.style.color = "blue";
                value.style.width = "100%";
                value.style.marginTop = "3px";
                value.innerText = `Top Eleven: ${formatBigNumber(team.values, ",")} ${currency}`;
                team.node.querySelector("td").appendChild(value);
                team.done = true;
            },
        });
    }

    async function fetchTeamValue(teams, username) {
        const url = `https://www.managerzone.com/xml/manager_data.php?username=${username}`;
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            context: { teams, username },
            onload: function (resp) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(resp.responseText, "text/xml");
                const teamId = doc.querySelector('Team[sport="soccer"]').getAttribute("teamId");
                fetchTopEleven(resp.context, teamId);
            },
        });
    }

    function getUsername(node) {
        return node.querySelector("a").innerText;
    }

    function getTableHeader() {
        const thead = document.querySelector("#federation_clash_members_list thead td");
        return thead.innerText;
    }

    function setTableHeader(text) {
        const thead = document.querySelector("#federation_clash_members_list thead td");
        thead.innerText = text;
    }

    function sortByTopEleven() {
        console.log("sorting in progress");
        const tbody = document.querySelector("#federation_clash_members_list tbody");

        const teams = [];
        for (const child of tbody.children) {
            const username = getUsername(child);
            teams.push({
                node: child,
                username,
                teamId: "",
                values: 0,
                done: false,
            });
            fetchTeamValue(teams, username);
        }

        let timeout = 60000;
        const step = 1000;
        const tableHeader = getTableHeader();
        let dots = 0;
        let interval = setInterval(() => {
            if (teams.every((t) => t.done)) {
                clearInterval(interval);
                teams.sort((a, b) => b.values - a.values);
                const newOrder = teams.map((t) => t.node);
                tbody.replaceChildren(...newOrder);
                console.log("done");
                setTableHeader(tableHeader + " ▼");
            } else {
                timeout -= step;
                setTableHeader(tableHeader + " " + ".".repeat(1 + (dots % 3)));
                dots++;
                if (timeout < 0) {
                    clearInterval(interval);
                    setTableHeader(tableHeader + " (failed)");
                    console.log("timeout");
                }
            }
        }, step);
    }

    /* *********************** Inject ********************************** */

    function isFederationFrontPage(uri) {
        return uri.endsWith("/?p=federations") || uri.search("/?p=federations&fid=") > -1;
    }

    function inject() {
        if (document.baseURI.search("/?p=federations&sub=clash") > -1) {
            createModal();
            addSquadButtonsToClashPage();
        } else if (isFederationFrontPage(document.baseURI)) {
            sortByTopEleven();
        } else if (document.baseURI.search("/?p=players&sub=alt") > -1) {
            const content = createSquadSummary(document);
            const place = document.querySelector("table#playerAltViewTable");
            if (place) {
                place.parentNode?.insertBefore(content, place);
            }
        }
    }

    if (document.readyState === "loading") {
        // Loading hasn't finished yet
        document.addEventListener("DOMContentLoaded", inject);
    } else {
        // `DOMContentLoaded` has already fired
        inject();
    }
})();