576 lines
20 KiB
JavaScript
576 lines
20 KiB
JavaScript
(() => {
|
|
const KEY = "__cab";
|
|
const MAGIC = "cab" + Math.random().toString(36).slice(2);
|
|
const origParse = JSON.parse;
|
|
const applied = new Set();
|
|
const hooked = new Set();
|
|
const S = {
|
|
jsonPrune: [], fetchPrune: [], xhrPrune: [], fetchReplace: [], xhrReplace: [],
|
|
nostif: [], nosiif: [], stb: [], sib: [], aeld: [], nowoif: [], nofetch: [], noxhr: [], noevalif: []
|
|
};
|
|
|
|
const hook = (name, fn) => {
|
|
if (hooked.has(name)) return;
|
|
hooked.add(name);
|
|
try { fn(); } catch (e) {}
|
|
};
|
|
|
|
const regexOf = p => {
|
|
const m = /^\/(.+)\/([gimsuy]*)$/.exec(p);
|
|
return m ? new RegExp(m[1], m[2].replace("g", "")) : null;
|
|
};
|
|
|
|
const needle = p => {
|
|
if (!p) return () => true;
|
|
let neg = false;
|
|
if (p[0] === "!") { neg = true; p = p.slice(1); }
|
|
const re = regexOf(p);
|
|
return s => (re ? re.test(String(s)) : String(s).includes(p)) !== neg;
|
|
};
|
|
|
|
const delayMatch = p => {
|
|
if (!p) return () => true;
|
|
const neg = p[0] === "!";
|
|
const n = parseInt(neg ? p.slice(1) : p, 10);
|
|
if (isNaN(n)) return () => true;
|
|
return d => (Number(d) === n) !== neg;
|
|
};
|
|
|
|
const urlMatcher = p => {
|
|
if (!p) return () => true;
|
|
if (p.startsWith("url:")) p = p.slice(4);
|
|
else if (/^\w+:/.test(p) && !/^https?:/.test(p)) return () => true;
|
|
const re = regexOf(p);
|
|
return u => (re ? re.test(u) : u.includes(p));
|
|
};
|
|
|
|
const propsMatcher = s => {
|
|
const props = [];
|
|
for (const tok of s.split(/\s+/).filter(Boolean)) {
|
|
const m = /^(\w+):(.*)$/.exec(tok);
|
|
if (m && !/^https?$/.test(m[1])) props.push([m[1].toLowerCase(), needle(m[2])]);
|
|
else props.push(["url", needle(tok)]);
|
|
}
|
|
return info => props.every(([k, n]) => info[k] !== undefined && n(info[k]));
|
|
};
|
|
|
|
const words = s => (s || "").split(/\s+/).filter(Boolean).map(x => x.split("."));
|
|
|
|
const varargs = a => {
|
|
const o = {};
|
|
for (let i = 0; i + 1 < a.length; i += 2) o[a[i]] = a[i + 1];
|
|
return o;
|
|
};
|
|
|
|
const toRegex = p => {
|
|
const m = /^\/(.+)\/([gimsuy]*)$/.exec(p);
|
|
if (m) return new RegExp(m[1], m[2]);
|
|
return new RegExp(p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
};
|
|
|
|
const has = (o, segs, i) => {
|
|
if (i >= segs.length) return true;
|
|
if (o === null || typeof o !== "object") return false;
|
|
const s = segs[i];
|
|
if (s === "[]" || s === "[-]") return Object.keys(o).some(k => has(o[k], segs, i + 1));
|
|
if (!(s in o)) return false;
|
|
return has(o[s], segs, i + 1);
|
|
};
|
|
|
|
const prunePath = (o, segs, i) => {
|
|
if (o === null || typeof o !== "object") return false;
|
|
const s = segs[i];
|
|
const last = i === segs.length - 1;
|
|
if (s === "[]") {
|
|
let ch = false;
|
|
for (const k of Object.keys(o)) ch = prunePath(o[k], segs, i + 1) || ch;
|
|
return ch;
|
|
}
|
|
if (s === "[-]") {
|
|
if (last) return false;
|
|
let ch = false;
|
|
if (Array.isArray(o)) {
|
|
for (let k = o.length - 1; k >= 0; k--) if (has(o[k], segs, i + 1)) { o.splice(k, 1); ch = true; }
|
|
} else {
|
|
for (const k of Object.keys(o)) if (has(o[k], segs, i + 1)) { delete o[k]; ch = true; }
|
|
}
|
|
return ch;
|
|
}
|
|
if (!(s in o)) return false;
|
|
if (last) { delete o[s]; return true; }
|
|
return prunePath(o[s], segs, i + 1);
|
|
};
|
|
|
|
const applyPrune = (obj, r) => {
|
|
if (obj === null || typeof obj !== "object") return false;
|
|
if (!r.needs.every(n => has(obj, n, 0))) return false;
|
|
let ch = false;
|
|
for (const p of r.paths) ch = prunePath(obj, p, 0) || ch;
|
|
return ch;
|
|
};
|
|
|
|
const transform = (txt, url, prune, replace) => {
|
|
const pr = prune.filter(r => r.match(url));
|
|
const rp = replace.filter(r => r.match(url));
|
|
if (!pr.length && !rp.length) return txt;
|
|
let out = txt;
|
|
for (const r of rp) { r.re.lastIndex = 0; out = out.replace(r.re, r.rep); }
|
|
if (pr.length) {
|
|
try {
|
|
const o = origParse(out);
|
|
let ch = false;
|
|
for (const r of pr) ch = applyPrune(o, r) || ch;
|
|
if (ch) out = JSON.stringify(o);
|
|
} catch (e) {}
|
|
}
|
|
return out;
|
|
};
|
|
|
|
const SKIP = {};
|
|
const constVal = (raw, trusted) => {
|
|
switch (raw) {
|
|
case "undefined": return undefined;
|
|
case "false": return false;
|
|
case "true": return true;
|
|
case "null": return null;
|
|
case "''": case "emptyStr": case "": return "";
|
|
case "noopFunc": return function () {};
|
|
case "trueFunc": return function () { return true; };
|
|
case "falseFunc": return function () { return false; };
|
|
case "throwFunc": return function () { throw new Error(); };
|
|
case "emptyObj": return {};
|
|
case "emptyArr": return [];
|
|
case "noopPromiseResolve": return function () { return Promise.resolve(); };
|
|
case "noopPromiseReject": return function () { return Promise.reject(); };
|
|
case "noopCallbackFunc": return function () { return function () {}; };
|
|
case "yes": case "no": case "on": case "off": return raw;
|
|
}
|
|
if (/^-?\d+$/.test(raw) && Math.abs(parseInt(raw, 10)) <= 32767) return parseInt(raw, 10);
|
|
if (trusted) { try { return origParse(raw); } catch (e) { return raw; } }
|
|
return SKIP;
|
|
};
|
|
|
|
const isObj = v => v !== null && (typeof v === "object" || typeof v === "function");
|
|
|
|
const onChain = (path, leaf) => {
|
|
if (!path) return;
|
|
const parts = path.replace(/^window\./, "").split(".");
|
|
const walk = (owner, i) => {
|
|
const p = parts[i];
|
|
if (i === parts.length - 1) { try { leaf(owner, p); } catch (e) {} return; }
|
|
let cur;
|
|
try { cur = owner[p]; } catch (e) { return; }
|
|
if (isObj(cur)) { walk(cur, i + 1); return; }
|
|
const d = Object.getOwnPropertyDescriptor(owner, p);
|
|
if (d && d.configurable === false) return;
|
|
let v = cur;
|
|
try {
|
|
Object.defineProperty(owner, p, {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get() { return v; },
|
|
set(nv) { v = nv; if (isObj(nv)) walk(nv, i + 1); }
|
|
});
|
|
} catch (e) {}
|
|
};
|
|
walk(window, 0);
|
|
};
|
|
|
|
const leafSet = (raw, trusted) => {
|
|
const value = constVal(raw, trusted);
|
|
return (o, p) => {
|
|
if (value === SKIP) return;
|
|
const d = Object.getOwnPropertyDescriptor(o, p);
|
|
if (d && d.configurable === false) return;
|
|
if (d && "value" in d) {
|
|
const cur = d.value;
|
|
if (cur !== undefined && cur !== null && value !== undefined && value !== null && typeof cur !== typeof value) return;
|
|
}
|
|
Object.defineProperty(o, p, {
|
|
configurable: true,
|
|
enumerable: d ? d.enumerable : true,
|
|
get() { return value; },
|
|
set() {}
|
|
});
|
|
};
|
|
};
|
|
|
|
const installErr = () => {
|
|
window.addEventListener("error", e => {
|
|
if (String(e.message).includes(MAGIC)) { e.preventDefault(); e.stopImmediatePropagation(); }
|
|
}, true);
|
|
};
|
|
|
|
const leafAopr = (o, p) => {
|
|
const d = Object.getOwnPropertyDescriptor(o, p);
|
|
if (d && d.configurable === false) return;
|
|
let v = d && "value" in d ? d.value : undefined;
|
|
Object.defineProperty(o, p, {
|
|
configurable: true,
|
|
get() { throw new ReferenceError(MAGIC); },
|
|
set(nv) { v = nv; }
|
|
});
|
|
};
|
|
|
|
const leafAopw = (o, p) => {
|
|
const d = Object.getOwnPropertyDescriptor(o, p);
|
|
if (d && d.configurable === false) return;
|
|
const v = d && "value" in d ? d.value : undefined;
|
|
Object.defineProperty(o, p, {
|
|
configurable: true,
|
|
get() { return v; },
|
|
set() { throw new ReferenceError(MAGIC); }
|
|
});
|
|
};
|
|
|
|
const leafAcs = n => (o, p) => {
|
|
const d = Object.getOwnPropertyDescriptor(o, p);
|
|
if (d && d.configurable === false) return;
|
|
let v = d && "value" in d ? d.value : undefined;
|
|
const g = d && d.get;
|
|
const s = d && d.set;
|
|
const check = () => {
|
|
const cs = document.currentScript;
|
|
if (!cs || cs.tagName !== "SCRIPT") return;
|
|
const text = cs.src ? cs.src + "\n" + cs.textContent : cs.textContent;
|
|
if (n(text)) throw new ReferenceError(MAGIC);
|
|
};
|
|
Object.defineProperty(o, p, {
|
|
configurable: true,
|
|
get() { check(); return g ? g.call(this) : v; },
|
|
set(nv) { check(); if (s) s.call(this, nv); else v = nv; }
|
|
});
|
|
};
|
|
|
|
const installTimers = () => {
|
|
const wrap = (orig, block, boost) => new Proxy(orig, {
|
|
apply(t, th, a) {
|
|
let s = "";
|
|
try { s = typeof a[0] === "function" ? Function.prototype.toString.call(a[0]) : String(a[0]); } catch (e) {}
|
|
for (const r of block) {
|
|
if (r.cb(s) && r.dl(a[1])) { a = [function () {}, a[1]]; break; }
|
|
}
|
|
for (const r of boost) {
|
|
if (r.cb(s) && (r.dl === "*" || Number(a[1]) === Number(r.dl))) { a = Array.from(a); a[1] = Number(a[1]) * r.boost; break; }
|
|
}
|
|
return Reflect.apply(t, th, a);
|
|
}
|
|
});
|
|
window.setTimeout = wrap(window.setTimeout, S.nostif, S.stb);
|
|
window.setInterval = wrap(window.setInterval, S.nosiif, S.sib);
|
|
};
|
|
|
|
const installAeld = () => {
|
|
const P = EventTarget.prototype;
|
|
P.addEventListener = new Proxy(P.addEventListener, {
|
|
apply(t, th, a) {
|
|
let hs = "";
|
|
try {
|
|
const h = a[1];
|
|
hs = typeof h === "function" ? Function.prototype.toString.call(h) : String(h && h.handleEvent);
|
|
} catch (e) {}
|
|
for (const r of S.aeld) if (r.type(String(a[0])) && r.h(hs)) return;
|
|
return Reflect.apply(t, th, a);
|
|
}
|
|
});
|
|
};
|
|
|
|
const decoy = () => ({
|
|
closed: false,
|
|
opener: null,
|
|
location: { href: "" },
|
|
document: { write() {}, writeln() {}, open() {}, close() {} },
|
|
close() { this.closed = true; },
|
|
focus() {},
|
|
blur() {},
|
|
postMessage() {},
|
|
addEventListener() {},
|
|
removeEventListener() {}
|
|
});
|
|
|
|
const installWo = () => {
|
|
window.open = new Proxy(window.open, {
|
|
apply(t, th, a) {
|
|
const url = String(a[0] === undefined ? "" : a[0]);
|
|
for (const n of S.nowoif) if (n(url)) return decoy();
|
|
return Reflect.apply(t, th, a);
|
|
}
|
|
});
|
|
};
|
|
|
|
const installJson = () => {
|
|
JSON.parse = new Proxy(origParse, {
|
|
apply(t, th, a) {
|
|
const r = Reflect.apply(t, th, a);
|
|
try { for (const rule of S.jsonPrune) applyPrune(r, rule); } catch (e) {}
|
|
return r;
|
|
}
|
|
});
|
|
const origJson = Response.prototype.json;
|
|
Response.prototype.json = new Proxy(origJson, {
|
|
apply(t, th, a) {
|
|
return Reflect.apply(t, th, a).then(r => {
|
|
try { for (const rule of S.jsonPrune) applyPrune(r, rule); } catch (e) {}
|
|
return r;
|
|
});
|
|
}
|
|
});
|
|
if (/(^|\.)youtube(-nocookie)?\.com$/.test(location.hostname)) {
|
|
let yipr = window.ytInitialPlayerResponse;
|
|
try {
|
|
Object.defineProperty(window, "ytInitialPlayerResponse", {
|
|
configurable: true,
|
|
get() { return yipr; },
|
|
set(v) {
|
|
try { for (const rule of S.jsonPrune) applyPrune(v, rule); } catch (e) {}
|
|
yipr = v;
|
|
}
|
|
});
|
|
} catch (e) {}
|
|
}
|
|
};
|
|
|
|
const installFetch = () => {
|
|
window.fetch = new Proxy(window.fetch, {
|
|
apply(t, th, a) {
|
|
let url = "";
|
|
let method = "GET";
|
|
try {
|
|
if (a[0] instanceof Request) { url = a[0].url; method = a[0].method; } else url = String(a[0]);
|
|
if (a[1] && a[1].method) method = String(a[1].method);
|
|
} catch (e) {}
|
|
const info = { url, method: method.toUpperCase() };
|
|
for (const m of S.nofetch) {
|
|
if (m(info)) return Promise.resolve(new Response("", { status: 200, statusText: "OK" }));
|
|
}
|
|
const p = Reflect.apply(t, th, a);
|
|
const any = S.fetchPrune.some(r => r.match(url)) || S.fetchReplace.some(r => r.match(url));
|
|
if (!any) return p;
|
|
return p.then(res => res.clone().text().then(txt => {
|
|
const out = transform(txt, url, S.fetchPrune, S.fetchReplace);
|
|
if (out === txt) return res;
|
|
const nr = new Response(out, { status: res.status, statusText: res.statusText, headers: res.headers });
|
|
Object.defineProperties(nr, {
|
|
url: { value: res.url },
|
|
redirected: { value: res.redirected },
|
|
type: { value: res.type }
|
|
});
|
|
return nr;
|
|
}).catch(() => res));
|
|
}
|
|
});
|
|
};
|
|
|
|
const installXhr = () => {
|
|
const XP = XMLHttpRequest.prototype;
|
|
const info = new WeakMap();
|
|
const cache = new WeakMap();
|
|
XP.open = new Proxy(XP.open, {
|
|
apply(t, th, a) {
|
|
try { info.set(th, { url: String(a[1]), method: String(a[0] || "GET").toUpperCase() }); cache.delete(th); } catch (e) {}
|
|
return Reflect.apply(t, th, a);
|
|
}
|
|
});
|
|
XP.send = new Proxy(XP.send, {
|
|
apply(t, th, a) {
|
|
const i = info.get(th);
|
|
if (i && S.noxhr.some(m => m(i))) {
|
|
const props = { readyState: 4, status: 200, statusText: "OK", response: "", responseText: "", responseURL: i.url };
|
|
for (const k in props) Object.defineProperty(th, k, { configurable: true, value: props[k] });
|
|
setTimeout(() => {
|
|
for (const ev of ["readystatechange", "load", "loadend"]) {
|
|
try { th.dispatchEvent(new Event(ev)); } catch (e) {}
|
|
}
|
|
}, 1);
|
|
return;
|
|
}
|
|
return Reflect.apply(t, th, a);
|
|
}
|
|
});
|
|
const dText = Object.getOwnPropertyDescriptor(XP, "responseText");
|
|
const dResp = Object.getOwnPropertyDescriptor(XP, "response");
|
|
const xform = xhr => {
|
|
if (xhr.readyState !== 4) return null;
|
|
if (cache.has(xhr)) return cache.get(xhr);
|
|
let result = null;
|
|
try {
|
|
const url = (info.get(xhr) || {}).url || "";
|
|
const type = xhr.responseType;
|
|
if (type === "" || type === "text" || type === "json") {
|
|
const raw = dResp.get.call(xhr);
|
|
const txt = type === "json" ? JSON.stringify(raw) : raw;
|
|
if (typeof txt === "string") {
|
|
const out = transform(txt, url, S.xhrPrune, S.xhrReplace);
|
|
if (out !== txt) result = { v: type === "json" ? origParse(out) : out };
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
cache.set(xhr, result);
|
|
return result;
|
|
};
|
|
Object.defineProperty(XP, "response", {
|
|
configurable: true,
|
|
enumerable: dResp.enumerable,
|
|
get() { const r = xform(this); return r ? r.v : dResp.get.call(this); }
|
|
});
|
|
Object.defineProperty(XP, "responseText", {
|
|
configurable: true,
|
|
enumerable: dText.enumerable,
|
|
get() { const r = xform(this); return r && typeof r.v === "string" ? r.v : dText.get.call(this); }
|
|
});
|
|
};
|
|
|
|
const onReady = fn => {
|
|
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", fn, { once: true });
|
|
else fn();
|
|
};
|
|
|
|
const domRemove = (a, kind) => {
|
|
const tokens = (a[0] || "").split(/\s*\|\s*/).filter(Boolean);
|
|
if (!tokens.length) return;
|
|
const selector = a[1] || tokens.map(t => (kind === "attr" ? "[" + t + "]" : "." + t)).join(",");
|
|
const run = () => {
|
|
try {
|
|
document.querySelectorAll(selector).forEach(el => {
|
|
for (const t of tokens) {
|
|
if (kind === "attr") el.removeAttribute(t);
|
|
else el.classList.remove(t);
|
|
}
|
|
});
|
|
} catch (e) {}
|
|
};
|
|
onReady(run);
|
|
window.addEventListener("load", run, { once: true });
|
|
if ((a[2] || "").includes("stay")) {
|
|
let pending = false;
|
|
const mo = new MutationObserver(() => {
|
|
if (pending) return;
|
|
pending = true;
|
|
requestAnimationFrame(() => { pending = false; run(); });
|
|
});
|
|
onReady(() => mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true }));
|
|
}
|
|
};
|
|
|
|
const LS_OK = new Set(["undefined", "false", "true", "null", "yes", "no", "on", "off", "accept", "accepted", "reject", "rejected", "allow", "allowed", "deny", "denied"]);
|
|
const lsItem = a => {
|
|
const key = a[0];
|
|
let raw = a[1] || "";
|
|
if (!key) return;
|
|
try {
|
|
if (raw === "$remove$") {
|
|
const re = regexOf(key);
|
|
if (re) { for (const k of Object.keys(localStorage)) if (re.test(k)) localStorage.removeItem(k); }
|
|
else localStorage.removeItem(key);
|
|
return;
|
|
}
|
|
if (raw === "emptyObj") raw = "{}";
|
|
else if (raw === "emptyArr") raw = "[]";
|
|
else if (raw === "''" || raw === "emptyStr") raw = "";
|
|
else if (!LS_OK.has(raw) && !(/^\d+$/.test(raw) && parseInt(raw, 10) <= 32767)) return;
|
|
localStorage.setItem(key, raw);
|
|
} catch (e) {}
|
|
};
|
|
|
|
const cookieRemove = a => {
|
|
const n = needle(a[0] || "");
|
|
const rm = () => {
|
|
for (const c of document.cookie.split(";")) {
|
|
const name = c.split("=")[0].trim();
|
|
if (!name || !n(name)) continue;
|
|
const exp = "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
|
|
const h = location.hostname;
|
|
document.cookie = name + exp;
|
|
document.cookie = name + exp + "; domain=" + h;
|
|
document.cookie = name + exp + "; domain=." + h.split(".").slice(-2).join(".");
|
|
}
|
|
};
|
|
rm();
|
|
window.addEventListener("beforeunload", rm);
|
|
onReady(rm);
|
|
};
|
|
|
|
const H = {
|
|
"json-prune": a => {
|
|
const paths = words(a[0]);
|
|
if (!paths.length) return;
|
|
S.jsonPrune.push({ paths, needs: words(a[1]) });
|
|
hook("json", installJson);
|
|
},
|
|
"json-prune-fetch-response": a => {
|
|
const paths = words(a[0]);
|
|
if (!paths.length) return;
|
|
S.fetchPrune.push({ paths, needs: words(a[1]), match: urlMatcher(varargs(a.slice(2)).propsToMatch) });
|
|
hook("fetch", installFetch);
|
|
},
|
|
"json-prune-xhr-response": a => {
|
|
const paths = words(a[0]);
|
|
if (!paths.length) return;
|
|
S.xhrPrune.push({ paths, needs: words(a[1]), match: urlMatcher(varargs(a.slice(2)).propsToMatch) });
|
|
hook("xhr", installXhr);
|
|
},
|
|
"trusted-replace-fetch-response": a => {
|
|
if (!a[0]) return;
|
|
S.fetchReplace.push({ re: toRegex(a[0]), rep: a[1] || "", match: urlMatcher(a[2]) });
|
|
hook("fetch", installFetch);
|
|
},
|
|
"trusted-replace-xhr-response": a => {
|
|
if (!a[0]) return;
|
|
S.xhrReplace.push({ re: toRegex(a[0]), rep: a[1] || "", match: urlMatcher(a[2]) });
|
|
hook("xhr", installXhr);
|
|
},
|
|
set: a => onChain(a[0], leafSet(a[1] || "undefined", false)),
|
|
"trusted-set": a => onChain(a[0], leafSet(a[1] || "undefined", true)),
|
|
aopr: a => { hook("err", installErr); onChain(a[0], leafAopr); },
|
|
aopw: a => { hook("err", installErr); onChain(a[0], leafAopw); },
|
|
acs: a => { hook("err", installErr); onChain(a[0], leafAcs(needle(a[1]))); },
|
|
nostif: a => { S.nostif.push({ cb: needle(a[0]), dl: delayMatch(a[1]) }); hook("timers", installTimers); },
|
|
nosiif: a => { S.nosiif.push({ cb: needle(a[0]), dl: delayMatch(a[1]) }); hook("timers", installTimers); },
|
|
"nano-stb": a => {
|
|
S.stb.push({ cb: needle(a[0] === "*" ? "" : a[0]), dl: a[1] || "1000", boost: parseFloat(a[2] || "0.05") || 0.05 });
|
|
hook("timers", installTimers);
|
|
},
|
|
"nano-sib": a => {
|
|
S.sib.push({ cb: needle(a[0] === "*" ? "" : a[0]), dl: a[1] || "1000", boost: parseFloat(a[2] || "0.05") || 0.05 });
|
|
hook("timers", installTimers);
|
|
},
|
|
aeld: a => { S.aeld.push({ type: needle(a[0]), h: needle(a[1]) }); hook("aeld", installAeld); },
|
|
nowoif: a => { S.nowoif.push(needle(a[0])); hook("wo", installWo); },
|
|
"no-fetch-if": a => { if (!a[0]) return; S.nofetch.push(propsMatcher(a[0])); hook("fetch", installFetch); },
|
|
"no-xhr-if": a => { if (!a[0]) return; S.noxhr.push(propsMatcher(a[0])); hook("xhr", installXhr); },
|
|
noeval: () => hook("noeval", () => { window.eval = new Proxy(window.eval, { apply() { return undefined; } }); }),
|
|
"noeval-if": a => {
|
|
S.noevalif.push(needle(a[0]));
|
|
hook("evalif", () => {
|
|
window.eval = new Proxy(window.eval, {
|
|
apply(t, th, x) {
|
|
if (S.noevalif.some(n => n(String(x[0])))) return undefined;
|
|
return Reflect.apply(t, th, x);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
ra: a => domRemove(a, "attr"),
|
|
rc: a => domRemove(a, "class"),
|
|
"set-local-storage-item": lsItem,
|
|
"remove-cookie": cookieRemove
|
|
};
|
|
|
|
const run = rules => {
|
|
if (!Array.isArray(rules)) return;
|
|
for (const r of rules) {
|
|
if (!r || !H[r.t]) continue;
|
|
const k = r.t + "|" + JSON.stringify(r.a || []);
|
|
if (applied.has(k)) continue;
|
|
applied.add(k);
|
|
try { H[r.t](r.a || []); } catch (e) {}
|
|
}
|
|
};
|
|
|
|
try {
|
|
const cached = localStorage.getItem(KEY);
|
|
if (cached) run(origParse(cached));
|
|
} catch (e) {}
|
|
window.addEventListener(KEY, e => { try { run(origParse(e.detail)); } catch (x) {} });
|
|
})();
|