MZ - Multiple League Standings in a Single View

Displays leagues and world leagues, grouped by div and/or region, in a single view

// ==UserScript==
// @name          MZ - Multiple League Standings in a Single View
// @namespace     douglaskampl
// @version       3.8
// @description   Displays leagues and world leagues, grouped by div and/or region, 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
// @grant         GM_getResourceText
// @resource      sLeagueStandingsStyles https://u18mz.vercel.app/mz/userscript/other/sLeagueStandings.css
// @run-at        document-idle
// @license       MIT
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(GM_getResourceText('sLeagueStandingsStyles'));

    const CONSTANTS = {
        REGIONS: {
            SOCCER: {
                COUNTRIES: [
                    { 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 }
                ],
                UXX_REGIONS: [
                    { 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: 'World (Asia, Oceania and Africa)', start: 1332 }
                ]
            },
            HOCKEY: {
                COUNTRIES: [
                    { 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 }
                ],
                UXX_REGIONS: [
                    { name: 'Northern Europe', start: 1 },
                    { name: 'Southern Europe', start: 122 },
                    { name: 'Rest of the World', start: 243 }
                ]
            }
        },
        LEAGUE_TYPES: {
            SENIOR: 'senior',
            U18: 'u18',
            U21: 'u21',
            U23: 'u23',
            WORLD: 'world',
            U18_WORLD: 'u18_world',
            U21_WORLD: 'u21_world',
            U23_WORLD: 'u23_world'
        },
        DIVISION: {
            NAMES: {
                TOP: 'Top Division',
                TOP_SERIES: 'Top Series'
            },
            STRUCTURE: {
                BASE_DIVISIONS: 3,
                MAX_LEVEL: 4
            }
        },
        SELECTORS: {
            TEAM_INFO: '#infoAboutTeam',
            STADIUM_WRAPPER: '#team-stadium-wrapper',
            SHORTCUT_LINK: '#shortcut_link_thezone'
        },
        TEXTS: {
            TOGGLE_UP: 'UP 上',
            TOGGLE_DOWN: 'DOWN 下',
            LOADING: 'Loading ロード中…'
        },
        BUTTON_NAMES: [
            'Senior Leagues',
            'U18 Leagues',
            'U21 Leagues',
            'U23 Leagues',
            'Senior World Leagues',
            'U18 World Leagues',
            'U21 World Leagues',
            'U23 World Leagues'
        ]
    };

    class LeagueManager {
        constructor() {
            const shortcutLink = document.querySelector(CONSTANTS.SELECTORS.SHORTCUT_LINK);
            this.sport = new URL(shortcutLink.href).searchParams.get('sport');
            const teamInfo = document.querySelector(CONSTANTS.SELECTORS.TEAM_INFO);
            this.teamId = RegExp(/\((\d+)\)/).exec(teamInfo.querySelector('dd').textContent)[1];
            this.seniorLeagues = this.getSeniorLeagues();
            this.worldLeagues = { World: this.getWorldLeaguesObj() };
            this.uxxLeagues = this.getUxxLeagues();
        }

        getSeniorLeagues() {
            const countries = this.sport === 'soccer' ? CONSTANTS.REGIONS.SOCCER.COUNTRIES : CONSTANTS.REGIONS.HOCKEY.COUNTRIES;
            return countries.reduce((acc, { name, start }) => {
                acc[name] = { [CONSTANTS.DIVISION.NAMES.TOP]: [start] };
                return acc;
            }, {});
        }

        getWorldLeaguesObj() {
            const leagues = {};
            let start = 1;
            for (let i = 0; i <= CONSTANTS.DIVISION.STRUCTURE.MAX_LEVEL; i++) {
                const divisionName = i === 0 ? CONSTANTS.DIVISION.NAMES.TOP_SERIES : `Division ${i}`;
                leagues[divisionName] = [];
                const numLeagues = Math.pow(CONSTANTS.DIVISION.STRUCTURE.BASE_DIVISIONS, i);
                for (let j = 0; j < numLeagues; j++) {
                    leagues[divisionName].push(start++);
                }
            }
            return leagues;
        }

        getUxxLeagues() {
            const regions = this.sport === 'soccer' ? CONSTANTS.REGIONS.SOCCER.UXX_REGIONS : CONSTANTS.REGIONS.HOCKEY.UXX_REGIONS;
            const obj = {};
            regions.forEach(region => {
                obj[region.name] = {
                    [CONSTANTS.DIVISION.NAMES.TOP]: [region.start],
                    'Division 1': Array.from({ length: CONSTANTS.DIVISION.STRUCTURE.BASE_DIVISIONS }, (_, i) => region.start + i + 1),
                    'Division 2': Array.from({ length: Math.pow(CONSTANTS.DIVISION.STRUCTURE.BASE_DIVISIONS, 2) }, (_, i) => region.start + i + 4)
                };
            });
            return obj;
        }

        getAllLeagues(leaguesObj) {
            const allLeagues = {};
            Object.entries(leaguesObj).forEach(([country, leagues]) => {
                Object.entries(leagues).forEach(([leagueName, ids]) => {
                    if (!allLeagues[leagueName]) allLeagues[leagueName] = [];
                    ids.forEach((id) => {
                        allLeagues[leagueName].push({ sid: id, region: country });
                    });
                });
            });
            return allLeagues;
        }

        getLeagueTypeFromButtonId(id) {
            if (id.includes('senior leagues')) return CONSTANTS.LEAGUE_TYPES.SENIOR;
            if (id.includes('u18 world leagues')) return CONSTANTS.LEAGUE_TYPES.U18_WORLD;
            if (id.includes('u21 world leagues')) return CONSTANTS.LEAGUE_TYPES.U21_WORLD;
            if (id.includes('u23 world leagues')) return CONSTANTS.LEAGUE_TYPES.U23_WORLD;
            if (id.includes('u18 leagues')) return CONSTANTS.LEAGUE_TYPES.U18;
            if (id.includes('u21 leagues')) return CONSTANTS.LEAGUE_TYPES.U21;
            if (id.includes('u23 leagues')) return CONSTANTS.LEAGUE_TYPES.U23;
            return CONSTANTS.LEAGUE_TYPES.WORLD;
        }

        getLeaguesObjFromLeagueType(leagueType, country) {
            switch (leagueType) {
                case CONSTANTS.LEAGUE_TYPES.SENIOR:
                    return country === 'All' ? this.getAllLeagues(this.seniorLeagues) : this.seniorLeagues[country];
                case CONSTANTS.LEAGUE_TYPES.WORLD:
                case CONSTANTS.LEAGUE_TYPES.U18_WORLD:
                case CONSTANTS.LEAGUE_TYPES.U21_WORLD:
                case CONSTANTS.LEAGUE_TYPES.U23_WORLD:
                    return this.worldLeagues.World;
                case CONSTANTS.LEAGUE_TYPES.U18:
                case CONSTANTS.LEAGUE_TYPES.U21:
                case CONSTANTS.LEAGUE_TYPES.U23:
                    return country === 'All' ? this.getAllLeagues(this.uxxLeagues) : this.uxxLeagues[country];
                default:
                    return {};
            }
        }

        getCountries(leagueType) {
            if (
                leagueType === CONSTANTS.LEAGUE_TYPES.WORLD ||
                leagueType === CONSTANTS.LEAGUE_TYPES.U18_WORLD ||
                leagueType === CONSTANTS.LEAGUE_TYPES.U21_WORLD ||
                leagueType === CONSTANTS.LEAGUE_TYPES.U23_WORLD
            ) {
                return ['World'];
            }
            if (leagueType === CONSTANTS.LEAGUE_TYPES.SENIOR) {
                return Object.keys(this.seniorLeagues);
            }
            return Object.keys(this.uxxLeagues);
        }
    }

    class UIManager {
        constructor(leagueManager) {
            this.leagueManager = leagueManager;
        }

        initializeInterface() {
            const mainContainer = document.createElement('div');
            mainContainer.id = 'league-buttons-container';
            const toggleBtn = this.createToggleButton();
            toggleBtn.onclick = this.toggleLeagueButtons;

            CONSTANTS.BUTTON_NAMES.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 = this.createLeaguesModal('leagues-modal', btn);
                    document.body.appendChild(leaguesModal);
                };
                mainContainer.appendChild(btn);
            });

            mainContainer.appendChild(toggleBtn);
            document.querySelector(CONSTANTS.SELECTORS.STADIUM_WRAPPER).appendChild(mainContainer);
        }

        createToggleButton() {
            const btn = document.createElement('button');
            btn.id = 'league-toggle-button';
            btn.textContent = CONSTANTS.TEXTS.TOGGLE_DOWN;
            return btn;
        }

        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 = CONSTANTS.TEXTS.TOGGLE_UP;
                } else {
                    btn.classList.remove('fade-in');
                    btn.classList.add('fade-out');
                    setTimeout(() => {
                        btn.style.display = 'none';
                    }, 200);
                    toggleBtn.textContent = CONSTANTS.TEXTS.TOGGLE_DOWN;
                }
            });
        }

        createLeaguesModal(modalId, button) {
            const leagueType = this.leagueManager.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 = this.createCountryDropdown(leagueType);
            content.appendChild(countryDropdown);

            const tabContainer = this.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;
        }

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

            if (
                leagueType !== CONSTANTS.LEAGUE_TYPES.WORLD &&
                leagueType !== CONSTANTS.LEAGUE_TYPES.U18_WORLD &&
                leagueType !== CONSTANTS.LEAGUE_TYPES.U21_WORLD &&
                leagueType !== CONSTANTS.LEAGUE_TYPES.U23_WORLD
            ) {
                const allOption = document.createElement('option');
                allOption.value = 'All';
                allOption.text = 'All';
                dropdown.appendChild(allOption);
            }

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

            dropdown.onchange = () => {
                const tablesContainer = document.getElementById('league-tables-container');
                const tabs = document.getElementById('league-tabs');
                while (tabs.firstChild) {
                    tabs.removeChild(tabs.firstChild);
                }
                while (tablesContainer.firstChild) {
                    tablesContainer.removeChild(tablesContainer.firstChild);
                }
                const leagueTypeFromTitle = this.leagueManager.getLeagueTypeFromButtonId('league-button-' + document.getElementById('leagues-modal-title').textContent.toLowerCase());
                const newTabs = this.createTabContainer(leagueTypeFromTitle, dropdown.value, tablesContainer);
                tabs.parentNode.replaceChild(newTabs, tabs);
                if (newTabs.firstChild) {
                    newTabs.firstChild.click();
                }
            };

            return dropdown;
        }

        createTabContainer(leagueType, country, tablesContainer) {
            const container = document.createElement('div');
            container.id = 'league-tabs';

            const leagues = this.leagueManager.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 = CONSTANTS.TEXTS.LOADING;
                    loadingOverlay.appendChild(spinner);
                    loadingOverlay.appendChild(loadingText);
                    modalContent.appendChild(loadingOverlay);
                    loadingOverlay.style.display = 'flex';

                    const leagueEntries = leagues[league];
                    const tables = await Promise.all(
                        leagueEntries.map((entry, index) => {
                            let sid, region;
                            if (typeof entry === 'object') {
                                sid = entry.sid;
                                region = entry.region;
                            } else {
                                sid = entry;
                                region = country;
                            }

                            let divisionCount = index + 1;
                            if (league === 'Division 1') {
                                divisionCount = (index % 3) + 1;
                            } else if (league === 'Division 2') {
                                divisionCount = (index % 9) + 1;
                            }

                            return this.fetchLeagueTable(sid, leagueType, divisionCount, league, region);
                        })
                    );

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

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

            return container;
        }

        async fetchLeagueTable(sid, leagueType, divisionCount, tabName, region) {
            try {
                const response = await fetch(
                    `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${sid}&tid=${this.leagueManager.teamId}&sport=${this.leagueManager.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;

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

                return {
                    table,
                    divisionCount,
                    region,
                    sid
                };
            } catch (error) {
                return null;
            }
        }

        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);';
                }
            });
        }

        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 = () => this.handleExtraLeagueData(sid, leagueType, tid);
            }
        }

        async handleExtraLeagueData(sid, leagueType, tid) {
            const response = await fetch(
                `https://www.managerzone.com/ajax.php?p=extraLeague&sub=division_runner_ups&type=${leagueType}&sid=${sid}&tid=${tid}&sport=${this.leagueManager.sport}`
            );
            const html = await response.text();

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

        createLeagueTitle(selectedLeague, divisionCount, leagueType, region, sid) {
            const p = document.createElement('p');
            p.classList.add('league-table-title');

            let leagueName;
            if (!selectedLeague.startsWith('Division')) {
                leagueName = selectedLeague;
            } else {
                const theDivision = selectedLeague + '.' + divisionCount;
                leagueName = theDivision.replace('Division', 'div');
            }

            let typeName = leagueType.charAt(0).toUpperCase() + leagueType.slice(1);
            typeName = typeName.replace('_', ' ');
            let finalTitle = leagueName + ' ' + typeName;

            if (region && region !== 'World') {
                finalTitle += ' ' + region;
            }

            const a = document.createElement('a');
            a.textContent = finalTitle;
            a.href = `https://www.managerzone.com/?p=league&type=${leagueType}&sid=${sid}`;
            a.target = '_blank';
            p.appendChild(a);

            return p;
        }
    }

    const infoAboutTeam = document.querySelector(CONSTANTS.SELECTORS.TEAM_INFO);
    const teamStadiumWrapper = document.querySelector(CONSTANTS.SELECTORS.STADIUM_WRAPPER);

    if (infoAboutTeam && teamStadiumWrapper) {
        const leagueManager = new LeagueManager();
        const uiManager = new UIManager(leagueManager);
        uiManager.initializeInterface();
    }
})();