Ultra Popup Blocker

Configurable popup blocker that blocks all popup windows by default, with whitelist and blacklist support.

当前为 2025-06-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Ultra Popup Blocker
// @description  Configurable popup blocker that blocks all popup windows by default, with whitelist and blacklist support.
// @namespace    eskander.github.io
// @author       Eskander & 1Td
// @version      5.0
// @include      *
// @license      MIT
// @homepage     https://github.com/Eskander/ultra-popup-blocker
// @supportURL   https://github.com/Eskander/ultra-popup-blocker/issues/new
// @compatible   firefox Tampermonkey / Violentmonkey
// @compatible   chrome Tampermonkey / Violentmonkey
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.deleteValue
// @grant        GM.listValues
// @grant        GM.registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    /* Constants and Globals */
    const CONSTANTS = {
        TIMEOUT_SECONDS: 15,
        TRUNCATE_LENGTH: 50,
        MODAL_WIDTH: '450px'
    }

    const STYLES = {
        modal: `position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; background-color: #ffffff !important; color: #000000 !important; width: ${CONSTANTS.MODAL_WIDTH} !important; border: 1px solid #000000 !important; z-index: 2147483647 !important; box-shadow: 0 2px 10px rgba(0,0,0,0.5) !important; margin: 0 !important; padding: 0 !important; font-family: Arial !important; font-size: 14px !important; line-height: 1.5 !important; box-sizing: border-box !important;`,
        modalHeader: `background-color: #000000 !important; padding: 20px 30px !important; color: #ffffff !important; text-align: center !important; margin: 0 !important; font-size: inherit !important; line-height: inherit !important;`,
        modalFooter: `background-color: #000000 !important; padding: 5px 30px !important; color: #ffffff !important; text-align: center !important; margin: 0 !important;`,
        button: `margin-right: 10px !important; padding: 5px !important; cursor: pointer !important; font-family: inherit !important; font-size: inherit !important; line-height: inherit !important; border: 1px solid #000000 !important; background: #ffffff !important; color: #000000 !important; border-radius: 3px !important;`,
        notificationBar: `position: fixed !important; bottom: 0 !important; left: 0 !important; z-index: 2147483646 !important; width: 100% !important; padding: 5px !important; font-family: Arial !important; font-size: 14px !important; line-height: 1.5 !important; background-color: #000000 !important; color: #ffffff !important; display: none !important; margin: 0 !important; box-sizing: border-box !important;`,
        listItem: `padding: 12px 8px 12px 40px !important; font-size: 16px !important; background-color: #ffffff !important; color: #000000 !important; border-bottom: 1px solid #dddddd !important; position: relative !important; transition: 0.2s !important; margin: 0 !important;`,
        removeButton: `cursor: pointer !important; position: absolute !important; right: 0 !important; top: 0 !important; padding: 12px 16px !important; background: transparent !important; border: none !important; color: #000000 !important; font-size: 20px !important;`
    }

    const global = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window;
    global.upbCounter = 0

    const realWindowOpen = global.open

    const FakeWindow = {
        blur: () => false,
        focus: () => false
    }

    /* Domain Management */
    class DomainManager {
        static async getCurrentTopDomain () {
            const hostname = document.location.hostname;
            const matches = hostname.match(/[^.]+\.[^.]+$/);
            return matches ? matches[0] : hostname;
        }
        static async getPermissionStatus (domain) {
            return await GM.getValue(domain, 'ask');
        }
        static async setPermission (domain, status) {
            await GM.setValue(domain, status);
        }
        static async removePermission (domain) {
            await GM.deleteValue(domain);
        }
        static async getAllPermissions () {
            const keys = await GM.listValues();
            const permissions = [];
            for (const key of keys) {
                const status = await GM.getValue(key);
                if (status === 'allowed' || status === 'denied') {
                    permissions.push({ domain: key, status: status });
                }
            }
            return permissions;
        }
    }

    /* UI Components */
    class UIComponents {
        static createButton (text, id, clickHandler, color) {
            const button = document.createElement('button')
            button.id = `upb-${id}`
            button.innerHTML = text
            button.style.cssText = `${STYLES.button} color: ${color} !important;`
            button.addEventListener('click', clickHandler)
            return button
        }
        static createNotificationBar () {
            const bar = document.createElement('div')
            bar.id = 'upb-notification-bar'
            bar.style.cssText = STYLES.notificationBar
            document.body.appendChild(bar);
            return bar
        }
        static createModalElement () {
            const modal = document.createElement('div')
            modal.id = 'upb-trusted-domains-modal'
            modal.style.cssText = STYLES.modal
            document.body.appendChild(modal);
            return modal
        }
        static updateDenyButtonText (button, timeLeft) {
            if (button) {
                button.innerHTML = `🔴 Deny (${timeLeft})`
            }
        }
    }

    /* Notification Bar */
    class NotificationBar {
        constructor () {
            this.element = null
            this.timeLeft = CONSTANTS.TIMEOUT_SECONDS
            this.denyTimeoutId = null
            this.denyButton = null
        }
        createElement () {
            if (!this.element) {
                this.element = UIComponents.createNotificationBar()
            }
            return this.element
        }
        show (url) {
            if (!this.element) {
                this.createElement()
            }
            this.element.style.display = 'block'
            this.setMessage(url)
            this.addButtons(url)
            this.startDenyTimeout()
        }
        hide () {
            if (this.element) {
                this.element.style.display = 'none'
                if (this.element.parentNode) {
                    this.element.parentNode.removeChild(this.element)
                }
                this.element = null
            }
            global.upbCounter = 0
            this.clearDenyTimeout()
        }
        clearDenyTimeout () {
            if (this.denyTimeoutId) {
                clearInterval(this.denyTimeoutId)
                this.denyTimeoutId = null
            }
        }
        setMessage (url) {
            const truncatedUrl = url.length > CONSTANTS.TRUNCATE_LENGTH
            ? `${url.substring(0, CONSTANTS.TRUNCATE_LENGTH)}..`
            : url
            this.element.innerHTML = `
              Ultra Popup Blocker: This site is attempting to open <b>${global.upbCounter}</b> popup(s).
              <a href="${url}" style="color:yellow;" target="_blank">${truncatedUrl}</a>
            `
        }
        async addButtons (url) {
            const currentDomain = await DomainManager.getCurrentTopDomain()
            this.element.appendChild(
                UIComponents.createButton('🟢 Allow Once', 'allow', () => {
                    realWindowOpen(url, '_blank')
                    this.hide()
                }, 'green')
            )
            this.element.appendChild(
                UIComponents.createButton('🔵 Always Allow', 'trust', async () => {
                    await DomainManager.setPermission(currentDomain, 'allowed')
                    realWindowOpen(url, '_blank')
                    this.hide()
                    await PopupBlocker.initialize()
                }, 'blue')
            )
            this.element.appendChild(
                UIComponents.createButton('🚫 Always Deny', 'deny-always', async () => {
                    await DomainManager.setPermission(currentDomain, 'denied');
                    this.hide();
                    await PopupBlocker.initialize();
                }, '#333')
            );
            this.denyButton = UIComponents.createButton(`🔴 Deny (${CONSTANTS.TIMEOUT_SECONDS})`, 'deny-once', () => {
                this.hide()
            }, 'red')
            this.element.appendChild(this.denyButton)
            const configButton = UIComponents.createButton('🟠 Config', 'config', () => {
                new TrustedDomainsModal().show()
            }, 'orange')
            configButton.style.float = 'right'
            this.element.appendChild(configButton)
        }
        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)
                if (this.timeLeft <= 0) {
                    this.clearDenyTimeout()
                    this.hide()
                }
            }, 1000)
        }
    }

    /* MODIFIED Trusted Domains Modal */
    class TrustedDomainsModal {
        constructor () {
            this.element = document.getElementById('upb-trusted-domains-modal') || this.createElement()
        }
        createElement () {
            const modal = UIComponents.createModalElement()
            const header = document.createElement('div')
            header.style.cssText = STYLES.modalHeader
            header.innerHTML = `<h2 style="color:white !important; margin:0; padding:0;">Ultra Popup Blocker</h2>`
            modal.appendChild(header)
            const listsContainer = document.createElement('div');
            listsContainer.id = 'upb-lists-container';
            listsContainer.style.cssText = 'max-height: 400px; overflow-y: auto;';
            const allowedHeader = document.createElement('h4');
            allowedHeader.innerText = '✅ Allowed Websites (Whitelist)';
            allowedHeader.style.cssText = 'padding: 10px 20px 5px; margin: 0; background: #f0f0f0; color: black;';
            const allowedList = document.createElement('ul');
            allowedList.id = 'upb-allowed-list';
            allowedList.style.cssText = 'margin:0;padding:0;list-style-type:none;';
            const blockedHeader = document.createElement('h4');
            blockedHeader.innerText = '🚫 Denied Websites (Blacklist)';
            blockedHeader.style.cssText = 'padding: 10px 20px 5px; margin: 0; background: #f0f0f0; color: black; border-top: 1px solid #ccc;';
            const deniedList = document.createElement('ul');
            deniedList.id = 'upb-denied-list';
            deniedList.style.cssText = 'margin:0;padding:0;list-style-type:none;';
            listsContainer.appendChild(allowedHeader);
            listsContainer.appendChild(allowedList);
            listsContainer.appendChild(blockedHeader);
            listsContainer.appendChild(deniedList);
            modal.appendChild(listsContainer);
            const footer = document.createElement('div')
            footer.style.cssText = STYLES.modalFooter
            const closeButton = document.createElement('button')
            closeButton.innerText = 'Close'
            closeButton.style.cssText = `background-color:#4CAF50;color:#ffffff;border:none;padding:10px 20px;cursor:pointer;border-radius:3px;`
            closeButton.onclick = () => this.hide()
            footer.appendChild(closeButton)
            modal.appendChild(footer)
            return modal
        }
        show () {
            this.refreshDomainsList()
            this.element.style.display = 'block'
        }
        hide () {
            this.element.style.display = 'none'
        }
        
        async refreshDomainsList () {
            const allowedList = document.getElementById('upb-allowed-list');
            const deniedList = document.getElementById('upb-denied-list');
            allowedList.innerHTML = '';
            deniedList.innerHTML = '';

            const permissions = await DomainManager.getAllPermissions();

            let allowedCount = 0;
            let deniedCount = 0;

            for (const perm of permissions) {
                if (perm.status === 'allowed') {
                    await this.addDomainListItem(allowedList, perm.domain);
                    allowedCount++;
                } else if (perm.status === 'denied') {
                    await this.addDomainListItem(deniedList, perm.domain);
                    deniedCount++;
                }
            }

            if (allowedCount === 0) {
                const message = document.createElement('p');
                message.style.cssText = 'padding: 20px; color: #555; text-align: center;';
                message.innerText = 'No allowed websites.';
                allowedList.appendChild(message);
            }

            if (deniedCount === 0) {
                const message = document.createElement('p');
                message.style.cssText = 'padding: 20px; color: #555; text-align: center;';
                message.innerText = 'No denied websites.';
                deniedList.appendChild(message);
            }
        }

        async addDomainListItem (list, domain) {
            const item = document.createElement('li')
            item.style.cssText = STYLES.listItem
            item.innerText = domain
            item.addEventListener('mouseover', () => { item.style.backgroundColor = '#f1f1f1' })
            item.addEventListener('mouseout', () => { item.style.backgroundColor = 'white' })
            const removeButton = document.createElement('span')
            removeButton.style.cssText = STYLES.removeButton
            removeButton.innerHTML = '&times;'
            removeButton.addEventListener('mouseover', () => { removeButton.style.backgroundColor = '#f44336'; removeButton.style.color = 'white'; })
            removeButton.addEventListener('mouseout', () => { removeButton.style.backgroundColor = 'transparent'; removeButton.style.color = 'black'; })
            removeButton.addEventListener('click', async () => {
                await DomainManager.removePermission(domain);
                await this.refreshDomainsList();
                await PopupBlocker.initialize();
            })
            item.appendChild(removeButton)
            list.appendChild(item)
        }
    }

    class PopupBlocker {
        static clickHandler = null;

        static async initialize () {
            global.open = realWindowOpen;
            if (this.clickHandler) {
                document.removeEventListener('click', this.clickHandler, true);
                this.clickHandler = null;
            }

            const currentDomain = await DomainManager.getCurrentTopDomain();
            const permission = await DomainManager.getPermissionStatus(currentDomain);

            if (permission === 'allowed') {
                console.log(`[UPB] Whitelisted domain: ${currentDomain}. Popups are allowed.`);
                return;
            }

            const notificationBar = new NotificationBar();

            const windowOpenHandler = (url) => {
                global.upbCounter++;
                console.log(`[UPB] Intercepted window.open popup: ${url}`);
                notificationBar.show(url);
                return FakeWindow;
            };
            const silentWindowOpenHandler = (url) => {
                console.log(`[UPB] Silently blocked window.open on denied domain: ${url}`);
                return FakeWindow;
            };
            
            const clickHandler = (event) => {
                let target = event.target;
                while (target && target.tagName !== 'A') {
                    target = target.parentNode;
                }

                if (target && target.target === '_blank' && target.href) {
                    event.preventDefault();
                    event.stopPropagation();
                    global.upbCounter++;
                    console.log(`[UPB] Intercepted click-based popup: ${target.href}`);
                    notificationBar.show(target.href);
                }
            };
            const silentClickHandler = (event) => {
                 let target = event.target;
                while (target && target.tagName !== 'A') {
                    target = target.parentNode;
                }
                if (target && target.target === '_blank' && target.href) {
                    event.preventDefault();
                    event.stopPropagation();
                    console.log(`[UPB] Silently blocked click-based popup on denied domain: ${target.href}`);
                }
            };

            if (permission === 'denied') {
                console.log(`[UPB] Denied domain: ${currentDomain}. All popups will be silently blocked.`);
                global.open = silentWindowOpenHandler;
                this.clickHandler = silentClickHandler;
                document.addEventListener('click', this.clickHandler, true);
                return;
            }

            console.log(`[UPB] Active on: ${currentDomain}. New popups will trigger a notification.`);
            global.open = windowOpenHandler;
            this.clickHandler = clickHandler;
            document.addEventListener('click', this.clickHandler, true);
        }
    }

    /* Initialize */
    (async function() {
        if (document.body) {
            await PopupBlocker.initialize();
            GM.registerMenuCommand('Ultra Popup Blocker: Manage Permissions', () => {
                new TrustedDomainsModal().show();
            });
        } else {
            window.addEventListener('load', async () => {
                await PopupBlocker.initialize();
                GM.registerMenuCommand('Ultra Popup Blocker: Manage Permissions', () => {
                    new TrustedDomainsModal().show();
                });
            });
        }
    })();

})();