-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Closure Compiler-compatible version of the code:
/* eslint-env browser, es2021 */
(function(what) {
function shouldIgnore(elem) {
for(let s of what.ignore?.selector ?? []) {
if(elem.matches(s)) { return true; }
}
for(let f of what.ignore?.func ?? []) {
if(f(elem)) { return true; }
}
return false;
}
function isContainerElem(/** @type {!Element} */elem) {
// .tagName returns UPPERCASE for some reason
return ["DIV", "SPAN"].includes(elem.tagName);
}
var rm = {
elem(/** @type {!Element} */elem) {
if(!shouldIgnore(elem)) {
removedElems.add([elem, elem.parentElement]);
elem.remove()
}
},
list(/** @type {!Iterable<!Element>} */elems) {
Array.from(elems).forEach(v => rm.elem(v))
},
cls(/**@type {!string} */name) {
rm.list(document.getElementsByClassName(name))
},
selector(/** @type {!string} */selector) {
rm.list(document.querySelectorAll(selector))
},
func({func, selector=null}) {
let elems = selector == null
? document.getElementsByClassName("*")
: document.querySelectorAll(selector);
for (let elem of elems) {
if (func(elem)) {
rm.elem(elem);
}
}
}
};
var /** @type {!Set<!Array<!Element>>} */ removedElems = new Set;
var /** @type {!Set<!Element>} */ handledElems = new Set;
for (let [name, args] of Object.entries(what)) {
// don't try to use the 'ignore' property as a thing to block
if(name != 'ignore') {
for (let arg of args) {
rm[name](arg);
}
}
}
for(let [elem, parent] of removedElems) {
if(handledElems.has(elem)) {
continue; // already handled
}
handledElems.add(elem);
if(!parent.isConnected) {
// (indirect) parent has been deleted so don't do anything here,
// instead go from the parent (which will also be in the Set)
continue;
}
if(!isContainerElem(parent)) {
continue; // parent might be an image or similar so don't delete
}
if(parent.hasChildNodes()) {
continue; // don't delete parent - info of other children would be lost
}
// no children, no info in self, so safe to delete
// NOTE: This will add `parent` to the end of removedElems (if not ignored) so will check again from the parent
rm.elem(parent);
}
})({
cls: ['adsbygoogle', 'mod_ad_container', 'brn-ads-box','gpt-ad','ad-box','top-ads-container', 'adthrive-ad'],
selector: [
'[aria-label="advertisement"]',
'[class*="-ad "],[class*="-ad-"],[class$="-ad"],[class^="ad-"],[class^="adthrive"]',
':is(div,iframe)[id^="google_ads_iframe_"]',
'#aipPrerollContainer',
// This should really select the top one but we let the 'only contains ads' functionality handle it.
// Yes I know its lazy, but it is more elegant than writing a whole new func filter (and more performant)
'span[data-ez-ph-id] span[data-ez-ph-owner-id] span.ezoicwhat',
],
/** @type {Array<{selector: string?, func: function(Element): boolean}>} */
func: [
{
selector: '[class*="ad" i],[id*="ad" i]',
/** This is the one that gets most of them, rest is just special cases */
func(elem) {
for (const name of [elem.id, ...elem.classList]) {
// TODO also check lowercase followed by uppercase at end e.g. adBox
if(/(?<!lo|re|he)(ad|Ad|AD)(vertisement)?s?([tT]hrive)?([cC]ontent)?([eE]ngine|[nN]gin)?([cC]ontainer)?s?($|[-_,\s])/.test(name)) {
return true;
}
}
}
},
{
selector: 'div#preroll',
func(elem) {
// match div#preroll that has child div#aipBranding
for (let c of elem.children) {
if(c.matches("div#aipBranding")) {
return true;
}
}
}
},
{
selector: 'html > iframe',
func(/** @type {HTMLIFrameElement} */elem) {
// Some sanity checks not to accidenally break websites
if(!(elem.sandbox.contains("allow-scripts") && elem.sandbox.contains("allow-same-origin") && elem.sandbox.length == 2)) {
return false;
}
if(!elem.src.toLowerCase().includes("gdpr")) { // Ad iframes very often include a `?gdpr=...` in the URL
return false;
}
return true;
}
},
],
ignore: {
selector: ["body", ".ad-layout", "#game-holder.game-holder-with-ad", ".no-interstitial-ads"],
func: [(elem) => {
let articles = document.getElementsByTagName('article');
for(let a of articles) {
if(elem.contains(a)) {
return true; // ignore if an article descends from it
}
}
}]
}
})Code size
As of 2024-08-07:
| Code version | Size | Improvement over Original (TS) | Improvement over Terser (release) |
|---|---|---|---|
| Original TS-types | 4807 B | 🟰 | ⬆️ |
| Original CC-types | 4779 B | 🟰🔻 0.6% | ⬆️ |
| Terser (debug) | 2288 B | ⬇️ 54.2% | 🔺 26.8% |
| Terser (release) | 1804 B | ⬇️ 62.5% | 🟰 |
| CC simple* | 1792 B | ⬇️ 62.7% | ⬇️ 0.7% |
| CC advanced* | 1681 B | ⬇️ 65% | ⬇️ 6.8% |
*But it prepends "use strict"; which we can remove to gain ~13B.
Pros & Cons
| Pros | Cons |
|---|---|
| Smaller code size (esp. advanced) | Need to redo the annotations in CC style |
| Faster code? / Can write more code* | Need to maintain 2 version of the code (or abandon TS-types) (or somehow make a TS->CC compiler) |
| Running Actions is free | SLOW (15-45s) |
*I presume bookmarks have a max size
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request