Ultra Popup Blocker

Configurable popup blocker that blocks all popup windows by default.

目前為 2021-06-01 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Ultra Popup Blocker
// @description  Configurable popup blocker that blocks all popup windows by default.
// @namespace    https://github.com/eskander
// @author       Eskander
// @version      3.2.1
// @include      *
// @license      MIT
// @homepage     https://eskander.github.io/ultra-popup-blocker/
// @supportURL   https://github.com/Eskander/ultra-popup-blocker/issues/new
// @compatible   firefox Tampermonkey recommended
// @compatible   chrome Tampermonkey recommended
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// ==/UserScript==

/* ---------------------------------------------------------------- */

const PERMISSION_DIALOG_ID = 'ultra_popup_blocker'; // HTML ID in the page
const CONTROL_PANEL = 'https://eskander.github.io/ultra-popup-blocker/settings';

// Reference to page's "window" through GreaseMonkey
const global = unsafeWindow;
global.upb_counter = 0;

// Storing a reference to real "window.open" method in case we wanted it
const realWindowOpen = global.open;

// We need to return the fake window to not encounter JS runtime error when the popup originator
// page wants to call focus() or blur().
const FakeWindow = {
  blur() {
    return false;
  },
  focus() {
    return false;
  },
};

// Timeout before confirmation dialog closes automatically
let timeleft = 15;

/* ---------------------------------------------------------------- */

// Add @domain to local storage
function addDomainToLocalStorage(domain) {
  GM_setValue(`trusted_${domain}`, true);
}

// Remove @domain from local storage
function removeDomainFromLocalStorage(domain) {
  GM_deleteValue(`trusted_${domain}`);
  GM_deleteValue(`${domain}`);
}

// Return true if @domain is trusted
function isDomainTrusted(domain) {
  return GM_getValue(`trusted_${domain}`);
}

// Return an Array of trusted domains
function getTrustedDomains() {
  return GM_listValues();
}

// Open permission manager in new tab
function openControlPanel() {
  GM_openInTab(CONTROL_PANEL, false);
}

// Add a link to permission manager in extensions' popup menu
function attachToExtensionMenu(name, callback) {
  GM_registerMenuCommand(name, callback);
}

// Permission bar; Return permission dialog, or create it if needed.
function getLogDiv() {
  let logDiv = document.getElementById(PERMISSION_DIALOG_ID);
  if (!logDiv) {
    logDiv = document.createElement('div');
    logDiv.setAttribute('id', PERMISSION_DIALOG_ID);
    logDiv.style.cssText = 'position: fixed;\
                            bottom: 0;\
                            left: 0;\
                            z-index: 99999;\
                            width: 100%;\
                            padding: 5px 5px 5px 5px;\
                            font: status-bar;\
                            background-color: black;\
                            color: white;\
                            cursor: help';
    document.body.appendChild(logDiv);
  }
  return logDiv;
}

// Permission bar; Hide dialog
function closeLogDiv(logDiv) {
  const currentLogDiv = logDiv;
  currentLogDiv.style.display = 'none';
}

// Return current top domain. eg: github.com
function getCurrentTopDomain() {
  const hostnameArray = document.location.hostname.split('.');
  const topLevelDomain = hostnameArray[hostnameArray.length - 1];
  const domainName = hostnameArray[hostnameArray.length - 2];
  const currentDomain = `${domainName}.${topLevelDomain}`;
  return currentDomain;
}

// Return true if current domain has been trusted by the user
function isCurrentDomainTrusted() {
  const domain = getCurrentTopDomain();
  return isDomainTrusted(domain);
}

// Permission manager; Create a button to remove domain from permissions list
function removeDomainFromPermissionList() {
  const div = this.parentElement;
  console.log(div);
  const domain = div.innerText.replace('\n\u00D7', '');
  removeDomainFromLocalStorage(domain);
  div.style.display = 'none';
  console.log(`[UPB] Domain removed from trust: ${domain}`);
}

// Permission manager; Add a new domain to permissions list
function addDomainToPermissionList(domain) {
  const domainName = domain.replace('trusted_', '');
  const li = document.createElement('li');
  const t = document.createTextNode(domainName);
  li.appendChild(t);
  document.getElementById('List').appendChild(li);
  // Add a remove button to li
  const span = document.createElement('SPAN');
  const txt = document.createTextNode('\u00D7');
  span.className = 'close';
  span.appendChild(txt);
  span.onclick = removeDomainFromPermissionList;
  li.appendChild(span);
  // Add domain to localStorage
  addDomainToLocalStorage(domainName);
  console.log(`[UPB] Domain added to trust: ${domainName}`);
}

// Permission manager; Button to add a new domain to permissions list
function addNewDomainButton() {
  document.getElementsByClassName('addBtn')[0].addEventListener(
    'click',
    () => {
      const DOMAIN = document.getElementById('Input').value;
      if (DOMAIN !== '') {
        addDomainToPermissionList(DOMAIN);
      }
      document.getElementById('Input').value = '';
    },
  );
}

// Permission bar; Create a button with inner text @text executing onclick
// @clickCallback, appended as a child of @logDiv, with style @inlineStyle.
function createButton(logDiv, text, id, clickCallback, inlineStyle) {
  const button = document.createElement('button');
  button.innerHTML = text;
  button.id = id;
  button.style.cssText = `text-decoration: none;\
                          color: black;\
                          cursor: pointer;\
                          margin: 0 5px;\
                          padding: 1px 3px;\
                          background-color: rgb(255, 255, 255);\
                          border-width: 0px;\
                          border-radius: 5px;\
                          color: black;\
                          ${inlineStyle}`;
  logDiv.appendChild(button);
  button.addEventListener('click', clickCallback);
}

// Permission bar; Create a button (child of @logDiv) which onclick trusts @domain
function createTrustButton(logDiv, domain, a, b, c) {
  createButton(
    logDiv,
    'Always Allow 🗸',
    'upb_trust',
    () => {
      addDomainToLocalStorage(domain);
      realWindowOpen(a, b, c);
      closeLogDiv(logDiv);
      global.open = realWindowOpen;
    },
    '',
  );
}

// Permission bar; Create a button (child of @logDiv) which onclick opens @domain
function createOpenPopupButton(logDiv, a, b, c) {
  createButton(
    logDiv,
    'Allow ↗',
    'upb_open',
    () => {
      realWindowOpen(a, b, c);
      closeLogDiv(logDiv);
    },
    '',
  );
}

// Permission bar; Create a button (child of @logDiv) which onclick hides @logDiv
function createCloseButton(logDiv) {
  createButton(
    logDiv,
    `Deny (${timeleft})`,
    'upb_close',
    () => {
      closeLogDiv(logDiv);
    },
    ' background-color: #a00;\
      color: white;',
  );
}

// Permission bar; Create a button (child of @logDiv) which onclick opens @controlPanel
function createConfigButton(logDiv) {
  createButton(
    logDiv,
    'Config ⚙',
    'upb_config',
    () => {
      openControlPanel();
    },
    ' float: right;\
      margin: 0 10px 0 0;',
  );
}

// Permission bar; Display a permission prompt when a new popup is detected
function createDialogMessage(logDiv, url) {
  const currentLogDiv = logDiv;
  const domain = getCurrentTopDomain();
  let msg;
  let popupUrl;

  global.upb_counter += 1;

  if (global.upb_counter === 1) {
    msg = `<b>[UPB]</b> Allow <b><u>${domain}</u></b> to open a popup ?`;
  } else {
    msg = `<b>[UPB]</b> Allow <b><u>${domain}</u></b> to open a popup ? <b>(${global.upb_counter})</b>`;
  }

  if (url[0] === '/') {
    popupUrl = document.domain + url;
  } else {
    popupUrl = url;
  }

  currentLogDiv.innerHTML = msg;
  currentLogDiv.title = popupUrl;
  console.log(msg);
  currentLogDiv.style.display = 'block';
}

function createTimer(logDiv) {
  console.log(timeleft);
  if (timeleft === 15) {
    const Timer = setInterval(() => {
      document.getElementById('upb_close').innerHTML = `Deny (${timeleft})`;
      timeleft -= 1;
      if (timeleft < 0) {
        clearInterval(Timer);
        closeLogDiv(logDiv);
        timeleft = 15;
      }
      console.log(timeleft);
    }, 1000);
  }
}

// This function will be called each time a script wants to open a new window
function fakeWindowOpen(a, b, c) {
  const domain = getCurrentTopDomain();
  const popupURL = a;
  const logDiv = getLogDiv();
  console.log(a, b, c);
  createDialogMessage(logDiv, popupURL);
  createOpenPopupButton(logDiv, a, b, c);
  createTrustButton(logDiv, domain, a, b, c);
  createCloseButton(logDiv);
  createConfigButton(logDiv);
  createTimer(logDiv);
  return FakeWindow;
}

// Override browser's "window.open" with our own implementation.
function activateBlocker() {
  const TRUSTED = isCurrentDomainTrusted();
  if (!TRUSTED) {
    global.open = fakeWindowOpen;
    console.log('[UPB] Current domain Not trusted.');
  } else {
    console.log('[UPB] Current domain Trusted. UPB disabled.');
  }
}

function activateControlPanel() {
  if (window.location.href === CONTROL_PANEL) {
    // Add listener to the add button
    addNewDomainButton();
    // Show already stored elements in the list
    const storedTrust = getTrustedDomains();
    storedTrust.forEach(addDomainToPermissionList);
    console.log(storedTrust);
  }
}

function activateExtensionMenu() {
  attachToExtensionMenu(
    'Configure popup permissions',
    () => {
      openControlPanel();
    },
  );
}

/* ---------------------------------------------------------------- */

// Add configure link to Tampermonkey's menu
activateExtensionMenu();

// Initiate Control Panel logic
activateControlPanel();

// Start Popup Blocker
activateBlocker();