Pop-Up Blocker

Simple but effective popup window blocker. Also tries to deny unsolicited redirections.

  1. // ==UserScript==
  2. // @name Pop-Up Blocker
  3. // @namespace HTML
  4. // @description Simple but effective popup window blocker. Also tries to deny unsolicited redirections.
  5. // @include *
  6. // @version $Id$
  7. // @grant none
  8. // @run-at document-start
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. var BLOCK_MODE = {'ALL': 0, // Block all popups
  13. 'CONFIRM': 2, // Confirm each popup (not recommended, but useful for testing)
  14. 'GRANT_PERIOD': 4, // Block popups that are initiated after the mouse click grant period
  15. 'ALLOW_SECURE': 8, // Allow popups from secure (HTTPS) sites (default don't allow)
  16. 'ALLOW_REDIRECTS': 16, // Allow unsolicited redirects (default block)
  17. 'CONFIRM_UNLOAD': 32} // Confirm assumed unsolicited page unload (default don't confirm)
  18.  
  19. // Configuration
  20. var block_mode = BLOCK_MODE.ALL;
  21. var grant_period = 100; // Milliseconds
  22. var debug = false; // Enable debug logging
  23.  
  24. // DO NOT CHANGE BELOW THIS POINT
  25. var allowed_elements = {'a': true, 'button': {'type': 'submit'}, 'input': true, 'select': true, 'option': true};
  26. var ts = 0, wopen = window.open, showmodaldlg = window.showModalDialog;
  27. var lastInteractedElement;
  28. var marginTop = null;
  29. var notifications = 0;
  30. var notificationOffsetTop = 0;
  31.  
  32. function confirmed(msg, arguments) {
  33. return block_mode & BLOCK_MODE.CONFIRM ? confirmPopup(msg, arguments) : false;
  34. }
  35.  
  36. function grantperiod_exceeded() {
  37. return block_mode & BLOCK_MODE.GRANT_PERIOD ? Date.now() > ts + grant_period : true;
  38. }
  39.  
  40. function protocol_allowed() {
  41. return block_mode & BLOCK_MODE.ALLOW_SECURE ? location.protocol == 'https:' : false;
  42. }
  43.  
  44. function element_allowed(element) {
  45. var allowed = element.tagName && allowed_elements[element.tagName.toLowerCase()];
  46. if (allowed && typeof allowed == "object") {
  47. for (var property in allowed) if (element[property] != element[property]) return false;
  48. }
  49. return allowed;
  50. }
  51.  
  52. window.addEventListener('mousedown', function (event) {
  53. ts = Date.now();
  54. if (debug) console.info('Mouse button', event.button != null ? event.button : event.which, 'down on', event.target);
  55. setlastInteractedElement(event);
  56. //mediateEventPropagation(event);
  57. }, true);
  58.  
  59. window.addEventListener('click', function (event) {
  60. ts = Date.now();
  61. if (debug) console.info('Mouse button', event.button != null ? event.button : event.which, 'click on', event.target);
  62. setlastInteractedElement(event);
  63. //mediateEventPropagation(event);
  64. }, true);
  65.  
  66. window.addEventListener('change', function (event) {
  67. ts = Date.now();
  68. if (debug) console.info('Changed selection on', event.target);
  69. setlastInteractedElement(event);
  70. //mediateEventPropagation(event);
  71. }, true);
  72.  
  73. function setlastInteractedElement(event) {
  74. // Deal with tags nested in (e.g.) links
  75. var element = event.target;
  76. if (event instanceof MouseEvent && (event.button != null ? event.button != 0 : event.which != 1)) return;
  77. while (element.parentElement && !element_allowed(element)) {
  78. element = element.parentElement;
  79. }
  80. lastInteractedElement = element;
  81. if (debug) console.info('Last interacted element', element);
  82. }
  83.  
  84. function mediateEventPropagation(event) {
  85. // Stop event propagation if element has a 'href' attribute that does not point to the current document
  86. // (prevents click hijacking)
  87. if (!protocol_allowed() &&
  88. lastInteractedElement &&
  89. lastInteractedElement.href &&
  90. boildown(lastInteractedElement.href) != boildown(window.location.href) &&
  91. lastInteractedElement.href.indexOf('javascript:') !== 0) {
  92. event.stopPropagation();
  93. console.warn('Pop-Up Blocker stopped event propagation for', event);
  94. }
  95. }
  96.  
  97. function regExpEscape(s) {
  98. return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
  99. replace(/\x08/g, '\\x08');
  100. };
  101.  
  102. var regExpProtHostPathQ = new RegExp('^((' + regExpEscape(location.protocol) + '//' + regExpEscape(location.host) + ')?' +
  103. '(' + regExpEscape(location.pathname) + ')?)?');
  104.  
  105. function boildown(uri) {
  106. var uri_boileddown = uri.replace(regExpProtHostPathQ, ''); // Strip current protocol + host + path
  107. uri_boileddown = uri_boileddown.replace(/#.*$/, ''); // Strip any hash
  108. // Sort query vars
  109. var query = uri_boileddown.match(/\?[^?]+/);
  110. if (query)
  111. query = '?' + query[0].substr(1).split('&').sort().join('&');
  112. uri_boileddown = uri_boileddown.replace(/\?[^?]+/, query || '');
  113. return uri_boileddown;
  114. };
  115.  
  116. // Deny unsolicited redirection
  117. if (!(block_mode & BLOCK_MODE.ALLOW_REDIRECTS) && typeof window.location.watch == 'function') window.location.watch(
  118. 'href',
  119. function (id, oldval, newval) {
  120. var href_boileddown, newval_boileddown = boildown(newval);
  121. console.info('location.' + id, '->', newval);
  122. if (lastInteractedElement && lastInteractedElement.tagName &&
  123. lastInteractedElement.tagName.toLowerCase() == 'a')
  124. href_boileddown = boildown(lastInteractedElement.href);
  125. var link_hijacked = href_boileddown != undefined && newval.indexOf('#') !== 0 && newval_boileddown != href_boileddown;
  126. if (debug) {
  127. console.info('Page secure?', location.protocol == 'https:');
  128. if (block_mode & BLOCK_MODE.ALLOW_SECURE) console.info('Allowed protocol?', protocol_allowed());
  129. console.info('Last interacted element?', lastInteractedElement);
  130. if (lastInteractedElement) {
  131. console.info('Last interacted element tag name?', lastInteractedElement.tagName);
  132. if (lastInteractedElement.tagName) {
  133. console.info('Allowed element?', !!element_allowed(lastInteractedElement));
  134. console.info('Last interacted element is link?', lastInteractedElement.tagName.toLowerCase() == 'a');
  135. if (lastInteractedElement.tagName.toLowerCase() == 'a') {
  136. console.info('New location (boiled down) =', newval_boileddown);
  137. console.info('Link HREF (boiled down) =', href_boileddown);
  138. console.info('New location is the same as link HREF?', newval_boileddown == href_boileddown);
  139. console.info('Link target is a new window?', lastInteractedElement.target == '_blank');
  140. }
  141. }
  142. }
  143. if (block_mode & BLOCK_MODE.GRANT_PERIOD) console.info('Grant period exceeded?', grantperiod_exceeded());
  144. }
  145. if ((!protocol_allowed() || grantperiod_exceeded()) &&
  146. (!lastInteractedElement ||
  147. (!element_allowed(lastInteractedElement) ||
  148. (/*lastInteractedElement.tagName.toLowerCase() == 'a' &&*/
  149. (link_hijacked ||
  150. lastInteractedElement.target == '_blank'))))) {
  151. notify('Denied redirection to', newval, null, 0, null, '_self');
  152. console.error('Pop-Up Blocker denied redirection to ' + newval);
  153. return '#' + location.hash.replace(/^#/, '');
  154. }
  155. return newval;
  156. }
  157. );
  158.  
  159. var onbeforeunload = window.onbeforeunload;
  160. if (block_mode & BLOCK_MODE.CONFIRM_UNLOAD) window.onbeforeunload = function (e) {
  161. if (debug) console.info('window.', e);
  162. if (!protocol_allowed()) {
  163. if (debug) {
  164. console.info('Page secure?', location.protocol == 'https:');
  165. if (block_mode & BLOCK_MODE.ALLOW_SECURE) console.info('Allowed protocol?', protocol_allowed());
  166. console.info('Last interacted element?', lastInteractedElement);
  167. if (lastInteractedElement) {
  168. console.info('Last interacted element tag name?', lastInteractedElement.tagName);
  169. if (lastInteractedElement.tagName) {
  170. console.info('Allowed element?', !!element_allowed(lastInteractedElement));
  171. }
  172. }
  173. if (block_mode & BLOCK_MODE.GRANT_PERIOD) console.info('Grant period exceeded?', grantperiod_exceeded());
  174. }
  175. console.warn('You are possibly involuntarily being redirected to another page.');
  176. (e || window.event).returnValue = 'You are possibly involuntarily being redirected to another page. Do you want to leave ' + location.href + ' or stay?';
  177. return (e || window.event).returnValue;
  178. }
  179. else if (typeof onbeforeunload === 'function')
  180. return onbeforeunload.apply(window, arguments);
  181. };
  182.  
  183. //var onkeydown = window.onkeydown;
  184. //window.onkeydown = function (e) {
  185. //lastInteractedElement = null;
  186. //if (typeof onkeydown === 'function')
  187. //return onkeydown.apply(window, arguments);
  188. //};
  189.  
  190. //var onmouseleave = document.body.onmouseleave;
  191. //document.body.onmouseleave = function (e) {
  192. //lastInteractedElement = null;
  193. //if (typeof onmouseleave === 'function')
  194. //return onmouseleave.apply(document.body, arguments);
  195. //};
  196.  
  197. function confirmPopup(msg, args) {
  198. return confirm(msg + ' (' + Array.prototype.slice.apply(arguments).join(', ') + ')');
  199. }
  200.  
  201. window.open = function () {
  202. var oargs = arguments;
  203. if (debug) {
  204. console.info('Page secure?', location.protocol == 'https:');
  205. if (block_mode & BLOCK_MODE.ALLOW_SECURE) console.info('Allowed protocol?', protocol_allowed());
  206. if (block_mode & BLOCK_MODE.GRANT_PERIOD) console.info('Grant period exceeded?', grantperiod_exceeded());
  207. }
  208. if (/*['_self', '_parent', '_top'].includes(arguments[1]) ||*/
  209. (confirmed('Allow popup?', arguments) &&
  210. (protocol_allowed() || !grantperiod_exceeded()))) {
  211. console.info('Pop-Up Blocker allowed window.open', Array.prototype.slice.apply(arguments));
  212. return wopen.apply(window, arguments);
  213. }
  214. else {
  215. console.error('Pop-Up Blocker blocked window.open', Array.prototype.slice.apply(arguments));
  216. notify('Blocked popup window', arguments[0], arguments[1], 0, function() {
  217. console.info('Pop-Up Blocker user clicked window.open', Array.prototype.slice.apply(oargs));
  218. wopen.apply(window, oargs);
  219. });
  220. }
  221. return {}
  222. };
  223.  
  224. window.showModalDialog = function () {
  225. if (debug) {
  226. console.info('Page secure?', location.protocol == 'https:');
  227. if (block_mode & BLOCK_MODE.ALLOW_SECURE) console.info('Allowed protocol?', protocol_allowed());
  228. if (block_mode & BLOCK_MODE.GRANT_PERIOD) console.info('Grant period exceeded?', grantperiod_exceeded());
  229. }
  230. if (confirmed('Allow modal dialog?', arguments) &&
  231. (protocol_allowed() || !grantperiod_exceeded())) {
  232. console.info('Pop-Up Blocker allowed window.showModalDialog', Array.prototype.slice.apply(arguments));
  233. return showmodaldlg.apply(window, arguments);
  234. }
  235. else {
  236. console.error('Pop-Up Blocker blocked modal showModalDialog', Array.prototype.slice.apply(arguments));
  237. notify('Blocked modal dialog', arguments[0], null, 0, function() {
  238. console.info('Pop-Up Blocker user clicked window.showModalDialog', Array.prototype.slice.apply(oargs));
  239. showmodaldlg.apply(window, oargs);
  240. });
  241. }
  242. return {}
  243. };
  244.  
  245. function notify(text, uri, title, timeout, onclick, target) {
  246. var rootElement = document.body.parentElement,
  247. notification = document.createElement('div');
  248. notification.onclick = function () {
  249. return false;
  250. }
  251. if (marginTop === null) marginTop = parseFloat((rootElement.currentStyle || window.getComputedStyle(rootElement)).marginTop)
  252. resetStyles(notification);
  253. notification.style.cssText += 'background: InfoBackground !important';
  254. notification.style.cssText += 'border-bottom: 1px solid WindowFrame !important';
  255. notification.style.cssText += 'box-sizing: border-box !important';
  256. notification.style.cssText += 'font: small-caption !important';
  257. notification.style.cssText += 'padding: .15em .9em !important';
  258. notification.style.cssText += 'position: fixed !important';
  259. notification.style.cssText += 'left: 0 !important';
  260. notification.style.cssText += 'line-height: 2.3 !important'; // 31px
  261. notification.style.cssText += 'right: 0 !important';
  262. notification.style.cssText += 'top: -100% !important';
  263. notification.style.cssText += 'transition: top .25s !important';
  264. notification.style.cssText += 'width: 100% !important';
  265. notification.style.cssText += 'white-space: nowrap !important';
  266. notification.style.cssText += 'z-index: 2147483647 !important';
  267. var closeButton = document.createElement('span');
  268. resetStyles(closeButton);
  269. closeButton.style.cssText += 'cursor: pointer !important';
  270. closeButton.style.cssText += 'display: inline-block !important';
  271. closeButton.style.cssText += 'float: right !important';
  272. closeButton.style.cssText += 'font: inherit !important';
  273. closeButton.style.cssText += 'line-height: 2.1 !important';
  274. closeButton.style.cssText += 'margin-left: .75em !important';
  275. closeButton.appendChild(document.createTextNode('╳'));
  276. function closeNotification(event) {
  277. if (event) event.stopPropagation();
  278. //notificationOffsetTop -= notification.offsetHeight;
  279. if (!--notifications) rootElement.style.cssText += 'margin-top: ' + marginTop + ' !important';
  280. notification.style.cssText += 'top: -' + notification.offsetHeight + 'px !important';
  281. setTimeout(function () {
  282. document.body.removeChild(notification);
  283. }, 250);
  284. return false;
  285. }
  286. closeButton.onclick = closeNotification;
  287. notification.appendChild(closeButton);
  288. notification.appendChild(document.createTextNode('🚫 ' + text));
  289. var numLinks = target == '_self' ? 1 : 2;
  290. for (var i = 0; i < numLinks; i ++) {
  291. var popupLink = document.createElement(!i ? 'a' : 'button');
  292. resetStyles(popupLink);
  293. if (i) {
  294. popupLink.style.cssText += '-moz-appearance: button !important';
  295. popupLink.style.cssText += '-webkit-appearance: button !important';
  296. popupLink.style.cssText += 'appearance: button !important';
  297. popupLink.style.cssText += 'background: ButtonFace !important';
  298. popupLink.style.cssText += 'border: 1px solid ButtonShadow !important';
  299. popupLink.style.cssText += 'color: ButtonText !important';
  300. popupLink.style.cssText += 'font: small-caption !important';
  301. popupLink.style.cssText += 'padding: .15em .5em !important';
  302. }
  303. else {
  304. popupLink.style.cssText += 'color: #00e !important';
  305. popupLink.style.cssText += 'color: -moz-nativehyperlinktext !important';
  306. popupLink.style.cssText += 'display: inline-block !important';
  307. popupLink.style.cssText += 'font: inherit !important';
  308. popupLink.style.cssText += 'max-width: 50% !important';
  309. popupLink.style.cssText += 'overflow: hidden !important';
  310. popupLink.style.cssText += 'text-decoration: underline !important';
  311. popupLink.style.cssText += 'text-overflow: ellipsis !important';
  312. popupLink.style.cssText += 'vertical-align: bottom !important';
  313. popupLink.style.cssText += 'white-space: nowrap !important';
  314. popupLink.setAttribute('href', uri);
  315. popupLink.setAttribute('target', target || '_blank');
  316. }
  317. if (title) popupLink.setAttribute('title', title);
  318. var linkText = i ? 'Open in this frame' : uri;
  319. popupLink.appendChild(document.createTextNode(linkText));
  320. popupLink.onclick = function(event) {
  321. event.stopPropagation();
  322. closeNotification();
  323. if (this.tagName.toLowerCase() != 'a') {
  324. location.href = uri;
  325. }
  326. else if (onclick) {
  327. onclick(event);
  328. return false;
  329. }
  330. };
  331. notification.appendChild(document.createTextNode(' '));
  332. notification.appendChild(popupLink);
  333. }
  334. if (!notifications) rootElement.style.cssText += 'transition: margin-top .25s !important';
  335. document.body.appendChild(notification);
  336. // Note: offsetHeight is zero while the element is not part of the document DOM tree
  337. notification.style.cssText += 'top: -' + notification.offsetHeight + 'px !important';
  338. setTimeout(function() {
  339. //notificationOffsetTop += notification.offsetHeight;
  340. notification.style.cssText += 'top: ' + notificationOffsetTop + 'px !important';
  341. if (!notifications) rootElement.style.cssText += 'margin-top: ' + (marginTop + notification.offsetHeight) + 'px !important';
  342. notifications ++;
  343. }, 0)
  344. if (timeout) {
  345. setTimeout(function () {
  346. closeNotification();
  347. }, timeout);
  348. }
  349. }
  350.  
  351. function resetStyles(element) {
  352. if (element.tagName.toLowerCase() != 'button') {
  353. element.style.cssText = 'background: transparent !important';
  354. element.style.cssText += 'border: none !important';
  355. element.style.cssText += 'border-radius: 0 !important';
  356. if (element.tagName.toLowerCase() == 'a')
  357. element.style.cssText += 'cursor: pointer !important';
  358. }
  359. else element.style.cssText += 'cursor: auto !important';
  360. element.style.cssText += 'bottom: auto !important';
  361. element.style.cssText += 'box-shadow: none !important';
  362. element.style.cssText += 'color: WindowText !important';
  363. element.style.cssText += 'font: medium serif !important';
  364. element.style.cssText += 'letter-spacing: 0 !important';
  365. element.style.cssText += 'line-height: normal !important';
  366. element.style.cssText += 'margin: 0 !important';
  367. element.style.cssText += 'opacity: 1 !important';
  368. element.style.cssText += 'outline: none !important';
  369. element.style.cssText += 'padding: 0 !important';
  370. element.style.cssText += 'position: static !important';
  371. element.style.cssText += 'text-align: left !important';
  372. element.style.cssText += 'text-shadow: none !important';
  373. element.style.cssText += 'text-transform: none !important';
  374. element.style.cssText += 'left: auto !important';
  375. element.style.cssText += 'right: auto !important';
  376. element.style.cssText += 'top: auto !important';
  377. element.style.cssText += 'white-space: normal !important';
  378. element.style.cssText += 'width: auto !important';
  379. }
  380. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址