Torn.com Background Theme Editor

Change Torn's background with predefined themes and custom colors, with dark mode toggle

目前为 2025-03-20 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Torn.com Background Theme Editor
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.0
  5. // @description Change Torn's background with predefined themes and custom colors, with dark mode toggle
  6. // @author TR0LL [2561502]
  7. // @match https://www.torn.com/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @license Proprietary
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. "use strict";
  15.  
  16. const CONFIG = {
  17. defaultBgColor: "#191919",
  18. darkModeBgColor: "#242424",
  19. selectorContentContainer: ".content.responsive-sidebar-container.logged-in",
  20. themes: {
  21. pureBlack: { name: "Pure Black", color: "#000000" },
  22. midnightBlue: { name: "Midnight Blue", color: "#1a2a42" },
  23. princess: { name: "Princess", color: "#c80e71" },
  24. custom: { name: "Custom", color: null }
  25. }
  26. };
  27.  
  28. const state = {
  29. bgColor: GM_getValue("bgColor", "#191919"),
  30. currentTheme: GM_getValue("currentTheme", null),
  31. isDarkMode: GM_getValue("isDarkMode", false),
  32. isObserving: false,
  33. isPanelVisible: false
  34. };
  35.  
  36. const domCache = { contentContainer: null, bodyElement: null, darkModeCheckbox: null };
  37.  
  38. function getContentContainer() {
  39. return domCache.contentContainer || (domCache.contentContainer = document.querySelector(CONFIG.selectorContentContainer));
  40. }
  41.  
  42. function getBodyElement() {
  43. return domCache.bodyElement || (domCache.bodyElement = document.getElementById('body') || document.body);
  44. }
  45.  
  46. function getDarkModeCheckbox() {
  47. return domCache.darkModeCheckbox || (domCache.darkModeCheckbox = document.querySelector("#dark-mode-state"));
  48. }
  49.  
  50. function throttle(func, delay) {
  51. let lastCall = 0;
  52. return function(...args) {
  53. const now = Date.now();
  54. if (now - lastCall >= delay) {
  55. lastCall = now;
  56. func.apply(this, args);
  57. }
  58. };
  59. }
  60.  
  61. function getAutoDetectedColor() {
  62. const isDarkModeActive = getBodyElement() && getBodyElement().classList.contains('dark-mode');
  63. return isDarkModeActive ? CONFIG.darkModeBgColor : CONFIG.defaultBgColor;
  64. }
  65.  
  66. function applyBackgroundColor(color) {
  67. const contentContainer = getContentContainer();
  68. if (contentContainer) {
  69. contentContainer.style.backgroundColor = color;
  70. return true;
  71. }
  72. return false;
  73. }
  74.  
  75. function saveBackgroundColor(color, themeName = null) {
  76. if (!/^#[0-9A-F]{6}$/i.test(color)) return false;
  77. state.bgColor = color;
  78. GM_setValue("bgColor", color);
  79. if (themeName !== undefined) {
  80. state.currentTheme = themeName;
  81. GM_setValue("currentTheme", themeName);
  82. }
  83. return applyBackgroundColor(color);
  84. }
  85.  
  86. function getCurrentThemeColor() {
  87. if (state.currentTheme === "custom") {
  88. return state.bgColor;
  89. } else if (state.currentTheme && CONFIG.themes[state.currentTheme]) {
  90. return CONFIG.themes[state.currentTheme].color;
  91. } else {
  92. return getAutoDetectedColor();
  93. }
  94. }
  95.  
  96. function toggleDarkMode(setDarkMode = null) {
  97. const targetDarkMode = setDarkMode !== null ? setDarkMode : !state.isDarkMode;
  98.  
  99. // Update our internal state
  100. state.isDarkMode = targetDarkMode;
  101. GM_setValue("isDarkMode", targetDarkMode);
  102.  
  103. // Get the body element
  104. const bodyElement = getBodyElement();
  105. if (bodyElement) {
  106. // Directly toggle class on body
  107. if (targetDarkMode) {
  108. bodyElement.classList.add('dark-mode');
  109. } else {
  110. bodyElement.classList.remove('dark-mode');
  111. }
  112. }
  113.  
  114. // Find Torn's own dark mode checkbox and update it
  115. const darkModeCheckbox = getDarkModeCheckbox();
  116. if (darkModeCheckbox) {
  117. // Only update if needed
  118. if (darkModeCheckbox.checked !== targetDarkMode) {
  119. darkModeCheckbox.checked = targetDarkMode;
  120.  
  121. // Trigger a change event to update Torn's internal state
  122. const changeEvent = new Event('change', { bubbles: true });
  123. darkModeCheckbox.dispatchEvent(changeEvent);
  124.  
  125. // Update the visual toggle as well by clicking the label if we're on preferences page
  126. if (window.location.href.includes("/preferences.php")) {
  127. const darkModeLabel = darkModeCheckbox.nextElementSibling;
  128. if (darkModeLabel && darkModeLabel.classList.contains('marker-css')) {
  129. darkModeLabel.click();
  130. }
  131. }
  132. }
  133. }
  134.  
  135. // Update our toggle if it exists
  136. const ourToggle = document.getElementById('bg-theme-dark-mode-toggle');
  137. if (ourToggle && ourToggle.checked !== targetDarkMode) {
  138. ourToggle.checked = targetDarkMode;
  139. }
  140. }
  141.  
  142. const observer = new MutationObserver((mutations) => {
  143. const shouldReapply = mutations.some(mutation =>
  144. (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||
  145. (mutation.type === 'attributes' &&
  146. (mutation.attributeName === 'style' ||
  147. mutation.attributeName === 'class'))
  148. );
  149.  
  150. if (shouldReapply) {
  151. const isDarkModeActive = getBodyElement() && getBodyElement().classList.contains('dark-mode');
  152.  
  153. if (state.isDarkMode !== isDarkModeActive) {
  154. toggleDarkMode(isDarkModeActive);
  155. }
  156.  
  157. // Only apply background color if we have a theme selected
  158. if (state.currentTheme) {
  159. applyBackgroundColor(getCurrentThemeColor());
  160. }
  161. }
  162. });
  163.  
  164. function startObserving() {
  165. if (state.isObserving) return;
  166.  
  167. const contentContainer = getContentContainer();
  168. if (contentContainer) {
  169. observer.observe(contentContainer, {
  170. attributes: true,
  171. attributeFilter: ['style', 'class'],
  172. childList: true,
  173. subtree: false
  174. });
  175.  
  176. const bodyElement = getBodyElement();
  177. if (bodyElement) {
  178. observer.observe(bodyElement, {
  179. attributes: true,
  180. attributeFilter: ['class'],
  181. subtree: false
  182. });
  183. }
  184.  
  185. state.isObserving = true;
  186. }
  187. }
  188.  
  189. function syncWithTornDarkMode() {
  190. // First, sync with body class state
  191. const bodyElement = getBodyElement();
  192. if (bodyElement) {
  193. const hasDarkModeClass = bodyElement.classList.contains('dark-mode');
  194. if (state.isDarkMode !== hasDarkModeClass) {
  195. state.isDarkMode = hasDarkModeClass;
  196. GM_setValue("isDarkMode", hasDarkModeClass);
  197.  
  198. // Update our toggle if it exists
  199. const ourToggle = document.getElementById('bg-theme-dark-mode-toggle');
  200. if (ourToggle) {
  201. ourToggle.checked = hasDarkModeClass;
  202. }
  203. }
  204. }
  205.  
  206. // Watch for changes to Torn's native dark mode checkbox
  207. const darkModeCheckbox = getDarkModeCheckbox();
  208. if (darkModeCheckbox) {
  209. // Add event listener to keep our state in sync
  210. darkModeCheckbox.addEventListener('change', function() {
  211. const isChecked = this.checked;
  212.  
  213. // Update our state
  214. state.isDarkMode = isChecked;
  215. GM_setValue("isDarkMode", isChecked);
  216.  
  217. // Update our toggle
  218. const ourToggle = document.getElementById('bg-theme-dark-mode-toggle');
  219. if (ourToggle && ourToggle.checked !== isChecked) {
  220. ourToggle.checked = isChecked;
  221. }
  222.  
  223. // Update body class
  224. const bodyElement = getBodyElement();
  225. if (bodyElement) {
  226. if (isChecked) {
  227. bodyElement.classList.add('dark-mode');
  228. } else {
  229. bodyElement.classList.remove('dark-mode');
  230. }
  231. }
  232. });
  233. }
  234.  
  235. // Observe body element for class changes
  236. const darkModeObserver = new MutationObserver((mutations) => {
  237. mutations.forEach(mutation => {
  238. if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
  239. const hasDarkModeClass = mutation.target.classList.contains('dark-mode');
  240.  
  241. // Only update if there's a change
  242. if (state.isDarkMode !== hasDarkModeClass) {
  243. state.isDarkMode = hasDarkModeClass;
  244. GM_setValue("isDarkMode", hasDarkModeClass);
  245.  
  246. // Update our toggle
  247. const ourToggle = document.getElementById('bg-theme-dark-mode-toggle');
  248. if (ourToggle && ourToggle.checked !== hasDarkModeClass) {
  249. ourToggle.checked = hasDarkModeClass;
  250. }
  251.  
  252. // Update Torn's checkbox
  253. const darkModeCheckbox = getDarkModeCheckbox();
  254. if (darkModeCheckbox && darkModeCheckbox.checked !== hasDarkModeClass) {
  255. darkModeCheckbox.checked = hasDarkModeClass;
  256. }
  257. }
  258. }
  259. });
  260. });
  261.  
  262. // Start observing body
  263. if (bodyElement) {
  264. darkModeObserver.observe(bodyElement, {
  265. attributes: true,
  266. attributeFilter: ['class']
  267. });
  268. }
  269. }
  270.  
  271. function createUI() {
  272. addStyles();
  273.  
  274. const toggleButton = document.createElement("div");
  275. toggleButton.className = "bg-theme-toggle";
  276. 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>`;
  277. document.body.appendChild(toggleButton);
  278.  
  279. const panel = document.createElement("div");
  280. panel.id = "bg-theme-panel";
  281. panel.className = "bg-theme-panel";
  282.  
  283. let panelHTML = `
  284. <div class="bg-theme-header">
  285. <span>Background Theme</span>
  286. <span class="bg-theme-close">×</span>
  287. </div>
  288. <div class="bg-theme-content">
  289. <div class="bg-theme-group">
  290. <label for="bg-theme-select">Theme:</label>
  291. <select id="bg-theme-select" class="bg-theme-select">`;
  292.  
  293. Object.keys(CONFIG.themes).forEach(themeKey => {
  294. const selected = themeKey === state.currentTheme ? 'selected' : '';
  295. panelHTML += `<option value="${themeKey}" ${selected}>${CONFIG.themes[themeKey].name}</option>`;
  296. });
  297.  
  298. panelHTML += `
  299. </select>
  300. </div>
  301.  
  302. <div id="custom-color-group" class="bg-theme-group" style="display: ${state.currentTheme === 'custom' ? 'block' : 'none'}">
  303. <label for="bg-theme-color">Custom Color:</label>
  304. <div class="bg-theme-color-preview" id="color-preview"></div>
  305. <div class="bg-theme-color-inputs">
  306. <input type="color" id="bg-theme-color-picker" value="${state.bgColor}">
  307. <input type="text" id="bg-theme-hex" value="${state.bgColor}" placeholder="#RRGGBB">
  308. </div>
  309. </div>
  310.  
  311. <div class="bg-theme-group">
  312. <div class="bg-theme-dark-mode">
  313. <label for="bg-theme-dark-mode-toggle">
  314. <div style="display: flex; align-items: center;">
  315. <div class="dark-mode-icon">
  316. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-6.01 -4 28.005 28.001" fill="#ccc" style="margin-right: 5px;">
  317. <path d="M2.42,13.57a8.28,8.28,0,0,1,0-11.71A8.37,8.37,0,0,1,5.29,0,8.28,8.28,0,0,0,16,10.71,8.28,8.28,0,0,1,5.29,15.44a8.15,8.15,0,0,1-2.87-1.87Zm8.67-5.46L9.16,7.23l2.08-.42.24-2.1,1,1.85,2.07-.42L13.16,7.69l1,1.85-1.92-.88-1.43,1.56Zm-2.9-2.8L7.28,6l.35-1.1-.91-.66H7.84l.35-1.07.35,1.07H9.66l-.91.66L9.1,6Zm3.67-2.78H10.41l1.15-.88L11.08.32l1.19.82L13.41.26,13,1.65l1.19.82H12.74l-.41,1.39Z"></path>
  318. </svg>
  319. </div>
  320. Dark Mode:
  321. </div>
  322. </label>
  323. <label class="bg-theme-switch">
  324. <input type="checkbox" id="bg-theme-dark-mode-toggle" ${state.isDarkMode ? 'checked' : ''}>
  325. <span class="bg-theme-slider"></span>
  326. </label>
  327. </div>
  328. </div>
  329.  
  330. <div class="bg-theme-buttons">
  331. <button id="bg-theme-reset" class="bg-theme-button">Reset to Default</button>
  332. <button id="bg-theme-save" class="bg-theme-button bg-theme-save">Save</button>
  333. </div>
  334.  
  335. <div id="bg-theme-save-indicator" class="bg-theme-save-indicator">Saved!</div>
  336. <div class="bg-theme-credit">TR0LL [2561502]</div>
  337. </div>
  338. `;
  339.  
  340. panel.innerHTML = panelHTML;
  341. document.body.appendChild(panel);
  342.  
  343. const themeSelect = document.getElementById('bg-theme-select');
  344. const colorGroup = document.getElementById('custom-color-group');
  345. const colorPreview = document.getElementById('color-preview');
  346. const colorPicker = document.getElementById('bg-theme-color-picker');
  347. const hexInput = document.getElementById('bg-theme-hex');
  348. const darkModeToggle = document.getElementById('bg-theme-dark-mode-toggle');
  349. const resetButton = document.getElementById('bg-theme-reset');
  350. const saveButton = document.getElementById('bg-theme-save');
  351. const saveIndicator = document.getElementById('bg-theme-save-indicator');
  352.  
  353. colorPreview.style.backgroundColor = state.bgColor;
  354.  
  355. toggleButton.addEventListener('click', () => {
  356. state.isPanelVisible = !state.isPanelVisible;
  357. panel.classList.toggle('visible', state.isPanelVisible);
  358. });
  359.  
  360. panel.querySelector('.bg-theme-close').addEventListener('click', () => {
  361. state.isPanelVisible = false;
  362. panel.classList.remove('visible');
  363. });
  364.  
  365. themeSelect.addEventListener('change', function() {
  366. const selectedTheme = this.value;
  367. state.currentTheme = selectedTheme;
  368. colorGroup.style.display = selectedTheme === 'custom' ? 'block' : 'none';
  369.  
  370. if (selectedTheme === 'custom') {
  371. colorPicker.value = state.bgColor;
  372. hexInput.value = state.bgColor;
  373. colorPreview.style.backgroundColor = state.bgColor;
  374. applyBackgroundColor(state.bgColor);
  375. } else if (selectedTheme && CONFIG.themes[selectedTheme]) {
  376. const themeColor = CONFIG.themes[selectedTheme].color;
  377. colorPicker.value = themeColor;
  378. hexInput.value = themeColor;
  379. colorPreview.style.backgroundColor = themeColor;
  380. applyBackgroundColor(themeColor);
  381. }
  382. });
  383.  
  384. const throttledColorApply = throttle((newColor) => {
  385. state.bgColor = newColor;
  386. hexInput.value = newColor;
  387. colorPreview.style.backgroundColor = newColor;
  388. applyBackgroundColor(newColor);
  389. }, 50);
  390.  
  391. colorPicker.addEventListener('input', function() {
  392. throttledColorApply(this.value);
  393. });
  394.  
  395. colorPicker.addEventListener('change', function() {
  396. const newColor = this.value;
  397. state.bgColor = newColor;
  398. hexInput.value = newColor;
  399. colorPreview.style.backgroundColor = newColor;
  400. applyBackgroundColor(newColor);
  401. });
  402.  
  403. hexInput.addEventListener('input', function() {
  404. let value = this.value;
  405. if (!value.startsWith('#') && value.length > 0) {
  406. value = '#' + value;
  407. this.value = value;
  408. }
  409. if (/^#[0-9A-Fa-f]{6}$/i.test(value)) {
  410. colorPicker.value = value;
  411. colorPreview.style.backgroundColor = value;
  412. state.bgColor = value;
  413. applyBackgroundColor(value);
  414. }
  415. });
  416.  
  417. // Dark mode toggle
  418. darkModeToggle.addEventListener('change', function() {
  419. // Toggle Torn's dark mode directly
  420. const tornDarkModeCheckbox = getDarkModeCheckbox();
  421. const isChecked = this.checked;
  422.  
  423. // Update our state
  424. state.isDarkMode = isChecked;
  425. GM_setValue("isDarkMode", isChecked);
  426.  
  427. // Add or remove dark-mode class from body
  428. const bodyElement = getBodyElement();
  429. if (bodyElement) {
  430. if (isChecked) {
  431. bodyElement.classList.add('dark-mode');
  432. } else {
  433. bodyElement.classList.remove('dark-mode');
  434. }
  435. }
  436.  
  437. // If we found Torn's checkbox, update it too
  438. if (tornDarkModeCheckbox) {
  439. tornDarkModeCheckbox.checked = isChecked;
  440.  
  441. // Create and dispatch a change event
  442. const changeEvent = new Event('change', { bubbles: true });
  443. tornDarkModeCheckbox.dispatchEvent(changeEvent);
  444.  
  445. // If on preferences page, click the actual UI element
  446. if (window.location.href.includes("/preferences.php")) {
  447. const darkModeLabel = tornDarkModeCheckbox.nextElementSibling;
  448. if (darkModeLabel && darkModeLabel.classList.contains('marker-css')) {
  449. darkModeLabel.click();
  450. }
  451. }
  452. }
  453. });
  454.  
  455. resetButton.addEventListener('click', () => {
  456. // Remove any color changes
  457. applyBackgroundColor('');
  458.  
  459. // Force Light Mode - turn off dark mode both in Torn and our toggle
  460. const bodyElement = getBodyElement();
  461. if (bodyElement && bodyElement.classList.contains('dark-mode')) {
  462. // Turn off dark mode in Torn (this will propagate to our UI via observers)
  463. const darkModeCheckbox = getDarkModeCheckbox();
  464. if (darkModeCheckbox && darkModeCheckbox.checked) {
  465. darkModeCheckbox.checked = false;
  466. const changeEvent = new Event('change', { bubbles: true });
  467. darkModeCheckbox.dispatchEvent(changeEvent);
  468. }
  469.  
  470. // Make sure our state matches
  471. toggleDarkMode(false);
  472. }
  473.  
  474. // Set our UI to match
  475. darkModeToggle.checked = false;
  476.  
  477. // Clear theme selection
  478. state.currentTheme = null;
  479. GM_setValue("currentTheme", null);
  480. themeSelect.selectedIndex = -1;
  481.  
  482. // Hide custom color controls
  483. colorGroup.style.display = 'none';
  484.  
  485. // Show confirmation
  486. saveIndicator.classList.add('visible');
  487. setTimeout(() => {
  488. saveIndicator.classList.remove('visible');
  489. }, 2000);
  490. });
  491.  
  492. saveButton.addEventListener('click', () => {
  493. if (state.currentTheme === 'custom') {
  494. saveBackgroundColor(colorPicker.value, 'custom');
  495. } else if (state.currentTheme && CONFIG.themes[state.currentTheme]) {
  496. saveBackgroundColor(CONFIG.themes[state.currentTheme].color, state.currentTheme);
  497. }
  498.  
  499. saveIndicator.classList.add('visible');
  500. setTimeout(() => {
  501. saveIndicator.classList.remove('visible');
  502. }, 2000);
  503.  
  504. state.isPanelVisible = false;
  505. panel.classList.remove('visible');
  506. });
  507.  
  508. return panel;
  509. }
  510.  
  511. function addStyles() {
  512. const styleElement = document.createElement('style');
  513. styleElement.textContent = `.bg-theme-toggle{position:fixed;right:10px;top:100px;width:32px;height:32px;background-color:#333;border-radius:50%;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:9998;box-shadow:0 2px 5px rgba(0,0,0,.3);transition:transform .2s}.bg-theme-toggle:hover{transform:scale(1.1)}.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,.3);color:#eee;font-family:Arial,sans-serif;font-size:13px;transition:right .3s ease}.bg-theme-panel.visible{right:10px}.bg-theme-header{display:flex;justify-content:space-between;align-items:center;padding:10px 15px;border-bottom:1px solid #444;font-weight:700}.bg-theme-close{cursor:pointer;font-size:20px;width:20px;height:20px;line-height:20px;text-align:center}.bg-theme-close:hover{color:#f44}.bg-theme-content{padding:15px}.bg-theme-group{margin-bottom:15px}.bg-theme-group label{display:block;margin-bottom:5px;font-weight:700;color:#ccc}.bg-theme-select{width:100%;padding:7px;background-color:#333;border:1px solid #444;color:#eee;border-radius:3px}.bg-theme-color-preview{height:25px;width:100%;margin-bottom:8px;border:1px solid #444;border-radius:3px}.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}.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:#fff;transition:.4s;border-radius:50%}input:checked+.bg-theme-slider{background-color:#4caf50}input:checked+.bg-theme-slider:before{transform:translateX(22px)}.bg-theme-buttons{display:flex;gap:10px;margin-top:15px}.dark-mode-icon svg:hover{fill:#fff}.bg-theme-button{flex:1;padding:8px 0;background-color:#444;border:none;border-radius:3px;color:#eee;font-weight:700;cursor:pointer;transition:background-color .2s}.bg-theme-button:hover{background-color:#555}.bg-theme-save{background-color:#4caf50}.bg-theme-save:hover{background-color:#3e8e41}.bg-theme-save-indicator{text-align:center;margin-top:10px;color:#4caf50;opacity:0;transition:opacity .3s;font-weight:700}.bg-theme-save-indicator.visible{opacity:1}.bg-theme-credit{margin-top:10px;text-align:center;font-size:11px;color:#777}@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}}`;
  514. document.head.appendChild(styleElement);
  515. }
  516.  
  517. function init() {
  518. const bodyElement = getBodyElement();
  519. const isDarkModeActive = bodyElement && bodyElement.classList.contains('dark-mode');
  520.  
  521. if (state.isDarkMode !== isDarkModeActive) {
  522. toggleDarkMode(isDarkModeActive);
  523. }
  524.  
  525. // Only apply background color if we have a theme selected
  526. if (state.currentTheme) {
  527. const currentColor = getCurrentThemeColor();
  528.  
  529. if (!applyBackgroundColor(currentColor)) {
  530. window.addEventListener("DOMContentLoaded", () => {
  531. applyBackgroundColor(getCurrentThemeColor());
  532. startObserving();
  533. syncWithTornDarkMode();
  534. });
  535. } else {
  536. startObserving();
  537. syncWithTornDarkMode();
  538. }
  539. } else {
  540. startObserving();
  541. syncWithTornDarkMode();
  542. }
  543.  
  544. const currentUrl = window.location.href;
  545. // Show UI on preferences page and profile pages
  546. if (currentUrl.includes("/preferences.php") || currentUrl.includes("/profiles.php?XID=")) {
  547. if (document.readyState === "loading") {
  548. document.addEventListener("DOMContentLoaded", createUI);
  549. } else {
  550. createUI();
  551. }
  552. }
  553. }
  554.  
  555. init();
  556. })();