Universal HTML5 Speed Hack (Pro Enhanced)

Lets you change the speed of any website. Made with ChatGPT, later enhanced by Claude.

Mint 2025.07.27.. Lásd a legutóbbi verzió

// ==UserScript==
// @name         Universal HTML5 Speed Hack (Pro Enhanced)
// @namespace    https://greasyforks.org/users/1498931-kubabreak
// @version      2.1
// @description  Lets you change the speed of any website. Made with ChatGPT, later enhanced by Claude.
// @author       kubabreak
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CONFIG = {
        defaultSpeed: 1.0,
        minSpeed: 0.001,
        maxSpeed: 100.0,
        step: 0.001,
        uiUpdateInterval: 100,
        favoritePresets: [0.1, 0.25, 0.5, 1, 2, 5, 10]
    };

    // Global state
    let globalSpeed = CONFIG.defaultSpeed;
    let uiElement = null;
    let isInitialized = false;
    let startTime = Date.now();

    const globalState = {
        perfNow: false,
        dateNow: false,
        setTimeout: false,
        setInterval: false,
        raf: false,
        clearTimeouts: true,
        customPresets: [],
        debugLogging: false,
        autoInjectFrames: true,
        persistentUI: true
    };

    // Storage for persistence
    const STORAGE_KEY = 'speedhack_settings';

    function saveSettings() {
        try {
            const settings = {
                speed: globalSpeed,
                state: globalState,
                uiPosition: uiElement ? {
                    left: uiElement.style.left,
                    top: uiElement.style.top
                } : null
            };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
        } catch (e) {
            console.warn('[SpeedHack] Could not save settings:', e);
        }
    }

    function loadSettings() {
        try {
            const saved = localStorage.getItem(STORAGE_KEY);
            if (saved) {
                const settings = JSON.parse(saved);
                globalSpeed = settings.speed || CONFIG.defaultSpeed;
                Object.assign(globalState, settings.state || {});
                return settings;
            }
        } catch (e) {
            console.warn('[SpeedHack] Could not load settings:', e);
        }
        return null;
    }

    function createInjectionScript(speed, state) {
        return `
        (function() {
            if (window.__speedHackInjected) return;
            window.__speedHackInjected = true;

            const realPerfNow = performance.now.bind(performance);
            const realDateNow = Date.now;
            const realSetTimeout = window.setTimeout;
            const realSetInterval = window.setInterval;
            const realClearTimeout = window.clearTimeout;
            const realClearInterval = window.clearInterval;
            const realRAF = window.requestAnimationFrame;
            const realCAF = window.cancelAnimationFrame;

            let currentSpeed = ${speed};
            let perfStartTime = realPerfNow();
            let virtualStartTime = perfStartTime;
            let dateStartTime = realDateNow();
            let virtualDateStart = dateStartTime;

            const timeoutMap = new Map();
            const intervalMap = new Map();
            let timeoutId = 1;
            let intervalId = 1;

            // Performance.now override
            ${state.perfNow ? `
            performance.now = function() {
                const realNow = realPerfNow();
                const realElapsed = realNow - perfStartTime;
                return virtualStartTime + (realElapsed * currentSpeed);
            };
            ` : ''}

            // Date.now override
            ${state.dateNow ? `
            Date.now = function() {
                const realNow = realPerfNow();
                const realElapsed = realNow - perfStartTime;
                return Math.floor(virtualDateStart + (realElapsed * currentSpeed));
            };
            ` : ''}

            // setTimeout override
            ${state.setTimeout ? `
            window.setTimeout = function(callback, delay, ...args) {
                if (typeof callback !== 'function') return realSetTimeout(callback, delay, ...args);

                const adjustedDelay = Math.max(0, delay / currentSpeed);
                const id = timeoutId++;
                const realId = realSetTimeout(() => {
                    timeoutMap.delete(id);
                    callback.apply(this, args);
                }, adjustedDelay);

                timeoutMap.set(id, realId);
                return id;
            };

            ${state.clearTimeouts ? `
            window.clearTimeout = function(id) {
                const realId = timeoutMap.get(id);
                if (realId !== undefined) {
                    timeoutMap.delete(id);
                    return realClearTimeout(realId);
                }
                return realClearTimeout(id);
            };
            ` : ''}
            ` : ''}

            // setInterval override
            ${state.setInterval ? `
            window.setInterval = function(callback, delay, ...args) {
                if (typeof callback !== 'function') return realSetInterval(callback, delay, ...args);

                const adjustedDelay = Math.max(1, delay / currentSpeed);
                const id = intervalId++;
                const realId = realSetInterval(() => {
                    callback.apply(this, args);
                }, adjustedDelay);

                intervalMap.set(id, realId);
                return id;
            };

            ${state.clearTimeouts ? `
            window.clearInterval = function(id) {
                const realId = intervalMap.get(id);
                if (realId !== undefined) {
                    intervalMap.delete(id);
                    return realClearInterval(realId);
                }
                return realClearInterval(id);
            };
            ` : ''}
            ` : ''}

            // requestAnimationFrame override
            ${state.raf ? `
            window.requestAnimationFrame = function(callback) {
                return realRAF((timestamp) => {
                    const virtualTime = performance.now();
                    callback(virtualTime);
                });
            };
            ` : ''}

            // Update speed function
            window.__updateSpeedHack = function(newSpeed, newState) {
                currentSpeed = newSpeed;

                // Update time references to prevent jumps
                const realNow = realPerfNow();
                const currentVirtualTime = virtualStartTime + ((realNow - perfStartTime) * currentSpeed);
                perfStartTime = realNow;
                virtualStartTime = currentVirtualTime;

                const currentVirtualDate = virtualDateStart + ((realNow - perfStartTime) * currentSpeed);
                virtualDateStart = currentVirtualDate;
            };

            ${state.debugLogging ? `console.log('[SpeedHack] Injected at ' + currentSpeed + 'x speed');` : ''}
        })();
        `;
    }

    function injectIntoWindow(targetWindow, speed, state) {
        try {
            if (!targetWindow || !targetWindow.document) return false;

            const script = targetWindow.document.createElement('script');
            script.textContent = createInjectionScript(speed, state);
            script.setAttribute('data-speedhack', 'true');

            const target = targetWindow.document.documentElement || targetWindow.document.head || targetWindow.document.body;
            if (target) {
                target.appendChild(script);
                return true;
            }
        } catch (e) {
            if (globalState.debugLogging) {
                console.warn('[SpeedHack] Injection failed:', e);
            }
        }
        return false;
    }

    function updateAllWindows() {
        // Update main window
        injectIntoWindow(window, globalSpeed, globalState);
        if (window.__updateSpeedHack) {
            window.__updateSpeedHack(globalSpeed, globalState);
        }

        // Update all iframes if enabled
        if (globalState.autoInjectFrames) {
            const frames = document.querySelectorAll('iframe');
            frames.forEach(frame => {
                try {
                    const frameWindow = frame.contentWindow;
                    if (frameWindow && frameWindow.document) {
                        injectIntoWindow(frameWindow, globalSpeed, globalState);
                        if (frameWindow.__updateSpeedHack) {
                            frameWindow.__updateSpeedHack(globalSpeed, globalState);
                        }
                    }
                } catch (e) {
                    // Cross-origin iframe, ignore
                }
            });
        }
    }

    function createUI() {
        if (uiElement) return;

        const ui = document.createElement('div');
        ui.id = 'speedhack-ui';
        ui.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            padding: 16px;
            background: linear-gradient(135deg, rgba(0,0,0,0.95), rgba(30,30,30,0.95));
            color: #fff;
            z-index: 2147483647;
            border-radius: 12px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            font-size: 13px;
            min-width: 280px;
            max-width: 320px;
            box-shadow: 0 8px 32px rgba(0,0,0,0.4);
            border: 1px solid rgba(255,255,255,0.15);
            backdrop-filter: blur(20px);
            user-select: none;
            transition: all 0.3s ease;
        `;

        ui.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                <strong style="color: #4CAF50; font-size: 14px;">⚡ Speed Hack Pro</strong>
                <div style="display: flex; gap: 8px;">
                    <button id="speedhack-settings" style="background: rgba(255,255,255,0.1); border: none; color: #fff; cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; transition: background 0.2s;">⚙️</button>
                    <button id="speedhack-minimize" style="background: rgba(255,255,255,0.1); border: none; color: #fff; cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; transition: background 0.2s;">−</button>
                </div>
            </div>
            <div id="speedhack-content">
                <div style="margin-bottom: 12px;">
                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; font-size: 12px;">
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-perfnow" style="margin-right: 6px; accent-color: #4CAF50;"> performance.now()
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-datenow" style="margin-right: 6px; accent-color: #4CAF50;"> Date.now()
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-settimeout" style="margin-right: 6px; accent-color: #4CAF50;"> setTimeout
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-setinterval" style="margin-right: 6px; accent-color: #4CAF50;"> setInterval
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s; grid-column: span 2;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-raf" style="margin-right: 6px; accent-color: #4CAF50;"> requestAnimationFrame
                        </label>
                    </div>
                </div>
                <hr style="border: none; border-top: 1px solid rgba(255,255,255,0.2); margin: 12px 0;">

                <!-- Speed Control Section -->
                <div style="margin-bottom: 12px;">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                        <span style="font-weight: 500;">Speed Control:</span>
                        <span id="speed-display" style="color: #4CAF50; font-weight: bold; font-size: 14px;">${globalSpeed.toFixed(3)}x</span>
                    </div>

                    <!-- Custom Speed Input -->
                    <div style="display: flex; gap: 8px; margin-bottom: 8px;">
                        <input type="number" id="speed-input"
                               value="${globalSpeed}"
                               min="${CONFIG.minSpeed}"
                               max="${CONFIG.maxSpeed}"
                               step="any"
                               placeholder="Enter speed..."
                               style="flex: 1; padding: 6px 8px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 6px; font-size: 12px; outline: none; transition: all 0.2s;"
                               onfocus="this.style.borderColor='#4CAF50'; this.style.background='rgba(76,175,80,0.1)'"
                               onblur="this.style.borderColor='rgba(255,255,255,0.2)'; this.style.background='rgba(255,255,255,0.1)'">
                        <button id="speed-apply" style="padding: 6px 12px; background: #4CAF50; border: none; color: white; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500; transition: background 0.2s;" onmouseover="this.style.background='#45a049'" onmouseout="this.style.background='#4CAF50'">Apply</button>
                    </div>

                    <!-- Speed Slider -->
                    <input type="range" id="speed-slider"
                           min="${Math.log(CONFIG.minSpeed)}"
                           max="${Math.log(CONFIG.maxSpeed)}"
                           value="${Math.log(globalSpeed)}"
                           step="0.01"
                           style="width: 100%; margin-bottom: 8px; accent-color: #4CAF50;">

                    <!-- Quick Presets -->
                    <div style="display: flex; gap: 4px; margin-bottom: 8px;">
                        <button class="speed-preset" data-speed="0.1" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.1x</button>
                        <button class="speed-preset" data-speed="0.25" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.25x</button>
                        <button class="speed-preset" data-speed="0.5" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.5x</button>
                        <button class="speed-preset" data-speed="1" style="flex: 1; padding: 4px; font-size: 10px; background: #4CAF50; border: 1px solid #4CAF50; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#45a049'" onmouseout="this.style.background='#4CAF50'">1x</button>
                        <button class="speed-preset" data-speed="2" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">2x</button>
                        <button class="speed-preset" data-speed="5" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">5x</button>
                        <button class="speed-preset" data-speed="10" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">10x</button>
                    </div>

                    <!-- Advanced Controls -->
                    <div style="display: flex; gap: 4px;">
                        <button id="speed-save-preset" style="flex: 1; padding: 6px; font-size: 11px; background: rgba(255,193,7,0.8); border: 1px solid #FFC107; color: #000; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;" onmouseover="this.style.background='#FFC107'" onmouseout="this.style.background='rgba(255,193,7,0.8)'">💾 Save</button>
                        <button id="speed-reset" style="flex: 1; padding: 6px; font-size: 11px; background: rgba(244,67,54,0.8); border: 1px solid #F44336; color: #fff; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;" onmouseover="this.style.background='#F44336'" onmouseout="this.style.background='rgba(244,67,54,0.8)'">🔄 Reset</button>
                    </div>
                </div>

                <!-- Statistics -->
                <div id="speed-stats" style="font-size: 11px; color: #aaa; text-align: center; padding: 8px; background: rgba(255,255,255,0.05); border-radius: 6px;">
                    <div>Active: <span id="active-functions">0</span> functions</div>
                    <div>Uptime: <span id="uptime">0s</span></div>
                </div>
            </div>

            <!-- Advanced Settings Panel -->
            <div id="speedhack-settings-panel" style="display: none; margin-top: 12px; padding: 12px; background: rgba(255,255,255,0.05); border-radius: 8px; border-top: 1px solid rgba(255,255,255,0.1);">
                <div style="font-weight: 500; margin-bottom: 8px; color: #4CAF50;">⚙️ Advanced Settings</div>
                <div style="font-size: 12px;">
                    <label style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
                        <input type="checkbox" id="auto-inject-frames" checked style="margin-right: 6px; accent-color: #4CAF50;"> Auto-inject into iframes
                    </label>
                    <label style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
                        <input type="checkbox" id="debug-logging" style="margin-right: 6px; accent-color: #4CAF50;"> Debug logging
                    </label>
                    <label style="display: flex; align-items: center; margin-bottom: 8px; cursor: pointer;">
                        <input type="checkbox" id="persistent-ui" checked style="margin-right: 6px; accent-color: #4CAF50;"> Remember UI position
                    </label>
                    <button id="clear-settings" style="width: 100%; padding: 6px; background: rgba(244,67,54,0.2); border: 1px solid #F44336; color: #F44336; border-radius: 4px; cursor: pointer; font-size: 11px;">Clear All Settings</button>
                </div>
            </div>
        `;

        document.body.appendChild(ui);
        uiElement = ui;

        // Set initial checkbox states
        ui.querySelector('#toggle-perfnow').checked = globalState.perfNow;
        ui.querySelector('#toggle-datenow').checked = globalState.dateNow;
        ui.querySelector('#toggle-settimeout').checked = globalState.setTimeout;
        ui.querySelector('#toggle-setinterval').checked = globalState.setInterval;
        ui.querySelector('#toggle-raf').checked = globalState.raf;
        ui.querySelector('#auto-inject-frames').checked = globalState.autoInjectFrames;
        ui.querySelector('#debug-logging').checked = globalState.debugLogging;
        ui.querySelector('#persistent-ui').checked = globalState.persistentUI;

        // Event listeners
        const speedSlider = ui.querySelector('#speed-slider');
        const speedDisplay = ui.querySelector('#speed-display');
        const speedInput = ui.querySelector('#speed-input');
        const speedApply = ui.querySelector('#speed-apply');

        // Logarithmic slider handling
        function updateSpeedFromSlider() {
            const logValue = parseFloat(speedSlider.value);
            const actualSpeed = Math.exp(logValue);
            globalSpeed = Math.round(actualSpeed * 1000) / 1000; // Round to 3 decimal places
            speedDisplay.textContent = `${globalSpeed.toFixed(3)}x`;
            speedInput.value = globalSpeed;
            updateAllWindows();
            saveSettings();
            updatePresetHighlight();
        }

        function updateSliderFromSpeed(speed) {
            speedSlider.value = Math.log(speed);
            speedDisplay.textContent = `${speed.toFixed(3)}x`;
            speedInput.value = speed;
            updatePresetHighlight();
        }

        function updatePresetHighlight() {
            ui.querySelectorAll('.speed-preset').forEach(btn => {
                const presetSpeed = parseFloat(btn.dataset.speed);
                if (Math.abs(presetSpeed - globalSpeed) < 0.001) {
                    btn.style.background = '#4CAF50';
                    btn.style.borderColor = '#4CAF50';
                } else {
                    btn.style.background = '#333';
                    btn.style.borderColor = '#555';
                }
            });
        }

        // Speed input validation and application
        function applyCustomSpeed() {
            let inputValue = parseFloat(speedInput.value);
            if (isNaN(inputValue) || inputValue <= 0) {
                speedInput.value = globalSpeed;
                speedInput.style.borderColor = '#F44336';
                setTimeout(() => {
                    speedInput.style.borderColor = 'rgba(255,255,255,0.2)';
                }, 1000);
                return;
            }

            inputValue = Math.max(CONFIG.minSpeed, Math.min(CONFIG.maxSpeed, inputValue));
            globalSpeed = inputValue;
            updateSliderFromSpeed(globalSpeed);
            updateAllWindows();
            saveSettings();

            // Visual feedback
            speedApply.textContent = '✓';
            speedApply.style.background = '#4CAF50';
            setTimeout(() => {
                speedApply.textContent = 'Apply';
                speedApply.style.background = '#4CAF50';
            }, 500);
        }

        speedSlider.addEventListener('input', updateSpeedFromSlider);
        speedApply.addEventListener('click', applyCustomSpeed);
        speedInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                applyCustomSpeed();
                speedInput.blur();
            }
        });

        // Update statistics
        function updateStats() {
            const activeFunctions = Object.values(globalState).filter((val, key) =>
                typeof val === 'boolean' && val && key < 5
            ).length;
            const uptime = Math.floor((Date.now() - startTime) / 1000);

            const statsElement = ui.querySelector('#active-functions');
            const uptimeElement = ui.querySelector('#uptime');

            if (statsElement) statsElement.textContent = activeFunctions;
            if (uptimeElement) {
                if (uptime < 60) {
                    uptimeElement.textContent = `${uptime}s`;
                } else if (uptime < 3600) {
                    uptimeElement.textContent = `${Math.floor(uptime/60)}m ${uptime%60}s`;
                } else {
                    uptimeElement.textContent = `${Math.floor(uptime/3600)}h ${Math.floor((uptime%3600)/60)}m`;
                }
            }
        }

        setInterval(updateStats, 1000);
        updateStats();

        // Checkbox listeners
        ui.querySelector('#toggle-perfnow').addEventListener('change', (e) => {
            globalState.perfNow = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-datenow').addEventListener('change', (e) => {
            globalState.dateNow = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-settimeout').addEventListener('change', (e) => {
            globalState.setTimeout = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-setinterval').addEventListener('change', (e) => {
            globalState.setInterval = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-raf').addEventListener('change', (e) => {
            globalState.raf = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        // Advanced settings listeners
        ui.querySelector('#auto-inject-frames').addEventListener('change', (e) => {
            globalState.autoInjectFrames = e.target.checked;
            saveSettings();
        });

        ui.querySelector('#debug-logging').addEventListener('change', (e) => {
            globalState.debugLogging = e.target.checked;
            saveSettings();
        });

        ui.querySelector('#persistent-ui').addEventListener('change', (e) => {
            globalState.persistentUI = e.target.checked;
            saveSettings();
        });

        // Preset buttons with improved handling
        ui.querySelectorAll('.speed-preset').forEach(button => {
            button.addEventListener('click', () => {
                const speed = parseFloat(button.dataset.speed);
                globalSpeed = speed;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            });
        });

        // Advanced controls
        ui.querySelector('#speed-save-preset').addEventListener('click', () => {
            const presetName = prompt(`Save current speed (${globalSpeed.toFixed(3)}x) as preset:`, `${globalSpeed.toFixed(3)}x`);
            if (presetName) {
                if (!globalState.customPresets) globalState.customPresets = [];
                globalState.customPresets.push({ name: presetName, speed: globalSpeed });
                saveSettings();

                // Show confirmation
                const btn = ui.querySelector('#speed-save-preset');
                const originalText = btn.textContent;
                btn.textContent = '✓ Saved';
                setTimeout(() => btn.textContent = originalText, 1000);
            }
        });

        ui.querySelector('#speed-reset').addEventListener('click', () => {
            if (confirm('Reset speed to 1.0x?')) {
                globalSpeed = 1.0;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Settings panel toggle
        ui.querySelector('#speedhack-settings').addEventListener('click', () => {
            const panel = ui.querySelector('#speedhack-settings-panel');
            panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
        });

        // Settings panel controls
        ui.querySelector('#clear-settings').addEventListener('click', () => {
            if (confirm('Clear all saved settings? This will reset everything to defaults.')) {
                localStorage.removeItem(STORAGE_KEY);
                location.reload();
            }
        });

        // Minimize button
        ui.querySelector('#speedhack-minimize').addEventListener('click', () => {
            const content = ui.querySelector('#speedhack-content');
            const button = ui.querySelector('#speedhack-minimize');
            const settingsPanel = ui.querySelector('#speedhack-settings-panel');

            if (content.style.display === 'none') {
                content.style.display = 'block';
                button.textContent = '−';
                ui.style.minWidth = '280px';
            } else {
                content.style.display = 'none';
                settingsPanel.style.display = 'none';
                button.textContent = '+';
                ui.style.minWidth = 'auto';
                ui.style.width = 'auto';
            }
        });

        // Make draggable with improved positioning
        let isDragging = false;
        let dragOffset = { x: 0, y: 0 };

        ui.addEventListener('mousedown', (e) => {
            if (!e.target.matches('input, button, label')) {
                isDragging = true;
                dragOffset.x = e.clientX - ui.offsetLeft;
                dragOffset.y = e.clientY - ui.offsetTop;
                ui.style.cursor = 'grabbing';
                ui.style.transition = 'none';
            }
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const newX = Math.max(0, Math.min(window.innerWidth - ui.offsetWidth, e.clientX - dragOffset.x));
                const newY = Math.max(0, Math.min(window.innerHeight - ui.offsetHeight, e.clientY - dragOffset.y));

                ui.style.left = newX + 'px';
                ui.style.top = newY + 'px';
                ui.style.right = 'auto';
                ui.style.bottom = 'auto';
            }
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                ui.style.cursor = 'default';
                ui.style.transition = 'all 0.3s ease';
                if (globalState.persistentUI) {
                    saveSettings();
                }
            }
        });

        // Keyboard shortcuts
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey) {
                switch(e.code) {
                    case 'KeyS':
                        e.preventDefault();
                        const panel = ui.querySelector('#speedhack-settings-panel');
                        panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
                        break;
                    case 'KeyR':
                        e.preventDefault();
                        globalSpeed = 1.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'KeyH':
                        e.preventDefault();
                        const content = ui.querySelector('#speedhack-content');
                        const button = ui.querySelector('#speedhack-minimize');
                        if (content.style.display === 'none') {
                            content.style.display = 'block';
                            button.textContent = '−';
                        } else {
                            content.style.display = 'none';
                            button.textContent = '+';
                        }
                        break;
                    case 'Digit1':
                        e.preventDefault();
                        globalSpeed = 0.1;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit2':
                        e.preventDefault();
                        globalSpeed = 0.25;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit3':
                        e.preventDefault();
                        globalSpeed = 0.5;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit4':
                        e.preventDefault();
                        globalSpeed = 1.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit5':
                        e.preventDefault();
                        globalSpeed = 2.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit6':
                        e.preventDefault();
                        globalSpeed = 5.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit7':
                        e.preventDefault();
                        globalSpeed = 10.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                }
            }
        });

        // Load saved UI position if persistent UI is enabled
        const savedSettings = loadSettings();
        if (globalState.persistentUI && savedSettings && savedSettings.uiPosition) {
            if (savedSettings.uiPosition.left && savedSettings.uiPosition.top) {
                ui.style.left = savedSettings.uiPosition.left;
                ui.style.top = savedSettings.uiPosition.top;
                ui.style.right = 'auto';
            }
        }

        // Initial slider position and preset highlight
        updateSliderFromSpeed(globalSpeed);

        // Add mouse wheel support for speed adjustment
        ui.addEventListener('wheel', (e) => {
            if (e.ctrlKey) {
                e.preventDefault();
                const delta = e.deltaY > 0 ? 0.9 : 1.1;
                globalSpeed = Math.max(CONFIG.minSpeed, Math.min(CONFIG.maxSpeed, globalSpeed * delta));
                globalSpeed = Math.round(globalSpeed * 1000) / 1000;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Add double-click to reset speed
        ui.addEventListener('dblclick', (e) => {
            if (e.target === speedDisplay) {
                globalSpeed = 1.0;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Add context menu for quick actions
        ui.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            const contextMenu = document.createElement('div');
            contextMenu.style.cssText = `
                position: fixed;
                left: ${e.clientX}px;
                top: ${e.clientY}px;
                background: rgba(0,0,0,0.9);
                border: 1px solid rgba(255,255,255,0.2);
                border-radius: 6px;
                padding: 8px 0;
                z-index: 2147483648;
                font-size: 12px;
                min-width: 150px;
                backdrop-filter: blur(10px);
            `;

            const menuItems = [
                { text: 'Reset to 1.0x', action: () => { globalSpeed = 1.0; updateSliderFromSpeed(globalSpeed); updateAllWindows(); saveSettings(); }},
                { text: 'Toggle All Functions', action: () => {
                    const allEnabled = Object.values(globalState).slice(0, 5).every(v => v);
                    ['perfNow', 'dateNow', 'setTimeout', 'setInterval', 'raf'].forEach(key => {
                        globalState[key] = !allEnabled;
                        ui.querySelector(`#toggle-${key.toLowerCase()}`).checked = !allEnabled;
                    });
                    updateAllWindows(); saveSettings();
                }},
                { text: 'Copy Current Speed', action: () => {
                    navigator.clipboard.writeText(globalSpeed.toString());
                    speedDisplay.textContent = 'Copied!';
                    setTimeout(() => speedDisplay.textContent = `${globalSpeed.toFixed(3)}x`, 1000);
                }},
                { text: 'Export Settings', action: () => {
                    const settings = JSON.stringify({ speed: globalSpeed, state: globalState }, null, 2);
                    navigator.clipboard.writeText(settings);
                    alert('Settings copied to clipboard!');
                }}
            ];

            menuItems.forEach(item => {
                const menuItem = document.createElement('div');
                menuItem.textContent = item.text;
                menuItem.style.cssText = `
                    padding: 6px 12px;
                    cursor: pointer;
                    color: #fff;
                    transition: background 0.2s;
                `;
                menuItem.addEventListener('mouseover', () => menuItem.style.background = 'rgba(255,255,255,0.1)');
                menuItem.addEventListener('mouseout', () => menuItem.style.background = 'transparent');
                menuItem.addEventListener('click', () => {
                    item.action();
                    document.body.removeChild(contextMenu);
                });
                contextMenu.appendChild(menuItem);
            });

            document.body.appendChild(contextMenu);

            // Remove context menu when clicking elsewhere
            const removeMenu = (e) => {
                if (!contextMenu.contains(e.target)) {
                    document.body.removeChild(contextMenu);
                    document.removeEventListener('click', removeMenu);
                }
            };
            setTimeout(() => document.addEventListener('click', removeMenu), 100);
        });

        // Add notification system
        function showNotification(message, type = 'info') {
            const notification = document.createElement('div');
            notification.style.cssText = `
                position: fixed;
                top: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: ${type === 'error' ? 'rgba(244,67,54,0.9)' : 'rgba(76,175,80,0.9)'};
                color: white;
                padding: 8px 16px;
                border-radius: 6px;
                font-size: 12px;
                z-index: 2147483648;
                backdrop-filter: blur(10px);
                animation: slideDown 0.3s ease;
            `;

            const style = document.createElement('style');
            style.textContent = `
                @keyframes slideDown {
                    from { opacity: 0; transform: translateX(-50%) translateY(-20px); }
                    to { opacity: 1; transform: translateX(-50%) translateY(0); }
                }
            `;
            document.head.appendChild(style);

            notification.textContent = message;
            document.body.appendChild(notification);

            setTimeout(() => {
                document.body.removeChild(notification);
                document.head.removeChild(style);
            }, 3000);
        }

        // Add success notification for settings save/load
        const originalSaveSettings = saveSettings;
        window.speedHackShowNotification = showNotification;
    }

    function initialize() {
        if (isInitialized) return;
        isInitialized = true;

        loadSettings();

        // Initial injection
        updateAllWindows();

        // Create UI when DOM is ready
        if (document.body) {
            createUI();
        } else {
            const observer = new MutationObserver((mutations, obs) => {
                if (document.body) {
                    createUI();
                    obs.disconnect();
                }
            });
            observer.observe(document, { childList: true, subtree: true });
        }

        // Monitor for new iframes
        if (globalState.autoInjectFrames) {
            const frameObserver = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const iframes = node.tagName === 'IFRAME' ? [node] : node.querySelectorAll('iframe');
                            iframes.forEach((iframe) => {
                                iframe.addEventListener('load', () => {
                                    setTimeout(() => updateAllWindows(), 100);
                                });
                            });
                        }
                    });
                });
            });

            frameObserver.observe(document, { childList: true, subtree: true });
        }

        // Add window resize handler for UI positioning
        window.addEventListener('resize', () => {
            if (uiElement && globalState.persistentUI) {
                const rect = uiElement.getBoundingClientRect();
                if (rect.right > window.innerWidth) {
                    uiElement.style.left = (window.innerWidth - uiElement.offsetWidth - 10) + 'px';
                }
                if (rect.bottom > window.innerHeight) {
                    uiElement.style.top = (window.innerHeight - uiElement.offsetHeight - 10) + 'px';
                }
            }
        });

        if (globalState.debugLogging) {
            console.log('[SpeedHack] Pro Enhanced version initialized successfully');
        }
    }

    // Initialize immediately if possible, otherwise wait for DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        setTimeout(initialize, 100);
    }

})();
长期地址
遇到问题?请前往 GitHub 提 Issues。