Universal Web Liberator

Regain Control: Unlocks RightClick/Selection/CopyPaste/Drag On Any Website, Toggle Status With Bottom-Right Button or Ctrl/Meta+Alt+L or Menu Command.

  1. // ==UserScript==
  2. // @name Universal Web Liberator
  3. // @name:zh-CN 网页枷锁破除
  4. // @name:zh-TW 網頁枷鎖破除
  5. // @description Regain Control: Unlocks RightClick/Selection/CopyPaste/Drag On Any Website, Toggle Status With Bottom-Right Button or Ctrl/Meta+Alt+L or Menu Command.
  6. // @description:zh-CN 解除网页右键/选择/复制及拖拽限制 恢复自由交互体验 单击右下角图标或使用 Ctrl/Meta+Alt+L 或油猴菜单切换状态
  7. // @description:zh-TW 解除網頁右鍵/選取/複製及拖曳限制 恢復自由互動體驗 單擊右下角圖標或使用 Ctrl/Meta+Alt+L 或油猴菜單切換狀態
  8. // @version 1.3.0
  9. // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/UniversalWebLiberatorIcon.svg
  10. // @author 念柚
  11. // @namespace https://github.com/MiPoNianYou/UserScripts
  12. // @supportURL https://github.com/MiPoNianYou/UserScripts/issues
  13. // @license GPL-3.0
  14. // @match *://*/*
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_addStyle
  18. // @grant GM_registerMenuCommand
  19. // @grant GM_unregisterMenuCommand
  20. // @run-at document-start
  21. // ==/UserScript==
  22.  
  23. (function () {
  24. "use strict";
  25.  
  26. function debounce(func, wait) {
  27. let timeout;
  28. return function executedFunction(...args) {
  29. const later = () => {
  30. clearTimeout(timeout);
  31. func.apply(this, args);
  32. };
  33. clearTimeout(timeout);
  34. timeout = setTimeout(later, wait);
  35. };
  36. }
  37.  
  38. const localizedStrings = {
  39. "zh-CN": {
  40. scriptTitle: "网页枷锁破除",
  41. stateEnabledText: "脚本已启用 ✅",
  42. stateDisabledText: "脚本已禁用 ❌",
  43. },
  44. "zh-TW": {
  45. scriptTitle: "網頁枷鎖破除",
  46. stateEnabledText: "腳本已啟用 ✅",
  47. stateDisabledText: "腳本已禁用 ❌",
  48. },
  49. "en-US": {
  50. scriptTitle: "Universal Web Liberator",
  51. stateEnabledText: "Liberator Activated ✅",
  52. stateDisabledText: "Liberator Deactivated ❌",
  53. },
  54. };
  55.  
  56. function detectUserLanguage() {
  57. const languages = navigator.languages || [navigator.language];
  58. for (const lang of languages) {
  59. const langLower = lang.toLowerCase();
  60. if (langLower === "zh-cn") return "zh-CN";
  61. if (
  62. langLower === "zh-tw" ||
  63. langLower === "zh-hk" ||
  64. langLower === "zh-mo"
  65. )
  66. return "zh-TW";
  67. if (langLower === "en-us") return "en-US";
  68. if (langLower.startsWith("zh-")) return "zh-CN";
  69. if (langLower.startsWith("en-")) return "en-US";
  70. }
  71. for (const lang of languages) {
  72. const langLower = lang.toLowerCase();
  73. if (langLower.startsWith("zh")) return "zh-CN";
  74. if (langLower.startsWith("en")) return "en-US";
  75. }
  76. return "en-US";
  77. }
  78.  
  79. class WebLiberator {
  80. static EventsToStop = [
  81. "contextmenu",
  82. "selectstart",
  83. "copy",
  84. "cut",
  85. "paste",
  86. "dragstart",
  87. "drag",
  88. ];
  89. static InlineEventPropsToClear = [
  90. "oncontextmenu",
  91. "onselectstart",
  92. "oncopy",
  93. "oncut",
  94. "onpaste",
  95. "ondrag",
  96. "ondragstart",
  97. "onmousedown",
  98. "onselect",
  99. "onbeforecopy",
  100. "onbeforecut",
  101. "onbeforepaste",
  102. ];
  103. static ScriptIconUrl =
  104. "https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/UniversalWebLiberatorIcon.svg";
  105. static NotificationId = "WebLiberatorNotification";
  106. static MenuButtonId = "WebLiberatorMenuButton";
  107. static NotificationTimeout = 2500;
  108. static AnimationDuration = 300;
  109. static STORAGE_KEY_PREFIX = "webLiberator_state_";
  110. static DEFAULT_ACTIVE_STATE = false;
  111.  
  112. observer = null;
  113. liberationStyleElement = null;
  114. menuButtonElement = null;
  115. isActive = WebLiberator.DEFAULT_ACTIVE_STATE;
  116. boundStopHandler = null;
  117. notificationTimer = null;
  118. removalTimer = null;
  119. currentOrigin = window.location.origin;
  120. locale = "en-US";
  121. strings = {};
  122. menuCommandId = null;
  123.  
  124. constructor() {
  125. this.locale = detectUserLanguage();
  126. this.strings = localizedStrings[this.locale] || localizedStrings["en-US"];
  127. this.boundStopHandler = this.stopImmediatePropagationHandler.bind(this);
  128. }
  129.  
  130. getOriginStorageKey() {
  131. const origin = String(this.currentOrigin || "").replace(/\/$/, "");
  132. return `${WebLiberator.STORAGE_KEY_PREFIX}${origin}`;
  133. }
  134.  
  135. loadState() {
  136. const storageKey = this.getOriginStorageKey();
  137. const defaultStateString = WebLiberator.DEFAULT_ACTIVE_STATE
  138. ? "enabled"
  139. : "disabled";
  140. let storedValue = defaultStateString;
  141. try {
  142. storedValue = GM_getValue(storageKey, defaultStateString);
  143. } catch (e) {}
  144. if (storedValue !== "enabled" && storedValue !== "disabled") {
  145. storedValue = defaultStateString;
  146. }
  147. this.isActive = storedValue === "enabled";
  148. return this.isActive;
  149. }
  150.  
  151. saveState() {
  152. const storageKey = this.getOriginStorageKey();
  153. const valueToStore = this.isActive ? "enabled" : "disabled";
  154. try {
  155. GM_setValue(storageKey, valueToStore);
  156. } catch (e) {}
  157. }
  158.  
  159. activate() {
  160. if (this.isActive) return;
  161. this.isActive = true;
  162. this.injectLiberationStyles();
  163. this.bindGlobalEventHijackers();
  164. this.processExistingNodes(document.documentElement);
  165. this.initMutationObserver();
  166. this.updateMenuStatus();
  167. }
  168.  
  169. deactivate() {
  170. if (!this.isActive) return;
  171. this.isActive = false;
  172. this.removeLiberationStyles();
  173. this.unbindGlobalEventHijackers();
  174. this.disconnectMutationObserver();
  175. this.updateMenuStatus();
  176. }
  177.  
  178. toggle() {
  179. const wasActive = this.isActive;
  180. if (wasActive) {
  181. this.deactivate();
  182. this.showNotification("stateDisabledText");
  183. } else {
  184. this.activate();
  185. this.showNotification("stateEnabledText");
  186. }
  187. this.saveState();
  188. this.updateMenuCommand();
  189. }
  190.  
  191. injectBaseStyles() {
  192. const notificationCSS = `
  193. :root {
  194. --wl-notify-bg-light: rgba(242, 242, 247, 0.85);
  195. --wl-notify-text-light: rgba(60, 60, 67, 0.9);
  196. --wl-notify-title-light: rgba(0, 0, 0, 0.9);
  197. --wl-notify-bg-dark: rgba(44, 44, 46, 0.85);
  198. --wl-notify-text-dark: rgba(235, 235, 245, 0.8);
  199. --wl-notify-title-dark: rgba(255, 255, 255, 0.9);
  200. --wl-shadow-light: 0 6px 20px rgba(100, 100, 100, 0.12);
  201. --wl-shadow-dark: 0 6px 20px rgba(0, 0, 0, 0.3);
  202. }
  203. #${WebLiberator.NotificationId} {
  204. position: fixed;
  205. top: 20px;
  206. right: -400px;
  207. width: 310px;
  208. background-color: var(--wl-notify-bg-dark);
  209. color: var(--wl-notify-text-dark);
  210. padding: 14px 18px;
  211. border-radius: 14px;
  212. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  213. z-index: 2147483646;
  214. box-shadow: var(--wl-shadow-dark);
  215. display: flex;
  216. align-items: center;
  217. opacity: 0;
  218. transition: right ${
  219. WebLiberator.AnimationDuration
  220. }ms cubic-bezier(0.32, 0.72, 0, 1),
  221. opacity ${
  222. WebLiberator.AnimationDuration * 0.8
  223. }ms ease-out;
  224. box-sizing: border-box;
  225. backdrop-filter: blur(18px) saturate(180%);
  226. -webkit-backdrop-filter: blur(18px) saturate(180%);
  227. text-align: left;
  228. border: 1px solid rgba(255, 255, 255, 0.1);
  229. }
  230. #${WebLiberator.NotificationId}.visible {
  231. right: 20px;
  232. opacity: 1;
  233. }
  234. #${WebLiberator.NotificationId} .wl-icon {
  235. width: 30px;
  236. height: 30px;
  237. margin-right: 14px;
  238. flex-shrink: 0;
  239. }
  240. #${WebLiberator.NotificationId} .wl-content {
  241. display: flex;
  242. flex-direction: column;
  243. flex-grow: 1;
  244. min-width: 0;
  245. }
  246. #${WebLiberator.NotificationId} .wl-title {
  247. font-size: 15px;
  248. font-weight: 600;
  249. margin-bottom: 4px;
  250. color: var(--wl-notify-title-dark);
  251. white-space: nowrap;
  252. overflow: hidden;
  253. text-overflow: ellipsis;
  254. }
  255. #${WebLiberator.NotificationId} .wl-message {
  256. font-size: 13px;
  257. line-height: 1.45;
  258. color: var(--wl-notify-text-dark);
  259. word-wrap: break-word;
  260. overflow-wrap: break-word;
  261. }
  262. @media (prefers-color-scheme: light) {
  263. #${WebLiberator.NotificationId} {
  264. background-color: var(--wl-notify-bg-light);
  265. color: var(--wl-notify-text-light);
  266. box-shadow: var(--wl-shadow-light);
  267. border: 1px solid rgba(60, 60, 67, 0.1);
  268. }
  269. #${WebLiberator.NotificationId} .wl-title {
  270. color: var(--wl-notify-title-light);
  271. }
  272. #${WebLiberator.NotificationId} .wl-message {
  273. color: var(--wl-notify-text-light);
  274. }
  275. }
  276. `;
  277.  
  278. const menuCSS = `
  279. :root {
  280. --wl-menu-bg-light: rgba(242, 242, 247, 0.8);
  281. --wl-menu-bg-dark: rgba(44, 44, 46, 0.8);
  282. --wl-shadow-light: 0 4px 15px rgba(100, 100, 100, 0.1);
  283. --wl-shadow-dark: 0 4px 15px rgba(0, 0, 0, 0.25);
  284. }
  285. #${WebLiberator.MenuButtonId} {
  286. position: fixed;
  287. bottom: 25px;
  288. right: 25px;
  289. width: 44px;
  290. height: 44px;
  291. background-color: var(--wl-menu-bg-dark);
  292. border-radius: 50%;
  293. cursor: pointer;
  294. z-index: 2147483647;
  295. box-shadow: var(--wl-shadow-dark);
  296. display: flex;
  297. align-items: center;
  298. justify-content: center;
  299. transition: transform 0.2s cubic-bezier(0.32, 0.72, 0, 1),
  300. background-color 0.2s ease,
  301. opacity 0.2s ease;
  302. backdrop-filter: blur(12px) saturate(180%);
  303. -webkit-backdrop-filter: blur(12px) saturate(180%);
  304. border: 1px solid rgba(255, 255, 255, 0.08);
  305. opacity: 0.7;
  306. user-select: none !important;
  307. -webkit-user-select: none !important;
  308. -moz-user-select: none !important;
  309. -ms-user-select: none !important;
  310. -webkit-user-drag: none !important;
  311. user-drag: none !important;
  312. }
  313. #${WebLiberator.MenuButtonId}:hover {
  314. transform: scale(1.08);
  315. opacity: 1;
  316. }
  317. #${WebLiberator.MenuButtonId} img {
  318. width: 22px;
  319. height: 22px;
  320. display: block;
  321. opacity: 0.9;
  322. transition: opacity 0.2s ease;
  323. pointer-events: none;
  324. }
  325. @media (prefers-color-scheme: light) {
  326. #${WebLiberator.MenuButtonId} {
  327. border: 1px solid rgba(60, 60, 67, 0.15);
  328. box-shadow: var(--wl-shadow-light);
  329. background-color: var(--wl-menu-bg-light);
  330. }
  331. #${WebLiberator.MenuButtonId} img {
  332. opacity: 0.8;
  333. }
  334. }
  335. `;
  336.  
  337. try {
  338. GM_addStyle(notificationCSS);
  339. GM_addStyle(menuCSS);
  340. } catch (e) {}
  341. }
  342.  
  343. injectLiberationStyles() {
  344. if (
  345. this.liberationStyleElement ||
  346. document.getElementById("web-liberator-styles")
  347. )
  348. return;
  349. const css = `
  350. *,
  351. *::before,
  352. *::after {
  353. user-select: text !important;
  354. -webkit-user-select: text !important;
  355. -moz-user-select: text !important;
  356. -ms-user-select: text !important;
  357. cursor: auto !important;
  358. -webkit-user-drag: auto !important;
  359. user-drag: auto !important;
  360. pointer-events: auto !important;
  361. }
  362. body {
  363. cursor: auto !important;
  364. }
  365. ::selection {
  366. background-color: highlight !important;
  367. color: highlighttext !important;
  368. }
  369. ::-moz-selection {
  370. background-color: highlight !important;
  371. color: highlighttext !important;
  372. }
  373. `;
  374. this.liberationStyleElement = document.createElement("style");
  375. this.liberationStyleElement.id = "web-liberator-styles";
  376. this.liberationStyleElement.textContent = css;
  377. (document.head || document.documentElement).appendChild(
  378. this.liberationStyleElement
  379. );
  380. }
  381.  
  382. removeLiberationStyles() {
  383. this.liberationStyleElement?.remove();
  384. this.liberationStyleElement = null;
  385. document.getElementById("web-liberator-styles")?.remove();
  386. }
  387.  
  388. ensureElementsCreated() {
  389. if (
  390. this.menuButtonElement &&
  391. document.body?.contains(this.menuButtonElement)
  392. ) {
  393. this.updateMenuStatus();
  394. return;
  395. }
  396. let existingButton = document.getElementById(WebLiberator.MenuButtonId);
  397. if (existingButton) {
  398. this.menuButtonElement = existingButton;
  399. if (!this.menuButtonElement.dataset.listenerAttached) {
  400. this.menuButtonElement.addEventListener("click", (e) => {
  401. e.stopPropagation();
  402. this.toggle();
  403. });
  404. this.menuButtonElement.dataset.listenerAttached = "true";
  405. }
  406. this.updateMenuStatus();
  407. return;
  408. }
  409. if (document.body) {
  410. this.createMenuElements();
  411. } else {
  412. document.addEventListener(
  413. "DOMContentLoaded",
  414. () => {
  415. this.ensureElementsCreated();
  416. },
  417. { once: true }
  418. );
  419. }
  420. }
  421.  
  422. createMenuElements() {
  423. if (!document.body || document.getElementById(WebLiberator.MenuButtonId))
  424. return;
  425. this.menuButtonElement = document.createElement("div");
  426. this.menuButtonElement.id = WebLiberator.MenuButtonId;
  427. this.menuButtonElement.title = this.strings.scriptTitle;
  428. this.menuButtonElement.innerHTML = `<img src="${WebLiberator.ScriptIconUrl}" alt="Icon">`;
  429. this.menuButtonElement.addEventListener("click", (e) => {
  430. e.stopPropagation();
  431. this.toggle();
  432. });
  433. this.menuButtonElement.dataset.listenerAttached = "true";
  434. document.body.appendChild(this.menuButtonElement);
  435. this.updateMenuStatus();
  436. }
  437.  
  438. updateMenuStatus() {
  439. const button =
  440. this.menuButtonElement ||
  441. document.getElementById(WebLiberator.MenuButtonId);
  442. if (!button) return;
  443. if (!this.menuButtonElement) this.menuButtonElement = button;
  444. const isActive = this.isActive;
  445. const isLightMode = window.matchMedia?.(
  446. "(prefers-color-scheme: light)"
  447. ).matches;
  448. const iconImg = button.querySelector("img");
  449. let buttonBgColor,
  450. iconOpacity,
  451. buttonOpacity = "0.7";
  452. if (isActive) {
  453. buttonBgColor = isLightMode
  454. ? "rgba(52, 199, 89, 0.8)"
  455. : "rgba(48, 209, 88, 0.8)";
  456. iconOpacity = "0.95";
  457. buttonOpacity = "1";
  458. } else {
  459. buttonBgColor = isLightMode
  460. ? "var(--wl-menu-bg-light)"
  461. : "var(--wl-menu-bg-dark)";
  462. iconOpacity = isLightMode ? "0.8" : "0.7";
  463. }
  464. button.style.backgroundColor = buttonBgColor;
  465. button.style.opacity = buttonOpacity;
  466. if (iconImg) iconImg.style.opacity = iconOpacity;
  467. }
  468.  
  469. showNotification(messageKey) {
  470. if (this.notificationTimer) clearTimeout(this.notificationTimer);
  471. if (this.removalTimer) clearTimeout(this.removalTimer);
  472. this.notificationTimer = null;
  473. this.removalTimer = null;
  474. const title = this.strings.scriptTitle;
  475. const message = this.strings[messageKey] || messageKey;
  476. const displayNotification = () => {
  477. let notificationElement = document.getElementById(
  478. WebLiberator.NotificationId
  479. );
  480. if (!notificationElement && document.body) {
  481. notificationElement = document.createElement("div");
  482. notificationElement.id = WebLiberator.NotificationId;
  483. notificationElement.innerHTML =
  484. `<img src="${WebLiberator.ScriptIconUrl}" alt="Icon" class="wl-icon"><div class="wl-content"><div class="wl-title"></div><div class="wl-message"></div></div>`.trim();
  485. document.body.appendChild(notificationElement);
  486. } else if (!notificationElement) return;
  487. const titleElement = notificationElement.querySelector(".wl-title");
  488. const messageElement = notificationElement.querySelector(".wl-message");
  489. if (titleElement) titleElement.textContent = title;
  490. if (messageElement) messageElement.textContent = message;
  491. notificationElement.classList.remove("visible");
  492. void notificationElement.offsetWidth;
  493. requestAnimationFrame(() => {
  494. const currentElement = document.getElementById(
  495. WebLiberator.NotificationId
  496. );
  497. if (currentElement) {
  498. setTimeout(() => {
  499. if (document.getElementById(WebLiberator.NotificationId)) {
  500. currentElement.classList.add("visible");
  501. }
  502. }, 20);
  503. }
  504. });
  505. this.notificationTimer = setTimeout(() => {
  506. const currentElement = document.getElementById(
  507. WebLiberator.NotificationId
  508. );
  509. if (currentElement) {
  510. currentElement.classList.remove("visible");
  511. this.removalTimer = setTimeout(() => {
  512. document.getElementById(WebLiberator.NotificationId)?.remove();
  513. this.notificationTimer = null;
  514. this.removalTimer = null;
  515. }, WebLiberator.AnimationDuration);
  516. } else {
  517. this.notificationTimer = null;
  518. this.removalTimer = null;
  519. }
  520. }, WebLiberator.NotificationTimeout);
  521. };
  522. if (document.readyState === "loading") {
  523. document.addEventListener("DOMContentLoaded", displayNotification, {
  524. once: true,
  525. });
  526. } else {
  527. displayNotification();
  528. }
  529. }
  530.  
  531. stopImmediatePropagationHandler(event) {
  532. event.stopImmediatePropagation();
  533. }
  534.  
  535. bindGlobalEventHijackers() {
  536. WebLiberator.EventsToStop.forEach((type) => {
  537. document.addEventListener(type, this.boundStopHandler, {
  538. capture: true,
  539. passive: false,
  540. });
  541. });
  542. }
  543.  
  544. unbindGlobalEventHijackers() {
  545. WebLiberator.EventsToStop.forEach((type) => {
  546. document.removeEventListener(type, this.boundStopHandler, {
  547. capture: true,
  548. });
  549. });
  550. }
  551.  
  552. processExistingNodes(rootNode) {
  553. if (!this.isActive || !rootNode) return;
  554. this.clearHandlersRecursive(rootNode);
  555. }
  556.  
  557. clearSingleElementHandlers(element) {
  558. if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
  559. for (const prop of WebLiberator.InlineEventPropsToClear) {
  560. if (
  561. prop in element &&
  562. (typeof element[prop] === "function" || element[prop] !== null)
  563. ) {
  564. try {
  565. element[prop] = null;
  566. } catch (e) {}
  567. }
  568. if (element.hasAttribute(prop)) {
  569. try {
  570. element.removeAttribute(prop);
  571. } catch (e) {}
  572. }
  573. }
  574. }
  575.  
  576. clearHandlersRecursive(rootNode) {
  577. if (!this.isActive || !rootNode) return;
  578. try {
  579. if (rootNode.nodeType === Node.ELEMENT_NODE) {
  580. if (
  581. rootNode.id !== WebLiberator.MenuButtonId &&
  582. rootNode.id !== WebLiberator.NotificationId
  583. ) {
  584. this.clearSingleElementHandlers(rootNode);
  585. }
  586. if (rootNode.shadowRoot?.mode === "open")
  587. this.clearHandlersRecursive(rootNode.shadowRoot);
  588. }
  589. const elements = rootNode.querySelectorAll?.("*");
  590. if (elements) {
  591. for (const element of elements) {
  592. if (
  593. element.id !== WebLiberator.MenuButtonId &&
  594. element.id !== WebLiberator.NotificationId &&
  595. !element.closest(`#${WebLiberator.MenuButtonId}`) &&
  596. !element.closest(`#${WebLiberator.NotificationId}`)
  597. ) {
  598. this.clearSingleElementHandlers(element);
  599. if (element.shadowRoot?.mode === "open")
  600. this.clearHandlersRecursive(element.shadowRoot);
  601. }
  602. }
  603. }
  604. } catch (error) {}
  605. }
  606.  
  607. handleMutation(mutations) {
  608. if (!this.isActive) return;
  609. for (const mutation of mutations) {
  610. if (mutation.type === "childList") {
  611. for (const node of mutation.addedNodes) {
  612. if (node.nodeType === Node.ELEMENT_NODE) {
  613. if (
  614. node.id !== WebLiberator.MenuButtonId &&
  615. node.id !== WebLiberator.NotificationId &&
  616. !node.closest(`#${WebLiberator.MenuButtonId}`) &&
  617. !node.closest(`#${WebLiberator.NotificationId}`)
  618. ) {
  619. this.clearSingleElementHandlers(node);
  620. }
  621. }
  622. }
  623. }
  624. }
  625. }
  626.  
  627. initMutationObserver() {
  628. if (this.observer || !document.documentElement) return;
  629. const observerOptions = { childList: true, subtree: true };
  630. this.observer = new MutationObserver(this.handleMutation.bind(this));
  631. try {
  632. this.observer.observe(document.documentElement, observerOptions);
  633. } catch (error) {
  634. this.observer = null;
  635. }
  636. }
  637.  
  638. disconnectMutationObserver() {
  639. if (this.observer) {
  640. this.observer.disconnect();
  641. this.observer = null;
  642. }
  643. }
  644.  
  645. updateMenuCommand() {
  646. if (this.menuCommandId) {
  647. try {
  648. GM_unregisterMenuCommand(this.menuCommandId);
  649. } catch (e) {}
  650. this.menuCommandId = null;
  651. }
  652. const label = this.isActive
  653. ? this.strings.stateEnabledText
  654. : this.strings.stateDisabledText;
  655. const fallbackLabel = this.isActive
  656. ? "Liberator Activated ✅"
  657. : "Liberator Deactivated ❌";
  658. const commandLabel = label || fallbackLabel;
  659. try {
  660. this.menuCommandId = GM_registerMenuCommand(commandLabel, () => {
  661. this.toggle();
  662. });
  663. } catch (e) {
  664. this.menuCommandId = null;
  665. }
  666. }
  667. }
  668.  
  669. if (window.self !== window.top) {
  670. return;
  671. }
  672.  
  673. try {
  674. const liberator = new WebLiberator();
  675. liberator.injectBaseStyles();
  676. liberator.loadState();
  677. liberator.updateMenuCommand();
  678.  
  679. const debouncedToggle = debounce(() => liberator.toggle(), 200);
  680.  
  681. document.addEventListener(
  682. "keydown",
  683. (event) => {
  684. if (
  685. (event.ctrlKey || event.metaKey) &&
  686. event.altKey &&
  687. event.code === "KeyL"
  688. ) {
  689. event.preventDefault();
  690. event.stopPropagation();
  691. debouncedToggle();
  692. }
  693. },
  694. { capture: true }
  695. );
  696.  
  697. const onDOMContentLoaded = () => {
  698. liberator.ensureElementsCreated();
  699. if (liberator.isActive) {
  700. liberator.activate();
  701. } else {
  702. liberator.updateMenuStatus();
  703. }
  704. };
  705.  
  706. if (document.readyState === "loading") {
  707. document.addEventListener("DOMContentLoaded", onDOMContentLoaded, {
  708. once: true,
  709. });
  710. } else {
  711. onDOMContentLoaded();
  712. }
  713. } catch (error) {}
  714. })();