您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A sleek, modern popup blocker with an Apple-inspired glassmorphism UI and advanced redirect protection.
当前为
// ==UserScript== // @name Ultra Popup Blocker (Enhanced Edition) // @description A sleek, modern popup blocker with an Apple-inspired glassmorphism UI and advanced redirect protection. // @namespace https://github.com/1Tdd // @author 1Tdd (Original by Eskander) // @version 5.1 // @include * // @license MIT // @homepage https://github.com/1Tdd/ultra-popup-blocker // @supportURL https://github.com/1Tdd/ultra-popup-blocker/issues/new // @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhdXJvcmEtZ3JhZGllbnQiIHgxPSIwJSIgeTE9IjEwMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiM1ODU2RDYiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGMkQ1NSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGOTgwQSIvPjwvbGluZWFyR3JhZGllbnQ+PG1hc2sgaWQ9InRleHQtbWFzayI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IndoaXRlIiAvPjx0ZXh0IHg9IjUwJSIgeT0iNTMlIiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0Rm9udCwgJ1NlZ29lIFVJJywgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjQwIiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iYmxhY2siPlVQQjwvdGV4dD48L21hc2s+PC9kZWZzPjxyZWN0IHg9IjEwIiB5PSIxMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMjIiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3QgeD0iMTAiIHk9IjEwIiB3aWR0aD0iODAiIGhlaWdodD0iODAiIHJ4PSIyMiIgZmlsbD0idXJsKCNhdXJvcmEtZ3JhZGllbnQpIiBtYXNrPSJ1cmwoI3RleHQtbWFzaykiIC8+PC9zdmc+ // @compatible firefox Tampermonkey / Violentmonkey // @compatible chrome Tampermonkey / Violentmonkey // @run-at document-start // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.listValues // @grant GM.registerMenuCommand // ==/UserScript== (function() { 'use strict'; try { const CONSTANTS = { TIMEOUT_SECONDS: 15, TRUNCATE_LENGTH: 50, MODAL_WIDTH: '550px', TOAST_TIMEOUT_SECONDS: 3, DEBOUNCE_MS: 250, MODAL_Z_INDEX_THRESHOLD: 1000, LOGO_SVG_URL: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhdXJvcmEtZ3JhZGllbnQiIHgxPSIwJSIgeTE9IjEwMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiM1ODU2RDYiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGMkQ1NSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGOTgwQSIvPjwvbGluZWFyR3JhZGllbnQ+PG1hc2sgaWQ9InRleHQtbWFzayI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IndoaXRlIiAvPjx0ZXh0IHg9IjUwJSIgeT0iNTMlIiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0Rm9udCwgJ1NlZ29lIFVJJywgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjQwIiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iYmxhY2siPlVQQjwvdGV4dD48L21hc2s+PC9kZWZzPjxyZWN0IHg9IjEwIiB5PSIxMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMjIiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3QgeD0iMTAiIHk9IjEwIiB3aWR0aD0iODAiIGhlaWdodD0iODAiIHJ4PSIyMiIgZmlsbD0idXJsKCNhdXJvcmEtZ3JhZGllbnQpIiBtYXNrPSJ1cmwoI3RleHQtbWFzaykiIC8+PC9zdmc+" }; const global = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window; const realWindowOpen = global.open; const FakeWindow = (() => { const p = { get: (t, r) => r === "closed" ? !0 : new Proxy(function() {}, p), set: () => !0, apply: () => void 0 }; return new Proxy(function() {}, p) })(); if (typeof global._realDocumentWrite === "undefined") { global._realDocumentWrite = document.write; global._realDocumentWriteln = document.writeln } const fullStyles = ` .upb-base-button { display: inline-flex !important; align-items: center !important; justify-content: center !important; gap: 8px !important; padding: 10px 20px !important; border-radius: 9999px !important; border: 1px solid rgba(255,255,255,0.1) !important; font-size: 14px !important; font-weight: 600 !important; cursor: pointer !important; transition: all 0.2s ease-out !important; line-height: 1.2 !important; } .upb-base-button:hover { filter: brightness(1.1); transform: translateY(-1px); } .upb-base-button:active { transform: scale(0.96); filter: brightness(0.95); transition-duration: 0.1s; } .upb-button--allow { background-image: linear-gradient(to right, #24D169, #23C15D) !important; color: #003D11 !important; border: none !important; } .upb-button--trust { background-image: linear-gradient(to right, #0B84FF, #3DA0FF) !important; color: white !important; border: none !important; } .upb-button--deny { background-image: linear-gradient(to right, #FF3B30, #FF453A) !important; color: white !important; border: none !important; } .upb-button--denyTemp { background-image: linear-gradient(to right, #5856D6, #6B69D6) !important; color: white !important; border: none !important; } .upb-button--config, .upb-button--neutral { background-color: rgba(118, 118, 128, 0.3) !important; color: white !important; border-color: rgba(255,255,255,0.15) !important;} .upb-button--config:hover, .upb-button--neutral:hover { background-color: rgba(118, 118, 128, 0.5) !important; } #upb-notification-bar { position: fixed !important; bottom: 20px !important; left: 50% !important; transform: translateX(-50%) !important; z-index: 2147483646 !important; width: auto !important; max-width: 95% !important; padding: 12px !important; border-radius: 20px !important; display: none; align-items: center !important; gap: 15px !important; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif !important; font-size: 14px !important; color: #F5F5F7 !important; background-color: rgba(28, 28, 30, 0.7) !important; -webkit-backdrop-filter: blur(25px) !important; backdrop-filter: blur(25px) !important; border: 1px solid rgba(255, 255, 255, 0.15) !important; box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.4), inset 0 0 0 1px rgba(255, 255, 255, 0.15); } #upb-config-modal { position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: ${CONSTANTS.MODAL_WIDTH} !important; z-index: 2147483647 !important; border-radius: 24px !important; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif !important; color: #F5F5F7 !important; overflow: hidden !important; background-color: rgba(28, 28, 30, 0.7) !important; -webkit-backdrop-filter: blur(25px) !important; backdrop-filter: blur(25px) !important; border: 1px solid rgba(255, 255, 255, 0.15) !important; box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.4), inset 0 0 0 1px rgba(255, 255, 255, 0.15); } #upb-modal-header { padding: 16px !important; text-align: center !important; font-size: 18px !important; font-weight: 600 !important; border-bottom: 1px solid rgba(255, 255, 255, 0.15) !important; background-color: rgba(255, 255, 255, 0.05) !important; display: flex; align-items: center; justify-content: center; gap: 10px; } #upb-modal-content { padding: 20px !important; display: flex !important; justify-content: space-between !important; gap: 20px !important; } .upb-list-section { width: 48%; } #upb-modal-footer { padding: 10px 20px !important; text-align: center !important; border-top: 1px solid rgba(255, 255, 255, 0.15) !important; background-color: rgba(0, 0, 0, 0.1) !important; } .upb-input { width: 100% !important; padding: 10px !important; background-color: rgba(118, 118, 128, 0.24) !important; border: 1px solid rgba(118, 118, 128, 0.32) !important; border-radius: 8px !important; color: #F5F5F7 !important; font-size: 14px !important; box-sizing: border-box !important; } .upb-list { margin: 0 !important; padding: 0 !important; list-style-type: none !important; max-height: 250px !important; overflow-y: auto !important; background-color: rgba(118, 118, 128, 0.12) !important; border-radius: 12px !important; border: 1px solid rgba(255,255,255,0.08) !important; } .upb-list-item { display: flex !important; align-items: center !important; justify-content: space-between !important; padding: 10px 12px !important; border-bottom: 1px solid rgba(118, 118, 128, 0.12) !important; } .upb-remove-button { width: 22px !important; height: 22px !important; border-radius: 50% !important; background-color: rgba(118, 118, 128, 0.24) !important; color: #F5F5F7 !important; font-weight: bold !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; padding-bottom: 2px !important; transition: all 0.2s ease-out; } .upb-remove-button:hover { background-color: #FF453A !important; } .upb-button-container { display: flex; gap: 8px; border-left: 1px solid rgba(255, 255, 255, 0.15); padding-left: 15px; } .upb-info-section { display: flex; align-items: center; gap: 15px; text-shadow: 0 1px 2px rgba(0,0,0,0.2); } @media (max-width: 600px) { #upb-notification-bar { flex-direction: column !important; text-align: center; } .upb-button-container { border-left: none !important; padding-left: 0 !important; border-top: 1px solid rgba(255, 255, 255, 0.15) !important; padding-top: 10px !important; flex-wrap: wrap; justify-content: center; } #upb-config-modal { width: 95vw !important; max-height: 85vh !important; display: flex !important; flex-direction: column !important; } #upb-modal-content { flex-direction: column !important; gap: 15px !important; overflow-y: auto; padding: 15px !important; } .upb-list-section { width: 100% !important; } } `; if (!document.getElementById("upb-styles")) { const styleSheet = document.createElement("style"); styleSheet.id = "upb-styles"; styleSheet.textContent = fullStyles; document.head.appendChild(styleSheet); } class EventManager { constructor() { this.events = {} } on(e, t) { this.events[e] = this.events[e] || [], this.events[e].push(t) } off(e, t) { this.events[e] && (this.events[e] = this.events[e].filter(n => n !== t)) } emit(e, t) { this.events[e] && this.events[e].forEach(n => n(t)) } } const events = new EventManager; class DomainManager { static PREFIX_ALLOW = "allow_"; static PREFIX_DENY = "deny_"; static INDEX_KEY = "upb_domain_index"; static MIGRATED_KEY = "upb_migrated_v9"; static async getIndex() { let e = await GM.getValue(this.INDEX_KEY); return e && typeof e.allowed != "undefined" || (e = { allowed: [], denied: [] }), e } static async saveIndex(e) { await GM.setValue(this.INDEX_KEY, e) } static async runMigration() { if (await GM.getValue(this.MIGRATED_KEY)) return; console.log("[UPB] Running one-time migration..."); try { const e = await GM.listValues(), t = { allowed: [], denied: [] }; for (const n of e) n.startsWith(this.PREFIX_ALLOW) ? t.allowed.push(n.substring(this.PREFIX_ALLOW.length)) : n.startsWith(this.PREFIX_DENY) && t.denied.push(n.substring(this.PREFIX_DENY.length)); await this.saveIndex(t), console.log("[UPB] Migration successful.") } catch (e) { console.error("[UPB] Migration failed.", e), await this.saveIndex({ allowed: [], denied: [] }) } await GM.setValue(this.MIGRATED_KEY, !0) } static parseAndValidateDomain(e) { try { if (!e || typeof e != "string") return null; let t = e.includes("//") ? new URL(e).hostname : e; if (t = t.trim().toLowerCase(), t.startsWith("www.") && (t = t.substring(4)), !t || !t.includes(".")) return null; const n = t.split("."); if (n.length < 2 || n.some(s => s.length === 0)) return null; const o = ["co.uk", "com.au", "com.br", "gov.uk", "ac.uk", "co.jp", "co.in"], i = n.slice(-2).join("."); return o.includes(i) && n.length > 2 ? n.slice(-3).join(".") : n.slice(-2).join(".") } catch (t) { return null } } static async getDomainState(e) { return e ? await GM.getValue(this.PREFIX_ALLOW + e) ? "allow" : await GM.getValue(this.PREFIX_DENY + e) ? "deny" : "ask" : "ask" } static async getCurrentDomainState() { const e = await this.getCurrentTopDomain(); return this.getDomainState(e) } static async getCurrentTopDomain() { return this.parseAndValidateDomain(location.hostname) } static async addAllowedDomain(e) { const t = await this.getIndex(); t.allowed.includes(e) || t.allowed.push(e), t.denied = t.denied.filter(n => n !== e), await this.saveIndex(t), await GM.setValue(this.PREFIX_ALLOW + e, !0), await GM.deleteValue(this.PREFIX_DENY + e), events.emit("domainListChanged") } static async addDeniedDomain(e) { const t = await this.getIndex(); t.denied.includes(e) || t.denied.push(e), t.allowed = t.allowed.filter(n => n !== e), await this.saveIndex(t), await GM.setValue(this.PREFIX_DENY + e, !0), await GM.deleteValue(this.PREFIX_ALLOW + e), events.emit("domainListChanged") } static async removeAllowedDomain(e) { const t = await this.getIndex(); t.allowed = t.allowed.filter(n => n !== e), await this.saveIndex(t), await GM.deleteValue(this.PREFIX_ALLOW + e), events.emit("domainListChanged") } static async removeDeniedDomain(e) { const t = await this.getIndex(); t.denied = t.denied.filter(n => n !== e), await this.saveIndex(t), await GM.deleteValue(this.PREFIX_DENY + e), events.emit("domainListChanged") } static async getAllowedDomains() { const e = await this.getIndex(); return e.allowed || [] } static async getDeniedDomains() { const e = await this.getIndex(); return e.denied || [] } } class UIComponents { static createButton(e, t, n) { const o = document.createElement("button"); o.className = "upb-base-button upb-button--" + t; const i = e.split(/ (.*)/s), s = i[0], a = i[1] || "", c = document.createElement("span"); c.textContent = s, a || (c.style.margin = "0"), o.appendChild(c); if (a) { const l = document.createElement("span"); l.textContent = a, o.appendChild(l) } return o.addEventListener("click", n), o } static updateDenyButtonText(e, t) { if (e) { const n = e.querySelector("span:last-child"); n && (n.textContent = `Deny (${t})`) } } } class ToastNotification { constructor() { this.element = null, this.timeoutId = null } show(e) { this.element && this.hide(!0), document.body ? (this.element = document.createElement("div"), this.element.style.cssText = `position: fixed !important; bottom: 20px !important; right: 20px !important; background-color: rgba(28, 28, 30, 0.75) !important; -webkit-backdrop-filter: blur(10px) !important; backdrop-filter: blur(10px) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; color: white !important; padding: 10px 20px !important; border-radius: 9999px !important; z-index: 2147483647 !important; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif !important; font-size: 14px !important; box-shadow: 0 4px 20px rgba(0,0,0,0.4) !important; opacity: 0 !important; transform: translateY(10px) !important; transition: all 0.3s ease-in-out !important;`, this.element.textContent = e, document.body.appendChild(this.element), setTimeout(() => { this.element && (this.element.style.opacity = "1", this.element.style.transform = "translateY(0)") }, 10), this.timeoutId = setTimeout(() => this.hide(), 1e3 * CONSTANTS.TOAST_TIMEOUT_SECONDS)) : window.addEventListener("DOMContentLoaded", () => this.show(e)) } hide(e = !1) { !this.element || (clearTimeout(this.timeoutId), this.timeoutId = null, e ? (this.element.parentNode && this.element.parentNode.removeChild(this.element), this.element = null) : (this.element.style.opacity = "0", this.element.style.transform = "translateY(10px)", setTimeout(() => { this.element && this.element.parentNode && this.element.parentNode.removeChild(this.element), this.element = null }, 300))) } } class RedirectShield { constructor() { this.isAllowed = !1, this.handler = e => { if (!this.isAllowed) { e.preventDefault(), e.returnValue = ""; return "" } } } arm() { window.addEventListener("beforeunload", this.handler, !0) } disarm() { window.removeEventListener("beforeunload", this.handler, !0) } allowOnce(e) { this.isAllowed = !0, e(), setTimeout(() => { this.isAllowed = !1 }, 100) } } class NotificationBar { constructor() { this.element = null, this.timeLeft = CONSTANTS.TIMEOUT_SECONDS, this.denyTimeoutId = null, this.denyButton = null, this.currentUrl = null } createElement() { let e = document.getElementById("upb-notification-bar"); return e || (e = document.createElement("div"), e.id = "upb-notification-bar", document.body.appendChild(e)), this.element = e, this.element } show(e) { this.element && this.element.style.display === "flex" && this.clearDenyTimeout(), this.currentUrl = e, document.body ? this.createElement() && (this.element.style.display = "flex", this.setMessage(e), this.addButtons(e), this.startDenyTimeout()) : window.addEventListener("DOMContentLoaded", () => this.show(e)) } hide() { redirectShield.disarm(), this.element && (this.clearDenyTimeout(), this.element.parentNode && this.element.parentNode.removeChild(this.element), this.element = null) } clearDenyTimeout() { this.denyTimeoutId && (clearInterval(this.denyTimeoutId), this.denyTimeoutId = null) } setMessage(e) { for (; this.element.firstChild;) this.element.removeChild(this.element.firstChild); const t = document.createElement("div"); t.className = "upb-info-section"; const n = document.createElement("img"); n.src = CONSTANTS.LOGO_SVG_URL, n.style.cssText = "width: 20px; height: 20px; flex-shrink: 0;", t.appendChild(n); const o = e || "", i = o.length > CONSTANTS.TRUNCATE_LENGTH ? `${o.substring(0,CONSTANTS.TRUNCATE_LENGTH)}..` : o, s = document.createElement("span"); s.appendChild(document.createTextNode("Blocked popup to ")); const a = document.createElement("a"); a.href = o, a.target = "_blank", a.style.cssText = "color:#64D2FF; text-decoration: none; font-weight: 500;", a.textContent = i || "an unspecified destination", s.appendChild(a), t.appendChild(s), this.element.appendChild(t) } async addButtons(e) { const t = await DomainManager.getCurrentTopDomain(), n = document.createElement("div"); n.className = "upb-button-container"; n.appendChild(UIComponents.createButton("✅ Allow Once", "allow", () => { this.hide(), redirectShield.allowOnce(() => { realWindowOpen(e) }) })), n.appendChild(UIComponents.createButton("💙 Always Allow", "trust", async () => { this.hide(), await DomainManager.addAllowedDomain(t) })), n.appendChild(UIComponents.createButton("❌ Always Deny", "deny", async () => { confirm(`Are you sure you want to permanently block popups from ${t}?`) && (this.hide(), await DomainManager.addDeniedDomain(t)) })), this.denyButton = UIComponents.createButton(`🚫 Deny (${this.timeLeft})`, "denyTemp", () => this.hide()), n.appendChild(this.denyButton), n.appendChild(UIComponents.createButton("⚙️ Config", "config", () => configModal.show())); const o = this.element.querySelector(".upb-button-container"); o && o.remove(), this.element.appendChild(n) } startDenyTimeout() { this.timeLeft = CONSTANTS.TIMEOUT_SECONDS, this.clearDenyTimeout(), UIComponents.updateDenyButtonText(this.denyButton, this.timeLeft), this.denyTimeoutId = setInterval(() => { this.timeLeft--, UIComponents.updateDenyButtonText(this.denyButton, this.timeLeft), this.timeLeft <= 0 && this.hide() }, 1e3) } } class ConfigModal { constructor() { this.element = null, this.refreshListener = () => { this.element && this.refreshAllLists() }, events.on("domainListChanged", this.refreshListener) } destroy() { events.off("domainListChanged", this.refreshListener), this.hide() } hide() { this.element && (this.element.remove(), this.element = null) } show() { if (!document.body) { window.addEventListener("DOMContentLoaded", () => this.show()); return } this.element && this.hide(); this.element = this.createElement(), document.body.appendChild(this.element), this.refreshAllLists() } createElement() { const e = document.createElement("div"); e.id = "upb-config-modal"; const t = document.createElement("div"); t.id = "upb-modal-header"; const n = document.createElement("img"); n.src = CONSTANTS.LOGO_SVG_URL, n.style.cssText = "width: 24px; height: 24px;"; const o = document.createElement("span"); o.textContent = "Ultra Popup Blocker", t.appendChild(n), t.appendChild(o), e.appendChild(t); const i = document.createElement("div"); i.id = "upb-modal-content", i.appendChild(this.createListSection("Allowed Websites", "allow")), i.appendChild(this.createListSection("Denied Websites", "deny")), e.appendChild(i); const s = document.createElement("div"); return s.id = "upb-modal-footer", s.appendChild(UIComponents.createButton("Close", "neutral", () => this.hide())), e.appendChild(s), e } createListSection(e, t) { const n = document.createElement("div"); n.className = "upb-list-section"; const o = document.createElement("h3"); o.style.cssText = "margin-top: 0; margin-bottom: 10px; text-align: center; font-weight: 500;", o.textContent = e; const i = document.createElement("div"); i.style.cssText = "display: flex; gap: 8px; margin-bottom: 10px;"; const s = document.createElement("input"); s.type = "text", s.className = "upb-input", s.placeholder = "e.g., example.com"; const a = UIComponents.createButton("Add", "trust", () => this.handleAdd(t, s)); i.appendChild(s), i.appendChild(a); const c = document.createElement("ul"); return c.className = "upb-list", c.classList.add(`upb-list--${t}`), n.appendChild(o), n.appendChild(i), n.appendChild(c), s.onkeydown = e => { e.key === "Enter" && this.handleAdd(t, s) }, n } handleAdd(e, t) { const n = DomainManager.parseAndValidateDomain(t.value); n ? (t.value = "", e === "allow" ? DomainManager.addAllowedDomain(n) : DomainManager.addDeniedDomain(n)) : alert("Invalid domain format. Please enter a valid domain.") } async refreshAllLists() { if (!this.element) return; await this.populateList(this.element.querySelector(".upb-list--allow"), await DomainManager.getAllowedDomains(), "allow"); await this.populateList(this.element.querySelector(".upb-list--deny"), await DomainManager.getDeniedDomains(), "deny") } populateList(e, t, n) { for (; e.firstChild;) e.removeChild(e.firstChild); if (t.length === 0) { const o = document.createElement("li"); o.textContent = "No websites in this list.", o.style.cssText = "padding: 10px; color: #8E8E93; text-align: center;", e.appendChild(o) } else t.sort().forEach(o => { const i = document.createElement("li"); i.className = "upb-list-item"; const s = document.createElement("span"); s.textContent = o; const a = document.createElement("div"); a.className = "upb-remove-button", a.textContent = "×", a.onclick = async () => { n === "allow" ? await DomainManager.removeAllowedDomain(o) : await DomainManager.removeDeniedDomain(o) }, i.appendChild(s), i.appendChild(a), e.appendChild(i) }) } } class ModalBlocker { static isModal(e) { if (!(e instanceof HTMLElement) || !document.body.contains(e) || e.offsetParent === null || getComputedStyle(e).visibility === "hidden") return !1; const t = getComputedStyle(e), n = parseInt(t.zIndex, 10) || 0; if (n < 1e3) return !1; const o = e.getBoundingClientRect(); if (o.width <= 0 || o.height <= 0) return !1; const i = window.innerWidth, s = window.innerHeight; return o.width > .8 * i && o.height > .8 * s || o.width > 300 && o.height > 200 && t.position === "fixed" || t.backgroundColor.startsWith("rgba") && parseFloat(t.backgroundColor.split(",")[3]) > .1 } static neutralize(e, t) { console.log("[UPB] Neutralizing suspected modal:", e), e.style.setProperty("display", "none", "important"), document.body.style.setProperty("overflow", "auto", "important"), document.documentElement.style.setProperty("overflow", "auto", "important"), t.show("🛡️ Modal popup hidden.") } static scan(e, t) { if (sessionStorage.getItem("upb_modal_block_disabled") === "true") return; for (const n of e) if (n.nodeType === Node.ELEMENT_NODE) { if (n.closest("#upb-notification-bar, #upb-config-modal, #upb-toast-notification")) continue; if (this.isModal(n)) this.neutralize(n, t); else { const o = n.querySelectorAll("div, form"); for (const i of o) this.isModal(i) && this.neutralize(i, t) } } } } class PopupBlocker { static async initialize() { if (global._upbListenerCleaner) { global._upbListenerCleaner(), global._upbListenerCleaner = null } const domainState = await DomainManager.getCurrentDomainState(); if (global._upb_toast || (global._upb_toast = new ToastNotification), domainState === "allow") { return redirectShield.disarm(), void(global.open !== realWindowOpen && (global.open = realWindowOpen), document.write = global._realDocumentWrite, document.writeln = global._realDocumentWriteln) } redirectShield.arm(); if (domainState === "deny") { const e = global._upb_toast; global.open = () => (e.show("🚫 Popup blocked on a denied site."), FakeWindow); const t = n => { let o = !1; if (n.type === "click") { const i = n.target.closest("a"); if (i && i.href) { const s = document.querySelector('base[target="_blank"]'); o = i.target === "_blank" || s && i.target !== "_self" } } else if (n.type === "submit") { const i = n.target.closest("form"); i && i.target === "_blank" && (o = !0) } o && (n.preventDefault(), n.stopPropagation(), n.stopImmediatePropagation(), e.show("🚫 Popup blocked on a denied site.")) }; return void(window.addEventListener("click", t, !0), window.addEventListener("submit", t, !0), global._upbListenerCleaner = () => { window.removeEventListener("click", t, !0), window.removeEventListener("submit", t, !0) }) } const notificationBar = new NotificationBar; global.open = (url) => { notificationBar.show(url); return FakeWindow; }; const clickBlocker = e => { if (e.target.closest("#upb-notification-bar, #upb-toast-notification")) return; const t = e.target.closest("a"); if (t && t.href) { const n = document.querySelector('base[target="_blank"]'), o = t.target === "_blank" || n && t.target !== "_self"; o && (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), notificationBar.show(t.href)) } }; const submitListener = e => { const t = e.target.closest("form"); t && t.target === "_blank" && (e.preventDefault(), notificationBar.show(t.action || location.href)) }; window.addEventListener("click", clickBlocker, !0), window.addEventListener("submit", submitListener, !0), global._upbListenerCleaner = () => { window.removeEventListener("click", clickBlocker, !0), window.removeEventListener("submit", submitListener, !0) } } } const configModal = new ConfigModal; const redirectShield = new RedirectShield; function debounce(e, t) { let n; return function(...o) { const i = this; clearTimeout(n), n = setTimeout(() => e.apply(i, o), t) } } const reinitializeDebounced = debounce(PopupBlocker.initialize, CONSTANTS.DEBOUNCE_MS); const observer = new MutationObserver(mutations => { const addedNodes = mutations.flatMap(m => Array.from(m.addedNodes)); if (addedNodes.length > 0) { sessionStorage.getItem("upb_modal_block_disabled") !== "true" && ModalBlocker.scan(addedNodes, global._upb_toast || new ToastNotification), reinitializeDebounced() } }); function startObserver() { document.body ? observer.observe(document.body, { childList: !0, subtree: !0 }) : window.addEventListener("DOMContentLoaded", () => { observer.observe(document.body, { childList: !0, subtree: !0 }) }, { once: !0 }) } GM.registerMenuCommand("Ultra Popup Blocker: Configure", () => configModal.show()); GM.registerMenuCommand("Disable Modal Blocker (1 Tab)", () => { sessionStorage.setItem("upb_modal_block_disabled", "true"); const e = new ToastNotification; e.show("Modal blocker disabled for this tab.") }); events.on("domainListChanged", PopupBlocker.initialize); (async () => { await DomainManager.runMigration(); await PopupBlocker.initialize(); startObserver(); })() } catch (e) { console.error("[UPB] A critical error occurred. Please report this on GitHub.", e) } })();