您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Lets you change the speed of any website. Made with ChatGPT, later enhanced by Claude.
当前为
// ==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); } })();