- // ==UserScript==
- // @name Torn Background Theme Editor
- // @namespace http://tampermonkey.net/
- // @version 1
- // @description Change Torn's background with predefined themes and custom colors
- // @author TR0LL [2561502]
- // @match https://www.torn.com/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @license 420
- // ==/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"),
- 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;
- }
- }
-
- // ===== 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 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-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"><a href="https://greasyfork.org/en/users/1431907-theeeunknown" target="_blank">Created by TR0LL [2561502]</a></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 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);
- }
- });
-
- // 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);
- });
-
- // 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;
- }
-
- /* 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();
- }
-
- // 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();
- })();