// ==UserScript==
// @name Ultra Popup Blocker
// @description Configurable popup blocker that blocks all popup windows by default.
// @namespace https://github.com/eskander
// @author eskander
// @version 2.99
// @include *
// @license MIT
// @homepage https://github.com/eskander/ultra-popup-blocker
// @supportURL https://github.com/eskander/ultra-popup-blocker/issues
// @compatible firefox Tampermonkey recommended
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @downloadURL none
// ==/UserScript==
/* ---------------------------------------------------------------- */
const PERMISSION_DIALOG_ID = 'ultra_popup_blocker'; // HTML ID in the page
const CONTROL_PANEL = 'https://eskander.github.io/ultra-popup-blocker/configure.html';
// 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;
},
};
/* ---------------------------------------------------------------- */
// 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;';
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, clickCallback, inlineStyle) {
const button = document.createElement('button');
button.innerHTML = text;
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) {
createButton(
logDiv,
'Trust 🗸',
() => {
addDomainToLocalStorage(domain);
},
'',
);
}
// Permission bar; Create a button (child of @logDiv) which onclick opens @domain
function createOpenPopupButton(logDiv, args) {
createButton(
logDiv,
'Open ↗',
() => {
realWindowOpen(...args);
},
'',
);
}
// Permission bar; Create a button (child of @logDiv) which onclick hides @logDiv
function createCloseButton(logDiv) {
createButton(
logDiv,
'Close ⨯',
() => {
closeLogDiv(logDiv);
},
' background-color: #a00;\
color: white;\
margin: 0 10px 0 0;\
float: right',
);
}
// Permission bar; Create a button (child of @logDiv) which onclick opens @controlPanel
function createConfigButton(logDiv) {
createButton(
logDiv,
'Config ⚙',
() => {
openControlPanel();
},
'float:right',
);
}
// Permission bar; Display a permission prompt when a new popup is detected
function createDialogMessage(logDiv, url) {
const currentLogDiv = logDiv;
let msg;
let popupUrl;
global.upb_counter += 1;
if (url[0] === '/') {
popupUrl = document.domain + url;
} else {
popupUrl = url;
}
if (global.upb_counter === 1) {
msg = `[UPB] Blocked 1 popup: ${popupUrl}`;
} else {
msg = `[UPB] Blocked ${global.upb_counter} popups, last: ${popupUrl}`;
}
currentLogDiv.innerHTML = msg;
console.log(msg);
currentLogDiv.style.display = 'block';
}
// This function will be called each time a script wants to open a new window
function fakeWindowOpen(...args) {
const domain = getCurrentTopDomain();
const popupURL = args[0];
const logDiv = getLogDiv();
console.log(...args);
createDialogMessage(logDiv, popupURL);
createOpenPopupButton(logDiv, args);
createTrustButton(logDiv, domain);
createCloseButton(logDiv);
createConfigButton(logDiv);
return FakeWindow;
}
// Override browser's "window.open" with our own implementation.
function activateBlocker() {
global.open = fakeWindowOpen;
}
/* ---------------------------------------------------------------- */
// Add configure link to Tampermonkey's menu
attachToExtensionMenu(
'Configure popup permissions',
() => {
openControlPanel();
},
);
// Only run trusting mechanism on the appropriate page
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);
}
const disabled = isCurrentDomainTrusted();
if (disabled) {
console.log('[UPB] Current domain was found on trust list. UPB disabled.');
} else {
activateBlocker();
}