Adblock Detector Bypasser

Bypasses various adblock detectors and paywalls.

  1. // ==UserScript==
  2. // @name Adblock Detector Bypasser
  3. // @name:vi Bỏ Qua Phát Hiện Adblock
  4. // @namespace http://tampermonkey.net/
  5. // @version 1.2
  6. // @description Bypasses various adblock detectors and paywalls.
  7. // @description:vi Bỏ qua phát hiện adblock và paywalls khác nhau.
  8. // @author Yuusei
  9. // @match *://*/*
  10. // @license GPL-3.0-only
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @grant window.onurlchange
  16. // @run-at document-start
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. // --- UserScript Configuration ---
  23. const SCRIPT_NAME = "Adblock Detector Bypasser";
  24. const ENABLED_STORAGE_KEY = "adblockerBypassEnabled";
  25.  
  26. // --- Original Code ---
  27.  
  28. // Debug mode toggle
  29. const DEBUG_MODE = false;
  30.  
  31. function log(message, ...args) {
  32. if (DEBUG_MODE) {
  33. console.log(`[${SCRIPT_NAME}] ${message}`, ...args);
  34. }
  35. }
  36.  
  37.  
  38. // Function to block adblockDetectors
  39. function blockAdblockDetectors() {
  40. const excludedElements = ['BODY', 'HTML'];
  41. const excludedPrefixes = ['ShortsLockupView', "ytSearchbox"];
  42.  
  43. const blockElements = [
  44. "#vndjywodduqjrmenpowdbkcevrszhtxzzowjcnuvaqmzgwdmtyzsdmrc", ".adblock_title", ".adblock_subtitle",
  45. ".popSc", ".popBc", ".ab-detector-wrap", "#abDetectorModal", "#adDetectorElm", ".ad", ".dialog-overlay",
  46. ".dialog-overlay-blur", ".dialog.dialog-open.dialog-variant-default", ".link.link--external",
  47. ".link.link--internal", ".bb089aa6", "#arlinablock", ".modal.fade.in", ".modal-backdrop.fade.in",
  48. ".modal-backdrop", ".fc-ab-root", "body > div.fc-ab-root", ".fbs-auth__container.fbs-auth__adblock",
  49. ".overlay-34_Kj", ".wrapper-3AzfF", "#qa-modal-body",
  50. ".fixed.left-0.right-0.bottom-0.paywall-overlay.checkout-paywall-overlay", "#paywall-ui-responsive-modal",
  51. '[data-qa="wall-background"]', "#wall-bottom-drawer", ".fEy1Z2XT",
  52. ".zephr-article-modal-backdrop.zephr-backdrop", ".zephr-modal-content",
  53. ".zephr-editorial-newsletter-backdrop.zephr-backdrop",
  54. ".nytc---modal-window---windowContainer.nytc---modal-window---isShown.nytc---shared---blackBG",
  55. ".tp-modal", ".tp-backdrop.tp-active", "modality-custom-element", "[name='metering-modal']",
  56. ".c-nudge__container.c-gate__container", ".c-nudge__container.c-regGate__container", ".css-n7r8pg",
  57. ".css-1bd8bfl", ".overlay__59af11e2", ".tp_modal", ".tp-backdrop.tp-active",
  58. "div[class^='sp_message_container']", "div[class^='sp_veil']", ".css-1mv333l", ".css-19q4c8e",
  59. ".css-gx5sib", ".myAccountAuth", ".css-1aa2f1p", "#lire-ui-886237", "#fortress-container-root",
  60. "#yzwall", "#variant_top_bar", "#tie-popup-adblock", ".tie-popup", "#modal-whitelist", "#modal-overlay",
  61. "#nindo-popup-portal", "#nindo-drawer-portal", ".bt-sw-container", ".bt-sw-modal", ".lbzrkre",
  62. "#adblock_tooltip", ".adblock-killme-overlay", ".bfddebf37-wrapper", ".bfddebf37-blackout",
  63. ".protection", ".q-sticky-footer.q-footer-body.q-red-bild-design", ".inactivity", ".angery-message",
  64. ".items-stretch.md\\:items-center.fill-mode-both.fixed.bottom-0.left-0.right-0.top-0.bg-backdrop\\/70.backdrop-blur-sm.animate-in.fade-in.ease-outExpo.duration-200",
  65. ".py-md.px-md.grow.flex.flex-col.justify-center.md\\:pt-lg.md\\:pb-lg",
  66. ".shadow-md.overflow-y-auto.scrollbar-thin.scrollbar-thumb-idle", ".bx-slab", 'div[id^="bx-shroud-"]',
  67. 'div[id^="bx-close-outside-"]',
  68. ".mx-4.flex.h-fit.flex-col.items-center.justify-center.self-center.rounded-lg.bg-foreground.p-4.sm\\:inset-x-12.sm\\:bottom-12.sm\\:top-auto.sm\\:flex-row.sm\\:self-end.sm\\:p-10.md\\:absolute.md\\:inset-x-4.md\\:mx-auto.pointer-events-auto",
  69. ".fixed.inset-x-0.bottom-0.z-20.flex.h-full.bg-white\\/80", "#mj-adblock-widget-2", "#template-container",
  70. ".o-message__container", "#tp-yt-iron-overlay-backdrop", ".tp-yt-iron-overlay-backdrop", "#bdn-paywall-view",
  71. ".wa-limit-modal", ".bck-adblock.is--active", ".adblock__container", ".adblock",
  72. ".Overlay__container___2TqtL Overlay__containerActive___2te0I", "#portal-root",
  73. ".unblocker-container", ".unblocker-wrapper", ".unblocker", "#cpidQokFQn",
  74. ".swal2-container.swal2-center.swal2-backdrop-show",
  75. ".eejqlhsrwxhwgizceicunpzytknyhvlkrdguafbrbvlkamnytyrtsdmrc",
  76. ];
  77.  
  78. let elementsBlocked = false;
  79.  
  80. blockElements.forEach(selector => {
  81. try {
  82. document.querySelectorAll(selector).forEach(el => {
  83. if (!excludedElements.includes(el.tagName.toUpperCase())) {
  84. el.style.setProperty("display", "none", "important");
  85. log(`Blocked element with selector: ${selector}`);
  86. elementsBlocked = true;
  87.  
  88. const suffix = el.id ? el.id.slice(-5) : "";
  89. if (suffix) {
  90. const sameIdElements = document.querySelectorAll(`[id$="${suffix}"]`);
  91. sameIdElements.forEach(sameIdEl => {
  92. if (sameIdEl.id.length > 30 && sameIdEl.id.endsWith(suffix) && sameIdEl !== el) {
  93. log(`Hiding additional ID with suffix: ${suffix} from ID: ${sameIdEl.id}`);
  94. sameIdEl.style.setProperty("display", "none", "important");
  95. elementsBlocked = true;
  96. }
  97. });
  98. }
  99. }
  100. });
  101. } catch (error) {
  102. log(`Error hiding element with selector: ${selector}`, error);
  103. }
  104. });
  105.  
  106. const randomPattern = /^[a-zA-Z0-9]{30,}$/;
  107. document.querySelectorAll('*').forEach(el => {
  108. try {
  109. if (excludedElements.includes(el.tagName.toUpperCase())) return;
  110.  
  111. const matchesPrefix = excludedPrefixes.some(prefix =>
  112. el.tagName.startsWith(prefix) ||
  113. (el.id && el.id.startsWith(prefix)) ||
  114. Array.from(el.classList).some(className => className.startsWith(prefix))
  115. );
  116. if (matchesPrefix) return;
  117.  
  118. if (el.id && randomPattern.test(el.id) && el.id.length > 30) {
  119. const suffix = el.id.slice(-5);
  120. el.style.setProperty("display", "none", "important");
  121. log(`Blocked element with random ID: ${el.id}`);
  122. elementsBlocked = true;
  123.  
  124. const sameIdElements = document.querySelectorAll(`[id$="${suffix}"]`);
  125. sameIdElements.forEach(sameIdEl => {
  126. if (sameIdEl.id.length > 30 && sameIdEl.id.endsWith(suffix) && sameIdEl !== el) {
  127. log(`Hiding additional ID with suffix: ${suffix} from ID: ${sameIdEl.id}`);
  128. sameIdEl.style.setProperty("display", "none", "important");
  129. elementsBlocked = true;
  130. }
  131. });
  132. }
  133.  
  134. Array.from(el.classList).forEach(cls => {
  135. if (randomPattern.test(cls) && cls.length > 30) {
  136. el.style.setProperty("display", "none", "important");
  137. log(`Blocked element with random class: ${cls}`);
  138. elementsBlocked = true;
  139. }
  140. });
  141.  
  142. if (el.src && el.src.includes("chp-ads-block-detector")) {
  143. el.style.setProperty("display", "none", "important");
  144. log(`Blocked element with src: ${el.src}`);
  145. elementsBlocked = true;
  146. }
  147. if (el.href && el.href.includes("chp-ads-block-detector")) {
  148. el.style.setProperty("display", "none", "important");
  149. log(`Blocked element with href: ${el.href}`);
  150. elementsBlocked = true;
  151. }
  152.  
  153. } catch (error) {
  154. log("Error blocking dynamic element", error);
  155. }
  156. });
  157.  
  158. log("Adblock detector element scan complete.");
  159. return elementsBlocked;
  160. }
  161.  
  162.  
  163. const forceEnableScrolling = () => {
  164. const allElements = document.querySelectorAll('body *');
  165. allElements.forEach(element => {
  166. const computedStyle = window.getComputedStyle(element);
  167. if (computedStyle.overflow === 'hidden' || computedStyle.overflowY === 'hidden') {
  168. element.style.setProperty('overflow', 'auto', 'important');
  169. element.style.setProperty('overflowY', 'auto', 'important');
  170. }
  171. });
  172. document.documentElement.style.setProperty('overflow', 'auto', 'important');
  173. document.documentElement.style.setProperty('overflowY', 'auto', 'important');
  174. document.body.style.setProperty('overflow', 'auto', 'important');
  175. document.body.style.setProperty('overflowY', 'auto', 'important');
  176. log("Forced scrolling on all elements.");
  177. };
  178.  
  179. let scrollObserver = null;
  180. function enableScrollingOnPage() {
  181. const scrollClasses = [
  182. "mol-fe-ab-dialog", "body--no-scroll", "nytc---modal-window---noScroll",
  183. "tp-modal-open", "with-gate", "overflow-hidden", "css-mcm29f", "scroll-lock",
  184. "modal-open", "is-modal-open", "fixed-modal", "zephr-modal-open",
  185. "bt-sw-overflow", "bx-client-overlay", "bx-client-overlay-ios", "swal2-shown"
  186. ];
  187. const otherScrollPopupFilters = [
  188. { value: "span[data-reactroot]", type: "popup" },
  189. { value: "body[data-paywall-overlay-status='show']", type: "scroll" },
  190. { value: ".overlay", type: "popup" }
  191. ];
  192.  
  193. const MAX_TRIES = 5;
  194. let tries = 0;
  195.  
  196. const attemptEnableScroll = () => {
  197. let canScroll = false;
  198. const htmlElement = document.documentElement;
  199. const bodyElement = document.body;
  200.  
  201. [htmlElement, bodyElement].forEach(element => {
  202. if (element) {
  203. if (element.style.overflow === "hidden") {
  204. element.style.setProperty("overflow", "auto", "important");
  205. canScroll = true;
  206. }
  207. if (element.style.overflowY === "hidden") {
  208. element.style.setProperty("overflowY", "auto", "important");
  209. canScroll = true;
  210. }
  211. element.style.removeProperty('overflow');
  212. element.style.removeProperty('overflow-y');
  213. }
  214. });
  215.  
  216. scrollClasses.forEach(filter => {
  217. const elements = document.getElementsByClassName(filter);
  218. while (elements.length > 0 && elements[0]) {
  219. elements[0].classList.remove(filter);
  220. canScroll = true;
  221. }
  222. });
  223.  
  224. otherScrollPopupFilters.forEach(filter => {
  225. const element = document.querySelector(filter.value);
  226. if (element) {
  227. if (filter.type === "scroll") {
  228. element.style.setProperty("overflow", "auto", "important");
  229. } else if (filter.type === "popup") {
  230. element.style.setProperty("display", "none", "important");
  231. }
  232. canScroll = true;
  233. }
  234. });
  235.  
  236. if (canScroll) {
  237. log("Scrolling has been re-enabled.");
  238. } else if (tries < MAX_TRIES) {
  239. tries++;
  240. setTimeout(attemptEnableScroll, 500);
  241. }
  242. };
  243.  
  244. if (document.body) {
  245. if (scrollObserver) scrollObserver.disconnect(); // Disconnect previous if any
  246. scrollObserver = new MutationObserver(attemptEnableScroll);
  247. scrollObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
  248. attemptEnableScroll();
  249. } else {
  250. window.addEventListener('DOMContentLoaded', () => {
  251. if (document.body) {
  252. if (scrollObserver) scrollObserver.disconnect();
  253. scrollObserver = new MutationObserver(attemptEnableScroll);
  254. scrollObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
  255. }
  256. attemptEnableScroll();
  257. });
  258. }
  259. }
  260.  
  261. function classOfStubbornSiteAdblockDetectors() {
  262. const tieBody = document.getElementById("tie-body");
  263. if (tieBody?.classList.contains("tie-popup-is-opend")) {
  264. tieBody.classList.remove("tie-popup-is-opend");
  265. log("Class 'tie-popup-is-opend' removed from #tie-body");
  266. }
  267. if (document.body?.classList.contains("bfddebf37-blur")) {
  268. document.body.classList.remove("bfddebf37-blur");
  269. log("Class 'bfddebf37-blur' removed from <body>");
  270. }
  271. }
  272.  
  273. function hideYtElements() {
  274. let changed = false;
  275. const dismissButton = document.querySelector('.ytd-enforcement-message-view-model #dismiss-button, .ytp-ad-skip-button-modern, button[aria-label*="Skip Ad"]');
  276. if (dismissButton && typeof dismissButton.click === 'function') {
  277. dismissButton.click();
  278. log("YouTube dismiss/skip button clicked.");
  279. changed = true;
  280. }
  281.  
  282. const elementsToHide = ['tp-yt-paper-dialog', 'ytd-enforcement-message-view-model', 'tp-yt-iron-overlay-backdrop'];
  283. elementsToHide.forEach(tagNameOrClass => {
  284. const selector = tagNameOrClass.startsWith('.') || tagNameOrClass.startsWith('#') ? tagNameOrClass : tagNameOrClass;
  285. document.querySelectorAll(selector).forEach(element => {
  286. if (element.style.display !== 'none') {
  287. element.style.setProperty('display', 'none', 'important');
  288. log(`<${tagNameOrClass}> has been hidden on YouTube.`);
  289. changed = true;
  290. }
  291. });
  292. });
  293.  
  294. const player = document.getElementById('movie_player');
  295. if (player?.classList.contains('ytp-pause-overlay')) {
  296. player.classList.remove('ytp-pause-overlay');
  297. const video = player.querySelector('video');
  298. if (video?.paused) {
  299. video.play().catch(e => log("Error trying to play video:", e));
  300. log("Removed ytp-pause-overlay and attempted to play video.");
  301. changed = true;
  302. }
  303. }
  304. return changed;
  305. }
  306.  
  307.  
  308. let runOnceExecutedForCurrentPage = false;
  309. function runOnce() {
  310. if (runOnceExecutedForCurrentPage) return;
  311. log("Running one-time setup logic for current page.");
  312.  
  313. if (window.location.hostname.includes("dailymotion.com") && window.location.pathname.startsWith('/video/')) {
  314. log('Dailymotion video page detected.');
  315. var videoId = window.location.pathname.split('/')[2];
  316. if (!videoId) {
  317. log('Could not extract Dailymotion video ID.');
  318. return;
  319. }
  320. log('Extracted Dailymotion video ID:', videoId);
  321.  
  322. let modal;
  323.  
  324. function createModal() {
  325. if (document.getElementById('adb-cpe-dailymotion-modal')) return;
  326.  
  327. modal = document.createElement('div');
  328. modal.id = 'adb-cpe-dailymotion-modal';
  329. modal.style.cssText = `
  330. position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  331. background-color: rgba(0, 0, 0, 0.8); display: flex;
  332. justify-content: center; align-items: center; z-index: 2147483647;`;
  333.  
  334. var closeButton = document.createElement('button');
  335. closeButton.innerText = 'Close AdB by CPE Player';
  336. closeButton.style.cssText = `
  337. position: absolute; top: 20px; right: 20px; padding: 10px;
  338. background-color: #4CAF50; border: none; cursor: pointer; z-index: 2147483647; color: white;`;
  339. closeButton.onclick = function() {
  340. if (modal && modal.parentNode) modal.parentNode.removeChild(modal);
  341. createOpenPlayerButton();
  342. };
  343.  
  344. var iframe = document.createElement('iframe');
  345. iframe.src = `https://www.dailymotion.com/embed/video/${videoId}?autoplay=1`;
  346. iframe.width = '80%';
  347. iframe.height = '80%';
  348. iframe.frameBorder = '0';
  349. iframe.allowFullscreen = true;
  350. iframe.allow = "autoplay; fullscreen";
  351.  
  352. modal.appendChild(iframe);
  353. modal.appendChild(closeButton);
  354. document.body.appendChild(modal);
  355. log('Dailymotion modal created.');
  356. }
  357.  
  358. function createOpenPlayerButton() {
  359. if (document.getElementById('adb-cpe-dailymotion-open-btn')) return;
  360.  
  361. var openPlayerButton = document.createElement('button');
  362. openPlayerButton.id = 'adb-cpe-dailymotion-open-btn';
  363. openPlayerButton.innerText = 'Open AdB by CPE Player';
  364. openPlayerButton.style.cssText = `
  365. position: fixed; bottom: 15px; right: 20px; padding: 10px;
  366. background-color: #4CAF50; border: none; cursor: pointer; z-index: 2147483646; color: white;`;
  367. openPlayerButton.onclick = function() {
  368. if (openPlayerButton.parentNode) openPlayerButton.parentNode.removeChild(openPlayerButton);
  369. const nameDiv = document.getElementById('adb-cpe-dailymotion-name');
  370. if (nameDiv && nameDiv.parentNode) nameDiv.parentNode.removeChild(nameDiv);
  371. createModal();
  372. };
  373.  
  374. var extensionName = document.createElement('div');
  375. extensionName.id = 'adb-cpe-dailymotion-name';
  376. extensionName.innerText = `Powered by ${SCRIPT_NAME}`;
  377. extensionName.style.cssText = `
  378. position: fixed; bottom: 50px; right: 20px;
  379. color: #4CAF50; font-size: 12px; z-index: 2147483646;`;
  380.  
  381. document.body.appendChild(openPlayerButton);
  382. document.body.appendChild(extensionName);
  383. log('Dailymotion open player button created.');
  384. }
  385.  
  386. var playerWrapper = document.getElementById('player-wrapper');
  387. if (playerWrapper) {
  388. playerWrapper.remove();
  389. log('Removed Dailymotion player-wrapper.');
  390. }
  391. createModal();
  392. }
  393. runOnceExecutedForCurrentPage = true;
  394. }
  395.  
  396. function updateStylesInStyleTag() {
  397. const styleTag = document.querySelector('style[data-id="std"]');
  398. if (styleTag && styleTag.sheet) {
  399. try {
  400. const styleSheet = styleTag.sheet;
  401. for (let i = 0; i < styleSheet.cssRules.length; i++) {
  402. const rule = styleSheet.cssRules[i];
  403. if (rule.style) {
  404. if (rule.style.filter && rule.style.filter !== 'none') {
  405. rule.style.setProperty('filter', 'none', 'important');
  406. log(`Updated filter for rule:`, rule.cssText);
  407. }
  408. if (
  409. rule.selectorText?.includes('body') &&
  410. rule.style.overflow === 'hidden' &&
  411. rule.style.position === 'fixed' &&
  412. rule.style.height === '100%' &&
  413. rule.style.width === '100%' &&
  414. rule.style.maxHeight === '100vh'
  415. ) {
  416. rule.style.setProperty('overflow', 'auto', 'important');
  417. log(`Updated overflow for body rule:`, rule.cssText);
  418. }
  419. }
  420. }
  421. } catch (e) {
  422. log("Error accessing stylesheet rules (likely cross-origin):", e.message);
  423. }
  424. }
  425. }
  426.  
  427.  
  428. // --- Main Execution Logic ---
  429. let mainObserver = null;
  430. let intervalId = null;
  431.  
  432. function runChecks() {
  433. log("Running checks...");
  434. const isYouTube = window.location.hostname.includes("www.youtube.com");
  435. let blockedSomething;
  436.  
  437. if (isYouTube) {
  438. blockedSomething = hideYtElements();
  439. } else {
  440. blockedSomething = blockAdblockDetectors();
  441. classOfStubbornSiteAdblockDetectors();
  442. enableScrollingOnPage();
  443. }
  444.  
  445. if (blockedSomething) {
  446. log("Blocked/modified something in this check cycle.");
  447. }
  448. }
  449.  
  450. async function checkAdblockerStatusAndRun() {
  451. const isEnabled = await GM_getValue(ENABLED_STORAGE_KEY, true);
  452. if (!isEnabled) {
  453. log("Bypasser is disabled via menu command.");
  454. if (mainObserver) { mainObserver.disconnect(); mainObserver = null; }
  455. if (scrollObserver) { scrollObserver.disconnect(); scrollObserver = null; }
  456. if (intervalId) { clearInterval(intervalId); intervalId = null; }
  457. return;
  458. }
  459. log("Bypasser is enabled. Running checks.");
  460.  
  461. runChecks(); // Initial run
  462.  
  463. if (!mainObserver && document.body) {
  464. mainObserver = new MutationObserver(() => {
  465. log("DOM changed (body observer), re-running checks.");
  466. runChecks();
  467. });
  468. mainObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'hidden'] }); // Watch attributes relevant to hiding
  469. log("Main observer attached to document.body.");
  470. } else if (!document.body && !mainObserver) { // Fallback if body not ready
  471. window.addEventListener('DOMContentLoaded', () => {
  472. if (document.body && !mainObserver) {
  473. mainObserver = new MutationObserver(() => {
  474. log("DOM changed (body observer post-DOMContentLoaded), re-running checks.");
  475. runChecks();
  476. });
  477. mainObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'hidden'] });
  478. log("Main observer attached to document.body (post-DOMContentLoaded).");
  479. }
  480. });
  481. }
  482.  
  483.  
  484. if (intervalId) clearInterval(intervalId);
  485.  
  486. let intervalTime = 3000;
  487. intervalId = setInterval(() => {
  488. log("Interval check running.");
  489. runChecks();
  490. }, intervalTime);
  491.  
  492. setTimeout(async () => {
  493. const stillEnabled = await GM_getValue(ENABLED_STORAGE_KEY, true);
  494. if (stillEnabled && intervalId) { // Check if intervalId is still ours
  495. clearInterval(intervalId);
  496. intervalTime = 7000;
  497. intervalId = setInterval(() => {
  498. log("Slower interval check running.");
  499. runChecks();
  500. }, intervalTime);
  501. log("Switched to slower interval.");
  502. }
  503. }, 60000);
  504. }
  505.  
  506. async function initPageSpecific() {
  507. runOnceExecutedForCurrentPage = false;
  508. const isEnabled = await GM_getValue(ENABLED_STORAGE_KEY, true);
  509. if (isEnabled) {
  510. runOnce();
  511. }
  512. }
  513.  
  514. async function toggleAdblockerStatus() {
  515. const currentValue = await GM_getValue(ENABLED_STORAGE_KEY, true);
  516. const newValue = !currentValue;
  517. await GM_setValue(ENABLED_STORAGE_KEY, newValue);
  518. alert(`${SCRIPT_NAME} is now ${newValue ? 'ENABLED' : 'DISABLED'}. Reload page or navigate for changes to fully apply if issues persist.`);
  519. log(`Toggled to: ${newValue}`);
  520.  
  521. if (newValue) {
  522. initPageSpecific(); // Re-run one-time stuff if needed
  523. checkAdblockerStatusAndRun(); // Start observers and checks
  524. } else {
  525. if (mainObserver) { mainObserver.disconnect(); mainObserver = null; }
  526. if (scrollObserver) { scrollObserver.disconnect(); scrollObserver = null; }
  527. if (intervalId) { clearInterval(intervalId); intervalId = null; }
  528. log("Disabled: Observers disconnected and intervals cleared.");
  529. // Note: Elements already hidden by the script might remain hidden until page reload.
  530. // Adding logic to revert all changes is complex and often not necessary for this type of script.
  531. }
  532. }
  533.  
  534. GM_registerMenuCommand(`Toggle ${SCRIPT_NAME}`, toggleAdblockerStatus);
  535.  
  536.  
  537. // Initializations
  538. initPageSpecific();
  539. checkAdblockerStatusAndRun();
  540.  
  541. // Handle SPA navigation
  542. let lastUrl = location.href;
  543.  
  544. if (typeof window.onurlchange !== 'undefined') {
  545. window.addEventListener('urlchange', (info) => {
  546. const newUrl = info.url || location.href;
  547. log(`urlchange event detected. New URL: ${newUrl}`);
  548. if (newUrl !== lastUrl) {
  549. lastUrl = newUrl;
  550. initPageSpecific();
  551. checkAdblockerStatusAndRun(); // Re-run checks and re-setup observers if needed
  552. }
  553. });
  554. }
  555. })();