Torn: Supremacy Merit Helper

Calculate your position relative to the top 5% of the team

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn: Supremacy Merit Helper
// @namespace    http://tampermonkey.net/
// @version      1.6.2
// @description  Calculate your position relative to the top 5% of the team
// @author       ARCANE [2297468]
// @match        https://www.torn.com/page.php?sid=competition*
// @grant        none

// @license MIT

// ==/UserScript==

(function() {
    'use strict';
    
    console.log('[Supremacy Merit Helper] Script loaded - URL:', window.location.href);

    // Get team size from localStorage
    function getTeamSize() {
        const saved = localStorage.getItem('torn-position-team-size');
        return saved ? parseInt(saved, 10) : null;
    }

    // Save team size to localStorage
    function saveTeamSize(size) {
        if (size && size > 0) {
            localStorage.setItem('torn-position-team-size', size.toString());
        } else {
            localStorage.removeItem('torn-position-team-size');
        }
    }

    // Get user's team name from localStorage
    function getUserTeamName() {
        return localStorage.getItem('torn-position-user-team-name');
    }

    // Save user's team name to localStorage
    function saveUserTeamName(name) {
        if (name && name.trim()) {
            localStorage.setItem('torn-position-user-team-name', name.trim());
        } else {
            localStorage.removeItem('torn-position-user-team-name');
        }
    }

    // Check if URL is a team page
    function isTeamPage() {
        const hash = window.location.hash;
        return hash && /^#\/team\/\d+/.test(hash);
    }

    // Get current team name from team page
    function getCurrentTeamName() {
        // Try the selector from team header
        const teamHeader = document.querySelector('.teamHeaderWrapper___AHQVJ strong');
        if (teamHeader) {
            const teamName = teamHeader.textContent || teamHeader.innerText;
            if (teamName && teamName.trim()) {
                console.log('[Supremacy Merit Helper] Found current team name:', teamName.trim());
                return teamName.trim();
            }
        }
        console.log('[Supremacy Merit Helper] Could not find current team name');
        return null;
    }

    // Auto-detect user's team name from competition main page
    function autoDetectUserTeamName() {
        // Only try on main competition page
        if (!isCompetitionMainPage()) {
            return null;
        }

        console.log('[Supremacy Merit Helper] Attempting to auto-detect user team name...');

        // First, try the description div with "You have been placed in <strong>Team Name</strong>"
        const descriptionDiv = document.querySelector('.description___OzHl9');
        if (descriptionDiv) {
            const strong = descriptionDiv.querySelector('strong');
            if (strong) {
                const teamName = strong.textContent || strong.innerText;
                if (teamName && teamName.trim()) {
                    console.log('[Supremacy Merit Helper] Found team name in description div strong tag:', teamName.trim());
                    return teamName.trim();
                }
            }
        }

        // Fallback: Find user's team row and try other methods
        let userTeamRow = null;
        userTeamRow = document.querySelector('.dataGridRow___FAAJF.row___jLX1I.yourRow___R9Oi8');
        if (!userTeamRow) {
            userTeamRow = document.querySelector('.yourRow___R9Oi8');
        }
        if (!userTeamRow) {
            const dataGridBody = document.querySelector('.dataGridBody___Y9aVA');
            if (dataGridBody) {
                userTeamRow = dataGridBody.querySelector('.yourRow___R9Oi8');
            }
        }

        if (!userTeamRow) {
            console.log('[Supremacy Merit Helper] Could not find user team row for name detection');
            return null;
        }

        console.log('[Supremacy Merit Helper] Found user team row for name detection');

        // Find team name - typically in the first cell or a name cell
        // Try multiple selectors
        const nameCell = userTeamRow.querySelector('.teamNameCell, [class*="name"], .dataGridData___dV6BM:first-child');
        if (nameCell) {
            const nameText = nameCell.textContent || nameCell.innerText;
            if (nameText && nameText.trim()) {
                console.log('[Supremacy Merit Helper] Found team name in nameCell:', nameText.trim());
                return nameText.trim();
            }
        }

        // Try finding a link or strong tag within the row
        const link = userTeamRow.querySelector('a[href*="/team/"]');
        if (link) {
            const linkText = link.textContent || link.innerText;
            if (linkText && linkText.trim()) {
                console.log('[Supremacy Merit Helper] Found team name in link:', linkText.trim());
                return linkText.trim();
            }
        }

        // Try finding strong tag
        const strong = userTeamRow.querySelector('strong');
        if (strong) {
            const strongText = strong.textContent || strong.innerText;
            if (strongText && strongText.trim()) {
                console.log('[Supremacy Merit Helper] Found team name in strong tag:', strongText.trim());
                return strongText.trim();
            }
        }

        console.log('[Supremacy Merit Helper] Could not find team name');
        return null;
    }

    // Check if URL is the main competition page
    function isCompetitionMainPage() {
        const hash = window.location.hash;
        // Main page can be empty, '#', '#/', or just '/'
        const isMain = !hash || hash === '#' || hash === '' || hash === '#/' || hash === '/';
        console.log('[Supremacy Merit Helper] isCompetitionMainPage check - hash:', hash, 'isMain:', isMain);
        return isMain;
    }

    // Auto-detect team size from competition page
    function autoDetectTeamSize() {
        // Only try on main competition page
        if (!isCompetitionMainPage()) {
            console.log('[Supremacy Merit Helper] Not on competition main page, skipping auto-detect');
            return null;
        }

        console.log('[Supremacy Merit Helper] Attempting to auto-detect team size...');

        // Try multiple approaches to find user's team row
        let userTeamRow = null;
        
        // Approach 1: Try the full selector
        userTeamRow = document.querySelector('.dataGridRow___FAAJF.row___jLX1I.yourRow___R9Oi8');
        if (userTeamRow) {
            console.log('[Supremacy Merit Helper] Found user team row using full selector');
        } else {
            // Approach 2: Try just the yourRow class (more flexible)
            userTeamRow = document.querySelector('.yourRow___R9Oi8');
            if (userTeamRow) {
                console.log('[Supremacy Merit Helper] Found user team row using .yourRow___R9Oi8 selector');
            } else {
                // Approach 3: Try finding within dataGridBody
                const dataGridBody = document.querySelector('.dataGridBody___Y9aVA');
                if (dataGridBody) {
                    userTeamRow = dataGridBody.querySelector('.yourRow___R9Oi8');
                    if (userTeamRow) {
                        console.log('[Supremacy Merit Helper] Found user team row within dataGridBody');
                    }
                }
            }
        }

        if (!userTeamRow) {
            console.log('[Supremacy Merit Helper] Could not find user team row with any selector');
            // Debug: log all rows with yourRow class
            const allYourRows = document.querySelectorAll('.yourRow___R9Oi8');
            console.log('[Supremacy Merit Helper] Found', allYourRows.length, 'elements with .yourRow___R9Oi8 class');
            return null;
        }

        console.log('[Supremacy Merit Helper] Found user team row:', userTeamRow);

        // Find members cell - try multiple approaches
        let membersCell = userTeamRow.querySelector('.membersCell___qpI4n');
        if (!membersCell) {
            // Try finding within the row's children
            const rowChildren = userTeamRow.querySelectorAll('.dataGridData___dV6BM.membersCell___qpI4n');
            if (rowChildren.length > 0) {
                membersCell = rowChildren[0];
                console.log('[Supremacy Merit Helper] Found members cell using alternative selector');
            }
        }

        if (!membersCell) {
            console.log('[Supremacy Merit Helper] Could not find members cell');
            // Debug: log what cells we can find
            const allCells = userTeamRow.querySelectorAll('.dataGridData___dV6BM');
            console.log('[Supremacy Merit Helper] Found', allCells.length, 'dataGridData cells in row');
            return null;
        }

        console.log('[Supremacy Merit Helper] Found members cell:', membersCell);

        // Extract number from the span (e.g., "1,607" -> 1607)
        // Try textContent first, then innerText, then look for span
        let membersText = membersCell.textContent || membersCell.innerText;
        if (!membersText || membersText.trim() === '') {
            // Try finding span within members cell
            const span = membersCell.querySelector('span');
            if (span) {
                membersText = span.textContent || span.innerText;
                console.log('[Supremacy Merit Helper] Found text in span:', membersText);
            }
        }

        if (!membersText || membersText.trim() === '') {
            console.log('[Supremacy Merit Helper] Members cell has no text content');
            console.log('[Supremacy Merit Helper] Members cell HTML:', membersCell.innerHTML);
            return null;
        }

        console.log('[Supremacy Merit Helper] Members text:', membersText);

        // Remove commas and extract number
        const numberMatch = membersText.match(/[\d,]+/);
        if (!numberMatch) {
            console.log('[Supremacy Merit Helper] Could not extract number from members text');
            return null;
        }

        const teamSize = parseInt(numberMatch[0].replace(/,/g, ''), 10);
        if (teamSize && teamSize > 0) {
            console.log('[Supremacy Merit Helper] Identified teamSize:', teamSize);
            return teamSize;
        }

        console.log('[Supremacy Merit Helper] Failed to parse valid team size from:', numberMatch[0]);
        return null;
    }

    // Create floating controls (input + button)
    function createControls() {
        // Only show if on a team page
        if (!isTeamPage()) {
            // Remove controls if they exist but we're not on a team page
            const existing = document.getElementById('torn-position-controls');
            if (existing) {
                existing.remove();
            }
            return;
        }

        // Check if we're viewing the user's team
        const userTeamName = getUserTeamName();
        const currentTeamName = getCurrentTeamName();
        
        console.log('[Supremacy Merit Helper] Checking team match - User team:', userTeamName, 'Current team:', currentTeamName);
        
        if (userTeamName && currentTeamName && userTeamName !== currentTeamName) {
            console.log('[Supremacy Merit Helper] Not viewing user\'s team, hiding controls');
            // Remove controls if they exist
            const existing = document.getElementById('torn-position-controls');
            if (existing) {
                existing.remove();
            }
            return;
        }

        // Check if already exists
        if (document.getElementById('torn-position-controls')) {
            return;
        }

        const container = document.createElement('div');
        container.id = 'torn-position-controls';
        container.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 999999;
            display: flex;
            align-items: center;
            gap: 10px;
            background-color: rgba(0, 0, 0, 0.8);
            padding: 10px 15px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
        `;

        // Create label and input for team size
        const label = document.createElement('label');
        label.textContent = 'Team Size:';
        label.style.cssText = `
            color: white;
            font-size: 14px;
            font-weight: bold;
        `;

        const input = document.createElement('input');
        input.type = 'number';
        input.id = 'torn-position-team-size-input';
        input.placeholder = 'Enter team size';
        input.min = '1';
        input.style.cssText = `
            width: 130px;
            padding: 10px 14px;
            border: 2px solid #4CAF50;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            background-color: white;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        `;

        // Load saved team size or auto-detect
        let savedTeamSize = getTeamSize();
        console.log('[Supremacy Merit Helper] Loaded saved team size from localStorage:', savedTeamSize);
        if (!savedTeamSize) {
            // Try to auto-detect from competition page
            const detectedSize = autoDetectTeamSize();
            if (detectedSize) {
                savedTeamSize = detectedSize;
                console.log('[Supremacy Merit Helper] Auto-detected and saving teamSize:', detectedSize);
                saveTeamSize(detectedSize);
            }
        }
        if (savedTeamSize) {
            console.log('[Supremacy Merit Helper] Setting input value to teamSize:', savedTeamSize);
            input.value = savedTeamSize;
        }

        // Save on blur (when user leaves the input)
        input.addEventListener('blur', () => {
            const value = parseInt(input.value, 10);
            if (value && value > 0) {
                saveTeamSize(value);
            } else if (input.value === '') {
                saveTeamSize(null);
            }
        });

        // Also save on Enter key
        input.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                input.blur();
            }
        });

        const button = document.createElement('button');
        button.textContent = 'Calculate';
        button.id = 'torn-position-calc-btn';
        button.style.cssText = `
            padding: 12px 24px;
            background-color: #4CAF50;
            color: white;
            border: 2px solid #45a049;
            border-radius: 6px;
            cursor: pointer;
            font-size: 15px;
            font-weight: bold;
            box-shadow: 0 3px 8px rgba(0,0,0,0.4);
            transition: all 0.2s ease;
        `;
        
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#45a049';
            button.style.transform = 'scale(1.05)';
            button.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)';
        });
        
        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#4CAF50';
            button.style.transform = 'scale(1)';
            button.style.boxShadow = '0 3px 8px rgba(0,0,0,0.4)';
        });
        
        button.addEventListener('click', calculatePosition);

        container.appendChild(label);
        container.appendChild(input);
        container.appendChild(button);
        
        document.body.appendChild(container);
    }

    // Get attack count from a row
    function getAttackCount(row) {
        const attackElement = row.querySelector('.attacks___IJtzw span');
        if (attackElement) {
            return parseInt(attackElement.textContent.trim(), 10);
        }
        return null;
    }

    // Get position from translateY
    function getPositionFromTranslateY(translateY) {
        return Math.floor(translateY / 36) + 1;
    }

    // Get translateY from row
    function getTranslateYFromRow(row) {
        const style = window.getComputedStyle(row);
        const transform = style.transform || style.webkitTransform || style.mozTransform;
        
        if (!transform || transform === 'none') {
            const inlineStyle = row.getAttribute('style');
            const translateYMatch = inlineStyle ? inlineStyle.match(/translateY\((\d+)px\)/) : null;
            if (translateYMatch) {
                return parseInt(translateYMatch[1], 10);
            }
            return null;
        }
        
        let translateY = 0;
        
        if (transform.includes('matrix') || transform.includes('matrix3d')) {
            const matrixMatch = transform.match(/matrix(?:3d)?\([^)]+\)/);
            if (matrixMatch) {
                const values = matrixMatch[0].match(/[\d.]+/g);
                if (values && values.length >= 6) {
                    translateY = parseFloat(values[values.length >= 14 ? 13 : 5]);
                }
            }
        } else if (transform.includes('translateY')) {
            const translateYMatch = transform.match(/translateY\(([^)]+)\)/);
            if (translateYMatch) {
                translateY = parseFloat(translateYMatch[1]);
            }
        }
        
        if (translateY === 0 && !transform.includes('translateY')) {
            const inlineStyle = row.getAttribute('style');
            const translateYMatch = inlineStyle ? inlineStyle.match(/translateY\((\d+)px\)/) : null;
            if (translateYMatch) {
                translateY = parseInt(translateYMatch[1], 10);
            } else {
                return null;
            }
        }
        
        return translateY;
    }

    // Store collected data
    let collectedData = {
        userRow: null,
        userPosition: null,
        userAttacks: null,
        top5PercentAttacks: null,
        top5PercentPosition: null
    };

    // Check if we have enough data and show toasts
    function checkAndShowToasts() {
        const teamSize = getTeamSize();
        if (!teamSize || teamSize <= 0) {
            return;
        }

        // Check if we have user data
        if (!collectedData.userRow) {
            collectedData.userRow = document.querySelector('.dataGridRow___FAAJF.teamRow___R3ZLF.yourRow___R9Oi8');
            if (collectedData.userRow) {
                const translateY = getTranslateYFromRow(collectedData.userRow);
                if (translateY !== null) {
                    collectedData.userPosition = getPositionFromTranslateY(translateY);
                    collectedData.userAttacks = getAttackCount(collectedData.userRow);
                }
            }
        }

        // Check if we have top 5% data
        if (!collectedData.top5PercentAttacks) {
            const top5PercentPosition = Math.ceil(teamSize * 0.05);
            collectedData.top5PercentPosition = top5PercentPosition;
            
            console.log('[Supremacy Merit Helper] Looking for top 5% position:', top5PercentPosition, 'out of team size:', teamSize);
            
            const allRows = document.querySelectorAll('.dataGridRow___FAAJF.teamRow___R3ZLF');
            console.log('[Supremacy Merit Helper] Found', allRows.length, 'team rows to check');
            
            for (const row of allRows) {
                const translateY = getTranslateYFromRow(row);
                if (translateY !== null) {
                    const position = getPositionFromTranslateY(translateY);
                    if (position === top5PercentPosition || Math.abs(position - top5PercentPosition) <= 2) {
                        const attacks = getAttackCount(row);
                        if (attacks !== null) {
                            console.log('[Supremacy Merit Helper] Found top 5% player at position:', position, 'with', attacks, 'attacks');
                            collectedData.top5PercentAttacks = attacks;
                            break;
                        }
                    }
                }
            }
            
            if (!collectedData.top5PercentAttacks) {
                console.log('[Supremacy Merit Helper] Could not find top 5% player data yet');
            }
        }

        // Check if we have both user and top 5% data
        const hasAllData = collectedData.userRow && collectedData.userPosition !== null && collectedData.userAttacks !== null && 
                          collectedData.top5PercentAttacks !== null && collectedData.top5PercentPosition !== null;

        // Remove collecting/scroll toast if we have all data
        if (hasAllData) {
            const collectingToast = document.getElementById('torn-position-toast');
            if (collectingToast) {
                collectingToast.remove();
            }
        }

        // If we have user data, show user toast
        if (collectedData.userRow && collectedData.userPosition !== null && !document.getElementById('torn-position-user-toast')) {
            const teamSize = getTeamSize();
            const messageParts = [];
            
            if (teamSize && teamSize > 0) {
                const percentile = ((collectedData.userPosition / teamSize) * 100).toFixed(2);
                messageParts.push(`Your percentile: ${percentile}%`);
            }
            
            messageParts.push(`Position: ${collectedData.userPosition}`);
            
            if (collectedData.userAttacks !== null) {
                messageParts.push(`Hits: ${collectedData.userAttacks}`);
            }
            
            showUserToast(messageParts.join(' | '));
        }

        // If we have both user and top 5% data, show all toasts (only if they don't already exist)
        if (hasAllData) {
            // Show top 5% toast (only if it doesn't exist)
            if (!document.getElementById('torn-position-top5-toast')) {
                const top5PercentPercentile = ((collectedData.top5PercentPosition / teamSize) * 100).toFixed(2);
                showTop5PercentToast(collectedData.top5PercentPosition, collectedData.top5PercentAttacks, top5PercentPercentile);
            }
            
            // Show difference toast (only if it doesn't exist)
            if (!document.getElementById('torn-position-difference-toast')) {
                const difference = collectedData.userAttacks - collectedData.top5PercentAttacks;
                showDifferenceToast(difference, collectedData.userAttacks, collectedData.top5PercentAttacks);
            }
        }
    }



    // Show user info toast
    function showUserToast(message) {
        ensureAnimationStyle();
        
        // Remove existing user toast if any
        const existingToast = document.getElementById('torn-position-user-toast');
        if (existingToast) {
            existingToast.remove();
        }

        const toast = document.createElement('div');
        toast.id = 'torn-position-user-toast';
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 95px;
            right: 20px;
            z-index: 999998;
            padding: 15px 25px;
            background-color: #4CAF50;
            color: white;
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            animation: slideIn 0.3s ease-out;
            max-width: 350px;
            cursor: pointer;
            transition: opacity 0.2s ease;
        `;
        
        // Add click handler to dismiss toast
        toast.addEventListener('click', () => {
            toast.style.opacity = '0';
            setTimeout(() => {
                toast.remove();
            }, 200);
        });
        
        toast.addEventListener('mouseenter', () => {
            toast.style.opacity = '0.9';
        });
        
        toast.addEventListener('mouseleave', () => {
            toast.style.opacity = '1';
        });
        
        document.body.appendChild(toast);
    }

    // Show top 5% info toast
    function showTop5PercentToast(top5PercentPosition, top5PercentAttacks, top5PercentPercentile) {
        ensureAnimationStyle();
        
        // Remove existing top 5% toast if any
        const existingToast = document.getElementById('torn-position-top5-toast');
        if (existingToast) {
            existingToast.remove();
        }

        const message = `5% percentile: ${top5PercentPercentile}% | Position: ${top5PercentPosition} | Hits: ${top5PercentAttacks}`;

        const toast = document.createElement('div');
        toast.id = 'torn-position-top5-toast';
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 165px;
            right: 20px;
            z-index: 999997;
            padding: 15px 25px;
            background-color: #2196F3;
            color: white;
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            animation: slideIn 0.3s ease-out;
            max-width: 350px;
            cursor: pointer;
            transition: opacity 0.2s ease;
        `;
        
        // Add click handler to dismiss toast
        toast.addEventListener('click', () => {
            toast.style.opacity = '0';
            setTimeout(() => {
                toast.remove();
            }, 200);
        });
        
        toast.addEventListener('mouseenter', () => {
            toast.style.opacity = '0.9';
        });
        
        toast.addEventListener('mouseleave', () => {
            toast.style.opacity = '1';
        });
        
        document.body.appendChild(toast);
    }

    // Show difference/advantage toast
    function showDifferenceToast(difference, userAttacks, top5PercentAttacks) {
        ensureAnimationStyle();
        
        // Remove existing difference toast if any
        const existingToast = document.getElementById('torn-position-difference-toast');
        if (existingToast) {
            existingToast.remove();
        }

        // Calculate delta (difference between user and top 5%)
        // Positive delta means user has more hits, negative means user needs more hits
        const delta = difference;
        
        const message = `Delta: ${delta > 0 ? '+' : ''}${delta}`;

        const toast = document.createElement('div');
        toast.id = 'torn-position-difference-toast';
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 235px;
            right: 20px;
            z-index: 999996;
            padding: 15px 25px;
            background-color: ${difference > 0 ? '#4CAF50' : '#FF9800'};
            color: white;
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            animation: slideIn 0.3s ease-out;
            max-width: 350px;
            cursor: pointer;
            transition: opacity 0.2s ease;
        `;
        
        // Add click handler to dismiss toast
        toast.addEventListener('click', () => {
            toast.style.opacity = '0';
            setTimeout(() => {
                toast.remove();
            }, 200);
        });
        
        toast.addEventListener('mouseenter', () => {
            toast.style.opacity = '0.9';
        });
        
        toast.addEventListener('mouseleave', () => {
            toast.style.opacity = '1';
        });
        
        document.body.appendChild(toast);
    }


    // Start passive data collection (no auto-scrolling)
    function startPassiveDataCollection() {
        // Reset collected data
        collectedData = {
            userRow: null,
            userPosition: null,
            userAttacks: null,
            top5PercentAttacks: null,
            top5PercentPosition: null
        };

        showToast('Keep scrolling down until you see a popup', 'success');

        // Check immediately
        checkAndShowToasts();

        // Set up MutationObserver to watch for DOM changes (rows appearing as user scrolls)
        const virtualContainer = document.querySelector('.virtualContainer___Ft72x');
        if (virtualContainer) {
            const observer = new MutationObserver(() => {
                checkAndShowToasts();
                
                // Stop observing once we have all data
                const hasAllData = collectedData.userRow && collectedData.userPosition !== null && 
                    collectedData.userAttacks !== null && collectedData.top5PercentAttacks !== null && 
                    collectedData.top5PercentPosition !== null;
                if (hasAllData) {
                    console.log('[Supremacy Merit Helper] All data collected, disconnecting observer');
                    observer.disconnect();
                }
            });
            
            observer.observe(virtualContainer, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['style']
            });
        }

        // Also set up periodic checking as backup (passive - only reads data, doesn't scroll)
        // Start with more frequent checks, then slow down after a bit
        let checkCount = 0;
        const checkInterval = setInterval(() => {
            checkAndShowToasts();
            checkCount++;
            
            // Stop checking once we have all data
            const hasAllData = collectedData.userRow && collectedData.userPosition !== null && 
                collectedData.userAttacks !== null && collectedData.top5PercentAttacks !== null && 
                collectedData.top5PercentPosition !== null;
            if (hasAllData) {
                console.log('[Supremacy Merit Helper] All data collected, stopping interval');
                clearInterval(checkInterval);
            } else if (checkCount > 10) {
                // After 10 checks (10 seconds), log what we're missing for debugging
                if (!collectedData.userRow) {
                    console.log('[Supremacy Merit Helper] Still missing after 10s: userRow');
                }
                if (collectedData.userPosition === null) {
                    console.log('[Supremacy Merit Helper] Still missing after 10s: userPosition');
                }
                if (collectedData.userAttacks === null) {
                    console.log('[Supremacy Merit Helper] Still missing after 10s: userAttacks');
                }
                if (collectedData.top5PercentAttacks === null) {
                    console.log('[Supremacy Merit Helper] Still missing after 10s: top5PercentAttacks');
                }
                if (collectedData.top5PercentPosition === null) {
                    console.log('[Supremacy Merit Helper] Still missing after 10s: top5PercentPosition');
                }
            }
        }, 500); // Check every 500ms for faster initial detection
    }

    // Calculate position
    function calculatePosition() {
        // Check if on team page
        if (!isTeamPage()) {
            showToast('Please navigate to a team page to calculate position.', 'error');
            return;
        }

        // Toggle off "show-available-targets" checkbox if it's on
        const showAvailableTargetsCheckbox = document.getElementById('show-available-targets');
        let checkboxWasToggled = false;
        
        if (showAvailableTargetsCheckbox) {
            const wasChecked = showAvailableTargetsCheckbox.checked;
            console.log('[Supremacy Merit Helper] "show-available-targets" checkbox state before toggle:', wasChecked);
            
            if (wasChecked) {
                // Try clicking the label first (most natural way to trigger checkbox in Torn's UI)
                const label = document.querySelector('label[for="show-available-targets"]');
                if (label) {
                    console.log('[Supremacy Merit Helper] Clicking label to toggle checkbox');
                    label.click();
                    checkboxWasToggled = true;
                } else {
                    // Fallback: manually uncheck and trigger events
                    showAvailableTargetsCheckbox.checked = false;
                    // Trigger multiple events to ensure Torn's UI updates
                    const events = ['change', 'input', 'click'];
                    events.forEach(eventType => {
                        showAvailableTargetsCheckbox.dispatchEvent(new Event(eventType, { bubbles: true, cancelable: true }));
                    });
                    console.log('[Supremacy Merit Helper] Toggled off "show-available-targets" checkbox via events');
                    checkboxWasToggled = true;
                }
            } else {
                console.log('[Supremacy Merit Helper] "show-available-targets" checkbox was already off');
            }
            
            // Log final state after a brief delay to allow UI to update
            setTimeout(() => {
                console.log('[Supremacy Merit Helper] "show-available-targets" checkbox state after toggle:', showAvailableTargetsCheckbox.checked);
            }, 100);
        } else {
            console.log('[Supremacy Merit Helper] Could not find "show-available-targets" checkbox');
        }

        // Start passive data collection (no auto-scrolling)
        // If checkbox was already off, add a small delay to ensure DOM is ready
        if (checkboxWasToggled) {
            // If we toggled it, start immediately (the toggle will trigger DOM refresh)
            startPassiveDataCollection();
        } else {
            // If it was already off, wait a bit for DOM to be ready, then start
            setTimeout(() => {
                startPassiveDataCollection();
            }, 200);
        }
    }

    // Ensure animation style is added (only once)
    function ensureAnimationStyle() {
        if (!document.getElementById('torn-toast-animation-style')) {
            const style = document.createElement('style');
            style.id = 'torn-toast-animation-style';
            style.textContent = `
                @keyframes slideIn {
                    from {
                        transform: translateX(100%);
                        opacity: 0;
                    }
                    to {
                        transform: translateX(0);
                        opacity: 1;
                    }
                }
            `;
            document.head.appendChild(style);
        }
    }

    // Show toast notification
    function showToast(message, type = 'success') {
        ensureAnimationStyle();
        
        // Remove existing toast if any
        const existingToast = document.getElementById('torn-position-toast');
        if (existingToast) {
            existingToast.remove();
        }
        
        const toast = document.createElement('div');
        toast.id = 'torn-position-toast';
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 95px;
            right: 20px;
            z-index: 999998;
            padding: 15px 25px;
            background-color: ${type === 'error' ? '#f44336' : '#4CAF50'};
            color: white;
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            animation: slideIn 0.3s ease-out;
            max-width: 350px;
            cursor: pointer;
            transition: opacity 0.2s ease;
        `;
        
        // Add click handler to dismiss toast
        toast.addEventListener('click', () => {
            toast.style.opacity = '0';
            setTimeout(() => {
                toast.remove();
            }, 200);
        });
        
        toast.addEventListener('mouseenter', () => {
            toast.style.opacity = '0.9';
        });
        
        toast.addEventListener('mouseleave', () => {
            toast.style.opacity = '1';
        });
        
        document.body.appendChild(toast);
        
        // Toast is now persistent - only removed on click or page refresh
    }

    // Function to check and update controls based on URL
    let lastHash = null;
    function checkAndUpdateControls() {
        const currentHash = window.location.hash;
        
        // Early exit if hash hasn't changed and controls already exist/not needed
        if (currentHash === lastHash) {
            const existingControls = document.getElementById('torn-position-controls');
            const isTeam = isTeamPage();
            
            // If we're on a team page and controls exist, or not on team page and no controls, skip
            if ((isTeam && existingControls) || (!isTeam && !existingControls)) {
                // Only check for main page auto-detect if on main page
                if (isCompetitionMainPage() && !getTeamSize()) {
                    // Need to auto-detect, continue
                } else {
                    return; // Skip unnecessary work
                }
            }
        }
        
        lastHash = currentHash;
        console.log('[Supremacy Merit Helper] checkAndUpdateControls called - URL:', window.location.href, 'Hash:', currentHash);
        
        if (isTeamPage()) {
            console.log('[Supremacy Merit Helper] On team page, creating controls');
            createControls();
        } else {
            console.log('[Supremacy Merit Helper] Not on team page, removing controls if they exist');
            const existing = document.getElementById('torn-position-controls');
            if (existing) {
                existing.remove();
            }
        }
        
        // Auto-detect team size on competition main page
        if (isCompetitionMainPage()) {
            console.log('[Supremacy Merit Helper] On competition main page, attempting auto-detect');
            // Helper function to handle detected team size and team name
            const handleDetectedTeamSize = (detectedSize) => {
                // Save if not already saved
                const currentSaved = getTeamSize();
                if (!currentSaved || currentSaved !== detectedSize) {
                    console.log('[Supremacy Merit Helper] Saving teamSize to localStorage:', detectedSize);
                    saveTeamSize(detectedSize);
                } else {
                    console.log('[Supremacy Merit Helper] Team size already saved:', currentSaved);
                }
                // Update input if it exists (user might be on team page after visiting main page)
                const input = document.getElementById('torn-position-team-size-input');
                if (input && !input.value) {
                    console.log('[Supremacy Merit Helper] Updating input field with teamSize:', detectedSize);
                    input.value = detectedSize;
                }
                
                // Also detect and save team name
                const detectedTeamName = autoDetectUserTeamName();
                if (detectedTeamName) {
                    console.log('[Supremacy Merit Helper] Detected user team name:', detectedTeamName);
                    saveUserTeamName(detectedTeamName);
                } else {
                    console.log('[Supremacy Merit Helper] Could not detect user team name');
                }
            };
            
            // Try immediate detection
            let detectedSize = autoDetectTeamSize();
            
            if (detectedSize) {
                handleDetectedTeamSize(detectedSize);
            } else {
                // If not found, retry with delays (content might be loading)
                console.log('[Supremacy Merit Helper] Retrying team size detection with delays...');
                setTimeout(() => {
                    detectedSize = autoDetectTeamSize();
                    if (detectedSize) {
                        handleDetectedTeamSize(detectedSize);
                    } else {
                        setTimeout(() => {
                            detectedSize = autoDetectTeamSize();
                            if (detectedSize) {
                                handleDetectedTeamSize(detectedSize);
                            } else {
                                console.log('[Supremacy Merit Helper] Could not detect team size after retries');
                            }
                        }, 1000);
                    }
                }, 500);
            }
        }
    }

    // Initialize when page loads
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', checkAndUpdateControls);
    } else {
        checkAndUpdateControls();
    }
    
    // Handle hash changes (SPA navigation)
    window.addEventListener('hashchange', () => {
        console.log('[Supremacy Merit Helper] Hash changed, calling checkAndUpdateControls');
        checkAndUpdateControls();
    });
    
    // Also handle dynamic content (SPA navigation) - throttled
    let mutationTimeout = null;
    const observer = new MutationObserver((mutations) => {
        // Skip mutations that are just our own toasts/controls being added/removed
        const relevantMutations = mutations.filter(mutation => {
            const target = mutation.target;
            // Skip if mutation is on our own elements
            if (target.id && (target.id.startsWith('torn-position-') || target.id === 'torn-toast-animation-style')) {
                return false;
            }
            // Skip if mutation is adding/removing our own elements
            if (mutation.addedNodes) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1 && node.id && (node.id.startsWith('torn-position-') || node.id === 'torn-toast-animation-style')) {
                        return false;
                    }
                }
            }
            if (mutation.removedNodes) {
                for (const node of mutation.removedNodes) {
                    if (node.nodeType === 1 && node.id && (node.id.startsWith('torn-position-') || node.id === 'torn-toast-animation-style')) {
                        return false;
                    }
                }
            }
            return true;
        });
        
        // Only process if there are relevant mutations
        if (relevantMutations.length === 0) {
            return;
        }
        
        // Throttle to avoid excessive calls
        if (mutationTimeout) {
            clearTimeout(mutationTimeout);
        }
        mutationTimeout = setTimeout(() => {
            console.log('[Supremacy Merit Helper] DOM mutation detected, calling checkAndUpdateControls');
            checkAndUpdateControls();
        }, 1000); // Increased from 300ms to 1000ms for better performance
    });
    
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

})();