MZ - Transfer Market Monitor for Small Countries

Monitors the transfer market for players from a specific country

目前為 2025-04-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name          MZ - Transfer Market Monitor for Small Countries
// @namespace     douglaskampl
// @version       1.6
// @description   Monitors the transfer market for players from a specific country
// @author        Douglas
// @match         https://www.managerzone.com/*
// @icon          https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant         GM_setValue
// @grant         GM_getValue
// @grant         GM_addStyle
// @run-at        document-idle
// @license       MIT
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(`
        :root {
            --dk-bg-dark: #120c1e;
            --dk-bg-medium: #23193b;
            --dk-bg-light: #3e2c6b;
            --dk-text-primary: #e8e0ff;
            --dk-text-secondary: #a094c0;
            --dk-accent-primary: #00f6ff;
            --dk-accent-secondary: #ff00ff;
            --dk-border-color: #5a4888;
            --dk-font: 'Segoe UI', 'Roboto', sans-serif;
            --dk-glow-primary: rgba(0, 246, 255, 0.7);
            --dk-glow-secondary: rgba(255, 0, 255, 0.6);
        }

        #country-monitor-wrapper {
            display: inline-flex;
            align-items: center;
            margin-left: 10px;
            position: relative;
            font-family: var(--dk-font);
            background-color: var(--dk-bg-medium);
            padding: 4px 8px;
            border-radius: 12px;
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3), inset 0 0 5px rgba(0,0,0,0.2);
            border: 1px solid var(--dk-border-color);
            vertical-align: middle;
        }
        #country-monitor-flag {
            width: 20px;
            height: 15px;
            object-fit: cover;
            vertical-align: middle;
            border: 1px solid var(--dk-border-color);
            border-radius: 3px;
            transition: transform 0.2s ease, box-shadow 0.2s ease;
            cursor: pointer;
            box-shadow: 0 0 3px rgba(0,0,0,0.4);
            flex-shrink: 0;
        }
        #country-monitor-flag:hover {
            transform: scale(1.15);
            box-shadow: 0 0 10px var(--dk-glow-primary);
        }
        #country-monitor-name {
            margin-left: 6px;
            font-size: 12px;
            color: var(--dk-text-primary);
            font-weight: 500;
            text-shadow: 0 0 3px rgba(0,0,0,0.5);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 150px;
            flex-shrink: 1;
        }
        #rare-finder-btn {
            margin-left: 8px;
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: linear-gradient(145deg, var(--dk-accent-primary), #00c4cc);
            color: var(--dk-bg-dark);
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.2s ease;
            font-size: 14px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.1);
            text-shadow: 0 0 5px var(--dk-glow-primary);
            flex-shrink: 0;
        }
        #rare-finder-btn:hover {
            background: linear-gradient(145deg, #33ffff, var(--dk-accent-primary));
            transform: scale(1.1) rotate(-5deg);
            box-shadow: 0 0 12px var(--dk-glow-primary), 0 2px 5px rgba(0,0,0,0.5);
        }
        #rare-finder-btn.scanning {
             opacity: 0.6;
             cursor: default;
             animation: pulse 1.5s infinite ease-in-out;
        }
         @keyframes pulse {
            0% { transform: scale(1); box-shadow: 0 0 5px var(--dk-glow-primary);}
            50% { transform: scale(1.05); box-shadow: 0 0 15px var(--dk-glow-primary); }
            100% { transform: scale(1); box-shadow: 0 0 5px var(--dk-glow-primary);}
        }

        #search-status {
            margin-left: 8px;
            font-size: 11px;
            color: var(--dk-text-secondary);
            font-style: italic;
            white-space: nowrap;
            cursor: default;
            flex-shrink: 0;
        }
         #search-status.has-results {
             cursor: pointer;
             color: var(--dk-accent-primary);
             text-decoration: underline;
             text-decoration-style: dotted;
         }
          #search-status.has-results:hover {
              color: #64ffff;
              text-shadow: 0 0 5px var(--dk-glow-primary);
          }

        #country-selector, #rare-scan-results-dropdown {
            position: absolute;
            top: calc(100% + 8px);
            right: 0;
            z-index: 10000;
            background: var(--dk-bg-dark);
            color: var(--dk-text-primary);
            border: 1px solid var(--dk-accent-primary);
            border-radius: 8px;
            box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5), 0 0 15px var(--dk-glow-primary) inset;
            width: 300px;
            max-height: 400px;
            overflow-y: auto;
            padding: 12px;
            display: none;
            opacity: 0;
            transform: translateY(-10px) scale(0.98);
            transition: opacity 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            font-family: var(--dk-font);
        }
        #country-selector.visible, #rare-scan-results-dropdown.visible {
            display: block;
            opacity: 1;
            transform: translateY(0) scale(1);
        }
        #country-selector::-webkit-scrollbar, #rare-scan-results-dropdown::-webkit-scrollbar {
            width: 6px;
        }
        #country-selector::-webkit-scrollbar-track, #rare-scan-results-dropdown::-webkit-scrollbar-track {
            background: var(--dk-bg-medium);
            border-radius: 3px;
        }
        #country-selector::-webkit-scrollbar-thumb, #rare-scan-results-dropdown::-webkit-scrollbar-thumb {
            background: var(--dk-accent-primary);
            border-radius: 3px;
            border: 1px solid var(--dk-bg-medium);
        }
        #country-selector::-webkit-scrollbar-thumb:hover, #rare-scan-results-dropdown::-webkit-scrollbar-thumb:hover {
            background: #64ffff;
            box-shadow: 0 0 5px var(--dk-glow-primary);
        }
        #country-search {
            width: 100%;
            padding: 8px 10px;
            margin-bottom: 10px;
            border: 1px solid var(--dk-border-color);
            background-color: var(--dk-bg-medium);
            color: var(--dk-text-primary);
            border-radius: 5px;
            font-size: 13px;
            box-sizing: border-box;
            transition: border-color 0.2s ease, box-shadow 0.2s ease;
        }
        #country-search::placeholder {
            color: var(--dk-text-secondary);
            opacity: 0.7;
        }
        #country-search:focus {
            outline: none;
            border-color: var(--dk-accent-primary);
            box-shadow: 0 0 10px var(--dk-glow-primary);
            background-color: var(--dk-bg-light);
        }
        .country-option, .rare-result-option {
            display: flex;
            align-items: center;
            padding: 6px 10px;
            cursor: pointer;
            border-radius: 5px;
            color: var(--dk-text-primary);
            margin-bottom: 3px;
            transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
            border: 1px solid transparent;
        }
        .country-option:hover, .rare-result-option:hover {
            background: var(--dk-bg-light);
            transform: translateX(3px);
            border-color: var(--dk-border-color);
        }
        .country-option img, .rare-result-option img {
            width: 20px;
            height: 14px;
            margin-right: 10px;
            object-fit: cover;
            border: 1px solid var(--dk-border-color);
            border-radius: 3px;
             flex-shrink: 0;
        }
         .country-option span, .rare-result-option .rare-name {
            font-size: 13px;
            flex-grow: 1;
             overflow: hidden;
             text-overflow: ellipsis;
             white-space: nowrap;
         }
         .rare-result-option .rare-count {
             font-size: 11px;
             font-weight: bold;
             color: var(--dk-accent-secondary);
             margin-left: 8px;
             text-shadow: 0 0 4px var(--dk-glow-secondary);
             flex-shrink: 0;
         }
        .country-option.selected {
            background: linear-gradient(90deg, var(--dk-accent-primary), var(--dk-accent-secondary));
            color: var(--dk-bg-dark);
            font-weight: 600;
            border-color: transparent;
            box-shadow: 0 0 8px var(--dk-glow-secondary);
        }
         .country-option.selected:hover {
             background: linear-gradient(90deg, #33ffff, #ff33ff);
             transform: translateX(0);
         }

        #dk-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(18, 12, 30, 0.85);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 10001;
            opacity: 0;
            transition: opacity 0.3s ease;
            backdrop-filter: blur(5px) saturate(150%);
        }
        #dk-modal-overlay.visible {
            opacity: 1;
        }
        #dk-modal-content {
            background: var(--dk-bg-dark);
            color: var(--dk-text-primary);
            padding: 20px 25px;
            border-radius: 10px;
            width: 90%;
            max-width: 420px;
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6), 0 0 20px var(--dk-glow-primary);
            border: 1px solid var(--dk-accent-primary);
            font-family: var(--dk-font);
            transform: scale(0.9) translateY(20px);
            transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.3s ease;
            opacity: 0;
        }
         #dk-modal-overlay.visible #dk-modal-content {
             transform: scale(1) translateY(0);
             opacity: 1;
         }
        #dk-modal-title {
            font-size: 20px;
            font-weight: 600;
            margin-bottom: 12px;
            color: var(--dk-accent-primary);
            border-bottom: 1px solid var(--dk-border-color);
            padding-bottom: 8px;
            text-shadow: 0 0 8px var(--dk-glow-primary);
        }
        #dk-modal-body {
            font-size: 14px;
            line-height: 1.5;
            margin-bottom: 20px;
            max-height: 55vh;
            overflow-y: auto;
            color: var(--dk-text-secondary);
            padding-right: 5px;
        }
         #dk-modal-body p {
             margin: 0 0 8px 0;
              color: var(--dk-text-primary);
         }
         #dk-modal-body strong {
             color: var(--dk-accent-secondary);
             font-weight: 600;
         }
        #dk-modal-body::-webkit-scrollbar {
            width: 5px;
        }
        #dk-modal-body::-webkit-scrollbar-track {
            background: var(--dk-bg-medium);
            border-radius: 2px;
        }
        #dk-modal-body::-webkit-scrollbar-thumb {
            background: var(--dk-accent-secondary);
            border-radius: 2px;
        }
         #dk-modal-body::-webkit-scrollbar-thumb:hover {
             background: #ff33ff;
             box-shadow: 0 0 5px var(--dk-glow-secondary);
         }
        #dk-modal-buttons {
            display: flex;
            justify-content: flex-end;
            gap: 8px;
        }
        .dk-modal-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 5px;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
            text-transform: uppercase;
            letter-spacing: 0.5px;
             box-shadow: 0 1px 3px rgba(0,0,0,0.3);
        }
        .dk-modal-btn:active {
            transform: scale(0.96);
             box-shadow: 0 0 1px rgba(0,0,0,0.2);
        }
        .dk-modal-btn-confirm {
            background: linear-gradient(145deg, var(--dk-accent-primary), #00c4cc);
            color: var(--dk-bg-dark);
             text-shadow: 0 0 3px var(--dk-glow-primary);
        }
        .dk-modal-btn-confirm:hover {
            background: linear-gradient(145deg, #33ffff, var(--dk-accent-primary));
            box-shadow: 0 0 12px var(--dk-glow-primary), 0 2px 4px rgba(0,0,0,0.4);
        }
        .dk-modal-btn-cancel {
            background-color: var(--dk-bg-light);
            color: var(--dk-text-primary);
        }
        .dk-modal-btn-cancel:hover {
            background-color: var(--dk-border-color);
            color: #fff;
             box-shadow: 0 0 5px rgba(160, 148, 192, 0.5);
        }
    `);

    const CONFIG = {
        CHECK_INTERVAL_HOURS: 6,
        DEFAULT_COUNTRY_NAME: 'Select Country',
        DEFAULT_COUNTRY_CID: null,
        COUNTRIES_JSON_URL: 'https://pub-02de1c06eac643f992bb26daeae5c7a0.r2.dev/json/countries.json',
        MAX_RARE_PLAYERS: 5
    };

    let countries = [];
    let selectedCountry = {
        cid: GM_getValue('selectedCountryCid', CONFIG.DEFAULT_COUNTRY_CID),
        name: GM_getValue('selectedCountryName', CONFIG.DEFAULT_COUNTRY_NAME),
        code: GM_getValue('selectedCountryCode', '')
    };

    let knownPlayers = GM_getValue('knownPlayers', {});
    let lastChecked = GM_getValue('lastChecked', 0);
    let isScanning = false;
    let modalConfirmCallback = null;
    let modalCancelCallback = null;
    let rareCountriesCache = [];

    function createModalElements() {
        if (document.getElementById('dk-modal-overlay')) return;

        const overlay = document.createElement('div');
        overlay.id = 'dk-modal-overlay';
        const content = document.createElement('div');
        content.id = 'dk-modal-content';
        const title = document.createElement('div');
        title.id = 'dk-modal-title';
        const body = document.createElement('div');
        body.id = 'dk-modal-body';
        const buttons = document.createElement('div');
        buttons.id = 'dk-modal-buttons';
        const confirmBtn = document.createElement('button');
        confirmBtn.id = 'dk-modal-btn-confirm';
        confirmBtn.className = 'dk-modal-btn dk-modal-btn-confirm';
        confirmBtn.textContent = 'Confirm';
        const cancelBtn = document.createElement('button');
        cancelBtn.id = 'dk-modal-btn-cancel';
        cancelBtn.className = 'dk-modal-btn dk-modal-btn-cancel';
        cancelBtn.textContent = 'Cancel';

        buttons.appendChild(cancelBtn);
        buttons.appendChild(confirmBtn);
        content.appendChild(title);
        content.appendChild(body);
        content.appendChild(buttons);
        overlay.appendChild(content);
        document.body.appendChild(overlay);

        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                hideModal();
                if (modalCancelCallback) modalCancelCallback();
            }
        });
        confirmBtn.addEventListener('click', () => {
            hideModal();
            if (modalConfirmCallback) modalConfirmCallback();
        });
        cancelBtn.addEventListener('click', () => {
            hideModal();
            if (modalCancelCallback) modalCancelCallback();
        });
    }

    function showModal(title, html, confirmText = 'OK', cancelText = 'Close', onConfirm = null, onCancel = null) {
        createModalElements();
        const overlay = document.getElementById('dk-modal-overlay');
        document.getElementById('dk-modal-title').textContent = title;
        document.getElementById('dk-modal-body').innerHTML = html;
        document.getElementById('dk-modal-btn-confirm').textContent = confirmText;
        document.getElementById('dk-modal-btn-cancel').textContent = cancelText;
        document.getElementById('dk-modal-btn-confirm').style.display = onConfirm ? 'inline-block' : 'none';
        document.getElementById('dk-modal-btn-cancel').style.display = cancelText ? 'inline-block' : 'none';
        modalConfirmCallback = onConfirm;
        modalCancelCallback = onCancel;

        overlay.style.display = 'flex';
        requestAnimationFrame(() => {
             overlay.classList.add('visible');
        });
    }

    function hideModal() {
        const overlay = document.getElementById('dk-modal-overlay');
        if (overlay) {
             overlay.classList.remove('visible');
             setTimeout(() => {
                 if (overlay && !overlay.classList.contains('visible')) {
                     overlay.style.display = 'none';
                 }
            }, 300);
        }
    }

    function getFlagUrl(code) {
        const upperCode = (code || '').toUpperCase();
        const bgColor = '23193b';
        const textColor = 'e8e0ff';
        const accentColor = '3e2c6b';

        if (!code) return `https://placehold.co/20x15/${bgColor}/${textColor}/png?text=?`;
        if (upperCode === 'SC') return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-sct.svg';
        if (upperCode === 'WL') return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-wls.svg';
        if (upperCode === 'NI') return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-nir.svg';
        if (upperCode === 'EN') return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-eng.svg';
        if (upperCode === 'DC') return `https://placehold.co/20x15/${accentColor}/${textColor}/png?text=MZ`;
        return `https://flagcdn.com/w40/${code.toLowerCase()}.png`;
    }

    function cleanupKnownPlayers() {
        const now = Date.now();
        const threeDaysInMs = 3 * 24 * 60 * 60 * 1000;
        let modified = false;
        Object.keys(knownPlayers).forEach(playerId => {
            if (!knownPlayers[playerId] || typeof knownPlayers[playerId].firstSeen !== 'number') {
                 delete knownPlayers[playerId];
                 modified = true;
                 return;
            }
            if (now - knownPlayers[playerId].firstSeen > threeDaysInMs) {
                delete knownPlayers[playerId];
                modified = true;
            }
        });
        if (modified) {
            GM_setValue('knownPlayers', knownPlayers);
        }
    }

    function fetchCountries() {
        fetch(CONFIG.COUNTRIES_JSON_URL)
            .then(response => {
                 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                 return response.json();
             })
            .then(data => {
                if (!Array.isArray(data)) throw new Error('Fetched data is not an array');
                countries = data;
                countries.sort((a, b) => a.name.localeCompare(b.name));
                buildUI();
            })
            .catch(error => {
                console.error('[CTM_ERROR] Failed to fetch or process countries:', error);
                 showModal('Error', `<p>Failed to load country list.</p><p><i>${error.message}</i></p>`, null, 'Close');
             });
    }

    function buildUI() {
        if (!isTransferPage() || document.getElementById('country-monitor-wrapper')) return;

        const searchForm = document.getElementById('transferSearchForm') || document.querySelector('.panel.panel-dark.search') || document.querySelector('#tds')?.closest('.transfertabs') || document.body;
        const insertTarget = searchForm.querySelector('.flex.justify-end') || searchForm.querySelector('#tds')?.parentElement || searchForm.querySelector('.panel-body > div:first-child') || searchForm.querySelector('.panel-body') || searchForm;

        if (!insertTarget) {
             console.error('[CTM_ERROR] Could not find UI insertion point.');
             return;
         }

        const monitorWrapper = document.createElement('div');
        monitorWrapper.id = 'country-monitor-wrapper';

        const flagImg = document.createElement('img');
        flagImg.id = 'country-monitor-flag';
        flagImg.src = getFlagUrl(selectedCountry.code);
        flagImg.alt = selectedCountry.name;
        flagImg.title = selectedCountry.cid ? `Monitoring: ${selectedCountry.name}` : "Select country to monitor";
        flagImg.onerror = () => { flagImg.src = getFlagUrl(null); };

        const countryName = document.createElement('span');
        countryName.id = 'country-monitor-name';
        countryName.textContent = selectedCountry.name;

        const rareFinderBtn = document.createElement('div');
        rareFinderBtn.id = 'rare-finder-btn';
        rareFinderBtn.innerHTML = '💎';
        rareFinderBtn.title = `Find countries with < ${CONFIG.MAX_RARE_PLAYERS} 19yo players`;

        const searchStatus = document.createElement('span');
        searchStatus.id = 'search-status';
        searchStatus.style.display = 'none';

        monitorWrapper.appendChild(flagImg);
        monitorWrapper.appendChild(countryName);
        monitorWrapper.appendChild(rareFinderBtn);
        monitorWrapper.appendChild(searchStatus);

        const selector = document.createElement('div');
        selector.id = 'country-selector';
        const search = document.createElement('input');
        search.id = 'country-search';
        search.type = 'text';
        search.placeholder = 'Search country...';
        selector.appendChild(search);

        countries.forEach(country => {
            const option = document.createElement('div');
            option.className = 'country-option';
            if (selectedCountry.cid === country.cid) option.classList.add('selected');
            const optionImg = document.createElement('img');
            optionImg.src = getFlagUrl(country.code);
            optionImg.alt = country.name;
            optionImg.onerror = () => { optionImg.src = getFlagUrl('DC'); };
            const optionName = document.createElement('span');
            optionName.textContent = country.name;
            option.appendChild(optionImg);
            option.appendChild(optionName);
            option.addEventListener('click', () => {
                selectCountry(country);
                hideDropdowns();
            });
            selector.appendChild(option);
        });
        search.addEventListener('input', function() {
            const query = this.value.toLowerCase().trim();
            Array.from(selector.querySelectorAll('.country-option')).forEach(option => {
                const name = option.querySelector('span').textContent.toLowerCase();
                option.style.display = name.includes(query) ? 'flex' : 'none';
            });
        });
        monitorWrapper.appendChild(selector);

        const rareDropdown = document.createElement('div');
        rareDropdown.id = 'rare-scan-results-dropdown';
        monitorWrapper.appendChild(rareDropdown);

        flagImg.addEventListener('click', (e) => {
             e.stopPropagation();
             toggleDropdown(selector.id);
        });
        rareFinderBtn.addEventListener('click', () => {
            findRareCountriesInline();
        });
        searchStatus.addEventListener('click', (e) => {
             if (searchStatus.classList.contains('has-results')) {
                 e.stopPropagation();
                 toggleDropdown(rareDropdown.id);
             }
        });

        document.addEventListener('click', (e) => {
            if (!monitorWrapper.contains(e.target)) {
                 hideDropdowns();
            }
        });

        insertTarget.appendChild(monitorWrapper);

        if (selectedCountry.cid) {
            checkTransferMarket(false);
        }
    }

    function toggleDropdown(dropdownId) {
        const countrySelector = document.getElementById('country-selector');
        const rareDropdown = document.getElementById('rare-scan-results-dropdown');
        const isCountrySelectorTarget = dropdownId === countrySelector.id;
        const isRareDropdownTarget = dropdownId === rareDropdown.id;

        if (isCountrySelectorTarget) {
            if (countrySelector.classList.contains('visible')) {
                hideDropdown(countrySelector);
            } else {
                showDropdown(countrySelector);
                hideDropdown(rareDropdown);
                const searchInput = countrySelector.querySelector('#country-search');
                if (searchInput) {
                    searchInput.focus();
                    searchInput.value = '';
                    searchInput.dispatchEvent(new Event('input'));
                }
            }
        } else if (isRareDropdownTarget) {
             if (rareDropdown.classList.contains('visible')) {
                 hideDropdown(rareDropdown);
             } else {
                 showDropdown(rareDropdown);
                 hideDropdown(countrySelector);
             }
        }
    }

    function showDropdown(dropdown) {
        if (!dropdown) return;
        dropdown.style.display = 'block';
        requestAnimationFrame(() => {
             dropdown.classList.add('visible');
        });
    }

    function hideDropdown(dropdown) {
         if (!dropdown) return;
        dropdown.classList.remove('visible');
        setTimeout(() => {
            if (!dropdown.classList.contains('visible')) {
                dropdown.style.display = 'none';
            }
        }, 300);
    }

    function hideDropdowns() {
         hideDropdown(document.getElementById('country-selector'));
         hideDropdown(document.getElementById('rare-scan-results-dropdown'));
    }

    function selectCountry(country) {
        selectedCountry = { cid: country.cid, name: country.name, code: country.code };
        GM_setValue('selectedCountryCid', country.cid);
        GM_setValue('selectedCountryName', country.name);
        GM_setValue('selectedCountryCode', country.code);

        const flagImg = document.getElementById('country-monitor-flag');
        if (flagImg) {
            flagImg.src = getFlagUrl(country.code);
            flagImg.alt = country.name;
            flagImg.title = "Monitoring: " + country.name;
            flagImg.onerror = () => { flagImg.src = getFlagUrl(null); };
        }
        const countryName = document.getElementById('country-monitor-name');
        if (countryName) countryName.textContent = country.name;

        const selector = document.getElementById('country-selector');
        if(selector){
            Array.from(selector.querySelectorAll('.country-option')).forEach(opt => opt.classList.remove('selected'));
            const selectedOption = Array.from(selector.querySelectorAll('.country-option')).find(opt => opt.querySelector('span').textContent === country.name);
             if(selectedOption) selectedOption.classList.add('selected');
        }

        knownPlayers = {};
        GM_setValue('knownPlayers', knownPlayers);
        checkTransferMarket(true);
    }

    function isTransferPage() {
        return window.location.href.includes('p=transfer');
    }

    function checkTransferMarket(forced = false) {
        if (!selectedCountry.cid) return;
        const now = Date.now();
        const checkIntervalMs = CONFIG.CHECK_INTERVAL_HOURS * 60 * 60 * 1000;
        if (!forced && (now - lastChecked < checkIntervalMs)) return;

        cleanupKnownPlayers();
        const url = `https://www.managerzone.com/ajax.php?p=transfer&sub=transfer-search&sport=soccer&issearch=true&u=&nationality=${selectedCountry.cid}&deadline=0&category=&valuea=&valueb=&bida=&bidb=&agea=19&ageb=37&tot_low=0&tot_high=110&s0a=0&s0b=10&s1a=0&s1b=10&s2a=0&s2b=10&s3a=0&s3b=10&s4a=0&s4b=10&s5a=0&s5b=10&s6a=0&s6b=10&s7a=0&s7b=10&s8a=0&s8b=10&s9a=0&s9b=10&s10a=0&s10b=10&s11a=0&s11b=10&s12a=0&s12b=10`;

        fetch(url)
            .then(response => {
                 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                 return response.json();
             })
            .then(data => {
                lastChecked = now;
                GM_setValue('lastChecked', lastChecked);
                if (data?.totalHits && parseInt(data.totalHits) > 0) {
                    processPlayers(data);
                }
            })
            .catch(error => {
                 console.error(`[CTM_ERROR] checkTransferMarket failed for ${selectedCountry.name}:`, error);
             });
    }

    function processPlayers(data) {
        if (!data?.players || typeof data.players !== 'string' || data.players.includes("No players found")) return 0;

        let players = [];
        try {
            const parser = new DOMParser();
            const doc = parser.parseFromString(data.players, 'text/html');
            const playerRows = doc.querySelectorAll('.player');

            playerRows.forEach((row) => {
                const link = row.querySelector('a[href*="pid="]');
                const nameSpan = row.querySelector('.player_name, span[id^="player_name_"]');
                const idSpan = row.querySelector('span[id^="player_id_"]');
                let id = null, name = null;

                if (link) {
                    const urlParams = new URLSearchParams(link.getAttribute('href'));
                    id = urlParams.get('pid');
                } else if(idSpan) {
                    id = idSpan.id.replace('player_id_', '');
                }
                if (nameSpan) name = nameSpan.textContent.trim();

                if (id && name && !players.some(p => p.id === id)) {
                    players.push({ id: id, name: name });
                }
            });

             if (players.length === 0) {
                 const pidRegex = /pid=(\d{9,})/g;
                 let match;
                 while ((match = pidRegex.exec(data.players)) !== null) {
                     const fallbackId = match[1];
                     const contextRegex = new RegExp(`pid=${fallbackId}[^>]*>[^>]*>([^<]+)<`);
                     const nameMatch = data.players.match(contextRegex);
                     const fallbackName = nameMatch ? nameMatch[1].trim() : `Player ${fallbackId}`;
                     if (!players.some(p => p.id === fallbackId)) {
                         players.push({ id: fallbackId, name: fallbackName });
                     }
                 }
             }
        } catch (e) {
             console.error('[CTM_ERROR] processPlayers DOM parsing error:', e);
        }

        if (!isScanning) {
            const newPlayers = players.filter(player => !knownPlayers[player.id]);
            if (newPlayers.length > 0) {
                showPlayerNotification(newPlayers);
                let updatedKnownPlayers = { ...knownPlayers };
                newPlayers.forEach(player => {
                    updatedKnownPlayers[player.id] = { name: player.name, firstSeen: Date.now() };
                });
                knownPlayers = updatedKnownPlayers;
                GM_setValue('knownPlayers', knownPlayers);
            }
        }
        return players.length;
    }

    function showPlayerNotification(players) {
        const htmlContent = `<p>Found ${players.length} new player(s) from <strong>${selectedCountry.name}</strong>.</p>`;
        showModal('✨ New Players Found!', htmlContent, 'View', 'Close',
            () => { window.location.href = `https://www.managerzone.com/?p=transfer&sub=search&sport=soccer&nationality=${selectedCountry.cid}`; }
        );
    }

    function countPlayersFromCountry(country) {
        const url = `https://www.managerzone.com/ajax.php?p=transfer&sub=transfer-search&sport=soccer&issearch=true&u=&nationality=${country.cid}&deadline=0&category=&valuea=&valueb=&bida=&bidb=&agea=19&ageb=19&tot_low=0&tot_high=110&s0a=0&s0b=10&s1a=0&s1b=10&s2a=0&s2b=10&s3a=0&s3b=10&s4a=0&s4b=10&s5a=0&s5b=10&s6a=0&s6b=10&s7a=0&s7b=10&s8a=0&s8b=10&s9a=0&s9b=10&s10a=0&s10b=10&s11a=0&s11b=10&s12a=0&s12b=10`;
        return new Promise((resolve) => {
            fetch(url)
                .then(response => {
                    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                    return response.json();
                })
                .then(data => {
                    let playerCount = (data?.totalHits) ? parseInt(data.totalHits) : 0;
                    if (isNaN(playerCount)) playerCount = 0;
                    resolve({ cid: country.cid, name: country.name, code: country.code, playerCount: playerCount });
                })
                .catch((error) => {
                    console.error(`[CTM_ERROR] countPlayersFromCountry Error for ${country.name}:`, error);
                    resolve({ cid: country.cid, name: country.name, code: country.code, playerCount: -1 });
                });
        });
    }

    function findRareCountriesInline() {
        if (isScanning) return;
        isScanning = true;
        rareCountriesCache = [];

        const searchStatus = document.getElementById('search-status');
        const rareFinderBtn = document.getElementById('rare-finder-btn');
        const rareDropdown = document.getElementById('rare-scan-results-dropdown');

        if (!searchStatus || !rareFinderBtn || !rareDropdown) {
            console.error('[CTM_ERROR] findRareCountriesInline UI elements missing.');
            isScanning = false;
            return;
        }

        hideDropdowns();
        rareDropdown.innerHTML = '';
        searchStatus.textContent = 'Scanning 0% ...';
        searchStatus.style.display = 'inline';
        searchStatus.classList.remove('has-results');
        rareFinderBtn.classList.add('scanning');
        rareFinderBtn.disabled = true;

        let processed = 0;
        const totalCountries = countries.length;
        const batchSize = 5;
        const delayBetweenBatches = 400;

        async function runBatch(startIdx) {
            if (!isScanning || startIdx >= totalCountries) {
                 if (startIdx >= totalCountries) finishScanning();
                 return;
             }

            const endIdx = Math.min(startIdx + batchSize, totalCountries);
            const batch = countries.slice(startIdx, endIdx);
            const promises = batch.map(country => countPlayersFromCountry(country));

            try {
                const results = await Promise.all(promises);
                processed += results.length;
                const percentage = totalCountries > 0 ? Math.round((processed / totalCountries) * 100) : 0;
                if(searchStatus) searchStatus.textContent = `Scanning ${percentage}% ...`;

                results.forEach(result => {
                    if (result.playerCount !== -1 && result.playerCount > 0 && result.playerCount < CONFIG.MAX_RARE_PLAYERS) {
                        rareCountriesCache.push(result);
                    }
                });
                setTimeout(() => runBatch(endIdx), delayBetweenBatches);
            } catch (error) {
                 console.error(`[CTM_ERROR] findRareCountriesInline batch error:`, error);
                 setTimeout(() => runBatch(endIdx), delayBetweenBatches * 2);
            }
        }

        function finishScanning() {
            isScanning = false;
            if(rareFinderBtn) {
                 rareFinderBtn.classList.remove('scanning');
                 rareFinderBtn.disabled = false;
             }

            rareCountriesCache.sort((a, b) => a.playerCount - b.playerCount || a.name.localeCompare(b.name));

            if (rareDropdown) {
                 rareDropdown.innerHTML = '';
                 if (rareCountriesCache.length > 0) {
                      rareCountriesCache.forEach(result => {
                          const option = document.createElement('div');
                          option.className = 'rare-result-option';
                          option.title = `${result.name} (${result.playerCount} U19 players)`;
                          option.dataset.cid = result.cid;
                          const flagImg = document.createElement('img');
                          flagImg.src = getFlagUrl(result.code);
                          flagImg.alt = result.name;
                          flagImg.onerror = () => { flagImg.src = getFlagUrl('DC'); };
                          const nameSpan = document.createElement('span');
                          nameSpan.className = 'rare-name';
                          nameSpan.textContent = result.name;
                          const countSpan = document.createElement('span');
                          countSpan.className = 'rare-count';
                          countSpan.textContent = result.playerCount;
                          option.appendChild(flagImg);
                          option.appendChild(nameSpan);
                          option.appendChild(countSpan);
                          option.addEventListener('click', () => {
                               const url = `https://www.managerzone.com/?p=transfer&sub=search&sport=soccer&nationality=${result.cid}&agea=19&ageb=19`;
                               window.open(url, '_blank');
                               hideDropdowns();
                           });
                          rareDropdown.appendChild(option);
                      });
                 } else {
                      const noResult = document.createElement('div');
                      noResult.textContent = 'No rare countries found.';
                      noResult.style.padding = '10px';
                      noResult.style.textAlign = 'center';
                      noResult.style.color = 'var(--dk-text-secondary)';
                      rareDropdown.appendChild(noResult);
                 }
            }

            if(searchStatus) {
                 if (rareCountriesCache.length === 0) {
                     searchStatus.textContent = 'No rare U19';
                     searchStatus.classList.remove('has-results');
                 } else {
                     searchStatus.textContent = `Found ${rareCountriesCache.length} small countries with U19 players on the market`;
                     searchStatus.classList.add('has-results');
                 }
                 setTimeout(() => {
                      if(searchStatus && !searchStatus.classList.contains('has-results')) {
                           searchStatus.style.display = 'none';
                       }
                 }, 4000);
             }
        }
        runBatch(0);
    }

    function initializeScript() {
        if (isTransferPage()) {
            fetchCountries();
        }
    }

    setTimeout(initializeScript, 500);

    setInterval(() => {
        if (selectedCountry.cid && !isScanning) {
            checkTransferMarket(false);
        }
    }, 30 * 60 * 1000);

    let lastUrl = location.href;
    const observer = new MutationObserver(() => {
         const currentUrl = location.href;
         if (currentUrl !== lastUrl) {
             lastUrl = currentUrl;
             setTimeout(() => {
                 const oldWrapper = document.getElementById('country-monitor-wrapper');
                 if (oldWrapper) oldWrapper.remove();
                 hideModal();
                 hideDropdowns();
                 initializeScript();
             }, 750);
         }
    });
    observer.observe(document.body, { subtree: true, childList: true });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址