Ultra Popup Blocker

Configurable popup blocker that blocks all popup windows by default.

目前为 2022-03-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Ultra Popup Blocker
  3. // @description Configurable popup blocker that blocks all popup windows by default.
  4. // @namespace https://github.com/eskander
  5. // @author Eskander
  6. // @version 3.5
  7. // @include *
  8. // @license MIT
  9. // @homepage https://eskander.tn/ultra-popup-blocker/
  10. // @supportURL https://github.com/Eskander/ultra-popup-blocker/issues/new
  11. // @compatible firefox Tampermonkey recommended
  12. // @compatible chrome Tampermonkey recommended
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_deleteValue
  16. // @grant GM_listValues
  17. // @grant GM_openInTab
  18. // @grant GM_registerMenuCommand
  19. // ==/UserScript==
  20.  
  21. /* ---------------------------------------------------------------- */
  22.  
  23. const PERMISSION_DIALOG_ID = 'ultra_popup_blocker'; // HTML ID in the page
  24. const CONTROL_PANEL = 'https://eskander.tn/ultra-popup-blocker/settings.html';
  25.  
  26. // Reference to page's "window" through GreaseMonkey
  27. const global = unsafeWindow;
  28. global.upb_counter = 0;
  29.  
  30. // Storing a reference to real "window.open" method in case we wanted it
  31. const realWindowOpen = global.open;
  32.  
  33. // We need to return the fake window to not encounter JS runtime error when the popup originator
  34. // page wants to call focus() or blur().
  35. const FakeWindow = {
  36. blur() {
  37. return false;
  38. },
  39. focus() {
  40. return false;
  41. },
  42. };
  43.  
  44. // Timeout before confirmation dialog closes automatically
  45. let timeleft = 15;
  46.  
  47. /* ---------------------------------------------------------------- */
  48.  
  49. // Add @domain to local storage
  50. function addDomainToLocalStorage(domain) {
  51. GM_setValue(`trusted_${domain}`, true);
  52. }
  53.  
  54. // Remove @domain from local storage
  55. function removeDomainFromLocalStorage(domain) {
  56. GM_deleteValue(`trusted_${domain}`);
  57. GM_deleteValue(`${domain}`);
  58. }
  59.  
  60. // Return true if @domain is trusted
  61. function isDomainTrusted(domain) {
  62. return GM_getValue(`trusted_${domain}`);
  63. }
  64.  
  65. // Return an Array of trusted domains
  66. function getTrustedDomains() {
  67. return GM_listValues();
  68. }
  69.  
  70. // Open permission manager in new tab
  71. function openControlPanel() {
  72. GM_openInTab(CONTROL_PANEL, false);
  73. }
  74.  
  75. // Add a link to permission manager in extensions' popup menu
  76. function attachToExtensionMenu(name, callback) {
  77. GM_registerMenuCommand(name, callback);
  78. }
  79.  
  80. // Permission bar; Return permission dialog, or create it if needed.
  81. function getLogDiv() {
  82. let logDiv = document.getElementById(PERMISSION_DIALOG_ID);
  83. if (!logDiv) {
  84. logDiv = document.createElement('div');
  85. logDiv.setAttribute('id', PERMISSION_DIALOG_ID);
  86. logDiv.style.cssText = 'position: fixed;\
  87. bottom: 0;\
  88. left: 0;\
  89. z-index: 99999;\
  90. width: 100%;\
  91. padding: 5px 5px 5px 5px;\
  92. font: status-bar;\
  93. background-color: black;\
  94. color: white;\
  95. cursor: help';
  96. document.body.appendChild(logDiv);
  97. }
  98. return logDiv;
  99. }
  100.  
  101. // Permission bar; Hide dialog
  102. function closeLogDiv(logDiv) {
  103. const currentLogDiv = logDiv;
  104. currentLogDiv.style.display = 'none';
  105. }
  106.  
  107. // Return current top domain. eg: github.com
  108. function getCurrentTopDomain() {
  109. const hostnameArray = document.location.hostname.split('.');
  110. const topLevelDomain = hostnameArray[hostnameArray.length - 1];
  111. const domainName = hostnameArray[hostnameArray.length - 2];
  112. const currentDomain = `${domainName}.${topLevelDomain}`;
  113. return currentDomain;
  114. }
  115.  
  116. // Return true if current domain has been trusted by the user
  117. function isCurrentDomainTrusted() {
  118. const domain = getCurrentTopDomain();
  119. return isDomainTrusted(domain);
  120. }
  121.  
  122. // Permission manager; Create a button to remove domain from permissions list
  123. function removeDomainFromPermissionList() {
  124. const div = this.parentElement;
  125. console.log(div);
  126. const domain = div.innerText.replace('\n\u00D7', '');
  127. removeDomainFromLocalStorage(domain);
  128. div.style.display = 'none';
  129. console.log(`[UPB] Domain removed from trust: ${domain}`);
  130. }
  131.  
  132. // Permission manager; Add a new domain to permissions list
  133. function addDomainToPermissionList(domain) {
  134. const domainName = domain.replace('trusted_', '');
  135. const li = document.createElement('li');
  136. const t = document.createTextNode(domainName);
  137. li.appendChild(t);
  138. document.getElementById('List').appendChild(li);
  139. // Add a remove button to li
  140. const span = document.createElement('SPAN');
  141. const txt = document.createTextNode('\u00D7');
  142. span.className = 'close';
  143. span.appendChild(txt);
  144. span.onclick = removeDomainFromPermissionList;
  145. li.appendChild(span);
  146. // Add domain to localStorage
  147. addDomainToLocalStorage(domainName);
  148. console.log(`[UPB] Domain added to trust: ${domainName}`);
  149. }
  150.  
  151. // Permission manager; Button to add a new domain to permissions list
  152. function addNewDomainButton() {
  153. document.getElementsByClassName('addBtn')[0].addEventListener(
  154. 'click',
  155. () => {
  156. const DOMAIN = document.getElementById('Input').value;
  157. if (DOMAIN !== '') {
  158. addDomainToPermissionList(DOMAIN);
  159. }
  160. document.getElementById('Input').value = '';
  161. },
  162. );
  163. }
  164.  
  165. // Permission bar; Create a button with inner text @text executing onclick
  166. // @clickCallback, appended as a child of @logDiv, with style @inlineStyle.
  167. function createButton(logDiv, text, id, clickCallback, inlineStyle) {
  168. const button = document.createElement('button');
  169. button.innerHTML = text;
  170. button.id = id;
  171. button.style.cssText = `text-decoration: none;\
  172. color: black;\
  173. cursor: pointer;\
  174. margin: 0 5px;\
  175. padding: 1px 3px;\
  176. background-color: rgb(255, 255, 255);\
  177. border-width: 0px;\
  178. border-radius: 5px;\
  179. color: black;\
  180. ${inlineStyle}`;
  181. logDiv.appendChild(button);
  182. button.addEventListener('click', clickCallback);
  183. }
  184.  
  185. // Permission bar; Create a button (child of @logDiv) which onclick trusts @domain
  186. function createTrustButton(logDiv, domain, a, b, c) {
  187. createButton(
  188. logDiv,
  189. 'Always Allow 🗸',
  190. 'upb_trust',
  191. () => {
  192. addDomainToLocalStorage(domain);
  193. realWindowOpen(a, b, c);
  194. closeLogDiv(logDiv);
  195. global.open = realWindowOpen;
  196. },
  197. '',
  198. );
  199. }
  200.  
  201. // Permission bar; Create a button (child of @logDiv) which onclick opens @domain
  202. function createOpenPopupButton(logDiv, a, b, c) {
  203. createButton(
  204. logDiv,
  205. 'Allow ↗',
  206. 'upb_open',
  207. () => {
  208. realWindowOpen(a, b, c);
  209. closeLogDiv(logDiv);
  210. },
  211. '',
  212. );
  213. }
  214.  
  215. // Permission bar; Create a button (child of @logDiv) which onclick hides @logDiv
  216. function createCloseButton(logDiv) {
  217. createButton(
  218. logDiv,
  219. `Deny (${timeleft})`,
  220. 'upb_close',
  221. () => {
  222. closeLogDiv(logDiv);
  223. },
  224. ' background-color: #a00;\
  225. color: white;',
  226. );
  227. }
  228.  
  229. // Permission bar; Create a button (child of @logDiv) which onclick opens @controlPanel
  230. function createConfigButton(logDiv) {
  231. createButton(
  232. logDiv,
  233. 'Config ⚙',
  234. 'upb_config',
  235. () => {
  236. openControlPanel();
  237. },
  238. ' float: right;\
  239. margin: 0 10px 0 0;',
  240. );
  241. }
  242.  
  243. // Permission bar; Display a permission prompt when a new popup is detected
  244. function createDialogMessage(logDiv, url) {
  245. const currentLogDiv = logDiv;
  246. const domain = getCurrentTopDomain();
  247. let msg;
  248. let popupUrl;
  249.  
  250. global.upb_counter += 1;
  251.  
  252. if (global.upb_counter === 1) {
  253. msg = `<b>[UPB]</b> Allow <b><u>${domain}</u></b> to open a popup ?`;
  254. } else {
  255. msg = `<b>[UPB]</b> Allow <b><u>${domain}</u></b> to open a popup ? <b>(${global.upb_counter})</b>`;
  256. }
  257.  
  258. if (url[0] === '/') {
  259. popupUrl = document.domain + url;
  260. } else {
  261. popupUrl = url;
  262. }
  263.  
  264. currentLogDiv.innerHTML = msg;
  265. currentLogDiv.title = popupUrl;
  266. console.log(msg);
  267. currentLogDiv.style.display = 'block';
  268. }
  269.  
  270. function createTimer(logDiv) {
  271. console.log(timeleft);
  272. if (timeleft === 15) {
  273. const Timer = setInterval(() => {
  274. document.getElementById('upb_close').innerHTML = `Deny (${timeleft})`;
  275. timeleft -= 1;
  276. if (timeleft < 0) {
  277. clearInterval(Timer);
  278. closeLogDiv(logDiv);
  279. timeleft = 15;
  280. }
  281. console.log(timeleft);
  282. }, 1000);
  283. }
  284. }
  285.  
  286. // This function will be called each time a script wants to open a new window
  287. function fakeWindowOpen(a, b, c) {
  288. const domain = getCurrentTopDomain();
  289. const popupURL = a;
  290. const logDiv = getLogDiv();
  291. console.log(a, b, c);
  292. createDialogMessage(logDiv, popupURL);
  293. createOpenPopupButton(logDiv, a, b, c);
  294. createTrustButton(logDiv, domain, a, b, c);
  295. createCloseButton(logDiv);
  296. createConfigButton(logDiv);
  297. createTimer(logDiv);
  298. return FakeWindow;
  299. }
  300.  
  301. // Override browser's "window.open" with our own implementation.
  302. function activateBlocker() {
  303. const TRUSTED = isCurrentDomainTrusted();
  304. if (!TRUSTED) {
  305. global.open = fakeWindowOpen;
  306. console.log('[UPB] Current domain Not trusted.');
  307. } else {
  308. console.log('[UPB] Current domain Trusted. UPB disabled.');
  309. }
  310. }
  311.  
  312. function activateControlPanel() {
  313. if (window.location.href === CONTROL_PANEL) {
  314. // Add listener to the add button
  315. addNewDomainButton();
  316. // Show already stored elements in the list
  317. const storedTrust = getTrustedDomains();
  318. storedTrust.forEach(addDomainToPermissionList);
  319. console.log(storedTrust);
  320. }
  321. }
  322.  
  323. function activateExtensionMenu() {
  324. attachToExtensionMenu(
  325. 'Configure popup permissions',
  326. () => {
  327. openControlPanel();
  328. },
  329. );
  330. }
  331.  
  332. /* ---------------------------------------------------------------- */
  333.  
  334. // Add configure link to Tampermonkey's menu
  335. activateExtensionMenu();
  336.  
  337. // Initiate Control Panel logic
  338. activateControlPanel();
  339.  
  340. // Start Popup Blocker
  341. activateBlocker();