├── .gitignore ├── README.md ├── WORKS_ON.md ├── chrome ├── images │ ├── courtain_128.png │ ├── courtain_32.png │ ├── courtain_38.png │ └── courtain_64.png ├── js │ ├── background.js │ └── overlay_remover.js └── manifest.json ├── courtain_big.jpg └── test ├── package.json ├── tests.html └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Behind The Overlay Extension 2 | 3 | [Chrome Extension](https://chrome.google.com/webstore/detail/behindtheoverlay/ljipkdpcjbmhkdjjmbbaggebcednbbme) 4 | 5 | [Firefox extension](https://addons.mozilla.org/firefox/addon/behind_the_overlay/) 6 | 7 | **One button to close any overlay on any website** follow [Behind_Overlay](https://twitter.com/Behind_Overlay) for updates 8 | 9 | ## News 10 | 11 | 16 Mar 2021 | The scrollbar unblocker was improved to better handle overflowY (thanks to ricobl). 12 | 13 | 23 Dec 2020 | Minor release, just changing the name from BehindTheOverlay to Behind The Overlay. 14 | 15 | 11 feb 2018 | Update Firefox extension to make it compatible with the latest version. 16 | 17 | 11 Jun 2014 | Firefox extension is available now ! 18 | 19 | 10 Jun 2014 | Assign a keyboard shortcut, [see how](https://twitter.com/Behind_Overlay/status/476250479706398722). 20 | 21 | 22 | ## What's it all about? 23 | 24 | Some websites will use an overlay to mask its content with a transparent background to force you to read a message before you can see the actual content. 25 | 26 | I find this very annoying as every site will have a different way to close that overlay popup. 27 | 28 | This extension solves this problem by offering **you one button to close any overlay** on any website you may ever encounter. 29 | 30 | ## In the news 31 | 32 | This extension was featured on [cnet.com](https://www.cnet.com/how-to/chrome-extension-provides-easy-button-to-close-overlay-pop-ups/), [thenextweb.com](https://thenextweb.com/apps/2014/06/06/chrome-extension-kills-website-overlay-popups/), [ghacks.net](https://www.ghacks.net/2017/05/25/close-annoying-website-overlays-in-chrome-and-firefox/) among others. 33 | 34 | ## Extension in action 35 | 36 |  37 | 38 | ## Does it work everywhere ? 39 | 40 | The extension should work on all sites that have overlays. I created a list of some of the websites that the extension is know to work: [WORKS_ON.md](WORKS_ON.md). 41 | 42 | ## Features 43 | 44 | * Requires no special permissions. 45 | * Extremely lightweight. The extension activates only when you click its button, thereby it has no impact on navigation performance when you don't use the extension. Doesn't use tons of rules as AdBlock/uBlock extensions are doing for example. 46 | * Supports hiding of multiple DOM overlay elements. 47 | * Enables overflow auto of the body when overlay script hides it to disable the scroll of the page. 48 | * Support of keyboard shortcut. Ctrl+Shift+X on Windows,Linux and Cmd+Shift+X on Mac. 49 | 50 | 51 | ## Feedback 52 | If you have any suggestion or comment, please create an issue here or send me a tweet to [NicolaeNMV](https://twitter.com/NicolaeNMV). Any feedback is highly appreciated. 53 | 54 | ## Licence 55 | Licensed under the [GPL License](https://www.gnu.org/licenses/gpl-3.0.en.html). 56 | -------------------------------------------------------------------------------- /WORKS_ON.md: -------------------------------------------------------------------------------- 1 | These are some of the sites where the extension was tested to be working 2 | 3 | * http://www.quora.com/Product-Launches/What-is-the-best-day-of-the-week-to-launch-a-new-product-or-company 4 | * https://www.youtube.com/user/SCStarNet?annotation_id=annotation_1748547763&feature=iv&src_vid=BOl7H0jGiOw&sub_confirmation=1 5 | * http://www.pbs.org/wgbh/pages/frontline/shows/teenbrain/work/adolescent.html 6 | * http://www.pcmag.com/ 7 | * http://www.billhowe.com/2012/08/take-our-survey-and-get-5-to-starbucks-and-be-entered-in-our-big-giveaway/ 8 | * http://www.parallels.com/products/desktop/ 9 | * http://slashdot.org/story/14/05/27/1326219/the-flaw-lurking-in-every-deep-neural-net 10 | * http://sfglobe.com/?id=846 11 | * http://www.thebetterindia.com/10904/jadav-molai-payeng-forest-man-india/ 12 | * http://www.maddyness.com/accompagnement/formation-accompagnement/2014/05/05/subvention-startup-horizon-2020/ 13 | * http://www.siteslike.com/similar/google.com 14 | * http://privesc.eu 15 | * http://www.facenews.ua/articles/2014/198055/ 16 | * http://www.sitepoint.com/using-beacon-image-github-website-email-analytics/ 17 | * http://www.ft.com/cms/s/1bef71b8-e433-11e3-a73a-00144feabdc0,Authorised=false.html?_i_location=http%3A%2F%2Fwww.ft.com%2Fcms%2Fs%2F0%2F1bef71b8-e433-11e3-a73a-00144feabdc0.html%3Fsiteedition%3Duk&siteedition=uk&_i_referer=#axzz32pxnHeyS 18 | * http://www.searchenginejournal.com/5-important-link-removal-facts-post-penguin-2-0/65027/ 19 | * http://www.lemonde.fr/sante/article/2014/05/30/paquets-neutres-interdiction-du-vapotage-en-public-les-mesures-chocs-anti-tabac_4428991_1651302.html 20 | * http://evroua.com/poroshenko-obyasnil-kak-ukraina-budet-zhit-bez-rossijskogo-gaza/ 21 | * http://www.motherjones.com/media/2014/05/internet-archive-wayback-machine-brewster-kahle 22 | * http://www.linearpublishing.com/Articles-s-15047.412112-203190.114123-p17698.114123-sub_Overlay_Ads.html 23 | * https://sowefund.com/projet/1/Brands-On-Air 24 | * http://www.scoop.it/t/sigfox/p/3633647007/2012/12/07/sigfox-recrute 25 | * http://www.devfright.com/ibeacons-tutorial-ios-7-clbeaconregion-clbeacon/ 26 | * https://www.heise.de/newsticker/ -------------------------------------------------------------------------------- /chrome/images/courtain_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolaeNMV/BehindTheOverlay/41c176c60253585195748678ab3c3ffc6ad93b4d/chrome/images/courtain_128.png -------------------------------------------------------------------------------- /chrome/images/courtain_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolaeNMV/BehindTheOverlay/41c176c60253585195748678ab3c3ffc6ad93b4d/chrome/images/courtain_32.png -------------------------------------------------------------------------------- /chrome/images/courtain_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolaeNMV/BehindTheOverlay/41c176c60253585195748678ab3c3ffc6ad93b4d/chrome/images/courtain_38.png -------------------------------------------------------------------------------- /chrome/images/courtain_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolaeNMV/BehindTheOverlay/41c176c60253585195748678ab3c3ffc6ad93b4d/chrome/images/courtain_64.png -------------------------------------------------------------------------------- /chrome/js/background.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Called when the user clicks on the browser action. 6 | 7 | var chrome = chrome || browser; 8 | 9 | chrome.action.onClicked.addListener((tab) => { 10 | chrome.scripting.executeScript({ 11 | target: {tabId: tab.id}, 12 | files: ['/js/overlay_remover.js'] 13 | }, () => { 14 | 15 | chrome.scripting.executeScript({ 16 | target: { tabId: tab.id }, 17 | function: () => { 18 | overlayRemoverRun(); 19 | }, 20 | }); 21 | 22 | }); 23 | 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /chrome/js/overlay_remover.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a script that will remove overlay popups in the 99% of the cases. 3 | * It's doing that by detecting DOM elements. 4 | * 5 | */ 6 | 7 | var debug = false; 8 | 9 | var utils = (function() { 10 | function hideElement(element) { 11 | styleImportant(element, 'display', 'none'); 12 | } 13 | 14 | function styleImportant(element, cssProperty, cssValue) { 15 | element.style[cssProperty] = ''; 16 | var cssText = element.style.cssText || ''; 17 | if (cssText.length > 0 && cssText.slice(-1) != ';') 18 | cssText += ';'; 19 | // Some pages are using !important on elements, so we must use it too 20 | element.style.cssText = cssText + cssProperty + ': ' + cssValue + ' !important;'; 21 | } 22 | 23 | function isVisible(element) { 24 | return element.offsetWidth > 0 && element.offsetHeight > 0; 25 | } 26 | 27 | function getZIndex(element) { 28 | return parseInt(window.getComputedStyle(element).zIndex); 29 | } 30 | 31 | function isAnElement(node) { 32 | return node.nodeType == 1; // nodeType 1 mean element 33 | } 34 | 35 | function nodeListToArray(nodeList) { 36 | return Array.prototype.slice.call(nodeList); 37 | } 38 | 39 | function forEachElement(nodeList, functionToApply) { 40 | nodeListToArray(nodeList).filter(isAnElement).forEach(function(element) { 41 | functionToApply.call(this, element); 42 | }); 43 | } 44 | 45 | function collectParrents(element, predicate) { 46 | var matchedElement = element && predicate(element) ? [element] : []; 47 | var parent = element.parentNode; 48 | 49 | if (parent && parent != document && parent != document.body) { 50 | return matchedElement.concat(collectParrents(parent, predicate)); 51 | } else { 52 | return matchedElement; 53 | } 54 | } 55 | 56 | // Calculate the number of DOM elements inside an element 57 | function elementWeight(element, maxThreshold) { 58 | var grandTotal = 0; 59 | var nextElement = element; 60 | var nextGrandChildNodes = []; 61 | 62 | function calculateBreathFirst(element) { 63 | var total = 0; 64 | var nextChildElements = []; 65 | 66 | var childNodes = element.childNodes; 67 | total = childNodes.length; 68 | 69 | forEachElement(childNodes, function(childNode) { 70 | var grandChildNodes = nodeListToArray(childNode.childNodes); 71 | total += grandChildNodes.length; 72 | nextChildElements = nextChildElements.concat(grandChildNodes.filter(isAnElement)); 73 | }); 74 | return [total, nextChildElements]; 75 | } 76 | 77 | while (nextElement) { 78 | var tuple_total_nextChildElements = calculateBreathFirst(nextElement); 79 | var total = tuple_total_nextChildElements[0]; 80 | 81 | grandTotal += total; 82 | nextGrandChildNodes = nextGrandChildNodes.concat(tuple_total_nextChildElements[1]); 83 | 84 | if (grandTotal >= maxThreshold) { 85 | break; 86 | } else { 87 | nextElement = nextGrandChildNodes.pop(); 88 | } 89 | } 90 | 91 | return grandTotal; 92 | } 93 | 94 | return { 95 | hideElement: hideElement, 96 | isVisible: isVisible, 97 | getZIndex: getZIndex, 98 | forEachElement: forEachElement, 99 | collectParrents: collectParrents, 100 | elementWeight: elementWeight, 101 | styleImportant: styleImportant 102 | } 103 | })(); 104 | 105 | var overlayRemover = function(debug, utils) { 106 | function hideElementsAtZIndexNear(nearElement, thresholdZIndex) { 107 | var parent = nearElement.parentNode; 108 | // The case when nearElement is a document 109 | if (parent === null) { 110 | return; 111 | } 112 | var children = parent.childNodes; 113 | 114 | utils.forEachElement(children, function(child) { 115 | if (utils.getZIndex(child) >= thresholdZIndex) { 116 | utils.hideElement(child); 117 | } 118 | }) 119 | } 120 | 121 | // Check the element in the middle of the screen 122 | // Search fo elements that have zIndex attribute 123 | function methodTwoHideElementMiddle() { 124 | var overlayPopup = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2); 125 | 126 | var overlayFound = utils.collectParrents( overlayPopup, function(el) { 127 | return utils.getZIndex(el) > 0; 128 | }); 129 | 130 | if (debug) 131 | console.debug('Overlay found: ', overlayFound); 132 | 133 | if (overlayFound.length == 0) 134 | return false; 135 | 136 | var olderParent = overlayFound.pop(); 137 | 138 | if (debug) 139 | console.debug('Hide parrent: ', olderParent); 140 | 141 | return olderParent; 142 | } 143 | 144 | function disableBlur() { 145 | var someContainerMaybe = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2); 146 | 147 | var bluredParentsFound = utils.collectParrents( someContainerMaybe, function(el) { 148 | return window.getComputedStyle(el).filter.includes('blur'); 149 | }); 150 | 151 | if (bluredParentsFound.length == 0) 152 | return false; 153 | 154 | var topParent = bluredParentsFound.pop(); 155 | 156 | // Some element can act as a container, that can be blured or masking the whole content 157 | var isContainerOccupyingAboutSpaceAsBody = topParent.offsetWidth >= (document.body.offsetWidth - 100); 158 | 159 | if (isContainerOccupyingAboutSpaceAsBody) { 160 | utils.styleImportant(topParent, 'filter', 'blur(0)'); 161 | 162 | if (debug) console.log('Blur removed!', topParent); 163 | 164 | return true; 165 | } 166 | 167 | return false; 168 | } 169 | 170 | function containersOverflowAuto() { 171 | var containers = [document.documentElement, document.body]; 172 | 173 | containers.forEach(function(element) { 174 | if (window.getComputedStyle(element).overflowY == 'hidden') { 175 | utils.styleImportant(element, 'overflow', 'auto'); 176 | } 177 | if (window.getComputedStyle(element).position == 'fixed') { 178 | utils.styleImportant(element, 'position', 'static'); 179 | } 180 | }) 181 | } 182 | 183 | function run() { 184 | for (var i = 0; i < 10; i++) { 185 | var candidate = methodTwoHideElementMiddle(); 186 | var first = i == 0; 187 | if (candidate === false) { 188 | if (first) 189 | alert('No overlay has been found on this website.'); 190 | break; 191 | } else { 192 | if (!first) { 193 | // Prevent to hide the actual content 194 | var weightThreshold = 100; 195 | var candidateWeight = utils.elementWeight(candidate, weightThreshold) 196 | if (candidateWeight < weightThreshold) { 197 | if (debug) 198 | console.log('Element is too lightweight, hide it', candidate); 199 | utils.hideElement(candidate); 200 | } else { 201 | if (debug) 202 | console.log("Element is too heavy, don't hide it", candidate); 203 | } 204 | } else { 205 | utils.hideElement(candidate); 206 | containersOverflowAuto(); 207 | disableBlur(); 208 | } 209 | } 210 | } 211 | } 212 | 213 | return { 214 | run: run 215 | }; 216 | 217 | }; 218 | 219 | overlayRemoverInstance = overlayRemover(debug, utils); 220 | 221 | function overlayRemoverRun() { 222 | overlayRemoverInstance.run(); 223 | } 224 | -------------------------------------------------------------------------------- /chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Behind The Overlay", 3 | "author": "Nicolae Namolovan", 4 | "description": "One click to close any overlay on any website.", 5 | "version": "0.2.2", 6 | "permissions": [ 7 | "activeTab", 8 | "scripting" 9 | ], 10 | "background": { 11 | "service_worker": "js/background.js" 12 | }, 13 | "action": { 14 | "default_icon": { 15 | "16": "images/courtain_32.png", 16 | "48": "images/courtain_38.png", 17 | "128": "images/courtain_64.png" 18 | }, 19 | "default_title": "❌ Overlay from this page" 20 | }, 21 | "commands": { 22 | "_execute_action": { 23 | "suggested_key": "Ctrl+Shift+X", 24 | "description": "❌ Overlay from this page" 25 | } 26 | }, 27 | "icons": { 28 | "128": "images/courtain_128.png" 29 | }, 30 | "manifest_version": 3 31 | } 32 | -------------------------------------------------------------------------------- /courtain_big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolaeNMV/BehindTheOverlay/41c176c60253585195748678ab3c3ffc6ad93b4d/courtain_big.jpg -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "tests.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "BSD-2-Clause", 11 | "devDependencies": { 12 | "qunitjs": "~1.14.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/tests.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |