Attack screen improvements

Improvements to the Attacking screen.

// ==UserScript==
// @name            Attack screen improvements
// @namespace       http://tampermonkey.net/
// @version         1.2.5
// @description     Improvements to the Attacking screen.
// @author          Cypher-[2641265]
// @license         MIT
// @match           https://www.torn.com/loader.php?sid=attack&user2ID=*
// @icon            https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant           none
// ==/UserScript==
 
// Todo: 
//  -add refresh when leave/mug/hosp window comes up. 
//
// Implemented:
//  -On/offline status icon for defender
//  -Energy display
//  -current status for target (hosp/okay etc with timer)
 
 
(function() {
    'use strict';
 
    // API Key management
    function getAPIKey() {
        return localStorage.getItem('torn_minimal_key');
    }
 
    function setAPIKey(key) {
        localStorage.setItem('torn_minimal_key', key);
    }
 
    const SVGs = {
        Online: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#43d854" stroke="#fff" stroke-width="0"/></svg>`,
        Idle: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#f7c325" stroke="#fff" stroke-width="0"/><rect x="5" y="3" width="4" height="4" fill="#f2f2f2"/></svg>`,
        Offline: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#b3b3b3" stroke="#fff" stroke-width="0"/><rect x="3" y="5" width="6" height="2" fill="#f2f2f2"/></svg>`
    };
 
    const urlParams = new URLSearchParams(window.location.search);
    const userID = urlParams.get('user2ID');
    if (!userID) return;
 
    // Check if API key is available
    let API_KEY = getAPIKey();
    if (!API_KEY) {
        showAPIKeySetup();
        return;
    }
 
    // API Key setup interface
    function showAPIKeySetup() {
        // Create popup immediately without any DOM elements
        const overlay = document.createElement('div');
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
        overlay.style.zIndex = '10000';
        overlay.style.display = 'flex';
        overlay.style.alignItems = 'center';
        overlay.style.justifyContent = 'center';

        const dialog = document.createElement('div');
        dialog.style.backgroundColor = '#2a2a2a';
        dialog.style.padding = '20px';
        dialog.style.borderRadius = '8px';
        dialog.style.border = '1px solid #444';
        dialog.style.minWidth = '300px';
        dialog.style.textAlign = 'center';

        const input = document.createElement('input');
        input.type = 'text';
        input.placeholder = 'Minimal API';
        input.style.width = '100%';
        input.style.padding = '8px';
        input.style.marginBottom = '15px';
        input.style.backgroundColor = '#1a1a1a';
        input.style.color = 'white';
        input.style.border = '1px solid #444';
        input.style.borderRadius = '4px';

        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.gap = '10px';
        buttonContainer.style.justifyContent = 'center';

        const getApiButton = document.createElement('button');
        getApiButton.textContent = 'Get API';
        getApiButton.style.padding = '8px 16px';
        getApiButton.style.backgroundColor = '#007bff';
        getApiButton.style.color = 'white';
        getApiButton.style.border = 'none';
        getApiButton.style.borderRadius = '4px';
        getApiButton.style.cursor = 'pointer';

        const okButton = document.createElement('button');
        okButton.textContent = 'OK';
        okButton.style.padding = '8px 16px';
        okButton.style.backgroundColor = '#0ea01fff';
        okButton.style.color = 'white';
        okButton.style.border = 'none';
        okButton.style.borderRadius = '4px';
        okButton.style.cursor = 'pointer';

        const cancelButton = document.createElement('button');
        cancelButton.textContent = 'Cancel';
        cancelButton.style.padding = '8px 16px';
        cancelButton.style.backgroundColor = '#6c757d';
        cancelButton.style.color = 'white';
        cancelButton.style.border = 'none';
        cancelButton.style.borderRadius = '4px';
        cancelButton.style.cursor = 'pointer';

        getApiButton.addEventListener('click', () => {
            window.open('https://www.torn.com/preferences.php#tab=api', '_blank');
        });

        okButton.addEventListener('click', () => {
            const apiKey = input.value.trim();
            if (apiKey) {
                setAPIKey(apiKey);
                API_KEY = apiKey;
                overlay.remove();
                initializeScript();
            }
        });

        cancelButton.addEventListener('click', () => {
            overlay.remove();
        });

        // Close on overlay click
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                overlay.remove();
            }
        });

        // Close on escape key
        document.addEventListener('keydown', function escapeHandler(e) {
            if (e.key === 'Escape') {
                overlay.remove();
                document.removeEventListener('keydown', escapeHandler);
            }
        });

        // Enter key submits
        input.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                okButton.click();
            }
        });

        buttonContainer.appendChild(getApiButton);
        buttonContainer.appendChild(okButton);
        buttonContainer.appendChild(cancelButton);
        dialog.appendChild(input);
        dialog.appendChild(buttonContainer);
        overlay.appendChild(dialog);
        document.body.appendChild(overlay);
        
        // Focus the input
        input.focus();
    }    // Initialize script features
    function initializeScript() {
        // Initial fetches
        fetchDefenderStatus();
        fetchAttackerEnergy();
    }
 
    // Unified refresh function for all elements
    function refreshAllData() {
        fetchDefenderStatus();
        fetchAttackerEnergy();
    }
 
    // Fetch defender status
    function fetchDefenderStatus() {
        fetch(`https://api.torn.com/user/${userID}?selections=profile&key=${API_KEY}&comment=attackpageimprovements`)
            .then(res => res.json())
            .then(data => {
                if (!data) return;
                
                // Handle online/offline status icon
                if (data.last_action && data.last_action.status) {
                    const state = data.last_action.status;
                    const svg = SVGs[state] || SVGs.Offline;
 
                    function insertIcon() {
                        const usernameElement = document.querySelector('div[class*="rose"] .user-name');
                        if (usernameElement) {
                            // Remove existing icon if present
                            const existingIcon = usernameElement.parentNode.querySelector('.torn-status-icon');
                            if (existingIcon) {
                                existingIcon.remove();
                            }
                            
                            const iconSpan = document.createElement('span');
                            iconSpan.className = 'torn-status-icon';
                            iconSpan.innerHTML = svg;
                            iconSpan.style.verticalAlign = "middle";
                            iconSpan.style.marginRight = "4px";
                            iconSpan.style.cursor = "pointer";
                            iconSpan.title = state + " - Click to refresh";
                            
                            // Add click handler to refresh defender status
                            iconSpan.addEventListener('click', () => {
                                refreshAllData();
                            });
                            
                            usernameElement.parentNode.insertBefore(iconSpan, usernameElement);
                        } else {
                            setTimeout(insertIcon, 200);
                        }
                    }
                    insertIcon();
                }
 
                // Handle health status display
                if (data.status && data.status.state) {
                    const statusState = data.status.state;
                    const statusColor = data.status.color || 'gray';
                    const statusUntil = data.status.until;
 
                    function insertHealthStatus() {
                        const usernameElement = document.querySelector('div[class*="rose"] .user-name');
                        if (usernameElement) {
                            // Remove existing health status if present
                            const existingHealthStatus = usernameElement.parentNode.querySelector('.torn-health-status');
                            if (existingHealthStatus) {
                                existingHealthStatus.remove();
                            }
                            
                            const healthContainer = document.createElement('span');
                            healthContainer.className = 'torn-health-status';
                            healthContainer.style.marginLeft = '8px';
                            healthContainer.style.fontSize = '0.85em';
                            healthContainer.style.fontWeight = 'bold';
                            healthContainer.style.cursor = 'pointer';
                            
                            // Color mapping for different states
                            const colorMap = {
                                'red': '#dc3545',
                                'orange': '#fd7e14', 
                                'yellow': '#ffc107',
                                'green': '#28a745',
                                'blue': '#007bff',
                                'gray': '#6c757d'
                            };
                            
                            healthContainer.style.color = colorMap[statusColor] || '#6c757d';
                            
                            // Add click handler for refresh
                            healthContainer.addEventListener('click', () => {
                                // Check if the display shows "Click to refresh"
                                if (healthContainer.textContent.includes("Click to refresh")) {
                                    // Timer expired, refresh whole page
                                    location.reload();
                                } else {
                                    // Timer still active, refresh all data
                                    refreshAllData();
                                }
                            });
                            
                            function updateCountdown() {
                                let displayText = statusState;
                                
                                // Add countdown timer if available
                                if (statusUntil) {
                                    const currentTime = Math.floor(Date.now() / 1000);
                                    const timeRemaining = statusUntil - currentTime;
                                    
                                    if (timeRemaining > 0) {
                                        const hours = Math.floor(timeRemaining / 3600);
                                        const minutes = Math.floor((timeRemaining % 3600) / 60);
                                        const seconds = timeRemaining % 60;
                                        
                                        if (hours > 0) {
                                            displayText += ` (${hours}h ${minutes}m)`;
                                        } else if (minutes > 0) {
                                            displayText += ` (${minutes}m ${seconds}s)`;
                                        } else {
                                            displayText += ` (${seconds}s)`;
                                        }
                                    } else {
                                        // Timer expired, show click to refresh message
                                        displayText = statusState + " - Click to refresh";
                                        healthContainer.style.textDecoration = 'underline';
                                    }
                                }
                                
                                healthContainer.textContent = displayText;
                            }
                            
                            // Initial update
                            updateCountdown();
                            
                            // Update countdown every second if there's a timer
                            if (statusUntil) {
                                setInterval(updateCountdown, 1000);
                            }
                            
                            usernameElement.parentNode.insertBefore(healthContainer, usernameElement.nextSibling);
                        } else {
                            setTimeout(insertHealthStatus, 200);
                        }
                    }
                    insertHealthStatus();
                }
            });
    }
 
    // Initial fetch
    fetchDefenderStatus();
 
    // Fetch attacker energy
    function fetchAttackerEnergy() {
        fetch(`https://api.torn.com/user/?selections=bars&key=${API_KEY}&comment=attackerEnergy&comment=attackpageimprovements`)
            .then(res => res.json())
            .then(data => {
                if (!data || !data.energy) return;
                const currentEnergy = data.energy.current;
                const maxEnergy = data.energy.maximum;
 
                function insertEnergyDisplay() {
                    const attackerUsernameElement = document.querySelector('div[class*="green"] .user-name');
                    if (attackerUsernameElement) {
                        // Remove existing energy display if present
                        const existingEnergyDisplay = attackerUsernameElement.parentNode.querySelector('.torn-energy-display');
                        if (existingEnergyDisplay) {
                            existingEnergyDisplay.remove();
                        }
                        
                        const energyContainer = document.createElement('div');
                        energyContainer.className = 'torn-energy-display';
                        energyContainer.style.display = 'inline-block';
                        energyContainer.style.marginLeft = '8px';
                        energyContainer.style.verticalAlign = 'middle';
                        energyContainer.style.cursor = 'pointer';
                        energyContainer.title = `Energy: ${currentEnergy}/${maxEnergy} - Click to refresh, Long press to change API key`;

                        // Long press functionality for API key change
                        let longPressTimer;
                        let isLongPress = false;

                        energyContainer.addEventListener('mousedown', () => {
                            isLongPress = false;
                            longPressTimer = setTimeout(() => {
                                isLongPress = true;
                                showAPIKeySetup();
                            }, 1000); // 1 second long press
                        });

                        energyContainer.addEventListener('mouseup', () => {
                            clearTimeout(longPressTimer);
                        });

                        energyContainer.addEventListener('mouseleave', () => {
                            clearTimeout(longPressTimer);
                        });

                        // Touch events for mobile
                        energyContainer.addEventListener('touchstart', (e) => {
                            e.preventDefault();
                            isLongPress = false;
                            longPressTimer = setTimeout(() => {
                                isLongPress = true;
                                showAPIKeySetup();
                            }, 1000);
                        });

                        energyContainer.addEventListener('touchend', (e) => {
                            e.preventDefault();
                            clearTimeout(longPressTimer);
                            // If it wasn't a long press, treat as regular click
                            if (!isLongPress) {
                                refreshAllData();
                            }
                        });

                        energyContainer.addEventListener('touchcancel', () => {
                            clearTimeout(longPressTimer);
                        });

                        // Regular click handler (for mouse)
                        energyContainer.addEventListener('click', (e) => {
                            // Only refresh if it wasn't a long press
                            if (!isLongPress) {
                                refreshAllData();
                            }
                        });                        // Create progress bar container
                        const progressContainer = document.createElement('div');
                        progressContainer.style.position = 'relative';
                        progressContainer.style.width = '80px';
                        progressContainer.style.height = '12px';
                        progressContainer.style.backgroundColor = '#2a2a2a';
                        progressContainer.style.borderRadius = '8px';
                        progressContainer.style.overflow = 'hidden';
                        progressContainer.style.border = '1px solid #444';
 
                        // Create progress bar fill
                        const progressBar = document.createElement('div');
                        const percentage = (currentEnergy / maxEnergy) * 100;
                        progressBar.style.width = `${percentage}%`;
                        progressBar.style.height = '100%';
                        progressBar.style.backgroundColor = '#0ea01fff';
                        progressBar.style.borderRadius = '8px';
                        progressBar.style.transition = 'width 0.3s ease';
 
                        // Create text display (overlaid on bar)
                        const textDisplay = document.createElement('span');
                        textDisplay.textContent = `${currentEnergy}/${maxEnergy}`;
                        textDisplay.style.position = 'absolute';
                        textDisplay.style.top = '50%';
                        textDisplay.style.left = '50%';
                        textDisplay.style.transform = 'translate(-50%, -50%)';
                        textDisplay.style.fontSize = '10px';
                        textDisplay.style.color = '#fff';
                        textDisplay.style.fontWeight = 'bold';
                        textDisplay.style.textShadow = '1px 1px 2px rgba(0,0,0,0.8)';
                        textDisplay.style.zIndex = '10';
 
                        progressContainer.appendChild(progressBar);
                        progressContainer.appendChild(textDisplay);
                        energyContainer.appendChild(progressContainer);
 
                        attackerUsernameElement.parentNode.insertBefore(energyContainer, attackerUsernameElement.nextSibling);
                    } else {
                        setTimeout(insertEnergyDisplay, 200);
                    }
                }
                insertEnergyDisplay();
            });
    }
 
    // Initialize script if API key is available
    if (API_KEY) {
        initializeScript();
    }
})();