您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Change Torn's background with predefined themes and custom colors, with dark mode toggle
当前为
// ==UserScript== // @name Torn.com Background Theme Editor // @namespace http://tampermonkey.net/ // @version 2.1 // @description Change Torn's background with predefined themes and custom colors, with dark mode toggle // @author TR0LL [2561502] // @match https://www.torn.com/* // @grant GM_getValue // @grant GM_setValue // @license Proprietary // ==/UserScript== (function() { "use strict"; // ===== Configuration ===== const CONFIG = { defaultBgColor: "#000000", selectorContentContainer: ".content.responsive-sidebar-container.logged-in", themes: { pureBlack: { name: "Pure Black", color: "#000000" }, darkGray: { name: "Dark Gray", color: "#121212" }, midnightBlue: { name: "Midnight Blue", color: "#0A1929" }, princess: { name: "Princess", color: "#c80e71" }, custom: { name: "Custom", color: null // Will use user's custom color } } }; // ===== State Management ===== const state = { bgColor: GM_getValue("bgColor", CONFIG.defaultBgColor), currentTheme: GM_getValue("currentTheme", "pureBlack"), isDarkMode: GM_getValue("isDarkMode", false), isObserving: false, isPanelVisible: false }; // ===== DOM Manipulation ===== function applyBackgroundColor(color) { const contentContainer = document.querySelector(CONFIG.selectorContentContainer); if (contentContainer) { contentContainer.style.backgroundColor = color; return true; } return false; } function saveBackgroundColor(color, themeName = null) { // Validate color format if (!/^#[0-9A-F]{6}$/i.test(color)) { console.warn("Invalid color format:", color); return false; } state.bgColor = color; GM_setValue("bgColor", color); // Save theme if provided if (themeName) { state.currentTheme = themeName; GM_setValue("currentTheme", themeName); } return applyBackgroundColor(color); } // Get current background color based on theme function getCurrentThemeColor() { if (state.currentTheme === "custom") { return state.bgColor; } else if (CONFIG.themes[state.currentTheme]) { return CONFIG.themes[state.currentTheme].color; } else { return CONFIG.defaultBgColor; } } // ===== Dark Mode Handling ===== function toggleDarkMode(setDarkMode = null) { // If a specific state is provided, use it, otherwise toggle const targetDarkMode = setDarkMode !== null ? setDarkMode : !state.isDarkMode; // Direct method: Toggle the dark-mode class on the body const bodyElement = document.getElementById('body') || document.body; if (bodyElement) { if (targetDarkMode) { bodyElement.classList.add('dark-mode'); } else { bodyElement.classList.remove('dark-mode'); } // Also try to update the checkbox state to match (for consistency) const darkModeCheckbox = document.querySelector("#dark-mode-state"); if (darkModeCheckbox) { darkModeCheckbox.checked = targetDarkMode; } // Update our state state.isDarkMode = targetDarkMode; GM_setValue("isDarkMode", targetDarkMode); } } // ===== Observer for Dynamic Content ===== const observer = new MutationObserver((mutations) => { // Only reapply if we found actual DOM changes that might affect our target const shouldReapply = mutations.some(mutation => mutation.type === 'childList' || (mutation.type === 'attributes' && (mutation.attributeName === 'style' || mutation.attributeName === 'class')) ); if (shouldReapply) { applyBackgroundColor(getCurrentThemeColor()); } }); function startObserving() { if (state.isObserving) return; const contentContainer = document.querySelector(CONFIG.selectorContentContainer); if (contentContainer) { observer.observe(contentContainer, { attributes: true, childList: true, subtree: false // Only observe direct children }); state.isObserving = true; } } function stopObserving() { observer.disconnect(); state.isObserving = false; } // ===== UI Creation ===== function createUI() { // Add stylesheet addStyles(); // Create the toggle button (small, with moon icon) const toggleButton = document.createElement("div"); toggleButton.className = "bg-theme-toggle"; toggleButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>`; document.body.appendChild(toggleButton); // Create the panel const panel = document.createElement("div"); panel.id = "bg-theme-panel"; panel.className = "bg-theme-panel"; // Add header let panelHTML = ` <div class="bg-theme-header"> <span>Background Theme</span> <span class="bg-theme-close">×</span> </div> <div class="bg-theme-content"> <div class="bg-theme-group"> <label for="bg-theme-select">Theme:</label> <select id="bg-theme-select" class="bg-theme-select">`; // Add theme options Object.keys(CONFIG.themes).forEach(themeKey => { const selected = themeKey === state.currentTheme ? 'selected' : ''; panelHTML += `<option value="${themeKey}" ${selected}>${CONFIG.themes[themeKey].name}</option>`; }); // Add custom color section, dark mode toggle, and buttons panelHTML += ` </select> </div> <div id="custom-color-group" class="bg-theme-group" style="display: ${state.currentTheme === 'custom' ? 'block' : 'none'}"> <label for="bg-theme-color">Custom Color:</label> <div class="bg-theme-color-preview" id="color-preview"></div> <div class="bg-theme-color-inputs"> <input type="color" id="bg-theme-color-picker" value="${state.bgColor}"> <input type="text" id="bg-theme-hex" value="${state.bgColor}" placeholder="#RRGGBB"> </div> </div> <div class="bg-theme-group"> <div class="bg-theme-dark-mode"> <label for="bg-theme-dark-mode-toggle">Force Dark Mode:</label> <label class="bg-theme-switch"> <input type="checkbox" id="bg-theme-dark-mode-toggle" ${state.isDarkMode ? 'checked' : ''}> <span class="bg-theme-slider"></span> </label> </div> </div> <div class="bg-theme-buttons"> <button id="bg-theme-reset" class="bg-theme-button">Reset</button> <button id="bg-theme-save" class="bg-theme-button bg-theme-save">Save</button> </div> <div id="bg-theme-save-indicator" class="bg-theme-save-indicator">Saved!</div> <div class="bg-theme-credit">TR0LL [2561502]</div> </div> `; panel.innerHTML = panelHTML; document.body.appendChild(panel); // Get references to elements const themeSelect = document.getElementById('bg-theme-select'); const colorGroup = document.getElementById('custom-color-group'); const colorPreview = document.getElementById('color-preview'); const colorPicker = document.getElementById('bg-theme-color-picker'); const hexInput = document.getElementById('bg-theme-hex'); const darkModeToggle = document.getElementById('bg-theme-dark-mode-toggle'); const resetButton = document.getElementById('bg-theme-reset'); const saveButton = document.getElementById('bg-theme-save'); const saveIndicator = document.getElementById('bg-theme-save-indicator'); // Set initial color preview colorPreview.style.backgroundColor = state.bgColor; // Event Listeners toggleButton.addEventListener('click', () => { state.isPanelVisible = !state.isPanelVisible; panel.classList.toggle('visible', state.isPanelVisible); }); panel.querySelector('.bg-theme-close').addEventListener('click', () => { state.isPanelVisible = false; panel.classList.remove('visible'); }); // Theme select change themeSelect.addEventListener('change', function() { const selectedTheme = this.value; state.currentTheme = selectedTheme; // Show/hide custom color controls colorGroup.style.display = selectedTheme === 'custom' ? 'block' : 'none'; // Apply theme color immediately if (selectedTheme === 'custom') { applyBackgroundColor(state.bgColor); } else { const themeColor = CONFIG.themes[selectedTheme].color; colorPicker.value = themeColor; hexInput.value = themeColor; colorPreview.style.backgroundColor = themeColor; applyBackgroundColor(themeColor); } }); // Color picker change colorPicker.addEventListener('input', function() { const newColor = this.value; state.bgColor = newColor; hexInput.value = newColor; colorPreview.style.backgroundColor = newColor; applyBackgroundColor(newColor); }); // Hex input change hexInput.addEventListener('input', function() { let value = this.value; // Auto-add hash if missing if (!value.startsWith('#') && value.length > 0) { value = '#' + value; this.value = value; } // For complete valid hex codes, update immediately if (/^#[0-9A-Fa-f]{6}$/i.test(value)) { colorPicker.value = value; colorPreview.style.backgroundColor = value; state.bgColor = value; applyBackgroundColor(value); } }); // Dark mode toggle darkModeToggle.addEventListener('change', function() { toggleDarkMode(this.checked); }); // Reset button resetButton.addEventListener('click', () => { // Reset to Pure Black theme themeSelect.value = 'pureBlack'; state.currentTheme = 'pureBlack'; colorGroup.style.display = 'none'; const pureBlackColor = CONFIG.themes.pureBlack.color; colorPicker.value = pureBlackColor; hexInput.value = pureBlackColor; colorPreview.style.backgroundColor = pureBlackColor; applyBackgroundColor(pureBlackColor); // Reset dark mode darkModeToggle.checked = false; toggleDarkMode(false); }); // Save button saveButton.addEventListener('click', () => { if (state.currentTheme === 'custom') { saveBackgroundColor(colorPicker.value, 'custom'); } else { saveBackgroundColor(CONFIG.themes[state.currentTheme].color, state.currentTheme); } // Show save confirmation saveIndicator.classList.add('visible'); setTimeout(() => { saveIndicator.classList.remove('visible'); }, 2000); // Close panel state.isPanelVisible = false; panel.classList.remove('visible'); }); return panel; } // Show save indicator function showSaveIndicator() { const indicator = document.getElementById('bg-theme-save-indicator'); if (indicator) { indicator.classList.add('visible'); setTimeout(() => { indicator.classList.remove('visible'); }, 2000); } } // Add CSS styles function addStyles() { const styleElement = document.createElement('style'); styleElement.textContent = ` /* Toggle Button */ .bg-theme-toggle { position: fixed; right: 10px; top: 100px; width: 32px; height: 32px; background-color: #333; border-radius: 50%; color: white; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 9998; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); transition: transform 0.2s; } .bg-theme-toggle:hover { transform: scale(1.1); } /* Panel */ .bg-theme-panel { position: fixed; right: -250px; top: 100px; width: 220px; background-color: #222; border-radius: 5px; z-index: 9999; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); color: #eee; font-family: Arial, sans-serif; font-size: 13px; transition: right 0.3s ease; } .bg-theme-panel.visible { right: 10px; } /* Header */ .bg-theme-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; border-bottom: 1px solid #444; font-weight: bold; } .bg-theme-close { cursor: pointer; font-size: 20px; width: 20px; height: 20px; line-height: 20px; text-align: center; } .bg-theme-close:hover { color: #ff4444; } /* Content */ .bg-theme-content { padding: 15px; } .bg-theme-group { margin-bottom: 15px; } .bg-theme-group label { display: block; margin-bottom: 5px; font-weight: bold; color: #ccc; } .bg-theme-select { width: 100%; padding: 7px; background-color: #333; border: 1px solid #444; color: #eee; border-radius: 3px; } /* Color Preview */ .bg-theme-color-preview { height: 25px; width: 100%; margin-bottom: 8px; border: 1px solid #444; border-radius: 3px; } /* Color Inputs */ .bg-theme-color-inputs { display: flex; gap: 8px; } #bg-theme-color-picker { width: 40px; height: 30px; padding: 0; border: 1px solid #444; cursor: pointer; } #bg-theme-hex { flex: 1; padding: 6px 8px; background-color: #333; border: 1px solid #444; color: #eee; border-radius: 3px; font-family: monospace; } /* Dark Mode Switch */ .bg-theme-dark-mode { display: flex; justify-content: space-between; align-items: center; } .bg-theme-switch { position: relative; display: inline-block; width: 44px; height: 22px; } .bg-theme-switch input { opacity: 0; width: 0; height: 0; } .bg-theme-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #555; transition: .4s; border-radius: 22px; } .bg-theme-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .bg-theme-slider { background-color: #4CAF50; } input:checked + .bg-theme-slider:before { transform: translateX(22px); } /* Buttons */ .bg-theme-buttons { display: flex; gap: 10px; margin-top: 15px; } .bg-theme-button { flex: 1; padding: 8px 0; background-color: #444; border: none; border-radius: 3px; color: #eee; font-weight: bold; cursor: pointer; transition: background-color 0.2s; } .bg-theme-button:hover { background-color: #555; } .bg-theme-save { background-color: #4CAF50; } .bg-theme-save:hover { background-color: #3e8e41; } /* Save Indicator */ .bg-theme-save-indicator { text-align: center; margin-top: 10px; color: #4CAF50; opacity: 0; transition: opacity 0.3s; font-weight: bold; } .bg-theme-save-indicator.visible { opacity: 1; } /* Credit */ .bg-theme-credit { margin-top: 10px; text-align: center; font-size: 11px; color: #777; } /* Mobile Responsiveness */ @media (max-width: 768px) { .bg-theme-toggle { width: 28px; height: 28px; right: 5px; top: 70px; } .bg-theme-panel { width: 190px; } .bg-theme-panel.visible { right: 5px; } } `; document.head.appendChild(styleElement); } // ===== Initialization ===== function init() { // Set initial color based on saved theme const currentColor = getCurrentThemeColor(); // Apply saved background color to all pages if (!applyBackgroundColor(currentColor)) { // If element not found immediately, wait for DOM to be ready window.addEventListener("DOMContentLoaded", () => { applyBackgroundColor(currentColor); startObserving(); }); } else { startObserving(); } // Set dark mode state based on saved preference if (state.isDarkMode) { toggleDarkMode(true); } // Only create UI on preferences page const currentUrl = window.location.href; if (currentUrl.includes("/preferences.php")) { // Wait for DOM to be ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", createUI); } else { createUI(); } } } // Start the script init(); })();