diff --git a/build.ts b/build.ts index 24fbbf5..fbb87b7 100644 --- a/build.ts +++ b/build.ts @@ -10,26 +10,10 @@ const { builtinScriptlets: scriptlets } = await import( `https://raw.githubusercontent.com/gorhill/uBlock/${tagName}/src/js/resources/scriptlets.js` ); -const index = new Map(); -for (const scriptlet of scriptlets) { - index.set(scriptlet.name, scriptlet); - for (const name of scriptlet.aliases || []) { - index.set(name, scriptlet); - } -} - -// uBO runs all scriptlets of a page in one scope, so they share one safeSelf() cache. Our scriptlets are separate functions, so they share it on globalThis. -// Else each hook captures the earlier hooks as natives, and calls grow as 2^n. The uBO version is in the key, so other versions do not share it. -const safeSelf = index.get('safe-self.fn').fn; -const shareSafeSelf = `try { - const key = Symbol.for('safeSelf.${tagName}'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -`; +// Every function once, by name; run() declares them all in one scope, so the scriptlets of an +// injection share one safeSelf() cache without touching globalThis, as in uBO. +const functions = new Map(scriptlets.map((scriptlet) => [scriptlet.fn.name, scriptlet.fn.toString()])); +const entries = scriptlets.filter((scriptlet) => scriptlet.name.endsWith('.js')); console.log(` /******************************************************************************* @@ -54,38 +38,27 @@ console.log(` */ const scriptlets = {}; - -${scriptlets - .filter(scriptlet => scriptlet.name.endsWith('.js')) - .map((scriptlet) => { - const allDependencies = new Set(); - - const addDeps = (aScriptlet) => { - for (const dep of aScriptlet.dependencies || []) { - allDependencies.add(dep); - const bScriptlet = index.get(dep); - addDeps(bScriptlet); - } - }; - - addDeps(scriptlet); - - const deps = [...allDependencies].reverse().map((dep) => index.get(dep).fn); - - return ` +${entries + .map( + (scriptlet) => ` scriptlets['${scriptlet.name}'] = { aliases: ${JSON.stringify(scriptlet.aliases || [])}, -${scriptlet.world ? `world: '${scriptlet.world}',` : '' } +${scriptlet.world ? `world: '${scriptlet.world}',` : ''} requiresTrust: ${scriptlet.requiresTrust || false}, -func: function (scriptletGlobals = {}, ...args) { -${deps.map((dep) => dep.toString()).join('\n')} -${scriptlet.fn.toString()}; -${deps.includes(safeSelf) ? shareSafeSelf : ''}${scriptlet.fn.name}(...args); -}, +fn: '${scriptlet.fn.name}', }; -`; - }) +`, + ) .join('\n')} +// The calls come last, so the class dependencies, which are not hoisted, are declared by then. +export function run(scriptletGlobals, calls) { +${[...functions.values()].join('\n')} +const table = { ${entries.map((scriptlet) => scriptlet.fn.name).join(', ')} }; +for (const [name, ...args] of calls) { + try { table[name](...args); } catch {} +} +} + export default scriptlets; `); diff --git a/index.js b/index.js index 447709c..bc6d315 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import SCRIPTLETS from './ubo.js'; +import SCRIPTLETS, { run } from './ubo.js'; const scriptlets = {}; @@ -9,4 +9,5 @@ for (const [name, scriptlet] of Object.entries(SCRIPTLETS)) { } } +export { run }; export default scriptlets; diff --git a/test.js b/test.js index 41b6ecf..a0be436 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,9 @@ import { test, suite } from "node:test"; import assert from "node:assert"; import vm from "node:vm"; -import scriptlets from "./index.js"; +import scriptlets, { run } from "./index.js"; + +const runSource = run.toString(); test("default export is an object", () => { assert(typeof scriptlets === "object"); @@ -11,8 +13,8 @@ test("each scriptlet has basic properties", () => { for (const [name, scriptlet] of Object.entries(scriptlets)) { assert(name.length > 0, `${name} - name is too short`); assert( - scriptlet.func instanceof Function, - `${name} - func is not have a Function` + runSource.includes(`function ${scriptlet.fn}(`), + `${name} - fn does not name a function of run()` ); assert( scriptlet.aliases instanceof Array, @@ -28,14 +30,16 @@ suite("uBO", () => { }); }); -suite("safeSelf() cache", () => { +suite("run()", () => { // Each hook clones the argument with safe.JSON_parse(safe.JSON_stringify(obj)) - const func = scriptlets["trusted-edit-inbound-object.js"].func; + const scriptlet = scriptlets["trusted-edit-inbound-object.js"]; const HOOKS = 6; - // Same code as the extension makes for each scriptlet; the first argument is scriptletGlobals - const hooks = Array.from({ length: HOOKS }, (_, i) => - `(${func})(...${JSON.stringify([{}, "JSON.stringify", "0", `[?.hook${i}]+={"edited${i}":true}`])});` - ); + const calls = Array.from({ length: HOOKS }, (_, i) => [ + scriptlet.fn, + "JSON.stringify", + "0", + `[?.hook${i}]+={"edited${i}":true}`, + ]); // A fresh realm is the page; counters wrap the native JSON methods before the scriptlets run function createPage() { @@ -52,11 +56,14 @@ suite("safeSelf() cache", () => { return page; } - test("is shared in a realm, so stacked hooks stay linear", () => { + test("declares safeSelf() once", () => { + assert.strictEqual(runSource.match(/^function safeSelf\(/gm).length, 1); + }); + + test("shares one safeSelf() cache, so stacked hooks stay linear", () => { const page = createPage(); - for (const hook of hooks) { - vm.runInContext(hook, page); - } + // Same call as chrome.scripting.executeScript() makes: the function source with JSON arguments + vm.runInContext(`(${runSource})(...${JSON.stringify([{}, calls])});`, page); const result = vm.runInContext( "calls.parse = 0; calls.stringify = 0; JSON.stringify({ hook0: true, hook5: true });", @@ -67,15 +74,13 @@ suite("safeSelf() cache", () => { assert.deepStrictEqual({ ...page.calls }, { parse: HOOKS, stringify: HOOKS + 1 }); }); - test("is read-only and not enumerable", () => { + test("leaves no global behind", () => { const page = createPage(); - vm.runInContext(hooks.join("\n"), page); + vm.runInContext(`(${runSource})(...${JSON.stringify([{}, calls])});`, page); const keys = vm .runInContext("Object.getOwnPropertySymbols(globalThis)", page) .filter((key) => key.description.startsWith("safeSelf.")); - assert.strictEqual(keys.length, 1); - const { value, ...flags } = Object.getOwnPropertyDescriptor(page, keys[0]); - assert.deepStrictEqual(flags, { writable: false, enumerable: false, configurable: false }); + assert.strictEqual(keys.length, 0); }); }); diff --git a/ubo.js b/ubo.js index a624943..fd9ad99 100644 --- a/ubo.js +++ b/ubo.js @@ -22,362 +22,11 @@ */ const scriptlets = {}; - scriptlets['abort-current-script.js'] = { aliases: ["abort-current-inline-script.js","acis.js","acs.js"], requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function runAtHtmlElementFn(fn) { - if ( document.documentElement ) { - fn(); - return; - } - const observer = new MutationObserver(( ) => { - observer.disconnect(); - fn(); - }); - observer.observe(document, { childList: true }); -} -function trapPropertyFn(propChain, handler, options = {}) { - if ( propChain === '' ) { return; } - let owner = self; - let prop = propChain; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - const safe = safeSelf(); - if ( trapPropertyFn.db === undefined ) { - trapPropertyFn.db = new WeakMap(); - trapPropertyFn.entryFromContext = (owner, prop) => { - const handlers = trapPropertyFn.db.get(owner); - return handlers?.get(prop); - }; - trapPropertyFn.getter = (owner, prop) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - let r = entry.value; - for ( const desc of entry.stack ) { - try { r = desc.get(); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - return r; - }; - trapPropertyFn.setter = (owner, prop, value) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - entry.value = value; - for ( const desc of entry.stack ) { - try { desc.set(value); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - }; - } - const { db } = trapPropertyFn; - const handlers = db.get(owner) || new Map(); - if ( handlers.size === 0 ) { - db.set(owner, handlers); - } - const entry = handlers.get(prop) || { - value: owner[prop], - stack: [], - }; - entry.stack.push(handler); - if ( entry.stack.length > 1 ) { return entry.value; } - Object.assign(entry, options); - handlers.set(prop, entry); - const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); - if ( desc instanceof safe.Object ) { - if ( desc.get || desc.set ) { - entry.stack.push(desc); - } - } - try { - safe.Object_defineProperty(owner, prop, { - get() { - return trapPropertyFn.getter(owner, prop); - }, - set(value) { - trapPropertyFn.setter(owner, prop, value); - } - }); - } catch { - } - return entry.value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function abortCurrentScriptFn( - target = '', - needle = '', - context = '' -) { - if ( typeof target !== 'string' ) { return; } - if ( target === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('abort-current-script', target, needle, context); - const reNeedle = safe.patternToRegex(needle); - const reContext = safe.patternToRegex(context); - const thisScript = document.currentScript; - const exceptionToken = getExceptionTokenFn(); - const scriptTexts = new WeakMap(); - const textContentGetter = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent').get; - const getScriptText = elem => { - let text = textContentGetter.call(elem); - if ( text.trim() !== '' ) { return text; } - if ( scriptTexts.has(elem) ) { return scriptTexts.get(elem); } - const [ , mime, content ] = /^data:([^,]*),(.+)$/.exec(elem.src.trim()) || - [ '', '', '' ]; - try { - switch ( true ) { - case mime.endsWith(';base64'): - text = self.atob(content); - break; - default: - text = self.decodeURIComponent(content); - break; - } - } catch { - } - scriptTexts.set(elem, text); - return text; - }; - const validate = ( ) => { - const e = document.currentScript; - if ( e instanceof HTMLScriptElement === false ) { return; } - if ( e === thisScript ) { return; } - if ( context !== '' && reContext.test(e.src) === false ) { return; } - if ( safe.logLevel > 1 && context !== '' ) { - safe.uboLog(logPrefix, `Matched src\n${e.src}`); - } - const scriptText = getScriptText(e); - if ( reNeedle.test(scriptText) === false ) { return; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched text\n${scriptText}`); - } - safe.uboLog(logPrefix, 'Aborted'); - throw new ReferenceError(exceptionToken); - }; - let currentValue = trapPropertyFn(target, { - get: function() { - validate(); - return currentValue; - }, - set: function(a) { - validate(); - currentValue = a; - } - }, { canThrow: true }); -} -function abortCurrentScript(...args) { - runAtHtmlElementFn(( ) => { - abortCurrentScriptFn(...args); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -abortCurrentScript(...args); -}, +fn: 'abortCurrentScript', }; @@ -385,39889 +34,780 @@ scriptlets['set-attr.js'] = { aliases: [], world: 'ISOLATED', requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function setAttrFn( - trusted = false, - logPrefix, - selector = '', - attr = '', - value = '', - options = {} -) { - if ( selector === '' ) { return; } - if ( attr === '' ) { return; } +fn: 'setAttr', +}; - const safe = safeSelf(); - const copyFrom = trusted === false && /^\[.+\]$/.test(value) - ? value.slice(1, -1) - : ''; - const extractValue = elem => copyFrom !== '' - ? elem.getAttribute(copyFrom) || '' - : value; +scriptlets['trusted-set-attr.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedSetAttr', +}; - const applySetAttr = ( ) => { - let elems; - try { - elems = document.querySelectorAll(selector); - } catch { - return false; - } - for ( const elem of elems ) { - const before = elem.getAttribute(attr); - const after = extractValue(elem); - if ( after === before ) { continue; } - if ( after !== '' && /^on/i.test(attr) ) { - if ( attr.toLowerCase() in elem ) { continue; } - } - elem.setAttribute(attr, after); - safe.uboLog(logPrefix, `${attr}="${after}"`); - } - return true; - }; - let observer, timer; - const onDomChanged = mutations => { - if ( timer !== undefined ) { return; } - let shouldWork = false; - for ( const mutation of mutations ) { - if ( mutation.addedNodes.length === 0 ) { continue; } - for ( const node of mutation.addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - shouldWork = true; - break; - } - if ( shouldWork ) { break; } - } - if ( shouldWork === false ) { return; } - timer = self.requestAnimationFrame(( ) => { - timer = undefined; - applySetAttr(); - }); - }; +scriptlets['remove-attr.js'] = { +aliases: ["ra.js"], - const start = ( ) => { - if ( applySetAttr() === false ) { return; } - observer = new MutationObserver(onDomChanged); - const root = document.documentElement; - if ( root instanceof self.Node === false ) { return; } - observer.observe(root, { subtree: true, childList: true }); - }; - runAt(( ) => { start(); }, options.runAt || 'idle'); -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function setAttr( - selector = '', - attr = '', - value = '', - ...varargs -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-attr', selector, attr, value); - const validValues = [ '', 'false', 'true' ]; - if ( validValues.includes(value.toLowerCase()) === false ) { - if ( /^\d+$/.test(value) ) { - const n = parseInt(value, 10); - if ( n >= 32768 ) { return; } - value = `${n}`; - } else if ( /^\[.+\]$/.test(value) === false ) { - return; - } - } - const options = safe.parseVarargs(varargs); - setAttrFn(false, logPrefix, selector, attr, value, options); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setAttr(...args); -}, +requiresTrust: false, +fn: 'removeAttr', }; -scriptlets['trusted-set-attr.js'] = { +scriptlets['trusted-create-html.js'] = { aliases: [], world: 'ISOLATED', requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function setAttrFn( - trusted = false, - logPrefix, - selector = '', - attr = '', - value = '', - options = {} -) { - if ( selector === '' ) { return; } - if ( attr === '' ) { return; } +fn: 'trustedCreateHTML', +}; - const safe = safeSelf(); - const copyFrom = trusted === false && /^\[.+\]$/.test(value) - ? value.slice(1, -1) - : ''; - const extractValue = elem => copyFrom !== '' - ? elem.getAttribute(copyFrom) || '' - : value; +scriptlets['href-sanitizer.js'] = { +aliases: ["urlskip.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'hrefSanitizer', +}; - const applySetAttr = ( ) => { - let elems; - try { - elems = document.querySelectorAll(selector); - } catch { - return false; - } - for ( const elem of elems ) { - const before = elem.getAttribute(attr); - const after = extractValue(elem); - if ( after === before ) { continue; } - if ( after !== '' && /^on/i.test(attr) ) { - if ( attr.toLowerCase() in elem ) { continue; } - } - elem.setAttribute(attr, after); - safe.uboLog(logPrefix, `${attr}="${after}"`); - } - return true; - }; - let observer, timer; - const onDomChanged = mutations => { - if ( timer !== undefined ) { return; } - let shouldWork = false; - for ( const mutation of mutations ) { - if ( mutation.addedNodes.length === 0 ) { continue; } - for ( const node of mutation.addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - shouldWork = true; - break; - } - if ( shouldWork ) { break; } - } - if ( shouldWork === false ) { return; } - timer = self.requestAnimationFrame(( ) => { - timer = undefined; - applySetAttr(); - }); - }; +scriptlets['proxy-apply-config.js'] = { +aliases: [], - const start = ( ) => { - if ( applySetAttr() === false ) { return; } - observer = new MutationObserver(onDomChanged); - const root = document.documentElement; - if ( root instanceof self.Node === false ) { return; } - observer.observe(root, { subtree: true, childList: true }); - }; - runAt(( ) => { start(); }, options.runAt || 'idle'); -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function trustedSetAttr( - selector = '', - attr = '', - value = '', - ...varargs -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-set-attr', selector, attr, value); - const options = safe.parseVarargs(varargs); - setAttrFn(true, logPrefix, selector, attr, value, options); +requiresTrust: false, +fn: 'proxyApplyConfig', }; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetAttr(...args); -}, + + +scriptlets['edit-outbound-object.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'editOutboundObject', }; -scriptlets['remove-attr.js'] = { -aliases: ["ra.js"], +scriptlets['trusted-edit-outbound-object.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedEditOutboundObject', +}; + + +scriptlets['json-edit.js'] = { +aliases: [], requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function offIdleFn(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); -} -function removeAttr( - rawToken = '', - rawSelector = '', - behavior = '', - ...varargs -) { - if ( typeof rawToken !== 'string' ) { return; } - if ( rawToken === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('remove-attr', - rawToken, rawSelector, behavior, ...varargs - ); - const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); - const selector = tokens.map(a => { - const b = CSS.escape(a); - return rawSelector.includes(`[${b}]`) ? rawSelector : `${rawSelector}[${b}]`; - }).join(','); - const lazily = /\basap\b/.test(behavior) === false; - const options = safe.parseVarargs(varargs); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); - } - const rmattrFromNode = node => { - for ( const attr of tokens ) { - if ( node.hasAttribute(attr) === false ) { continue; } - node.removeAttribute(attr); - safe.uboLog(logPrefix, `Removed attribute '${attr}'`); - } - }; - const rmattr = nodes => { - for ( const node of nodes ?? document.querySelectorAll(selector) ) { - rmattrFromNode(node); - } - }; - const rmAttrLazily = ( ) => { - if ( rmAttrLazily.timer !== undefined ) { return; } - rmAttrLazily.timer = onIdleFn(( ) => { - rmAttrLazily.timer = undefined; - rmattr(); - }, { timeout: 17 }); - }; - const mutationHandler = mutations => { - for ( const { addedNodes, removedNodes } of mutations ) { - for ( const node of addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - if ( lazily ) { return rmAttrLazily(); } - if ( node.matches(selector) ) { - rmattrFromNode(node); - } - if ( node.childElementCount ) { - rmattr(node.querySelectorAll(selector)); - } - } - if ( lazily ) { return; } - for ( const node of removedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - if ( node.matches(selector) ) { - rmattrFromNode(node); - } - } - } - }; - const stop = ( ) => { - if ( start.observer ) { - start.observer.disconnect(); - start.observer = undefined; - } - if ( rmAttrLazily.timer ) { - offIdleFn(rmAttrLazily.timer); - rmAttrLazily.timer = undefined; - } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Quitting'); - } - }; - const start = ( ) => { - rmattr(); - if ( /\bstay\b/.test(behavior) === false ) { - if ( options.quitAfter === undefined ) { return; } - } - start.observer = new MutationObserver(mutationHandler); - start.observer.observe(document, { - attributes: true, - attributeFilter: tokens, - childList: true, - subtree: true, - }); - if ( options.quitAfter ) { - runAt(( ) => { - self.setTimeout(stop, options.quitAfter * 1000); - }, 'load'); - } - }; - runAt(( ) => { start(); }, safe.String_split.call(behavior, /\s+/)); +fn: 'jsonEdit', }; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -removeAttr(...args); -}, + + +scriptlets['trusted-json-edit.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonEdit', }; -scriptlets['trusted-create-html.js'] = { +scriptlets['edit-inbound-object.js'] = { aliases: [], -world: 'ISOLATED', + +requiresTrust: false, +fn: 'editInboundObject', +}; + + +scriptlets['trusted-edit-inbound-object.js'] = { +aliases: [], + requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function trustedCreateHTML( - parentSelector, - htmlStr = '', - durationStr = '', - ...varargs -) { - if ( parentSelector === '' ) { return; } - if ( htmlStr === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-create-html', parentSelector, htmlStr, durationStr); - const extraArgs = safe.parseVarargs(varargs); - // We do not want to recursively create elements - self.trustedCreateHTML = true; - let ancestor = self.frameElement; - while ( ancestor ) { - const doc = ancestor.ownerDocument; - if ( doc === null ) { break; } - const win = doc.defaultView; - if ( win === null ) { break; } - if ( win.trustedCreateHTML ) { return; } - ancestor = ancestor.frameElement; - } - const duration = parseInt(durationStr, 10); - const domParser = new DOMParser(); - const externalDoc = domParser.parseFromString(htmlStr, 'text/html'); - const toAppend = []; - while ( externalDoc.body.firstChild !== null ) { - toAppend.push(document.adoptNode(externalDoc.body.firstChild)); - } - if ( toAppend.length === 0 ) { return; } - const toRemove = []; - const remove = ( ) => { - for ( const node of toRemove ) { - if ( node.parentNode === null ) { continue; } - node.parentNode.removeChild(node); - } - safe.uboLog(logPrefix, 'Node(s) removed'); - }; - const appendOne = (target, nodes) => { - for ( const node of nodes ) { - target.append(node); - if ( isNaN(duration) ) { continue; } - toRemove.push(node); - } - }; - const append = ( ) => { - const targets = document.querySelectorAll(parentSelector); - if ( targets.length === 0 ) { return false; } - const limit = Math.min(targets.length, extraArgs.limit || 1) - 1; - for ( let i = 0; i < limit; i++ ) { - appendOne(targets[i], toAppend.map(a => a.cloneNode(true))); - } - appendOne(targets[limit], toAppend); - safe.uboLog(logPrefix, 'Node(s) appended'); - if ( toRemove.length === 0 ) { return true; } - setTimeout(remove, duration); - return true; - }; - const start = ( ) => { - if ( append() ) { return; } - const observer = new MutationObserver(( ) => { - if ( append() === false ) { return; } - observer.disconnect(); - }); - const observerOptions = { - childList: true, - subtree: true, - }; - if ( /[#.[]/.test(parentSelector) ) { - observerOptions.attributes = true; - if ( parentSelector.includes('[') === false ) { - observerOptions.attributeFilter = []; - if ( parentSelector.includes('#') ) { - observerOptions.attributeFilter.push('id'); - } - if ( parentSelector.includes('.') ) { - observerOptions.attributeFilter.push('class'); - } - } - } - observer.observe(document, observerOptions); - }; - runAt(start, extraArgs.runAt || 'loading'); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedCreateHTML(...args); -}, -}; - - -scriptlets['href-sanitizer.js'] = { -aliases: ["urlskip.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function urlSkip(url, blocked, steps) { - try { - let redirectBlocked = false; - let urlout = url; - for ( const step of steps ) { - const urlin = urlout; - const c0 = step.charCodeAt(0); - // Extract from hash - if ( c0 === 0x23 && step === '#' ) { // # - const pos = urlin.indexOf('#'); - urlout = pos !== -1 ? urlin.slice(pos+1) : ''; - continue; - } - // Extract from URL parameter name at position i - if ( c0 === 0x26 ) { // & - const i = (parseInt(step.slice(1)) || 0) - 1; - if ( i < 0 ) { return; } - const url = new URL(urlin); - if ( i >= url.searchParams.size ) { return; } - const params = Array.from(url.searchParams.keys()); - urlout = decodeURIComponent(params[i]); - continue; - } - // Enforce https - if ( c0 === 0x2B && step === '+https' ) { // + - const s = urlin.replace(/^https?:\/\//, ''); - if ( /^[\w-]:\/\//.test(s) ) { return; } - urlout = `https://${s}`; - continue; - } - // Decode - if ( c0 === 0x2D ) { // - - // Base64 - if ( step === '-base64' ) { - urlout = self.atob(urlin); - continue; - } - // Safe Base64 - if ( step === '-safebase64' ) { - if ( urlSkip.safeBase64Replacer === undefined ) { - urlSkip.safeBase64Map = { '-': '+', '_': '/' }; - urlSkip.safeBase64Replacer = s => urlSkip.safeBase64Map[s]; - } - urlout = urlin.replace(/[-_]/g, urlSkip.safeBase64Replacer); - urlout = self.atob(urlout); - continue; - } - // URI component - if ( step === '-uricomponent' ) { - urlout = decodeURIComponent(urlin); - continue; - } - // Enable skip of blocked requests - if ( step === '-blocked' ) { - redirectBlocked = true; - continue; - } - } - // Regex extraction from first capture group - if ( c0 === 0x2F ) { // / - const re = new RegExp(step.slice(1, -1)); - const match = re.exec(urlin); - if ( match === null ) { return; } - if ( match.length <= 1 ) { return; } - urlout = match[1]; - continue; - } - // Extract from URL parameter - if ( c0 === 0x3F ) { // ? - urlout = (new URL(urlin)).searchParams.get(step.slice(1)); - if ( urlout === null ) { return; } - if ( urlout.includes(' ') ) { - urlout = urlout.replace(/ /g, '%20'); - } - continue; - } - // Unknown directive - return; - } - const urlfinal = new URL(urlout); - if ( urlfinal.protocol !== 'https:' ) { - if ( urlfinal.protocol !== 'http:' ) { return; } - } - if ( blocked && redirectBlocked !== true ) { return; } - return urlout; - } catch { - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function hrefSanitizer( - selector = '', - source = '' -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source); - if ( source === '' ) { source = 'text'; } - const sanitizeCopycats = (href, text) => { - let elems = []; - try { - elems = document.querySelectorAll(`a[href="${href}"`); - } - catch { - } - for ( const elem of elems ) { - elem.setAttribute('href', text); - } - return elems.length; - }; - const validateURL = text => { - if ( typeof text !== 'string' ) { return ''; } - if ( text === '' ) { return ''; } - if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; } - try { - const url = new URL(text, document.location); - return url.href; - } catch { - } - return ''; - }; - const extractURL = (elem, source) => { - if ( /^\[.*\]$/.test(source) ) { - return elem.getAttribute(source.slice(1,-1).trim()) || ''; - } - if ( source === 'text' ) { - return elem.textContent - .replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters - .replace(/[^\x21-\x7e]+$/, ''); // remove trailing invalid characters - } - const steps = source.replace(/(\S)\?/g, '\\1 ?').split(/\s+/); - const url = urlSkip(elem.href, false, steps); - if ( url === undefined ) { return; } - return url.replace(/ /g, '%20'); - }; - const sanitize = ( ) => { - let elems = []; - try { - elems = document.querySelectorAll(selector); - } - catch { - return false; - } - for ( const elem of elems ) { - if ( elem.localName !== 'a' ) { continue; } - if ( elem.hasAttribute('href') === false ) { continue; } - const href = elem.getAttribute('href'); - const text = extractURL(elem, source); - const hrefAfter = validateURL(text); - if ( hrefAfter === '' ) { continue; } - if ( hrefAfter === href ) { continue; } - elem.setAttribute('href', hrefAfter); - const count = sanitizeCopycats(href, hrefAfter); - safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`); - } - return true; - }; - let observer, timer; - const onDomChanged = mutations => { - if ( timer !== undefined ) { return; } - let shouldSanitize = false; - for ( const mutation of mutations ) { - if ( mutation.addedNodes.length === 0 ) { continue; } - for ( const node of mutation.addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - shouldSanitize = true; - break; - } - if ( shouldSanitize ) { break; } - } - if ( shouldSanitize === false ) { return; } - timer = onIdleFn(( ) => { - timer = undefined; - sanitize(); - }); - }; - const start = ( ) => { - if ( sanitize() === false ) { return; } - observer = new MutationObserver(onDomChanged); - observer.observe(document.body, { - subtree: true, - childList: true, - }); - }; - runAt(( ) => { start(); }, 'interactive'); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -hrefSanitizer(...args); -}, -}; - - -scriptlets['proxy-apply-config.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function proxyApplyConfig(config = '') { - try { - if ( typeof proxyApplyFn !== 'function' ) { return; } - config = JSON.parse(config); - if ( typeof config !== 'object' ) { return; } - Object.assign(proxyApplyFn, config); - } catch { - } -}; -proxyApplyConfig(...args); -}, -}; - - -scriptlets['edit-outbound-object.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editOutboundObjectFn( - trusted = false, - propChain = '', - jsonq = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-outbound-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - proxyApplyFn(propChain, function(context) { - const obj = context.reflect(); - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return obj; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }); -} -function editOutboundObject(propChain = '', jsonq = '') { - editOutboundObjectFn(false, propChain, jsonq); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editOutboundObject(...args); -}, -}; - - -scriptlets['trusted-edit-outbound-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editOutboundObjectFn( - trusted = false, - propChain = '', - jsonq = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-outbound-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - proxyApplyFn(propChain, function(context) { - const obj = context.reflect(); - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return obj; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }); -} -function trustedEditOutboundObject(propChain = '', jsonq = '') { - editOutboundObjectFn(true, propChain, jsonq); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditOutboundObject(...args); -}, -}; - - -scriptlets['json-edit.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditFn(trusted = false, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit`, - jsonq, - ...varargs - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const pattern = extraArgs.matches && safe.initPattern(extraArgs.matches); - proxyApplyFn('JSON.parse', function(context) { - const json = context.callArgs[0]; - const obj = context.reflect(); - if ( pattern && safe.testPattern(pattern, json) === false ) { return obj; } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return obj; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }); -} -function jsonEdit(jsonq = '', ...varargs) { - jsonEditFn(false, jsonq, ...varargs); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonEdit(...args); -}, -}; - - -scriptlets['trusted-json-edit.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditFn(trusted = false, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit`, - jsonq, - ...varargs - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const pattern = extraArgs.matches && safe.initPattern(extraArgs.matches); - proxyApplyFn('JSON.parse', function(context) { - const json = context.callArgs[0]; - const obj = context.reflect(); - if ( pattern && safe.testPattern(pattern, json) === false ) { return obj; } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return obj; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }); -} -function trustedJsonEdit(jsonq = '', ...varargs) { - jsonEditFn(true, jsonq, ...varargs); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonEdit(...args); -}, -}; - - -scriptlets['edit-inbound-object.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editInboundObjectFn( - trusted = false, - propChain = '', - argPosRaw = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-inbound-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const argPos = parseInt(argPosRaw, 10); - if ( isNaN(argPos) ) { return; } - const getArgPos = args => { - if ( Array.isArray(args) === false ) { return; } - if ( argPos >= 0 ) { - if ( args.length <= argPos ) { return; } - return argPos; - } - if ( args.length < -argPos ) { return; } - return args.length + argPos; - }; - const editObj = obj => { - let clone; - try { - clone = safe.JSON_parse(safe.JSON_stringify(obj)); - } catch { - } - if ( typeof clone !== 'object' || clone === null ) { return; } - const objAfter = jsonp.apply(clone); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }; - proxyApplyFn(propChain, function(context) { - const i = getArgPos(context.callArgs); - if ( i !== undefined ) { - const obj = editObj(context.callArgs[i]); - if ( obj ) { - context.callArgs[i] = obj; - } - } - return context.reflect(); - }); -} -function editInboundObject(propChain = '', argPos = '', jsonq = '') { - editInboundObjectFn(false, propChain, argPos, jsonq); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editInboundObject(...args); -}, -}; - - -scriptlets['trusted-edit-inbound-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editInboundObjectFn( - trusted = false, - propChain = '', - argPosRaw = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-inbound-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const argPos = parseInt(argPosRaw, 10); - if ( isNaN(argPos) ) { return; } - const getArgPos = args => { - if ( Array.isArray(args) === false ) { return; } - if ( argPos >= 0 ) { - if ( args.length <= argPos ) { return; } - return argPos; - } - if ( args.length < -argPos ) { return; } - return args.length + argPos; - }; - const editObj = obj => { - let clone; - try { - clone = safe.JSON_parse(safe.JSON_stringify(obj)); - } catch { - } - if ( typeof clone !== 'object' || clone === null ) { return; } - const objAfter = jsonp.apply(clone); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }; - proxyApplyFn(propChain, function(context) { - const i = getArgPos(context.callArgs); - if ( i !== undefined ) { - const obj = editObj(context.callArgs[i]); - if ( obj ) { - context.callArgs[i] = obj; - } - } - return context.reflect(); - }); -} -function trustedEditInboundObject(propChain = '', argPos = '', jsonq = '') { - editInboundObjectFn(true, propChain, argPos, jsonq); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditInboundObject(...args); -}, -}; - - -scriptlets['edit-this-object.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editThisObjectFn( - trusted = false, - propChain = '', - jsonq = '', - order = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-this-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - proxyApplyFn(propChain, function(context) { - const { thisArg } = context; - let r; - if ( order === 'after' ) { - r = context.reflect(); - } - editObj(thisArg); - if ( order !== 'after' ) { - r = context.reflect(); - } - return r; - }); -} -function editThisObject(...args) { - editThisObjectFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editThisObject(...args); -}, -}; - - -scriptlets['trusted-edit-this-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editThisObjectFn( - trusted = false, - propChain = '', - jsonq = '', - order = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-this-object`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - proxyApplyFn(propChain, function(context) { - const { thisArg } = context; - let r; - if ( order === 'after' ) { - r = context.reflect(); - } - editObj(thisArg); - if ( order !== 'after' ) { - r = context.reflect(); - } - return r; - }); -} -function trustedEditThisObject(...args) { - editThisObjectFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditThisObject(...args); -}, -}; - - -scriptlets['edit-object-on-getter.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function trapPropertyFn(propChain, handler, options = {}) { - if ( propChain === '' ) { return; } - let owner = self; - let prop = propChain; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - const safe = safeSelf(); - if ( trapPropertyFn.db === undefined ) { - trapPropertyFn.db = new WeakMap(); - trapPropertyFn.entryFromContext = (owner, prop) => { - const handlers = trapPropertyFn.db.get(owner); - return handlers?.get(prop); - }; - trapPropertyFn.getter = (owner, prop) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - let r = entry.value; - for ( const desc of entry.stack ) { - try { r = desc.get(); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - return r; - }; - trapPropertyFn.setter = (owner, prop, value) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - entry.value = value; - for ( const desc of entry.stack ) { - try { desc.set(value); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - }; - } - const { db } = trapPropertyFn; - const handlers = db.get(owner) || new Map(); - if ( handlers.size === 0 ) { - db.set(owner, handlers); - } - const entry = handlers.get(prop) || { - value: owner[prop], - stack: [], - }; - entry.stack.push(handler); - if ( entry.stack.length > 1 ) { return entry.value; } - Object.assign(entry, options); - handlers.set(prop, entry); - const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); - if ( desc instanceof safe.Object ) { - if ( desc.get || desc.set ) { - entry.stack.push(desc); - } - } - try { - safe.Object_defineProperty(owner, prop, { - get() { - return trapPropertyFn.getter(owner, prop); - }, - set(value) { - trapPropertyFn.setter(owner, prop, value); - } - }); - } catch { - } - return entry.value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editObjectOnGetterFn( - trusted = false, - propChain = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-object-on-getter`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - let currentValue = trapPropertyFn(propChain, { - get: function() { - if ( currentValue instanceof Object ) { - editObj(currentValue); - } - return currentValue; - }, - set: function(a) { - currentValue = a; - } - }); -} -function editObjectOnGetter(...args) { - editObjectOnGetterFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editObjectOnGetter(...args); -}, -}; - - -scriptlets['trusted-edit-object-on-getter.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function trapPropertyFn(propChain, handler, options = {}) { - if ( propChain === '' ) { return; } - let owner = self; - let prop = propChain; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - const safe = safeSelf(); - if ( trapPropertyFn.db === undefined ) { - trapPropertyFn.db = new WeakMap(); - trapPropertyFn.entryFromContext = (owner, prop) => { - const handlers = trapPropertyFn.db.get(owner); - return handlers?.get(prop); - }; - trapPropertyFn.getter = (owner, prop) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - let r = entry.value; - for ( const desc of entry.stack ) { - try { r = desc.get(); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - return r; - }; - trapPropertyFn.setter = (owner, prop, value) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - entry.value = value; - for ( const desc of entry.stack ) { - try { desc.set(value); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - }; - } - const { db } = trapPropertyFn; - const handlers = db.get(owner) || new Map(); - if ( handlers.size === 0 ) { - db.set(owner, handlers); - } - const entry = handlers.get(prop) || { - value: owner[prop], - stack: [], - }; - entry.stack.push(handler); - if ( entry.stack.length > 1 ) { return entry.value; } - Object.assign(entry, options); - handlers.set(prop, entry); - const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); - if ( desc instanceof safe.Object ) { - if ( desc.get || desc.set ) { - entry.stack.push(desc); - } - } - try { - safe.Object_defineProperty(owner, prop, { - get() { - return trapPropertyFn.getter(owner, prop); - }, - set(value) { - trapPropertyFn.setter(owner, prop, value); - } - }); - } catch { - } - return entry.value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editObjectOnGetterFn( - trusted = false, - propChain = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-object-on-getter`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - let currentValue = trapPropertyFn(propChain, { - get: function() { - if ( currentValue instanceof Object ) { - editObj(currentValue); - } - return currentValue; - }, - set: function(a) { - currentValue = a; - } - }); -} -function trustedEditObjectOnGetter(...args) { - editObjectOnGetterFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditObjectOnGetter(...args); -}, -}; - - -scriptlets['edit-object-on-setter.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function trapPropertyFn(propChain, handler, options = {}) { - if ( propChain === '' ) { return; } - let owner = self; - let prop = propChain; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - const safe = safeSelf(); - if ( trapPropertyFn.db === undefined ) { - trapPropertyFn.db = new WeakMap(); - trapPropertyFn.entryFromContext = (owner, prop) => { - const handlers = trapPropertyFn.db.get(owner); - return handlers?.get(prop); - }; - trapPropertyFn.getter = (owner, prop) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - let r = entry.value; - for ( const desc of entry.stack ) { - try { r = desc.get(); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - return r; - }; - trapPropertyFn.setter = (owner, prop, value) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - entry.value = value; - for ( const desc of entry.stack ) { - try { desc.set(value); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - }; - } - const { db } = trapPropertyFn; - const handlers = db.get(owner) || new Map(); - if ( handlers.size === 0 ) { - db.set(owner, handlers); - } - const entry = handlers.get(prop) || { - value: owner[prop], - stack: [], - }; - entry.stack.push(handler); - if ( entry.stack.length > 1 ) { return entry.value; } - Object.assign(entry, options); - handlers.set(prop, entry); - const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); - if ( desc instanceof safe.Object ) { - if ( desc.get || desc.set ) { - entry.stack.push(desc); - } - } - try { - safe.Object_defineProperty(owner, prop, { - get() { - return trapPropertyFn.getter(owner, prop); - }, - set(value) { - trapPropertyFn.setter(owner, prop, value); - } - }); - } catch { - } - return entry.value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editObjectOnSetterFn( - trusted = false, - propChain = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-object-on-setter`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - let currentValue = trapPropertyFn(propChain, { - get: function() { - return currentValue; - }, - set: function(a) { - currentValue = a; - if ( currentValue instanceof Object ) { - editObj(currentValue); - } - } - }); - if ( currentValue instanceof Object ) { - editObj(currentValue); - } -} -function editObjectOnSetter(...args) { - editObjectOnSetterFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editObjectOnSetter(...args); -}, -}; - - -scriptlets['trusted-edit-object-on-setter.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function trapPropertyFn(propChain, handler, options = {}) { - if ( propChain === '' ) { return; } - let owner = self; - let prop = propChain; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - const safe = safeSelf(); - if ( trapPropertyFn.db === undefined ) { - trapPropertyFn.db = new WeakMap(); - trapPropertyFn.entryFromContext = (owner, prop) => { - const handlers = trapPropertyFn.db.get(owner); - return handlers?.get(prop); - }; - trapPropertyFn.getter = (owner, prop) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - let r = entry.value; - for ( const desc of entry.stack ) { - try { r = desc.get(); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - return r; - }; - trapPropertyFn.setter = (owner, prop, value) => { - const entry = trapPropertyFn.entryFromContext(owner, prop); - if ( entry === undefined ) { return; } - entry.value = value; - for ( const desc of entry.stack ) { - try { desc.set(value); } catch (e) { - if ( entry.canThrow ) { throw e; } - } - } - }; - } - const { db } = trapPropertyFn; - const handlers = db.get(owner) || new Map(); - if ( handlers.size === 0 ) { - db.set(owner, handlers); - } - const entry = handlers.get(prop) || { - value: owner[prop], - stack: [], - }; - entry.stack.push(handler); - if ( entry.stack.length > 1 ) { return entry.value; } - Object.assign(entry, options); - handlers.set(prop, entry); - const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); - if ( desc instanceof safe.Object ) { - if ( desc.get || desc.set ) { - entry.stack.push(desc); - } - } - try { - safe.Object_defineProperty(owner, prop, { - get() { - return trapPropertyFn.getter(owner, prop); - }, - set(value) { - trapPropertyFn.setter(owner, prop, value); - } - }); - } catch { - } - return entry.value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function editObjectOnSetterFn( - trusted = false, - propChain = '', - jsonq = '', -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-object-on-setter`, - propChain, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const editObj = objBefore => { - const objAfter = jsonp.apply(objBefore); - if ( objAfter === undefined ) { return; } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel <= 1 ) { return; } - safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); - }; - let currentValue = trapPropertyFn(propChain, { - get: function() { - return currentValue; - }, - set: function(a) { - currentValue = a; - if ( currentValue instanceof Object ) { - editObj(currentValue); - } - } - }); - if ( currentValue instanceof Object ) { - editObj(currentValue); - } -} -function trustedEditObjectOnSetter(...args) { - editObjectOnSetterFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditObjectOnSetter(...args); -}, -}; - - -scriptlets['edit-element-object.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function sleepFn(ms = 0) { - const nap = ( ) => { - return new Promise(resolve => { - self.requestAnimationFrame(resolve); - }); - } - const until = Date.now() + ms; - const sleep = async resolve => { - do { - await nap(); - } while ( Date.now() < until ); - resolve(); - }; - return new Promise(resolve => { sleep(resolve); }); -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function offIdleFn(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); -} -function lookupElementsFn(directive, until = 0) { - if ( lookupElementsFn.querySelectorEx === undefined ) { - lookupElementsFn.getShadowRoot = elem => { - if ( elem.openOrClosedShadowRoot ) { // Firefox - return elem.openOrClosedShadowRoot; - } - if ( self.chrome?.dom?.openOrClosedShadowRoot ) { // Chromium - return self.chrome.dom.openOrClosedShadowRoot(elem); - } - return elem.shadowRoot; - }; - lookupElementsFn.queryOrEvaluateSelector = (selector, context) => { - if ( selector.startsWith('xpath:') === false ) { - return Array.from(context.querySelectorAll(selector)); - } - const result = document.evaluate(selector.slice(6), context, null, 7, null); - const out = []; - if ( result.resultType === 7 ) { - for ( let i = 0; i < result.snapshotLength; i++ ) { - out[i] = result.snapshotItem(i); - } - } - return out; - } - lookupElementsFn.querySelectorEx = (selector, context = document) => { - const pos = selector.indexOf(' >>> '); - if ( pos === -1 ) { - return lookupElementsFn.queryOrEvaluateSelector(selector, context); - } - const outside = selector.slice(0, pos).trim(); - const inside = selector.slice(pos + 5).trim(); - const elems = lookupElementsFn.queryOrEvaluateSelector(outside, context); - const out = []; - for ( let i = 0; i < elems.length; i++ ) { - const shadowRoot = lookupElementsFn.getShadowRoot(elems[i]); - if ( Boolean(shadowRoot) === false ) { continue; } - lookupElementsFn.querySelectorEx(inside, shadowRoot).forEach(a => out.push(a)); - } - return out; - }; - lookupElementsFn.lookup = directive => { - const beVisible = directive.startsWith('when-visible:'); - const selector = beVisible ? directive.slice(13) : directive; - const elems = lookupElementsFn.querySelectorEx(selector); - if ( beVisible !== true ) { return elems; } - return elems.filter(a => a.checkVisibility({ - opacityProperty: true, - visibilityProperty: true, - })); - }; - lookupElementsFn.lookupAsync = details => { - const elems = lookupElementsFn.lookup(details.directive); - if ( elems.length || Date.now() >= details.until ) { - if ( details.observer ) { - details.observer.disconnect(); - details.observer = undefined; - } - if ( details.timer ) { - offIdleFn(details.timer); - details.timer = undefined; - } - return details.resolve(elems); - } - if ( details.observer === undefined ) { - details.observer = new MutationObserver(( ) => { - lookupElementsFn.lookupAsync(details); - }); - details.observer.observe(document, { - attributes: true, - childList: true, - subtree: true, - }); - } - if ( details.timer === undefined ) { - details.timer = onIdleFn(( ) => { - details.timer = undefined; - lookupElementsFn.lookupAsync(details); - }, { timeout: 151 }); - } - }; - } - if ( until === 0 ) { - return lookupElementsFn.lookup(directive); - } - return new Promise(resolve => { - lookupElementsFn.lookupAsync({ directive, until, resolve }); - }); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -async function editElementObjectFn( - trusted = false, - selector = '', - jsonq = '', - timeout = '' -) { - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-element-object`, - selector, jsonq, timeout - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const process = elems => { - for ( const elem of elems ) { - const r = jsonp.apply(elem); - if ( r === undefined ) { continue; } - safe.uboLog(logPrefix, 'Edited'); - } - }; - const until = timeout !== '' ? Date.now() + parseInt(timeout, 10) : 0; - do { - const r = lookupElementsFn(selector, until); - if ( Array.isArray(r) ) { - process(r); - } else if ( r instanceof Promise ) { - const elems = await r; - process(elems); - } - await sleepFn(); - } while ( Date.now() < until ); -} -function editElementObject(...args) { - editElementObjectFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -editElementObject(...args); -}, -}; - - -scriptlets['trusted-edit-element-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function sleepFn(ms = 0) { - const nap = ( ) => { - return new Promise(resolve => { - self.requestAnimationFrame(resolve); - }); - } - const until = Date.now() + ms; - const sleep = async resolve => { - do { - await nap(); - } while ( Date.now() < until ); - resolve(); - }; - return new Promise(resolve => { sleep(resolve); }); -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function offIdleFn(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); -} -function lookupElementsFn(directive, until = 0) { - if ( lookupElementsFn.querySelectorEx === undefined ) { - lookupElementsFn.getShadowRoot = elem => { - if ( elem.openOrClosedShadowRoot ) { // Firefox - return elem.openOrClosedShadowRoot; - } - if ( self.chrome?.dom?.openOrClosedShadowRoot ) { // Chromium - return self.chrome.dom.openOrClosedShadowRoot(elem); - } - return elem.shadowRoot; - }; - lookupElementsFn.queryOrEvaluateSelector = (selector, context) => { - if ( selector.startsWith('xpath:') === false ) { - return Array.from(context.querySelectorAll(selector)); - } - const result = document.evaluate(selector.slice(6), context, null, 7, null); - const out = []; - if ( result.resultType === 7 ) { - for ( let i = 0; i < result.snapshotLength; i++ ) { - out[i] = result.snapshotItem(i); - } - } - return out; - } - lookupElementsFn.querySelectorEx = (selector, context = document) => { - const pos = selector.indexOf(' >>> '); - if ( pos === -1 ) { - return lookupElementsFn.queryOrEvaluateSelector(selector, context); - } - const outside = selector.slice(0, pos).trim(); - const inside = selector.slice(pos + 5).trim(); - const elems = lookupElementsFn.queryOrEvaluateSelector(outside, context); - const out = []; - for ( let i = 0; i < elems.length; i++ ) { - const shadowRoot = lookupElementsFn.getShadowRoot(elems[i]); - if ( Boolean(shadowRoot) === false ) { continue; } - lookupElementsFn.querySelectorEx(inside, shadowRoot).forEach(a => out.push(a)); - } - return out; - }; - lookupElementsFn.lookup = directive => { - const beVisible = directive.startsWith('when-visible:'); - const selector = beVisible ? directive.slice(13) : directive; - const elems = lookupElementsFn.querySelectorEx(selector); - if ( beVisible !== true ) { return elems; } - return elems.filter(a => a.checkVisibility({ - opacityProperty: true, - visibilityProperty: true, - })); - }; - lookupElementsFn.lookupAsync = details => { - const elems = lookupElementsFn.lookup(details.directive); - if ( elems.length || Date.now() >= details.until ) { - if ( details.observer ) { - details.observer.disconnect(); - details.observer = undefined; - } - if ( details.timer ) { - offIdleFn(details.timer); - details.timer = undefined; - } - return details.resolve(elems); - } - if ( details.observer === undefined ) { - details.observer = new MutationObserver(( ) => { - lookupElementsFn.lookupAsync(details); - }); - details.observer.observe(document, { - attributes: true, - childList: true, - subtree: true, - }); - } - if ( details.timer === undefined ) { - details.timer = onIdleFn(( ) => { - details.timer = undefined; - lookupElementsFn.lookupAsync(details); - }, { timeout: 151 }); - } - }; - } - if ( until === 0 ) { - return lookupElementsFn.lookup(directive); - } - return new Promise(resolve => { - lookupElementsFn.lookupAsync({ directive, until, resolve }); - }); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -async function editElementObjectFn( - trusted = false, - selector = '', - jsonq = '', - timeout = '' -) { - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}edit-element-object`, - selector, jsonq, timeout - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const process = elems => { - for ( const elem of elems ) { - const r = jsonp.apply(elem); - if ( r === undefined ) { continue; } - safe.uboLog(logPrefix, 'Edited'); - } - }; - const until = timeout !== '' ? Date.now() + parseInt(timeout, 10) : 0; - do { - const r = lookupElementsFn(selector, until); - if ( Array.isArray(r) ) { - process(r); - } else if ( r instanceof Promise ) { - const elems = await r; - process(elems); - } - await sleepFn(); - } while ( Date.now() < until ); -} -function trustedEditElementObject(...args) { - editElementObjectFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedEditElementObject(...args); -}, -}; - - -scriptlets['json-edit-xhr-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditXhrResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-xhr-response`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { return innerResponse; } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - let obj; - if ( typeof innerResponse === 'object' ) { - obj = innerResponse; - } else if ( typeof innerResponse === 'string' ) { - try { obj = safe.JSON_parse(innerResponse); } catch { } - } - if ( typeof obj !== 'object' || obj === null ) { - return (xhrDetails.response = innerResponse); - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - return (xhrDetails.response = innerResponse); - } - safe.uboLog(logPrefix, 'Edited'); - const outerResponse = typeof innerResponse === 'string' - ? JSONPath.toJSON(objAfter, safe.JSON_stringify) - : objAfter; - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} -function jsonEditXhrResponse(jsonq = '', ...args) { - jsonEditXhrResponseFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonEditXhrResponse(...args); -}, -}; - - -scriptlets['trusted-json-edit-xhr-response.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditXhrResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-xhr-response`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { return innerResponse; } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - let obj; - if ( typeof innerResponse === 'object' ) { - obj = innerResponse; - } else if ( typeof innerResponse === 'string' ) { - try { obj = safe.JSON_parse(innerResponse); } catch { } - } - if ( typeof obj !== 'object' || obj === null ) { - return (xhrDetails.response = innerResponse); - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - return (xhrDetails.response = innerResponse); - } - safe.uboLog(logPrefix, 'Edited'); - const outerResponse = typeof innerResponse === 'string' - ? JSONPath.toJSON(objAfter, safe.JSON_stringify) - : objAfter; - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} -function trustedJsonEditXhrResponse(jsonq = '', ...args) { - jsonEditXhrResponseFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonEditXhrResponse(...args); -}, -}; - - -scriptlets['json-edit-xhr-request.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditXhrRequestFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-xhr-request`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - send(body) { - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails ) { - body = this.#filterBody(body) || body; - } - super.send(body); - } - #filterBody(body) { - if ( typeof body !== 'string' ) { return; } - let data; - try { data = safe.JSON_parse(body); } - catch { } - if ( data instanceof Object === false ) { return; } - const objAfter = jsonp.apply(data); - if ( objAfter === undefined ) { return; } - body = safe.JSON_stringify(objAfter); - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${body}`); - } - return body; - } - }; -} -function jsonEditXhrRequest(jsonq = '', ...args) { - jsonEditXhrRequestFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonEditXhrRequest(...args); -}, -}; - - -scriptlets['trusted-json-edit-xhr-request.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonEditXhrRequestFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-xhr-request`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - send(body) { - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails ) { - body = this.#filterBody(body) || body; - } - super.send(body); - } - #filterBody(body) { - if ( typeof body !== 'string' ) { return; } - let data; - try { data = safe.JSON_parse(body); } - catch { } - if ( data instanceof Object === false ) { return; } - const objAfter = jsonp.apply(data); - if ( objAfter === undefined ) { return; } - body = safe.JSON_stringify(objAfter); - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${body}`); - } - return body; - } - }; -} -function trustedJsonEditXhrRequest(jsonq = '', ...args) { - jsonEditXhrRequestFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonEditXhrRequest(...args); -}, -}; - - -scriptlets['json-edit-fetch-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonEditFetchResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-fetch-response`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - proxyApplyFn('fetch', function(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.json().then(obj => { - if ( typeof obj !== 'object' ) { return responseBefore; } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return responseBefore; } - safe.uboLog(logPrefix, 'Edited'); - const responseAfter = Response.json(objAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }); -} -function jsonEditFetchResponse(jsonq = '', ...args) { - jsonEditFetchResponseFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonEditFetchResponse(...args); -}, -}; - - -scriptlets['trusted-json-edit-fetch-response.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonEditFetchResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-fetch-response`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - proxyApplyFn('fetch', function(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.json().then(obj => { - if ( typeof obj !== 'object' ) { return responseBefore; } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { return responseBefore; } - safe.uboLog(logPrefix, 'Edited'); - const responseAfter = Response.json(objAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }); -} -function trustedJsonEditFetchResponse(jsonq = '', ...args) { - jsonEditFetchResponseFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonEditFetchResponse(...args); -}, -}; - - -scriptlets['json-edit-fetch-request.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonEditFetchRequestFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-fetch-request`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const filterBody = body => { - if ( typeof body !== 'string' ) { return; } - let data; - try { data = safe.JSON_parse(body); } - catch { } - if ( data instanceof Object === false ) { return; } - const objAfter = jsonp.apply(data); - if ( objAfter === undefined ) { return; } - return safe.JSON_stringify(objAfter); - } - const proxyHandler = context => { - const args = context.callArgs; - const [ resource, options ] = args; - const bodyBefore = options?.body; - if ( Boolean(bodyBefore) === false ) { return context.reflect(); } - const bodyAfter = filterBody(bodyBefore); - if ( bodyAfter === undefined || bodyAfter === bodyBefore ) { - return context.reflect(); - } - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(resource, options); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return context.reflect(); } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${bodyAfter}`); - } - options.body = bodyAfter; - return context.reflect(); - }; - proxyApplyFn('fetch', proxyHandler); - proxyApplyFn('Request', proxyHandler); -} -function jsonEditFetchRequest(jsonq = '', ...args) { - jsonEditFetchRequestFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonEditFetchRequest(...args); -}, -}; - - -scriptlets['trusted-json-edit-fetch-request.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonEditFetchRequestFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-fetch-request`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const filterBody = body => { - if ( typeof body !== 'string' ) { return; } - let data; - try { data = safe.JSON_parse(body); } - catch { } - if ( data instanceof Object === false ) { return; } - const objAfter = jsonp.apply(data); - if ( objAfter === undefined ) { return; } - return safe.JSON_stringify(objAfter); - } - const proxyHandler = context => { - const args = context.callArgs; - const [ resource, options ] = args; - const bodyBefore = options?.body; - if ( Boolean(bodyBefore) === false ) { return context.reflect(); } - const bodyAfter = filterBody(bodyBefore); - if ( bodyAfter === undefined || bodyAfter === bodyBefore ) { - return context.reflect(); - } - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(resource, options); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return context.reflect(); } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${bodyAfter}`); - } - options.body = bodyAfter; - return context.reflect(); - }; - proxyApplyFn('fetch', proxyHandler); - proxyApplyFn('Request', proxyHandler); -} -function trustedJsonEditFetchRequest(jsonq = '', ...args) { - jsonEditFetchRequestFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonEditFetchRequest(...args); -}, -}; - - -scriptlets['jsonl-edit-xhr-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function jsonlEditFn(jsonp, text = '', jsonExtract) { - const safe = safeSelf(); - const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; - const linesBefore = text.split('\n'); - const linesAfter = []; - for ( const lineBefore of linesBefore ) { - const match = jsonExtract.exec(lineBefore); - if ( match === null ) { - linesAfter.push(lineBefore); - continue; - } - const jsonBefore = match[1]; - let obj; - try { obj = safe.JSON_parse(jsonBefore); } catch { } - if ( typeof obj !== 'object' || obj === null ) { - linesAfter.push(lineBefore); - continue; - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - linesAfter.push(lineBefore); - continue; - } - const jsonAfter = safe.JSON_stringify(objAfter); - const lineAfter = [ - lineBefore.slice(0, match.index), - jsonAfter, - lineBefore.slice(match.index + jsonBefore.length), - ].join(''); - linesAfter.push(lineAfter); - } - return linesAfter.join(lineSeparator); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonlEditXhrResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}jsonl-edit-xhr-response`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const jsonExtract = extraArgs.jsonExtract - ? new RegExp(extraArgs.jsonExtract) - : /^(.*)$/; - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - if ( typeof innerResponse !== 'string' ) { - return (xhrDetails.response = innerResponse); - } - const outerResponse = jsonlEditFn(jsonp, innerResponse, jsonExtract); - if ( outerResponse !== innerResponse ) { - safe.uboLog(logPrefix, 'Pruned'); - } - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} -function jsonlEditXhrResponse(jsonq = '', ...args) { - jsonlEditXhrResponseFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonlEditXhrResponse(...args); -}, -}; - - -scriptlets['trusted-jsonl-edit-xhr-response.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function jsonlEditFn(jsonp, text = '', jsonExtract) { - const safe = safeSelf(); - const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; - const linesBefore = text.split('\n'); - const linesAfter = []; - for ( const lineBefore of linesBefore ) { - const match = jsonExtract.exec(lineBefore); - if ( match === null ) { - linesAfter.push(lineBefore); - continue; - } - const jsonBefore = match[1]; - let obj; - try { obj = safe.JSON_parse(jsonBefore); } catch { } - if ( typeof obj !== 'object' || obj === null ) { - linesAfter.push(lineBefore); - continue; - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - linesAfter.push(lineBefore); - continue; - } - const jsonAfter = safe.JSON_stringify(objAfter); - const lineAfter = [ - lineBefore.slice(0, match.index), - jsonAfter, - lineBefore.slice(match.index + jsonBefore.length), - ].join(''); - linesAfter.push(lineAfter); - } - return linesAfter.join(lineSeparator); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function jsonlEditXhrResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}jsonl-edit-xhr-response`, - jsonq - ); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const jsonExtract = extraArgs.jsonExtract - ? new RegExp(extraArgs.jsonExtract) - : /^(.*)$/; - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - if ( typeof innerResponse !== 'string' ) { - return (xhrDetails.response = innerResponse); - } - const outerResponse = jsonlEditFn(jsonp, innerResponse, jsonExtract); - if ( outerResponse !== innerResponse ) { - safe.uboLog(logPrefix, 'Pruned'); - } - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} -function trustedJsonlEditXhrResponse(jsonq = '', ...args) { - jsonlEditXhrResponseFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonlEditXhrResponse(...args); -}, -}; - - -scriptlets['jsonl-edit-fetch-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function jsonlEditFn(jsonp, text = '', jsonExtract) { - const safe = safeSelf(); - const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; - const linesBefore = text.split('\n'); - const linesAfter = []; - for ( const lineBefore of linesBefore ) { - const match = jsonExtract.exec(lineBefore); - if ( match === null ) { - linesAfter.push(lineBefore); - continue; - } - const jsonBefore = match[1]; - let obj; - try { obj = safe.JSON_parse(jsonBefore); } catch { } - if ( typeof obj !== 'object' || obj === null ) { - linesAfter.push(lineBefore); - continue; - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - linesAfter.push(lineBefore); - continue; - } - const jsonAfter = safe.JSON_stringify(objAfter); - const lineAfter = [ - lineBefore.slice(0, match.index), - jsonAfter, - lineBefore.slice(match.index + jsonBefore.length), - ].join(''); - linesAfter.push(lineAfter); - } - return linesAfter.join(lineSeparator); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonlEditFetchResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const jsonExtract = extraArgs.jsonExtract - ? new RegExp(extraArgs.jsonExtract) - : /^(.*)$/; - const logall = jsonq === ''; - proxyApplyFn('fetch', function(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(textBefore => { - if ( typeof textBefore !== 'string' ) { return textBefore; } - if ( logall ) { - safe.uboLog(logPrefix, textBefore); - return responseBefore; - } - const textAfter = jsonlEditFn(jsonp, textBefore, jsonExtract); - if ( textAfter === textBefore ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }); -} -function jsonlEditFetchResponse(jsonq = '', ...args) { - jsonlEditFetchResponseFn(false, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonlEditFetchResponse(...args); -}, -}; - - -scriptlets['trusted-jsonl-edit-fetch-response.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function jsonlEditFn(jsonp, text = '', jsonExtract) { - const safe = safeSelf(); - const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; - const linesBefore = text.split('\n'); - const linesAfter = []; - for ( const lineBefore of linesBefore ) { - const match = jsonExtract.exec(lineBefore); - if ( match === null ) { - linesAfter.push(lineBefore); - continue; - } - const jsonBefore = match[1]; - let obj; - try { obj = safe.JSON_parse(jsonBefore); } catch { } - if ( typeof obj !== 'object' || obj === null ) { - linesAfter.push(lineBefore); - continue; - } - const objAfter = jsonp.apply(obj); - if ( objAfter === undefined ) { - linesAfter.push(lineBefore); - continue; - } - const jsonAfter = safe.JSON_stringify(objAfter); - const lineAfter = [ - lineBefore.slice(0, match.index), - jsonAfter, - lineBefore.slice(match.index + jsonBefore.length), - ].join(''); - linesAfter.push(lineAfter); - } - return linesAfter.join(lineSeparator); -} -class JSONPath { - static create(query) { - const jsonp = new JSONPath(); - jsonp.compile(query); - return jsonp; - } - static toJSON(obj, stringifier, ...args) { - return (stringifier || JSON.stringify)(obj, ...args) - .replace(/\//g, '\\/'); - } - static keys = Object.keys; - static entries = Object.entries; - static hasOwn = Object.hasOwn; - static Regex = RegExp; - get value() { - return this.#compiled && this.#compiled.rval; - } - set value(v) { - if ( this.#compiled === undefined ) { return; } - this.#compiled.rval = v; - } - get valid() { - return this.#compiled !== undefined; - } - compile(query) { - this.#compiled = undefined; - this.v2 = query.startsWith('v2:'); - if ( this.v2 ) { query = query.slice(3); } - const r = this.#compile(query, 0); - if ( r === undefined ) { return; } - if ( r.i !== query.length ) { - let val; - if ( query.startsWith('=', r.i) ) { - const match = this.#reRval.exec(query.slice(r.i)); - if ( match ) { - r.modify = match[1]; - val = match[2]; - } else { - val = query.slice(r.i+1); - } - } else if ( query.startsWith('+=', r.i) ) { - r.modify = '+'; - val = query.slice(r.i+2); - } - try { r.rval = JSON.parse(val); } - catch { return; } - } - r.v2 = this.v2; - this.#compiled = r; - } - evaluate(root) { - if ( this.valid === false ) { return []; } - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - this.#root = null; - return paths; - } - apply(root) { - if ( this.valid === false ) { return; } - const { rval } = this.#compiled; - this.#root = { '$': root }; - const paths = this.#evaluate(this.#compiled.steps, []); - let i = paths.length - if ( i === 0 ) { this.#root = null; return; } - while ( i-- ) { - const { obj, key } = this.#resolvePath(paths[i]); - if ( obj === undefined ) { continue; } - if ( rval !== undefined ) { - this.#modifyVal(obj, key); - } else if ( Array.isArray(obj) && typeof key === 'number' ) { - obj.splice(key, 1); - } else { - delete obj[key]; - } - } - const result = this.#root['$'] ?? null; - this.#root = null; - return result; - } - dump() { - return JSON.stringify(this.#compiled); - } - toJSON(obj, ...args) { - return JSONPath.toJSON(obj, null, ...args) - } - get [Symbol.toStringTag]() { - return 'JSONPath'; - } - #UNDEFINED = 0; - #ROOT = 1; - #CURRENT = 2; - #CHILDREN = 3; - #DESCENDANTS = 4; - #QUANTIFIER = 5; - #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; - #reIndice = /^-?\d+/; - #reRval = /^=([a-z]+)\((.+)\)$/; - #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; - #root; - #compiled; - #compile(query, i) { - if ( query.length === 0 ) { return; } - const steps = []; - let c = query.charCodeAt(i); - if ( c === 0x24 /* $ */ ) { - steps.push({ mv: this.#ROOT }); - i += 1; - } else if ( c === 0x40 /* @ */ ) { - steps.push({ mv: this.#CURRENT }); - i += 1; - } else { - steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); - } - let mv = this.#UNDEFINED; - for (;;) { - if ( i === query.length ) { break; } - c = query.charCodeAt(i); - if ( c === 0x20 /* whitespace */ ) { - i += 1; - continue; - } - // Dot accessor syntax - if ( c === 0x2E /* . */ ) { - if ( mv !== this.#UNDEFINED ) { return; } - if ( query.startsWith('..', i) ) { - mv = this.#DESCENDANTS; - i += 2; - } else { - mv = this.#CHILDREN; - i += 1; - } - continue; - } - if ( c === 0x3B /* ; */ ) { - if ( query.startsWith(';$', i) === false ) { return; } - steps.push( - { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, - { mv: this.#ROOT } - ); - i += 2; - mv = this.#UNDEFINED; - continue; - } - if ( c === 0x7B /* { */ ) { - const match = this.#reQuantifier.exec(query.slice(i)); - if ( match === null ) { return; } - const comma = match[1].indexOf(','); - let min, max; - if ( comma === -1 ) { - min = max = parseInt(match[1]); - } else { - min = parseInt(match[1].slice(0, comma)) || 0; - max = parseInt(match[1].slice(comma+1)) || 1e6; - } - steps.push( - { mv: this.#QUANTIFIER, min, max }, - { mv: this.#ROOT } - ); - i += match[0].length; - mv = this.#UNDEFINED; - continue; - } - if ( c !== 0x5B /* [ */ ) { - if ( mv === this.#UNDEFINED ) { - const step = steps.at(-1); - if ( step === undefined ) { return; } - const j = this.#compileExpr(query, step, i); - if ( j ) { i = j; } - break; - } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } - // Bracket accessor syntax - if ( mv === this.#CHILDREN ) { return; } - if ( query.startsWith('[?', i) ) { - const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; - const j = i + 2 + not; - const r = this.#compile(query, j); - if ( r === undefined ) { return; } - if ( query.startsWith(']', r.i) === false ) { return; } - if ( not ) { r.steps.at(-1).not = true; } - steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 1; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - const r = this.#consumeIdentifier(query, i+1); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i + 1; - mv = this.#UNDEFINED; - } - if ( steps.length === 0 ) { return; } - if ( mv !== this.#UNDEFINED ) { return; } - return { steps, i }; - } - #evaluate(steps, pathin) { - let resultset = []; - if ( Array.isArray(steps) === false ) { return resultset; } - for ( const step of steps ) { - switch ( step.mv ) { - case this.#ROOT: - resultset = [ [ '$' ] ]; - break; - case this.#CURRENT: - if ( step.op ) { - const { obj, key } = this.#resolvePath(pathin); - if ( obj === undefined ) { return []; } - const outcome = this.#evaluateExpr(step, obj, key); - if ( outcome !== true ) { break; } - } - resultset = [ pathin ]; - break; - case this.#CHILDREN: - case this.#DESCENDANTS: { - if ( resultset.length === 0 ) { break; } - resultset = this.#getMatches(resultset, step); - break; - } - case this.#QUANTIFIER: { - const { length } = resultset; - if ( length < step.min || length > step.max ) { return []; } - resultset = []; - break; - } - default: - break; - } - } - return resultset; - } - #getMatches(listin, step) { - const listout = []; - for ( const pathin of listin ) { - const { value: owner } = this.#resolvePath(pathin); - if ( owner === undefined ) { continue; } - if ( step.steps ) { - this.#getMatchesFromExpr(pathin, step, owner, listout); - continue; - } - const iter = this.#expandKey(owner, step.k); - if ( iter ) { - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, owner, k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, k ]); - } - } - if ( step.mv !== this.#DESCENDANTS ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { - const iter = this.#expandKey(obj[key], step.k); - if ( iter === undefined ) { continue; } - for ( const k of iter ) { - const outcome = this.#evaluateExpr(step, obj[key], k); - if ( outcome !== true ) { continue; } - listout.push([ ...pathin, ...path, k ]); - } - } - } - return listout; - } - #expandKey(owner, k) { - if ( typeof owner !== 'object' || owner === null ) { return; } - if ( Array.isArray(k) ) { - const out = []; - for ( const a of k ) { - const iter = this.#expandKey(owner, a); - if ( iter === undefined ) { continue; } - out.push(...iter); - } - return out; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(owner) === false ) { return; } - return [ k >= 0 ? k : owner.length + k ]; - } - if ( k === '*' ) { - if ( Array.isArray(owner) ) { return owner.keys(); } - return JSONPath.keys(owner); - } - if ( k instanceof JSONPath.Regex ) { - const out = []; - for ( const key of JSONPath.keys(owner) ) { - if ( k.test(key) === false ) { continue; } - out.push(key); - } - return out; - } - return [ k ]; - } - #getMatchesFromExpr(pathin, step, owner, out) { - const recursive = step.mv === this.#DESCENDANTS; - const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); - for ( const { path } of this.#getDescendants(owner, recursive) ) { - const q = v2 ? [ ...pathin, ...path ] : pathin; - const r = this.#evaluate(step.steps, q); - if ( Boolean(r?.length) === false ) { continue; } - out.push(q); - if ( v2 === false ) { break; } - } - } - #getDescendants(v, recursive) { - const iterator = { - next() { - const n = this.stack.length; - if ( n === 0 ) { - this.value = undefined; - this.done = true; - return this; - } - const details = this.stack[n-1]; - const entry = details.keys.next(); - if ( entry.done ) { - this.stack.pop(); - this.path.pop(); - return this.next(); - } - this.path[n-1] = entry.value; - this.value = { - obj: details.obj, - key: entry.value, - path: this.path.slice(), - }; - const v = this.value.obj[this.value.key]; - if ( recursive ) { - if ( Array.isArray(v) ) { - this.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - } - return this; - }, - path: [], - value: undefined, - done: false, - stack: [], - [Symbol.iterator]() { return this; }, - }; - if ( Array.isArray(v) ) { - iterator.stack.push({ obj: v, keys: v.keys() }); - } else if ( typeof v === 'object' && v !== null ) { - iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); - } - return iterator; - } - #consumeIdentifier(query, i) { - const keys = []; - let needIdentifier = true; - while ( i < query.length ) { - const c0 = query.charCodeAt(i); - if ( c0 === 0x5D /* ] */ ) { break; } - if ( c0 === 0x20 /* SPACE */ ) { - i += 1; - continue; - } - if ( c0 === 0x2C /* , */ ) { - if ( needIdentifier ) { return; } - i += 1; - needIdentifier = true; - continue; - } - if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { - const r = this.#untilChar(query, c0, i+1); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - needIdentifier = false; - continue; - } - if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { - const match = this.#reIndice.exec(query.slice(i)); - if ( match === null ) { return; } - const indice = parseInt(query.slice(i), 10); - keys.push(indice); - i += match[0].length; - needIdentifier = false; - continue; - } - if ( this.v2 ) { return; } - const r = this.#consumeUnquotedIdentifier(query, i); - if ( r === undefined ) { return; } - keys.push(r.s); - i = r.i; - } - if ( needIdentifier ) { return; } - return { s: keys.length === 1 ? keys[0] : keys, i }; - } - #consumeUnquotedIdentifier(query, i) { - if ( query.charCodeAt(i) === 0x2F /* / */ ) { - const r = this.#untilChar(query, 0x2F, i+1); - if ( r === undefined ) { return; } - let re; - try { re = new JSONPath.Regex(r.s); } catch { return; } - return { s: re, i: r.i }; - } - const match = this.#reUnquotedIdentifier.exec(query.slice(i)); - if ( match === null ) { return; } - return { s: match[0], i: i + match[0].length }; - } - #untilChar(query, targetCharCode, i) { - const len = query.length; - const parts = []; - let beg = i, end = i; - for (;;) { - if ( end === len ) { return; } - const c = query.charCodeAt(end); - if ( c === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - break; - } - if ( c === 0x5C /* \ */ && (end+1) < len ) { - const d = query.charCodeAt(end+1); - if ( d === targetCharCode ) { - parts.push(query.slice(beg, end)); - end += 1; - beg = end; - } - } - end += 1; - } - return { s: parts.join(''), i: end }; - } - #compileExpr(query, step, i) { - if ( query.startsWith('=/', i) ) { - const r = this.#untilChar(query, 0x2F /* / */, i+2); - if ( r === undefined ) { return i; } - const match = /^[i]/.exec(query.slice(r.i)); - try { - step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); - } catch { return; } - step.op = 're'; - if ( match ) { r.i += match[0].length; } - return r.i; - } - const match = this.#reExpr.exec(query.slice(i)); - if ( match === null ) { return; } - const op = match[1], rval = match[2]; - if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { - const r = this.#untilChar(rval, 0x27, 1); - if ( r === undefined ) { return; } - step.rval = r.s; - step.op = op; - } else { - try { - step.rval = JSON.parse(rval); - step.op = op; - } catch { return; } - } - return i + match[0].length - 1; - } - #resolvePath(path) { - if ( path.length === 0 ) { return { value: this.#root }; } - const key = path.at(-1); - let obj = this.#root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; - if ( obj instanceof Object === false ) { return {}; } - } - return { obj, key, value: obj[key] }; - } - #evaluateExpr(step, owner, k) { - if ( owner === undefined || owner === null ) { return; } - const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); - if ( step.op !== undefined && hasOwn === false ) { return; } - const target = step.not !== true; - const v = owner[k]; - switch ( step.op ) { - case '==': return (v === step.rval) === target; - case '!=': return (v !== step.rval) === target; - case '<': return (v < step.rval) === target; - case '<=': return (v <= step.rval) === target; - case '>': return (v > step.rval) === target; - case '>=': return (v >= step.rval) === target; - case '^=': return `${v}`.startsWith(step.rval) === target; - case '$=': return `${v}`.endsWith(step.rval) === target; - case '*=': return `${v}`.includes(step.rval) === target; - case 're': return step.rval.test(`${v}`); - default: break; - } - return hasOwn === target; - } - #modifyVal(obj, key) { - let { modify, rval } = this.#compiled; - if ( typeof rval === 'string' ) { - rval = rval.replace('${now}', `${Date.now()}`); - } - switch ( modify ) { - case undefined: - obj[key] = rval; - break; - case '+': { - if ( rval instanceof Object === false ) { return; } - const lval = obj[key]; - if ( lval instanceof Object === false ) { return; } - if ( Array.isArray(lval) ) { return; } - for ( const [ k, v ] of JSONPath.entries(rval) ) { - lval[k] = v; - } - break; - } - case 'call': { - const entries = rval.slice(); - if ( entries.length < 2 ) { break; } - entries.forEach((a, i, aa) => { - if ( a === '${obj}' ) { aa[i] = obj; } - else if ( a === '${key}' ) { aa[i] = key; } - else if ( a === '${val}' ) { aa[i] = obj[key]; } - }); - const instance = entries[0] ?? self; - instance[entries[1]](...entries.slice(2)); - break; - } - case 'repl': { - const lval = obj[key]; - if ( typeof lval !== 'string' ) { return; } - if ( this.#compiled.re === undefined ) { - this.#compiled.re = null; - try { - this.#compiled.re = rval.regex !== undefined - ? new JSONPath.Regex(rval.regex, rval.flags) - : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); - } catch { } - } - if ( this.#compiled.re === null ) { return; } - obj[key] = lval.replace(this.#compiled.re, rval.replacement); - break; - } - default: - break; - } - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonlEditFetchResponseFn(trusted, jsonq = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const jsonExtract = extraArgs.jsonExtract - ? new RegExp(extraArgs.jsonExtract) - : /^(.*)$/; - const logall = jsonq === ''; - proxyApplyFn('fetch', function(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(textBefore => { - if ( typeof textBefore !== 'string' ) { return textBefore; } - if ( logall ) { - safe.uboLog(logPrefix, textBefore); - return responseBefore; - } - const textAfter = jsonlEditFn(jsonp, textBefore, jsonExtract); - if ( textAfter === textBefore ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }); -} -function trustedJsonlEditFetchResponse(jsonq = '', ...args) { - jsonlEditFetchResponseFn(true, jsonq, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedJsonlEditFetchResponse(...args); -}, -}; - - -scriptlets['abort-on-stack-trace.js'] = { -aliases: ["aost.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function abortOnStackTrace( - chain = '', - needle = '', - ...varargs -) { - if ( typeof chain !== 'string' ) { return; } - const safe = safeSelf(); - const needleDetails = safe.initPattern(needle, { canNegate: true }); - const extraArgs = safe.parseVarargs(varargs); - if ( needle === '' ) { extraArgs.log = 'all'; } - const makeProxy = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - let v = owner[chain]; - Object.defineProperty(owner, chain, { - get: function() { - const log = safe.logLevel > 1 ? 'all' : 'match'; - if ( matchesStackTraceFn(needleDetails, log) ) { - throw new ReferenceError(getExceptionTokenFn()); - } - return v; - }, - set: function(a) { - const log = safe.logLevel > 1 ? 'all' : 'match'; - if ( matchesStackTraceFn(needleDetails, log) ) { - throw new ReferenceError(getExceptionTokenFn()); - } - v = a; - }, - }); - return; - } - const prop = chain.slice(0, pos); - let v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v ) { - makeProxy(v, chain); - return; - } - const desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.set !== undefined ) { return; } - Object.defineProperty(owner, prop, { - get: function() { return v; }, - set: function(a) { - v = a; - if ( a instanceof Object ) { - makeProxy(a, chain); - } - } - }); - }; - const owner = window; - makeProxy(owner, chain); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -abortOnStackTrace(...args); -}, -}; - - -scriptlets['trusted-prune-inbound-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function trustedPruneInboundObject( - entryPoint = '', - argPos = '', - rawPrunePaths = '', - rawNeedlePaths = '', - ...varargs -) { - if ( entryPoint === '' ) { return; } - let context = globalThis; - let prop = entryPoint; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - if ( typeof context[prop] !== 'function' ) { return; } - const argIndex = parseInt(argPos); - if ( isNaN(argIndex) ) { return; } - if ( argIndex < 1 ) { return; } - const safe = safeSelf(); - const extraArgs = safe.parseVarargs(varargs); - const needlePaths = []; - if ( rawPrunePaths !== '' ) { - needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/)); - } - if ( rawNeedlePaths !== '' ) { - needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/)); - } - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - const mustProcess = root => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - context[prop] = new Proxy(context[prop], { - apply: function(target, thisArg, args) { - const targetArg = argIndex <= args.length - ? args[argIndex-1] - : undefined; - if ( targetArg instanceof Object && mustProcess(targetArg) ) { - let objBefore = targetArg; - if ( extraArgs.dontOverwrite ) { - try { - objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg)); - } catch { - objBefore = undefined; - } - } - if ( objBefore !== undefined ) { - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - args[argIndex-1] = objAfter || objBefore; - } - } - return Reflect.apply(target, thisArg, args); - }, - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedPruneInboundObject(...args); -}, -}; - - -scriptlets['trusted-prune-outbound-object.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function trustedPruneOutboundObject( - propChain = '', - rawPrunePaths = '', - rawNeedlePaths = '', - ...varargs -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const extraArgs = safe.parseVarargs(varargs); - proxyApplyFn(propChain, function(context) { - const objBefore = context.reflect(); - if ( objBefore instanceof Object === false ) { return objBefore; } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - { matchAll: true }, - extraArgs - ); - return objAfter || objBefore; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedPruneOutboundObject(...args); -}, -}; - - -scriptlets['json-prune.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function jsonPrune( - rawPrunePaths = '', - rawNeedlePaths = '', - stackNeedle = '', - ...varargs -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune', rawPrunePaths, rawNeedlePaths, stackNeedle); - const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true }); - const extraArgs = safe.parseVarargs(varargs); - proxyApplyFn('JSON.parse', function(context) { - const objBefore = context.reflect(); - if ( rawPrunePaths === '' ) { - safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails, - extraArgs - ); - if ( objAfter === undefined ) { return objBefore; } - safe.uboLog(logPrefix, 'Pruned'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After pruning:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonPrune(...args); -}, -}; - - -scriptlets['json-prune-fetch-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function jsonPruneFetchResponse( - rawPrunePaths = '', - rawNeedlePaths = '', - ...varargs -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune-fetch-response', rawPrunePaths, rawNeedlePaths); - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - const logall = rawPrunePaths === ''; - const applyHandler = function(target, thisArg, args) { - const fetchPromise = Reflect.apply(target, thisArg, args); - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.json().then(objBefore => { - if ( typeof objBefore !== 'object' ) { return responseBefore; } - if ( logall ) { - safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); - return responseBefore; - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - if ( typeof objAfter !== 'object' ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = Response.json(objAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }; - self.fetch = new Proxy(self.fetch, { - apply: applyHandler - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonPruneFetchResponse(...args); -}, -}; - - -scriptlets['json-prune-xhr-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function jsonPruneXhrResponse( - rawPrunePaths = '', - rawNeedlePaths = '', - ...varargs -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune-xhr-response', rawPrunePaths, rawNeedlePaths); - const xhrInstances = new WeakMap(); - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - let outcome = 'match'; - if ( propNeedles.size !== 0 ) { - if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { - outcome = 'nomatch'; - } - } - if ( outcome === 'match' ) { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched optional "propsToMatch", "${extraArgs.propsToMatch}"`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - let objBefore; - if ( typeof innerResponse === 'object' ) { - objBefore = innerResponse; - } else if ( typeof innerResponse === 'string' ) { - try { - objBefore = safe.JSON_parse(innerResponse); - } catch { - } - } - if ( typeof objBefore !== 'object' ) { - return (xhrDetails.response = innerResponse); - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - let outerResponse; - if ( typeof objAfter === 'object' ) { - outerResponse = typeof innerResponse === 'string' - ? safe.JSON_stringify(objAfter) - : objAfter; - safe.uboLog(logPrefix, 'Pruned'); - } else { - outerResponse = innerResponse; - } - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -jsonPruneXhrResponse(...args); -}, -}; - - -scriptlets['evaldata-prune.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function objectFindOwnerFn( - root, - path, - prune = false -) { - const safe = safeSelf(); - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return safe.Object_hasOwn(owner, chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( safe.Object_hasOwn(owner, key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( safe.Object_hasOwn(owner, chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} -function evaldataPrune( - rawPrunePaths = '', - rawNeedlePaths = '' -) { - proxyApplyFn('eval', function(context) { - const before = context.reflect(); - if ( typeof before !== 'object' ) { return before; } - if ( before === null ) { return null; } - const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); - return after || before; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -evaldataPrune(...args); -}, -}; - - -scriptlets['noeval-if.js'] = { -aliases: ["prevent-eval-if.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function noEvalIf( - needle = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('noeval-if', needle); - const reNeedle = safe.patternToRegex(needle); - proxyApplyFn('eval', function(context) { - const { callArgs } = context; - const a = String(callArgs[0]); - if ( needle !== '' && reNeedle.test(a) ) { - safe.uboLog(logPrefix, 'Prevented:\n', a); - return; - } - if ( needle === '' || safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Not prevented:\n', a); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -noEvalIf(...args); -}, -}; - - -scriptlets['prevent-addEventListener.js'] = { -aliases: ["addEventListener-defuser.js","aeld.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventAddEventListener( - type = '', - pattern = '', - ...varargs -) { - const safe = safeSelf(); - const extraArgs = safe.parseVarargs(varargs); - const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); - const reType = safe.patternToRegex(type, undefined, true); - const rePattern = safe.patternToRegex(pattern); - const targetSelector = extraArgs.elements || undefined; - const elementMatches = elem => { - if ( targetSelector === 'window' ) { return elem === window; } - if ( targetSelector === 'document' ) { return elem === document; } - if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } - const elems = Array.from(document.querySelectorAll(targetSelector)); - return elems.includes(elem); - }; - const elementDetails = elem => { - if ( elem instanceof Window ) { return 'window'; } - if ( elem instanceof Document ) { return 'document'; } - if ( elem instanceof Element === false ) { return '?'; } - const parts = []; - // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 - const id = String(elem.id); - if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); } - for ( let i = 0; i < elem.classList.length; i++ ) { - parts.push(`.${CSS.escape(elem.classList.item(i))}`); - } - for ( let i = 0; i < elem.attributes.length; i++ ) { - const attr = elem.attributes.item(i); - if ( attr.name === 'id' ) { continue; } - if ( attr.name === 'class' ) { continue; } - parts.push(`[${CSS.escape(attr.name)}="${attr.value}"]`); - } - return parts.join(''); - }; - const shouldPrevent = (thisArg, type, handler) => { - const matchesType = safe.RegExp_test(reType, type); - const matchesHandler = safe.RegExp_test(rePattern, handler); - const matchesEither = matchesType || matchesHandler; - const matchesBoth = matchesType && matchesHandler; - if ( safe.logLevel > 1 && matchesEither ) { - debugger; // eslint-disable-line no-debugger - } - if ( matchesBoth && targetSelector !== undefined ) { - if ( elementMatches(thisArg) === false ) { return false; } - } - return matchesBoth; - }; - const proxyFn = function(context) { - const { callArgs, thisArg } = context; - let t, h; - try { - t = String(callArgs[0]); - if ( typeof callArgs[1] === 'function' ) { - h = String(safe.Function_toString(callArgs[1])); - } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { - if ( typeof callArgs[1].handleEvent === 'function' ) { - h = String(safe.Function_toString(callArgs[1].handleEvent)); - } - } else { - h = String(callArgs[1]); - } - } catch { - } - if ( type === '' && pattern === '' ) { - safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); - } else if ( shouldPrevent(thisArg, t, h) ) { - return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); - } - return context.reflect(); - }; - const protect = owner => { - const { addEventListener } = owner; - Object.defineProperty(owner, 'addEventListener', { - set() { }, - get() { return addEventListener; } - }); - }; - runAt(( ) => { - proxyApplyFn('EventTarget.prototype.addEventListener', proxyFn); - if ( extraArgs.protect ) { protect(EventTarget.prototype); } - if ( Object.hasOwn(document, 'addEventListener') ) { - proxyApplyFn('document.addEventListener', proxyFn); - if ( extraArgs.protect ) { protect(document); } - } - if ( Object.hasOwn(window, 'addEventListener') ) { - proxyApplyFn('window.addEventListener', proxyFn); - if ( extraArgs.protect ) { protect(window); } - } - }, extraArgs.runAt); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventAddEventListener(...args); -}, -}; - - -scriptlets['prevent-bab.js'] = { -aliases: ["bab-defuser.js","nobab.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventBab() { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-bab'); - const signatures = [ - [ 'blockadblock' ], - [ 'babasbm' ], - [ /getItem\('babn'\)/ ], - [ - 'getElementById', - 'String.fromCharCode', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'charAt', - 'DOMContentLoaded', - 'AdBlock', - 'addEventListener', - 'doScroll', - 'fromCharCode', - '<<2|r>>4', - 'sessionStorage', - 'clientWidth', - 'localStorage', - 'Math', - 'random', - ], - ]; - const check = function(s) { - if ( typeof s !== 'string' ) { return false; } - for ( const tokens of signatures ) { - let match = 0; - for ( const token of tokens ) { - const hit = token instanceof RegExp - ? token.test(s) - : s.includes(token); - if ( hit ) { match += 1; } - } - if ( (match / tokens.length) >= 0.8 ) { return true; } - } - return false; - }; - proxyApplyFn('eval', function(context) { - const a = context.callArgs[0]; - if ( !check(a) ) { - return context.reflect(); - } - safe.uboLog(logPrefix, 'Prevented'); - if ( document.body ) { - document.body.style.removeProperty('visibility'); - } - const el = document.getElementById('babasbmsgx'); - if ( el ) { - el.parentNode.removeChild(el); - } - }); - proxyApplyFn('setTimeout', function(context) { - const { callArgs } = context; - const a = callArgs[0]; - if ( typeof a === 'string' && /\.bab_elementid.$/.test(a) ) { - callArgs[0] = ( ) => { }; - safe.uboLog(logPrefix, 'Prevented'); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventBab(...args); -}, -}; - - -scriptlets['prevent-clipboard-write.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventClipboardWrite(matches = '', ...varargs) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-clipboard-write'); - const pattern = safe.initPattern(matches); - const extraArgs = safe.parseVarargs(varargs); - const excludePattern = extraArgs.excludeMatches && - safe.initPattern(extraArgs.excludeMatches); - const htmlTemplate = [ - '
', - '${warning}\n', - '', - '
', - ].join(''); - const domAlert = clipboardText => { - const doc = document; - const domAlert = extraArgs.domAlert.replace(/\\n/g, '\n'); - let html; - if ( domAlert.includes('${text}') ) { - const code = doc.createElement('code'); - const styles = [ - 'background-color: #ddc', - 'display: inline-block', - 'font-family: monospace', - 'font-size: 100%', - 'max-height: 8em', - 'overflow: auto', - 'padding: 0.25em', - 'user-select: all', - 'width: 100%;', - 'word-break: break-all' - ]; - if ( Boolean(extraArgs.selectable ?? true) === false ) { - styles.push('user-select: none'); - } - code.style = styles.join(';'); - code.textContent = clipboardText; - html = htmlTemplate.replace('${warning}', - domAlert.replace('${text}', code.outerHTML) - ); - } else { - html = htmlTemplate.replace('${warning}', domAlert); - } - if ( currentAlert ) { currentAlert.remove(); } - const domParser = new DOMParser(); - const fragment = domParser.parseFromString(html, 'text/html'); - currentAlert = fragment.querySelector('div'); - const button = currentAlert.querySelector('button'); - button.addEventListener('click', ( ) => { - if ( currentAlert === null ) { return; } - currentAlert.remove(); - currentAlert = null; - }); - currentAlert.setAttribute('popover', 'manual'); - doc.documentElement.append(currentAlert); - if ( typeof currentAlert.showPopover === 'function' ) { - currentAlert.showPopover(); - } - }; - let currentAlert = null; - const prevent = text => { - if ( typeof text !== 'string' ) { return; } - text = text.trim(); - if ( safe.testPattern(pattern, text) !== true ) { return; } - if ( extraArgs.excludeMatches ) { - if ( safe.testPattern(excludePattern, text) ) { return; } - } - if ( extraArgs.domAlert ) { - domAlert(text); - } - safe.uboLog(logPrefix, 'Prevented:\n\t', text); - return true; - }; - const installTraps = ( ) => { - proxyApplyFn('navigator.clipboard.writeText', async function(context) { - const text = `${context.callArgs[0]}`; - if ( prevent(text) ) { return; } - return context.reflect(); - }, { skipToString: true }); - proxyApplyFn('document.execCommand', function(context) { - const { callArgs } = context; - if ( callArgs[0] === 'copy' || callArgs[0] === 'cut' ) { - const text = document.getSelection()?.toString(); - if ( prevent(text) ) { return true; } - } - return context.reflect(); - }, { skipToString: true }); - }; - runAt(( ) => { - self.document.addEventListener('mousemove', installTraps, { - once: true, - capture: true, - }); - }, 'interactive') -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventClipboardWrite(...args); -}, -}; - - -scriptlets['prevent-dialog.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function preventDialog( - selector = '', -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-dialog', selector); - const prevent = ( ) => { - debouncer = undefined; - const elems = document.querySelectorAll(`dialog${selector}`); - for ( const elem of elems ) { - if ( typeof elem.close !== 'function' ) { continue; } - if ( elem.open === false ) { continue; } - elem.close(); - safe.uboLog(logPrefix, 'Closed'); - } - }; - let debouncer; - const observer = new MutationObserver(( ) => { - if ( debouncer !== undefined ) { return; } - debouncer = requestAnimationFrame(prevent); - }); - observer.observe(document, { - attributes: true, - childList: true, - subtree: true, - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventDialog(...args); -}, -}; - - -scriptlets['prevent-fetch.js'] = { -aliases: ["no-fetch-if.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function generateContentFn(trusted, directive) { - const safe = safeSelf(); - const randomize = len => { - const chunks = []; - let textSize = 0; - do { - const s = safe.Math_random().toString(36).slice(2); - chunks.push(s); - textSize += s.length; - } - while ( textSize < len ); - return chunks.join(' ').slice(0, len); - }; - if ( directive === 'true' ) { - return randomize(10); - } - if ( directive === 'emptyObj' ) { - return '{}'; - } - if ( directive === 'emptyArr' ) { - return '[]'; - } - if ( directive === 'emptyStr' ) { - return ''; - } - if ( directive.startsWith('length:') ) { - const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match === null ) { return ''; } - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return randomize(len | 0); - } - if ( directive.startsWith('war:') ) { - if ( scriptletGlobals.warOrigin === undefined ) { return ''; } - return new Promise(resolve => { - const warOrigin = scriptletGlobals.warOrigin; - const warName = directive.slice(4); - const fullpath = [ warOrigin, '/', warName ]; - const warSecret = scriptletGlobals.warSecret; - if ( warSecret !== undefined ) { - fullpath.push('?secret=', warSecret); - } - const warXHR = new safe.XMLHttpRequest(); - warXHR.responseType = 'text'; - warXHR.onloadend = ev => { - resolve(ev.target.responseText || ''); - }; - warXHR.open('GET', fullpath.join('')); - warXHR.send(); - }).catch(( ) => ''); - } - if ( directive.startsWith('join:') ) { - const parts = directive.slice(7) - .split(directive.slice(5, 7)) - .map(a => generateContentFn(trusted, a)); - return parts.some(a => a instanceof Promise) - ? Promise.all(parts).then(parts => parts.join('')) - : parts.join(''); - } - if ( trusted ) { - return directive; - } - return ''; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function preventFetchFn( - trusted = false, - propsToMatch = '', - responseBody = '', - responseType = '', - ...varargs -) { - const safe = safeSelf(); - const setTimeout = self.setTimeout; - const scriptletName = `${trusted ? 'trusted-' : ''}prevent-fetch`; - const logPrefix = safe.makeLogPrefix( - scriptletName, - propsToMatch, - responseBody, - responseType - ); - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const validResponseProps = { - ok: [ false, true ], - status: [ 403 ], - statusText: [ '', 'Not Found' ], - type: [ 'basic', 'cors', 'default', 'error', 'opaque' ], - }; - const responseProps = { - statusText: { value: 'OK' }, - }; - const responseHeaders = {}; - if ( /^\{.*\}$/.test(responseType) ) { - try { - Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => { - if ( p === 'headers' && trusted ) { - Object.assign(responseHeaders, v); - return; - } - if ( validResponseProps[p] === undefined ) { return; } - if ( validResponseProps[p].includes(v) === false ) { return; } - responseProps[p] = { value: v }; - }); - } - catch { } - } else if ( responseType !== '' ) { - if ( validResponseProps.type.includes(responseType) ) { - responseProps.type = { value: responseType }; - } - } - proxyApplyFn('fetch', function fetch(context) { - const { callArgs } = context; - const details = collateFetchArgumentsFn(...callArgs); - if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(Object.entries(details)).map(a => `${a[0]}:${a[1]}`); - safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); - } - if ( propsToMatch === '' && responseBody === '' ) { - return context.reflect(); - } - const matched = matchObjectPropertiesFn(propNeedles, details); - if ( matched === undefined || matched.length === 0 ) { - return context.reflect(); - } - return Promise.resolve(generateContentFn(trusted, responseBody)).then(text => { - safe.uboLog(logPrefix, `Prevented with response "${text}"`); - const headers = Object.assign({}, responseHeaders); - if ( headers['content-length'] === undefined ) { - headers['content-length'] = text.length; - } - const response = new Response(text, { headers }); - const props = Object.assign( - { url: { value: details.url } }, - responseProps - ); - safe.Object_defineProperties(response, props); - if ( extraArgs.throttle ) { - return new Promise(resolve => { - setTimeout(( ) => { resolve(response); }, extraArgs.throttle); - }); - } - return response; - }); - }); -} -function preventFetch(...args) { - preventFetchFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventFetch(...args); -}, -}; - - -scriptlets['trusted-prevent-fetch.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function generateContentFn(trusted, directive) { - const safe = safeSelf(); - const randomize = len => { - const chunks = []; - let textSize = 0; - do { - const s = safe.Math_random().toString(36).slice(2); - chunks.push(s); - textSize += s.length; - } - while ( textSize < len ); - return chunks.join(' ').slice(0, len); - }; - if ( directive === 'true' ) { - return randomize(10); - } - if ( directive === 'emptyObj' ) { - return '{}'; - } - if ( directive === 'emptyArr' ) { - return '[]'; - } - if ( directive === 'emptyStr' ) { - return ''; - } - if ( directive.startsWith('length:') ) { - const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match === null ) { return ''; } - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return randomize(len | 0); - } - if ( directive.startsWith('war:') ) { - if ( scriptletGlobals.warOrigin === undefined ) { return ''; } - return new Promise(resolve => { - const warOrigin = scriptletGlobals.warOrigin; - const warName = directive.slice(4); - const fullpath = [ warOrigin, '/', warName ]; - const warSecret = scriptletGlobals.warSecret; - if ( warSecret !== undefined ) { - fullpath.push('?secret=', warSecret); - } - const warXHR = new safe.XMLHttpRequest(); - warXHR.responseType = 'text'; - warXHR.onloadend = ev => { - resolve(ev.target.responseText || ''); - }; - warXHR.open('GET', fullpath.join('')); - warXHR.send(); - }).catch(( ) => ''); - } - if ( directive.startsWith('join:') ) { - const parts = directive.slice(7) - .split(directive.slice(5, 7)) - .map(a => generateContentFn(trusted, a)); - return parts.some(a => a instanceof Promise) - ? Promise.all(parts).then(parts => parts.join('')) - : parts.join(''); - } - if ( trusted ) { - return directive; - } - return ''; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function collateFetchArgumentsFn(resource, options) { - const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { - } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; -} -function preventFetchFn( - trusted = false, - propsToMatch = '', - responseBody = '', - responseType = '', - ...varargs -) { - const safe = safeSelf(); - const setTimeout = self.setTimeout; - const scriptletName = `${trusted ? 'trusted-' : ''}prevent-fetch`; - const logPrefix = safe.makeLogPrefix( - scriptletName, - propsToMatch, - responseBody, - responseType - ); - const extraArgs = safe.parseVarargs(varargs); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const validResponseProps = { - ok: [ false, true ], - status: [ 403 ], - statusText: [ '', 'Not Found' ], - type: [ 'basic', 'cors', 'default', 'error', 'opaque' ], - }; - const responseProps = { - statusText: { value: 'OK' }, - }; - const responseHeaders = {}; - if ( /^\{.*\}$/.test(responseType) ) { - try { - Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => { - if ( p === 'headers' && trusted ) { - Object.assign(responseHeaders, v); - return; - } - if ( validResponseProps[p] === undefined ) { return; } - if ( validResponseProps[p].includes(v) === false ) { return; } - responseProps[p] = { value: v }; - }); - } - catch { } - } else if ( responseType !== '' ) { - if ( validResponseProps.type.includes(responseType) ) { - responseProps.type = { value: responseType }; - } - } - proxyApplyFn('fetch', function fetch(context) { - const { callArgs } = context; - const details = collateFetchArgumentsFn(...callArgs); - if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(Object.entries(details)).map(a => `${a[0]}:${a[1]}`); - safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); - } - if ( propsToMatch === '' && responseBody === '' ) { - return context.reflect(); - } - const matched = matchObjectPropertiesFn(propNeedles, details); - if ( matched === undefined || matched.length === 0 ) { - return context.reflect(); - } - return Promise.resolve(generateContentFn(trusted, responseBody)).then(text => { - safe.uboLog(logPrefix, `Prevented with response "${text}"`); - const headers = Object.assign({}, responseHeaders); - if ( headers['content-length'] === undefined ) { - headers['content-length'] = text.length; - } - const response = new Response(text, { headers }); - const props = Object.assign( - { url: { value: details.url } }, - responseProps - ); - safe.Object_defineProperties(response, props); - if ( extraArgs.throttle ) { - return new Promise(resolve => { - setTimeout(( ) => { resolve(response); }, extraArgs.throttle); - }); - } - return response; - }); - }); -} -function trustedPreventFetch(...args) { - preventFetchFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedPreventFetch(...args); -}, -}; - - -scriptlets['freeze-element-property.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function freezeElementProperty( - property = '', - selector = '', - pattern = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('freeze-element-property', property, selector, pattern); - const matcher = safe.initPattern(pattern, { canNegate: true }); - const owner = (( ) => { - if ( Object.hasOwn(HTMLScriptElement.prototype, property) ) { - return HTMLScriptElement.prototype; - } - if ( Object.hasOwn(HTMLElement.prototype, property) ) { - return HTMLElement.prototype; - } - if ( Object.hasOwn(Element.prototype, property) ) { - return Element.prototype; - } - if ( Object.hasOwn(Node.prototype, property) ) { - return Node.prototype; - } - return null; - })(); - if ( owner === null ) { return; } - const current = safe.Object_getOwnPropertyDescriptor(owner, property); - if ( current === undefined ) { return; } - const shouldPreventSet = (elem, a) => { - if ( selector !== '' ) { - if ( typeof elem.matches !== 'function' ) { return false; } - if ( elem.matches(selector) === false ) { return false; } - } - return safe.testPattern(matcher, `${a}`); - }; - Object.defineProperty(owner, property, { - get: function() { - return current.get - ? current.get.call(this) - : current.value; - }, - set: function(a) { - if ( shouldPreventSet(this, a) ) { - safe.uboLog(logPrefix, 'Assignment prevented'); - } else if ( current.set ) { - current.set.call(this, a); - } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Assigned:\n${a}`); - } - current.value = a; - }, - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -freezeElementProperty(...args); -}, -}; - - -scriptlets['prevent-innerHTML.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function freezeElementProperty( - property = '', - selector = '', - pattern = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('freeze-element-property', property, selector, pattern); - const matcher = safe.initPattern(pattern, { canNegate: true }); - const owner = (( ) => { - if ( Object.hasOwn(HTMLScriptElement.prototype, property) ) { - return HTMLScriptElement.prototype; - } - if ( Object.hasOwn(HTMLElement.prototype, property) ) { - return HTMLElement.prototype; - } - if ( Object.hasOwn(Element.prototype, property) ) { - return Element.prototype; - } - if ( Object.hasOwn(Node.prototype, property) ) { - return Node.prototype; - } - return null; - })(); - if ( owner === null ) { return; } - const current = safe.Object_getOwnPropertyDescriptor(owner, property); - if ( current === undefined ) { return; } - const shouldPreventSet = (elem, a) => { - if ( selector !== '' ) { - if ( typeof elem.matches !== 'function' ) { return false; } - if ( elem.matches(selector) === false ) { return false; } - } - return safe.testPattern(matcher, `${a}`); - }; - Object.defineProperty(owner, property, { - get: function() { - return current.get - ? current.get.call(this) - : current.value; - }, - set: function(a) { - if ( shouldPreventSet(this, a) ) { - safe.uboLog(logPrefix, 'Assignment prevented'); - } else if ( current.set ) { - current.set.call(this, a); - } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Assigned:\n${a}`); - } - current.value = a; - }, - }); -} -function preventInnerHTML( - selector = '', - pattern = '' -) { - freezeElementProperty('innerHTML', selector, pattern); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventInnerHTML(...args); -}, -}; - - -scriptlets['prevent-navigation.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function preventNavigation( - pattern = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-navigation', pattern); - const needle = pattern === 'location.href' ? self.location.href : pattern; - const matcher = safe.initPattern(needle, { canNegate: true }); - self.navigation.addEventListener('navigate', ev => { - if ( ev.userInitiated ) { return; } - const { url } = ev.destination; - if ( pattern === '' ) { - safe.uboLog(logPrefix, `Navigation to ${url}`); - return; - } - if ( safe.testPattern(matcher, url) ) { - ev.preventDefault(); - safe.uboLog(logPrefix, `Prevented navigation to ${url}`); - } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventNavigation(...args); -}, -}; - - -scriptlets['prevent-setTimeout.js'] = { -aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class RangeParser { - constructor(s) { - this.not = s.charAt(0) === '!'; - if ( this.not ) { s = s.slice(1); } - if ( s === '' ) { return; } - const pos = s.indexOf('-'); - if ( pos !== 0 ) { - this.min = this.max = parseInt(s, 10) || 0; - } - if ( pos !== -1 ) { - this.max = parseInt(s.slice(pos + 1), 10) || Number.MAX_SAFE_INTEGER; - } - } - unbound() { - return this.min === undefined && this.max === undefined; - } - test(v) { - const n = Math.min(Math.max(Number(v) || 0, 0), Number.MAX_SAFE_INTEGER); - if ( this.min === this.max ) { - return (this.min === undefined || n === this.min) !== this.not; - } - if ( this.min === undefined ) { - return (n <= this.max) !== this.not; - } - if ( this.max === undefined ) { - return (n >= this.min) !== this.not; - } - return (n >= this.min && n <= this.max) !== this.not; - } -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventSetTimeout( - needleRaw = '', - delayRaw = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needleRaw, delayRaw); - const needleNot = needleRaw.charAt(0) === '!'; - const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); - const range = new RangeParser(delayRaw); - proxyApplyFn('setTimeout', function(context) { - const { callArgs } = context; - const a = callArgs[0] instanceof Function - ? safe.String(safe.Function_toString(callArgs[0])) - : safe.String(callArgs[0]); - const b = callArgs[1]; - if ( needleRaw === '' && range.unbound() ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return context.reflect(); - } - if ( reNeedle.test(a) !== needleNot && range.test(b) ) { - callArgs[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventSetTimeout(...args); -}, -}; - - -scriptlets['prevent-setInterval.js'] = { -aliases: ["no-setInterval-if.js","nosiif.js","setInterval-defuser.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -class RangeParser { - constructor(s) { - this.not = s.charAt(0) === '!'; - if ( this.not ) { s = s.slice(1); } - if ( s === '' ) { return; } - const pos = s.indexOf('-'); - if ( pos !== 0 ) { - this.min = this.max = parseInt(s, 10) || 0; - } - if ( pos !== -1 ) { - this.max = parseInt(s.slice(pos + 1), 10) || Number.MAX_SAFE_INTEGER; - } - } - unbound() { - return this.min === undefined && this.max === undefined; - } - test(v) { - const n = Math.min(Math.max(Number(v) || 0, 0), Number.MAX_SAFE_INTEGER); - if ( this.min === this.max ) { - return (this.min === undefined || n === this.min) !== this.not; - } - if ( this.min === undefined ) { - return (n <= this.max) !== this.not; - } - if ( this.max === undefined ) { - return (n >= this.min) !== this.not; - } - return (n >= this.min && n <= this.max) !== this.not; - } -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventSetInterval( - needleRaw = '', - delayRaw = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-setInterval', needleRaw, delayRaw); - const needleNot = needleRaw.charAt(0) === '!'; - const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); - const range = new RangeParser(delayRaw); - proxyApplyFn('setInterval', function(context) { - const { callArgs } = context; - const a = callArgs[0] instanceof Function - ? safe.String(safe.Function_toString(callArgs[0])) - : safe.String(callArgs[0]); - const b = callArgs[1]; - if ( needleRaw === '' && range.unbound() ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return context.reflect(); - } - if ( reNeedle.test(a) !== needleNot && range.test(b) ) { - callArgs[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventSetInterval(...args); -}, -}; - - -scriptlets['prevent-requestAnimationFrame.js'] = { -aliases: ["no-requestAnimationFrame-if.js","norafif.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function preventRequestAnimationFrame( - needleRaw = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-requestAnimationFrame', needleRaw); - const needleNot = needleRaw.charAt(0) === '!'; - const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); - proxyApplyFn('requestAnimationFrame', function(context) { - const { callArgs } = context; - const a = callArgs[0] instanceof Function - ? safe.String(safe.Function_toString(callArgs[0])) - : safe.String(callArgs[0]); - if ( needleRaw === '' ) { - safe.uboLog(logPrefix, `Called:\n${a}`); - } else if ( reNeedle.test(a) !== needleNot ) { - callArgs[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}`); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventRequestAnimationFrame(...args); -}, -}; - - -scriptlets['set-constant.js'] = { -aliases: ["set.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function validateConstantFn(trusted, raw, extraArgs = {}) { - const safe = safeSelf(); - let value; - if ( raw === 'undefined' ) { - value = undefined; - } else if ( raw === 'false' ) { - value = false; - } else if ( raw === 'true' ) { - value = true; - } else if ( raw === 'null' ) { - value = null; - } else if ( raw === "''" || raw === '' ) { - value = ''; - } else if ( raw === '[]' || raw === 'emptyArr' ) { - value = []; - } else if ( raw === '{}' || raw === 'emptyObj' ) { - value = {}; - } else if ( raw === 'noopFunc' ) { - value = function(){}; - } else if ( raw === 'trueFunc' ) { - value = function(){ return true; }; - } else if ( raw === 'falseFunc' ) { - value = function(){ return false; }; - } else if ( raw === 'throwFunc' ) { - value = function(){ throw ''; }; - } else if ( /^-?\d+$/.test(raw) ) { - value = parseInt(raw); - if ( isNaN(raw) ) { return; } - if ( Math.abs(raw) > 0x7FFF ) { return; } - } else if ( trusted ) { - if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } - } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch { return; } - } - } else { - return; - } - if ( extraArgs.as !== undefined ) { - if ( extraArgs.as === 'function' ) { - return ( ) => value; - } else if ( extraArgs.as === 'callback' ) { - return ( ) => (( ) => value); - } else if ( extraArgs.as === 'resolved' ) { - return Promise.resolve(value); - } else if ( extraArgs.as === 'rejected' ) { - return Promise.reject(value); - } - } - return value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function setConstantFn( - trusted = false, - chain = '', - rawValue = '', - ...varargs -) { - if ( chain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue); - const extraArgs = safe.parseVarargs(varargs); - function setConstant(chain, rawValue) { - const trappedProp = (( ) => { - const pos = chain.lastIndexOf('.'); - if ( pos === -1 ) { return chain; } - return chain.slice(pos+1); - })(); - const cloakFunc = fn => { - safe.Object_defineProperty(fn, 'name', { value: trappedProp }); - return new Proxy(fn, { - defineProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.defineProperty(...arguments); - } - return true; - }, - deleteProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.deleteProperty(...arguments); - } - return true; - }, - get(target, prop) { - if ( prop === 'toString' ) { - return function() { - return `function ${trappedProp}() { [native code] }`; - }.bind(null); - } - return Reflect.get(...arguments); - }, - }); - }; - if ( trappedProp === '' ) { return; } - const thisScript = document.currentScript; - let normalValue = validateConstantFn(trusted, rawValue, extraArgs); - if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { - normalValue = cloakFunc(normalValue); - } - let aborted = false; - const mustAbort = function(v) { - if ( trusted ) { return false; } - if ( aborted ) { return true; } - aborted = - (v !== undefined && v !== null) && - (normalValue !== undefined && normalValue !== null) && - (typeof v !== typeof normalValue); - if ( aborted ) { - safe.uboLog(logPrefix, `Aborted because value set to ${v}`); - } - return aborted; - }; - // https://github.com/uBlockOrigin/uBlock-issues/issues/156 - // Support multiple trappers for the same property. - const trapProp = function(owner, prop, configurable, handler) { - if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; } - const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop); - let prevGetter, prevSetter; - if ( odesc instanceof safe.Object ) { - owner[prop] = normalValue; - if ( odesc.get instanceof Function ) { - prevGetter = odesc.get; - } - if ( odesc.set instanceof Function ) { - prevSetter = odesc.set; - } - } - try { - safe.Object_defineProperty(owner, prop, { - configurable, - get() { - if ( prevGetter !== undefined ) { - prevGetter(); - } - return handler.getter(); - }, - set(a) { - if ( prevSetter !== undefined ) { - prevSetter(a); - } - handler.setter(a); - } - }); - safe.uboLog(logPrefix, 'Trap installed'); - } catch(ex) { - safe.uboErr(logPrefix, ex); - } - }; - const trapChain = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - trapProp(owner, chain, false, { - v: undefined, - init: function(v) { - if ( mustAbort(v) ) { return false; } - this.v = v; - return true; - }, - getter: function() { - if ( document.currentScript === thisScript ) { - return this.v; - } - safe.uboLog(logPrefix, 'Property read'); - return normalValue; - }, - setter: function(a) { - if ( mustAbort(a) === false ) { return; } - normalValue = a; - } - }); - return; - } - const prop = chain.slice(0, pos); - const v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) { - trapChain(v, chain); - return; - } - trapProp(owner, prop, true, { - v: undefined, - init: function(v) { - this.v = v; - return true; - }, - getter: function() { - return this.v; - }, - setter: function(a) { - this.v = a; - if ( a instanceof safe.Object ) { - trapChain(a, chain); - } - } - }); - }; - trapChain(window, chain); - } - runAt(( ) => { - setConstant(chain, rawValue); - }, extraArgs.runAt); -} -function setConstant( - ...args -) { - setConstantFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setConstant(...args); -}, -}; - - -scriptlets['trusted-set-constant.js'] = { -aliases: ["trusted-set.js"], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function validateConstantFn(trusted, raw, extraArgs = {}) { - const safe = safeSelf(); - let value; - if ( raw === 'undefined' ) { - value = undefined; - } else if ( raw === 'false' ) { - value = false; - } else if ( raw === 'true' ) { - value = true; - } else if ( raw === 'null' ) { - value = null; - } else if ( raw === "''" || raw === '' ) { - value = ''; - } else if ( raw === '[]' || raw === 'emptyArr' ) { - value = []; - } else if ( raw === '{}' || raw === 'emptyObj' ) { - value = {}; - } else if ( raw === 'noopFunc' ) { - value = function(){}; - } else if ( raw === 'trueFunc' ) { - value = function(){ return true; }; - } else if ( raw === 'falseFunc' ) { - value = function(){ return false; }; - } else if ( raw === 'throwFunc' ) { - value = function(){ throw ''; }; - } else if ( /^-?\d+$/.test(raw) ) { - value = parseInt(raw); - if ( isNaN(raw) ) { return; } - if ( Math.abs(raw) > 0x7FFF ) { return; } - } else if ( trusted ) { - if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } - } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch { return; } - } - } else { - return; - } - if ( extraArgs.as !== undefined ) { - if ( extraArgs.as === 'function' ) { - return ( ) => value; - } else if ( extraArgs.as === 'callback' ) { - return ( ) => (( ) => value); - } else if ( extraArgs.as === 'resolved' ) { - return Promise.resolve(value); - } else if ( extraArgs.as === 'rejected' ) { - return Promise.reject(value); - } - } - return value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function setConstantFn( - trusted = false, - chain = '', - rawValue = '', - ...varargs -) { - if ( chain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue); - const extraArgs = safe.parseVarargs(varargs); - function setConstant(chain, rawValue) { - const trappedProp = (( ) => { - const pos = chain.lastIndexOf('.'); - if ( pos === -1 ) { return chain; } - return chain.slice(pos+1); - })(); - const cloakFunc = fn => { - safe.Object_defineProperty(fn, 'name', { value: trappedProp }); - return new Proxy(fn, { - defineProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.defineProperty(...arguments); - } - return true; - }, - deleteProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.deleteProperty(...arguments); - } - return true; - }, - get(target, prop) { - if ( prop === 'toString' ) { - return function() { - return `function ${trappedProp}() { [native code] }`; - }.bind(null); - } - return Reflect.get(...arguments); - }, - }); - }; - if ( trappedProp === '' ) { return; } - const thisScript = document.currentScript; - let normalValue = validateConstantFn(trusted, rawValue, extraArgs); - if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { - normalValue = cloakFunc(normalValue); - } - let aborted = false; - const mustAbort = function(v) { - if ( trusted ) { return false; } - if ( aborted ) { return true; } - aborted = - (v !== undefined && v !== null) && - (normalValue !== undefined && normalValue !== null) && - (typeof v !== typeof normalValue); - if ( aborted ) { - safe.uboLog(logPrefix, `Aborted because value set to ${v}`); - } - return aborted; - }; - // https://github.com/uBlockOrigin/uBlock-issues/issues/156 - // Support multiple trappers for the same property. - const trapProp = function(owner, prop, configurable, handler) { - if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; } - const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop); - let prevGetter, prevSetter; - if ( odesc instanceof safe.Object ) { - owner[prop] = normalValue; - if ( odesc.get instanceof Function ) { - prevGetter = odesc.get; - } - if ( odesc.set instanceof Function ) { - prevSetter = odesc.set; - } - } - try { - safe.Object_defineProperty(owner, prop, { - configurable, - get() { - if ( prevGetter !== undefined ) { - prevGetter(); - } - return handler.getter(); - }, - set(a) { - if ( prevSetter !== undefined ) { - prevSetter(a); - } - handler.setter(a); - } - }); - safe.uboLog(logPrefix, 'Trap installed'); - } catch(ex) { - safe.uboErr(logPrefix, ex); - } - }; - const trapChain = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - trapProp(owner, chain, false, { - v: undefined, - init: function(v) { - if ( mustAbort(v) ) { return false; } - this.v = v; - return true; - }, - getter: function() { - if ( document.currentScript === thisScript ) { - return this.v; - } - safe.uboLog(logPrefix, 'Property read'); - return normalValue; - }, - setter: function(a) { - if ( mustAbort(a) === false ) { return; } - normalValue = a; - } - }); - return; - } - const prop = chain.slice(0, pos); - const v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) { - trapChain(v, chain); - return; - } - trapProp(owner, prop, true, { - v: undefined, - init: function(v) { - this.v = v; - return true; - }, - getter: function() { - return this.v; - }, - setter: function(a) { - this.v = a; - if ( a instanceof safe.Object ) { - trapChain(a, chain); - } - } - }); - }; - trapChain(window, chain); - } - runAt(( ) => { - setConstant(chain, rawValue); - }, extraArgs.runAt); -} -function trustedSetConstant( - ...args -) { - setConstantFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetConstant(...args); -}, -}; - - -scriptlets['trusted-replace-argument.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function validateConstantFn(trusted, raw, extraArgs = {}) { - const safe = safeSelf(); - let value; - if ( raw === 'undefined' ) { - value = undefined; - } else if ( raw === 'false' ) { - value = false; - } else if ( raw === 'true' ) { - value = true; - } else if ( raw === 'null' ) { - value = null; - } else if ( raw === "''" || raw === '' ) { - value = ''; - } else if ( raw === '[]' || raw === 'emptyArr' ) { - value = []; - } else if ( raw === '{}' || raw === 'emptyObj' ) { - value = {}; - } else if ( raw === 'noopFunc' ) { - value = function(){}; - } else if ( raw === 'trueFunc' ) { - value = function(){ return true; }; - } else if ( raw === 'falseFunc' ) { - value = function(){ return false; }; - } else if ( raw === 'throwFunc' ) { - value = function(){ throw ''; }; - } else if ( /^-?\d+$/.test(raw) ) { - value = parseInt(raw); - if ( isNaN(raw) ) { return; } - if ( Math.abs(raw) > 0x7FFF ) { return; } - } else if ( trusted ) { - if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } - } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch { return; } - } - } else { - return; - } - if ( extraArgs.as !== undefined ) { - if ( extraArgs.as === 'function' ) { - return ( ) => value; - } else if ( extraArgs.as === 'callback' ) { - return ( ) => (( ) => value); - } else if ( extraArgs.as === 'resolved' ) { - return Promise.resolve(value); - } else if ( extraArgs.as === 'rejected' ) { - return Promise.reject(value); - } - } - return value; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -class ArglistParser { - constructor(separatorChar = ',', mustQuote = false) { - this.separatorChar = this.actualSeparatorChar = separatorChar; - this.separatorCode = this.actualSeparatorCode = separatorChar.charCodeAt(0); - this.mustQuote = mustQuote; - this.quoteBeg = 0; this.quoteEnd = 0; - this.argBeg = 0; this.argEnd = 0; - this.separatorBeg = 0; this.separatorEnd = 0; - this.transform = false; - this.failed = false; - this.reWhitespaceStart = /^\s+/; - this.reWhitespaceEnd = /(?:^|\S)(\s+)$/; - this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; - this.reTrailingEscapeChars = /\\+$/; - } - nextArg(pattern, beg = 0) { - const len = pattern.length; - this.quoteBeg = beg + this.leftWhitespaceCount(pattern.slice(beg)); - this.failed = false; - const qc = pattern.charCodeAt(this.quoteBeg); - if ( qc === 0x22 /* " */ || qc === 0x27 /* ' */ || qc === 0x60 /* ` */ ) { - this.indexOfNextArgSeparator(pattern, qc); - if ( this.argEnd !== len ) { - this.quoteEnd = this.argEnd + 1; - this.separatorBeg = this.separatorEnd = this.quoteEnd; - this.separatorEnd += this.leftWhitespaceCount(pattern.slice(this.quoteEnd)); - if ( this.separatorEnd === len ) { return this; } - if ( pattern.charCodeAt(this.separatorEnd) === this.separatorCode ) { - this.separatorEnd += 1; - return this; - } - } - } - this.indexOfNextArgSeparator(pattern, this.separatorCode); - this.separatorBeg = this.separatorEnd = this.argEnd; - if ( this.separatorBeg < len ) { - this.separatorEnd += 1; - } - this.argEnd -= this.rightWhitespaceCount(pattern.slice(0, this.separatorBeg)); - this.quoteEnd = this.argEnd; - if ( this.mustQuote ) { - this.failed = true; - } - return this; - } - normalizeArg(s, char = '') { - if ( char === '' ) { char = this.actualSeparatorChar; } - let out = ''; - let pos = 0; - while ( (pos = s.lastIndexOf(char)) !== -1 ) { - out = s.slice(pos) + out; - s = s.slice(0, pos); - const match = this.reTrailingEscapeChars.exec(s); - if ( match === null ) { continue; } - const tail = (match[0].length & 1) !== 0 - ? match[0].slice(0, -1) - : match[0]; - out = tail + out; - s = s.slice(0, -match[0].length); - } - if ( out === '' ) { return s; } - return s + out; - } - leftWhitespaceCount(s) { - const match = this.reWhitespaceStart.exec(s); - return match === null ? 0 : match[0].length; - } - rightWhitespaceCount(s) { - const match = this.reWhitespaceEnd.exec(s); - return match === null ? 0 : match[1].length; - } - indexOfNextArgSeparator(pattern, separatorCode) { - this.argBeg = this.argEnd = separatorCode !== this.separatorCode - ? this.quoteBeg + 1 - : this.quoteBeg; - this.transform = false; - if ( separatorCode !== this.actualSeparatorCode ) { - this.actualSeparatorCode = separatorCode; - this.actualSeparatorChar = String.fromCharCode(separatorCode); - } - while ( this.argEnd < pattern.length ) { - const pos = pattern.indexOf(this.actualSeparatorChar, this.argEnd); - if ( pos === -1 ) { - return (this.argEnd = pattern.length); - } - if ( this.reOddTrailingEscape.test(pattern.slice(0, pos)) === false ) { - return (this.argEnd = pos); - } - this.transform = true; - this.argEnd = pos + 1; - } - } -} -function parseReplaceFn(s) { - if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } - const parser = new ArglistParser('/'); - parser.nextArg(s, 1); - let pattern = s.slice(parser.argBeg, parser.argEnd); - if ( parser.transform ) { - pattern = parser.normalizeArg(pattern); - } - if ( pattern === '' ) { return; } - parser.nextArg(s, parser.separatorEnd); - let replacement = s.slice(parser.argBeg, parser.argEnd); - if ( parser.separatorEnd === parser.separatorBeg ) { return; } - if ( parser.transform ) { - replacement = parser.normalizeArg(replacement); - } - const flags = s.slice(parser.separatorEnd); - try { - return { re: new RegExp(pattern, flags), replacement }; - } catch { - } -} -function trustedReplaceArgument( - propChain = '', - argposRaw = '', - argraw = '', - ...varargs -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); - const argoffset = parseInt(argposRaw, 10) || 0; - const extraArgs = safe.parseVarargs(varargs); - let replacer; - if ( argraw.startsWith('repl:/') ) { - const parsed = parseReplaceFn(argraw.slice(5)); - if ( parsed === undefined ) { return; } - replacer = arg => `${arg}`.replace(replacer.re, replacer.replacement); - Object.assign(replacer, parsed); - } else if ( argraw.startsWith('add:') ) { - const delta = parseFloat(argraw.slice(4)); - if ( isNaN(delta) ) { return; } - replacer = arg => Number(arg) + delta; - } else { - const value = validateConstantFn(true, argraw, extraArgs); - replacer = ( ) => value; - } - const reCondition = extraArgs.condition - ? safe.patternToRegex(`${extraArgs.condition}`) - : /^/; - const getArg = context => { - if ( argposRaw === 'this' ) { return context.thisArg; } - const { callArgs } = context; - const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; - if ( argpos < 0 || argpos >= callArgs.length ) { return; } - context.private = { argpos }; - return callArgs[argpos]; - }; - const setArg = (context, value) => { - if ( argposRaw === 'this' ) { - if ( value !== context.thisArg ) { - context.thisArg = value; - } - } else if ( context.private ) { - context.callArgs[context.private.argpos] = value; - } - }; - proxyApplyFn(propChain, function(context) { - if ( argposRaw === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${context.callArgs.join('\n')}`); - return context.reflect(); - } - const argBefore = getArg(context); - if ( extraArgs.condition !== undefined ) { - if ( safe.RegExp_test(reCondition, argBefore) === false ) { - return context.reflect(); - } - } - const argAfter = replacer(argBefore); - if ( argAfter !== argBefore ) { - setArg(context, argAfter); - safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`); - } - return context.reflect(); - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedReplaceArgument(...args); -}, -}; - - -scriptlets['spoof-css.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function spoofCSS( - selector, - ...args -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase()); - const propToValueMap = new Map(); - const privatePropToValueMap = new Map(); - for ( let i = 0; i < args.length; i += 2 ) { - const prop = toCamelCase(args[i+0]); - if ( prop === '' ) { break; } - const value = args[i+1]; - if ( typeof value !== 'string' ) { break; } - if ( prop.charCodeAt(0) === 0x5F /* _ */ ) { - privatePropToValueMap.set(prop, value); - } else { - propToValueMap.set(prop, value); - } - } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('spoof-css', selector, ...args); - const instanceProperties = [ 'cssText', 'length', 'parentRule' ]; - const spoofStyle = (prop, real) => { - const normalProp = toCamelCase(prop); - const shouldSpoof = propToValueMap.has(normalProp); - const value = shouldSpoof ? propToValueMap.get(normalProp) : real; - if ( shouldSpoof ) { - safe.uboLog(logPrefix, `Spoofing ${prop} to ${value}`); - } - return value; - }; - const cloackFunc = (fn, thisArg, name) => { - const trap = fn.bind(thisArg); - Object.defineProperty(trap, 'name', { value: name }); - Object.defineProperty(trap, 'toString', { - value: ( ) => `function ${name}() { [native code] }` - }); - return trap; - }; - self.getComputedStyle = new Proxy(self.getComputedStyle, { - apply: function(target, thisArg, args) { - // eslint-disable-next-line no-debugger - if ( privatePropToValueMap.has('_debug') ) { debugger; } - const style = Reflect.apply(target, thisArg, args); - const targetElements = new WeakSet(document.querySelectorAll(selector)); - if ( targetElements.has(args[0]) === false ) { return style; } - const proxiedStyle = new Proxy(style, { - get(target, prop) { - if ( typeof target[prop] === 'function' ) { - if ( prop === 'getPropertyValue' ) { - return cloackFunc(function getPropertyValue(prop) { - return spoofStyle(prop, target[prop]); - }, target, 'getPropertyValue'); - } - return cloackFunc(target[prop], target, prop); - } - if ( instanceProperties.includes(prop) ) { - return Reflect.get(target, prop); - } - return spoofStyle(prop, Reflect.get(target, prop)); - }, - getOwnPropertyDescriptor(target, prop) { - if ( propToValueMap.has(prop) ) { - return { - configurable: true, - enumerable: true, - value: propToValueMap.get(prop), - writable: true, - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); - }, - }); - return proxiedStyle; - }, - get(target, prop) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop); - }, - }); - Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, { - apply: function(target, thisArg, args) { - // eslint-disable-next-line no-debugger - if ( privatePropToValueMap.has('_debug') ) { debugger; } - const rect = Reflect.apply(target, thisArg, args); - const targetElements = new WeakSet(document.querySelectorAll(selector)); - if ( targetElements.has(thisArg) === false ) { return rect; } - let { x, y, height, width } = rect; - if ( privatePropToValueMap.has('_rectx') ) { - x = parseFloat(privatePropToValueMap.get('_rectx')); - } - if ( privatePropToValueMap.has('_recty') ) { - y = parseFloat(privatePropToValueMap.get('_recty')); - } - if ( privatePropToValueMap.has('_rectw') ) { - width = parseFloat(privatePropToValueMap.get('_rectw')); - } else if ( propToValueMap.has('width') ) { - width = parseFloat(propToValueMap.get('width')); - } - if ( privatePropToValueMap.has('_recth') ) { - height = parseFloat(privatePropToValueMap.get('_recth')); - } else if ( propToValueMap.has('height') ) { - height = parseFloat(propToValueMap.get('height')); - } - return new self.DOMRect(x, y, width, height); - }, - get(target, prop) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop); - }, - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -spoofCSS(...args); -}, -}; - - -scriptlets['prevent-xhr.js'] = { -aliases: ["no-xhr-if.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function generateContentFn(trusted, directive) { - const safe = safeSelf(); - const randomize = len => { - const chunks = []; - let textSize = 0; - do { - const s = safe.Math_random().toString(36).slice(2); - chunks.push(s); - textSize += s.length; - } - while ( textSize < len ); - return chunks.join(' ').slice(0, len); - }; - if ( directive === 'true' ) { - return randomize(10); - } - if ( directive === 'emptyObj' ) { - return '{}'; - } - if ( directive === 'emptyArr' ) { - return '[]'; - } - if ( directive === 'emptyStr' ) { - return ''; - } - if ( directive.startsWith('length:') ) { - const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match === null ) { return ''; } - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return randomize(len | 0); - } - if ( directive.startsWith('war:') ) { - if ( scriptletGlobals.warOrigin === undefined ) { return ''; } - return new Promise(resolve => { - const warOrigin = scriptletGlobals.warOrigin; - const warName = directive.slice(4); - const fullpath = [ warOrigin, '/', warName ]; - const warSecret = scriptletGlobals.warSecret; - if ( warSecret !== undefined ) { - fullpath.push('?secret=', warSecret); - } - const warXHR = new safe.XMLHttpRequest(); - warXHR.responseType = 'text'; - warXHR.onloadend = ev => { - resolve(ev.target.responseText || ''); - }; - warXHR.open('GET', fullpath.join('')); - warXHR.send(); - }).catch(( ) => ''); - } - if ( directive.startsWith('join:') ) { - const parts = directive.slice(7) - .split(directive.slice(5, 7)) - .map(a => generateContentFn(trusted, a)); - return parts.some(a => a instanceof Promise) - ? Promise.all(parts).then(parts => parts.join('')) - : parts.join(''); - } - if ( trusted ) { - return directive; - } - return ''; -} -function preventXhrFn( - trusted = false, - propsToMatch = '', - directive = '' -) { - if ( typeof propsToMatch !== 'string' ) { return; } - const safe = safeSelf(); - const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr'; - const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive); - const xhrInstances = new WeakMap(); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const warOrigin = scriptletGlobals.warOrigin; - const safeDispatchEvent = (xhr, type) => { - try { - xhr.dispatchEvent(new Event(type)); - } catch { - } - }; - proxyApplyFn('XMLHttpRequest.prototype.open', function(context) { - const { thisArg, callArgs } = context; - xhrInstances.delete(thisArg); - const [ method, url, ...args ] = callArgs; - if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { - return context.reflect(); - } - const haystack = { method, url }; - if ( propsToMatch === '' && directive === '' ) { - safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); - return context.reflect(); - } - if ( matchObjectPropertiesFn(propNeedles, haystack) ) { - const xhrDetails = Object.assign(haystack, { - xhr: thisArg, - defer: args.length === 0 || !!args[0], - directive, - headers: { - 'date': '', - 'content-type': '', - 'content-length': '', - }, - url: haystack.url, - props: { - response: { value: '' }, - responseText: { value: '' }, - responseXML: { value: null }, - }, - }); - xhrInstances.set(thisArg, xhrDetails); - } - return context.reflect(); - }); - proxyApplyFn('XMLHttpRequest.prototype.send', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined ) { - return context.reflect(); - } - xhrDetails.headers['date'] = (new Date()).toUTCString(); - let xhrText = ''; - switch ( thisArg.responseType ) { - case 'arraybuffer': - xhrDetails.props.response.value = new ArrayBuffer(0); - xhrDetails.headers['content-type'] = 'application/octet-stream'; - break; - case 'blob': - xhrDetails.props.response.value = new Blob([]); - xhrDetails.headers['content-type'] = 'application/octet-stream'; - break; - case 'document': { - const parser = new DOMParser(); - const doc = parser.parseFromString('', 'text/html'); - xhrDetails.props.response.value = doc; - xhrDetails.props.responseXML.value = doc; - xhrDetails.headers['content-type'] = 'text/html'; - break; - } - case 'json': - xhrDetails.props.response.value = {}; - xhrDetails.props.responseText.value = '{}'; - xhrDetails.headers['content-type'] = 'application/json'; - break; - default: { - if ( directive === '' ) { break; } - xhrText = generateContentFn(trusted, xhrDetails.directive); - if ( xhrText instanceof Promise ) { - xhrText = xhrText.then(text => { - xhrDetails.props.response.value = text; - xhrDetails.props.responseText.value = text; - }); - } else { - xhrDetails.props.response.value = xhrText; - xhrDetails.props.responseText.value = xhrText; - } - xhrDetails.headers['content-type'] = 'text/plain'; - break; - } - } - if ( xhrDetails.defer === false ) { - xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length; - Object.defineProperties(xhrDetails.xhr, { - readyState: { value: 4 }, - responseURL: { value: xhrDetails.url }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }); - Object.defineProperties(xhrDetails.xhr, xhrDetails.props); - return; - } - Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 1, configurable: true }, - responseURL: { value: xhrDetails.url }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - xhrDetails.headers['content-length'] = `${details.props.response.value}`.length; - Object.defineProperties(details.xhr, { - readyState: { value: 2, configurable: true }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 3, configurable: true }, - }); - Object.defineProperties(details.xhr, details.props); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 4 }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - safeDispatchEvent(details.xhr, 'load'); - safeDispatchEvent(details.xhr, 'loadend'); - safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); - }); - }); - proxyApplyFn('XMLHttpRequest.prototype.getResponseHeader', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { - return context.reflect(); - } - const headerName = `${context.callArgs[0]}`; - const value = xhrDetails.headers[headerName.toLowerCase()]; - if ( value !== undefined && value !== '' ) { return value; } - return null; - }); - proxyApplyFn('XMLHttpRequest.prototype.getAllResponseHeaders', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { - return context.reflect(); - } - const out = []; - for ( const [ name, value ] of Object.entries(xhrDetails.headers) ) { - if ( !value ) { continue; } - out.push(`${name}: ${value}`); - } - if ( out.length !== 0 ) { out.push(''); } - return out.join('\r\n'); - }); -} -function preventXhr(...args) { - preventXhrFn(false, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventXhr(...args); -}, -}; - - -scriptlets['trusted-prevent-xhr.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function generateContentFn(trusted, directive) { - const safe = safeSelf(); - const randomize = len => { - const chunks = []; - let textSize = 0; - do { - const s = safe.Math_random().toString(36).slice(2); - chunks.push(s); - textSize += s.length; - } - while ( textSize < len ); - return chunks.join(' ').slice(0, len); - }; - if ( directive === 'true' ) { - return randomize(10); - } - if ( directive === 'emptyObj' ) { - return '{}'; - } - if ( directive === 'emptyArr' ) { - return '[]'; - } - if ( directive === 'emptyStr' ) { - return ''; - } - if ( directive.startsWith('length:') ) { - const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match === null ) { return ''; } - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return randomize(len | 0); - } - if ( directive.startsWith('war:') ) { - if ( scriptletGlobals.warOrigin === undefined ) { return ''; } - return new Promise(resolve => { - const warOrigin = scriptletGlobals.warOrigin; - const warName = directive.slice(4); - const fullpath = [ warOrigin, '/', warName ]; - const warSecret = scriptletGlobals.warSecret; - if ( warSecret !== undefined ) { - fullpath.push('?secret=', warSecret); - } - const warXHR = new safe.XMLHttpRequest(); - warXHR.responseType = 'text'; - warXHR.onloadend = ev => { - resolve(ev.target.responseText || ''); - }; - warXHR.open('GET', fullpath.join('')); - warXHR.send(); - }).catch(( ) => ''); - } - if ( directive.startsWith('join:') ) { - const parts = directive.slice(7) - .split(directive.slice(5, 7)) - .map(a => generateContentFn(trusted, a)); - return parts.some(a => a instanceof Promise) - ? Promise.all(parts).then(parts => parts.join('')) - : parts.join(''); - } - if ( trusted ) { - return directive; - } - return ''; -} -function preventXhrFn( - trusted = false, - propsToMatch = '', - directive = '' -) { - if ( typeof propsToMatch !== 'string' ) { return; } - const safe = safeSelf(); - const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr'; - const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive); - const xhrInstances = new WeakMap(); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const warOrigin = scriptletGlobals.warOrigin; - const safeDispatchEvent = (xhr, type) => { - try { - xhr.dispatchEvent(new Event(type)); - } catch { - } - }; - proxyApplyFn('XMLHttpRequest.prototype.open', function(context) { - const { thisArg, callArgs } = context; - xhrInstances.delete(thisArg); - const [ method, url, ...args ] = callArgs; - if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { - return context.reflect(); - } - const haystack = { method, url }; - if ( propsToMatch === '' && directive === '' ) { - safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); - return context.reflect(); - } - if ( matchObjectPropertiesFn(propNeedles, haystack) ) { - const xhrDetails = Object.assign(haystack, { - xhr: thisArg, - defer: args.length === 0 || !!args[0], - directive, - headers: { - 'date': '', - 'content-type': '', - 'content-length': '', - }, - url: haystack.url, - props: { - response: { value: '' }, - responseText: { value: '' }, - responseXML: { value: null }, - }, - }); - xhrInstances.set(thisArg, xhrDetails); - } - return context.reflect(); - }); - proxyApplyFn('XMLHttpRequest.prototype.send', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined ) { - return context.reflect(); - } - xhrDetails.headers['date'] = (new Date()).toUTCString(); - let xhrText = ''; - switch ( thisArg.responseType ) { - case 'arraybuffer': - xhrDetails.props.response.value = new ArrayBuffer(0); - xhrDetails.headers['content-type'] = 'application/octet-stream'; - break; - case 'blob': - xhrDetails.props.response.value = new Blob([]); - xhrDetails.headers['content-type'] = 'application/octet-stream'; - break; - case 'document': { - const parser = new DOMParser(); - const doc = parser.parseFromString('', 'text/html'); - xhrDetails.props.response.value = doc; - xhrDetails.props.responseXML.value = doc; - xhrDetails.headers['content-type'] = 'text/html'; - break; - } - case 'json': - xhrDetails.props.response.value = {}; - xhrDetails.props.responseText.value = '{}'; - xhrDetails.headers['content-type'] = 'application/json'; - break; - default: { - if ( directive === '' ) { break; } - xhrText = generateContentFn(trusted, xhrDetails.directive); - if ( xhrText instanceof Promise ) { - xhrText = xhrText.then(text => { - xhrDetails.props.response.value = text; - xhrDetails.props.responseText.value = text; - }); - } else { - xhrDetails.props.response.value = xhrText; - xhrDetails.props.responseText.value = xhrText; - } - xhrDetails.headers['content-type'] = 'text/plain'; - break; - } - } - if ( xhrDetails.defer === false ) { - xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length; - Object.defineProperties(xhrDetails.xhr, { - readyState: { value: 4 }, - responseURL: { value: xhrDetails.url }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }); - Object.defineProperties(xhrDetails.xhr, xhrDetails.props); - return; - } - Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 1, configurable: true }, - responseURL: { value: xhrDetails.url }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - xhrDetails.headers['content-length'] = `${details.props.response.value}`.length; - Object.defineProperties(details.xhr, { - readyState: { value: 2, configurable: true }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 3, configurable: true }, - }); - Object.defineProperties(details.xhr, details.props); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 4 }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - safeDispatchEvent(details.xhr, 'load'); - safeDispatchEvent(details.xhr, 'loadend'); - safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); - }); - }); - proxyApplyFn('XMLHttpRequest.prototype.getResponseHeader', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { - return context.reflect(); - } - const headerName = `${context.callArgs[0]}`; - const value = xhrDetails.headers[headerName.toLowerCase()]; - if ( value !== undefined && value !== '' ) { return value; } - return null; - }); - proxyApplyFn('XMLHttpRequest.prototype.getAllResponseHeaders', function(context) { - const { thisArg } = context; - const xhrDetails = xhrInstances.get(thisArg); - if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { - return context.reflect(); - } - const out = []; - for ( const [ name, value ] of Object.entries(xhrDetails.headers) ) { - if ( !value ) { continue; } - out.push(`${name}: ${value}`); - } - if ( out.length !== 0 ) { out.push(''); } - return out.join('\r\n'); - }); -} -function trustedPreventXhr(...args) { - preventXhrFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedPreventXhr(...args); -}, -}; - - -scriptlets['mpegdash-prune.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyToStringFn(proxiedFn, nativeFn) { - if ( proxyToStringFn.proxies === undefined ) { - proxyToStringFn.proxies = new WeakMap(); - proxyToStringFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyToStringFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyToStringFn.nativeToString.call(proxied); - } - }); - proxyToStringFn.proxies.set(proxiedToString, proxyToStringFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - proxyToStringFn.proxies.set(proxiedFn, nativeFn); -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function modifyXhrResponseFn( - propsToMatch = '', - modifierFn = '' -) { - if ( typeof propsToMatch !== 'string' ) { return; } - const safe = safeSelf(); - if ( modifyXhrResponseFn.xhrInstances === undefined ) { - modifyXhrResponseFn.xhrInstances = new WeakMap(); - } - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const NativeXMLHttpRequest = self.XMLHttpRequest; - const TrappedXMLHttpRequest = class XMLHttpRequest extends NativeXMLHttpRequest { - open(method, url, ...args) { - const haystack = { method, url }; - if ( propsToMatch === '' ) { - safe.uboLog(`modifyXhrResponseFn() / Called: ${safe.JSON_stringify(haystack, null, 2)}`); - } else if ( matchObjectPropertiesFn(propNeedles, haystack) ) { - modifyXhrResponseFn.xhrInstances.set(this, modifierFn); - } - return super.open(method, url, ...args); - } - get response() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.response) - : super.response; - } - get responseText() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.responseText) - : super.responseText; - } - get responseXML() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.responseXML) - : super.responseXML; - } - }; - proxyToStringFn(TrappedXMLHttpRequest.prototype.open, NativeXMLHttpRequest.prototype.open); - proxyToStringFn(TrappedXMLHttpRequest, NativeXMLHttpRequest); - self.XMLHttpRequest = TrappedXMLHttpRequest; -} -function mpegdashPrune( - selector = '', - propsToMatch = '' -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('mpegdash-prune', selector, propsToMatch); - const queryAll = (xmlDoc, selector) => { - if ( selector.startsWith('xpath:') === false ) { - return Array.from(xmlDoc.querySelectorAll(selector)); - } - const xpr = xmlDoc.evaluate( - selector.slice(6), - xmlDoc, - null, - XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, - null - ); - const out = []; - for ( let i = 0; i < xpr.snapshotLength; i++ ) { - const node = xpr.snapshotItem(i); - out.push(node); - } - return out; - }; - const rePTparse = /^PT(\d+D)?(\d+H)?(\d+M)?([\d.]+S)?$/; - const secondsPerDay = 24 * 60 * 60; - const secondsPerHour = 60 * 60; - const secondsPerMinute = 60; - const secondsFromPT = pt => { - const match = rePTparse.exec(pt); - if ( match === null ) { return; } - let seconds = 0; - if ( match[1] ) { - const d = parseFloat(match[1].slice(0, -1)); - if ( isNaN(d) ) { return; } - seconds += d * secondsPerDay; - } - if ( match[2] ) { - const h = parseFloat(match[2].slice(0, -1)); - if ( isNaN(h) ) { return; } - seconds += h * secondsPerHour; - } - if ( match[3] ) { - const m = parseFloat(match[3].slice(0, -1)); - if ( isNaN(m) ) { return; } - seconds += m * secondsPerMinute; - } - if ( match[4] ) { - const s = parseFloat(match[4].slice(0, -1)); - if ( isNaN(s) ) { return; } - seconds += s; - } - return seconds; - }; - const ptFromSeconds = seconds => { - const parts = [ 'PT' ]; - const d = Math.floor(seconds / secondsPerDay); - if ( d ) { - parts.push(`${d}D`); - seconds -= d * secondsPerDay; - } - const h = Math.floor(seconds / secondsPerHour); - if ( h ) { - parts.push(`${h}H`); - seconds -= h * secondsPerHour; - } - const m = Math.floor(seconds / secondsPerMinute); - if ( m ) { - parts.push(`${m}M`); - seconds -= m * secondsPerMinute; - } - parts.push(`${seconds}S`); - return parts.join(''); - }; - const fixTimeAttributes = xmlDoc => { - try { - const periods = queryAll(xmlDoc, 'MPD > Period'); - if ( periods.length === 0 ) { return; } - let seconds = 0; - for ( const period of periods ) { - const startAttrBefore = period.getAttribute('start'); - const durAttr = period.getAttribute('duration'); - if ( startAttrBefore === null || durAttr === null ) { continue; } - const startAttrAfter = ptFromSeconds(seconds); - period.setAttribute('start', startAttrAfter); - if ( period.hasAttribute('id') ) { - const idAttr = period.getAttribute('id'); - period.setAttribute('id', idAttr.replace(startAttrBefore, startAttrAfter)); - } - seconds += secondsFromPT(durAttr); - } - const mpds = queryAll(xmlDoc, 'MPD[mediaPresentationDuration]'); - if ( mpds.length !== 1 ) { return; } - mpds[0].setAttribute('mediaPresentationDuration', ptFromSeconds(seconds)); - } catch { - } - }; - const pruneFromDoc = xmlDoc => { - try { - if ( selector === '' ) { - const serializer = new XMLSerializer(); - safe.uboLog(logPrefix, `Document is\n\t${serializer.serializeToString(xmlDoc)}`); - } - const items = queryAll(xmlDoc, selector); - if ( items.length === 0 ) { return xmlDoc; } - safe.uboLog(logPrefix, `Patching ${items.length} items`); - for ( const item of items ) { - if ( item.nodeType !== 1 ) { continue; } - item.setAttribute('duration', 'PT0S'); - } - fixTimeAttributes(xmlDoc); - } catch(ex) { - safe.uboErr(logPrefix, `Error: ${ex}`); - } - return xmlDoc; - }; - const pruneFromText = text => { - if ( (/^\s*\s*$/.test(text)) === false ) { - return text; - } - try { - const xmlParser = new DOMParser(); - const xmlDoc = xmlParser.parseFromString(text, 'text/xml'); - pruneFromDoc(xmlDoc); - const serializer = new XMLSerializer(); - text = serializer.serializeToString(xmlDoc); - } catch { - } - return text; - }; - modifyXhrResponseFn(propsToMatch, (xhr, before) => { - if ( before instanceof XMLDocument ) { - return pruneFromDoc(before); - } - if ( typeof before === 'string' ) { - return pruneFromText(before); - } - return before; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -mpegdashPrune(...args); -}, -}; - - -scriptlets['set-cookie.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function getCookieFn( - name = '' -) { - const safe = safeSelf(); - for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { - const pos = s.indexOf('='); - if ( pos === -1 ) { continue; } - if ( s.slice(0, pos) !== name ) { continue; } - return s.slice(pos+1).trim(); - } -} -function setCookieFn( - trusted = false, - name = '', - value = '', - expires = '', - path = '', - options = {}, -) { - // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 - // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 - if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { - name = encodeURIComponent(name); - } - // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 - // The characters [",] are given a pass from the RFC requirements because - // apparently browsers do not follow the RFC to the letter. - if ( /[^ -:<-[\]-~]/.test(value) ) { - value = encodeURIComponent(value); - } - - const cookieBefore = getCookieFn(name); - if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } - if ( cookieBefore === value && options.reload ) { return; } - - const cookieParts = [ name, '=', value ]; - if ( expires !== '' ) { - cookieParts.push('; expires=', expires); - } - - if ( path === '' ) { path = '/'; } - else if ( path === 'none' ) { path = ''; } - if ( path !== '' && path !== '/' ) { return; } - if ( path === '/' ) { - cookieParts.push('; path=/'); - } - - if ( trusted ) { - if ( options.domain ) { - let domain = options.domain; - if ( /^\/.+\//.test(domain) ) { - const baseURL = new URL(document.baseURI); - const reDomain = new RegExp(domain.slice(1, -1)); - const match = reDomain.exec(baseURL.hostname); - domain = match ? match[0] : undefined; - } - if ( domain ) { - cookieParts.push(`; domain=${domain}`); - } - } - cookieParts.push('; Secure'); - } else if ( /^__(Host|Secure)-/.test(name) ) { - cookieParts.push('; Secure'); - } - - try { - document.cookie = cookieParts.join(''); - } catch { - } - - const done = getCookieFn(name) === value; - if ( done && options.reload ) { - window.location.reload(); - } - - return done; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setCookie( - name = '', - value = '', - path = '', - ...varargs -) { - if ( name === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - const validValues = getSafeCookieValuesFn(); - if ( validValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(value, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - - const done = setCookieFn( - false, - name, - value, - '', - path, - safe.parseVarargs(varargs) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setCookie(...args); -}, -}; - - -scriptlets['set-cookie-reload.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function getCookieFn( - name = '' -) { - const safe = safeSelf(); - for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { - const pos = s.indexOf('='); - if ( pos === -1 ) { continue; } - if ( s.slice(0, pos) !== name ) { continue; } - return s.slice(pos+1).trim(); - } -} -function setCookieFn( - trusted = false, - name = '', - value = '', - expires = '', - path = '', - options = {}, -) { - // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 - // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 - if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { - name = encodeURIComponent(name); - } - // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 - // The characters [",] are given a pass from the RFC requirements because - // apparently browsers do not follow the RFC to the letter. - if ( /[^ -:<-[\]-~]/.test(value) ) { - value = encodeURIComponent(value); - } - - const cookieBefore = getCookieFn(name); - if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } - if ( cookieBefore === value && options.reload ) { return; } - - const cookieParts = [ name, '=', value ]; - if ( expires !== '' ) { - cookieParts.push('; expires=', expires); - } - - if ( path === '' ) { path = '/'; } - else if ( path === 'none' ) { path = ''; } - if ( path !== '' && path !== '/' ) { return; } - if ( path === '/' ) { - cookieParts.push('; path=/'); - } - - if ( trusted ) { - if ( options.domain ) { - let domain = options.domain; - if ( /^\/.+\//.test(domain) ) { - const baseURL = new URL(document.baseURI); - const reDomain = new RegExp(domain.slice(1, -1)); - const match = reDomain.exec(baseURL.hostname); - domain = match ? match[0] : undefined; - } - if ( domain ) { - cookieParts.push(`; domain=${domain}`); - } - } - cookieParts.push('; Secure'); - } else if ( /^__(Host|Secure)-/.test(name) ) { - cookieParts.push('; Secure'); - } - - try { - document.cookie = cookieParts.join(''); - } catch { - } - - const done = getCookieFn(name) === value; - if ( done && options.reload ) { - window.location.reload(); - } - - return done; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setCookie( - name = '', - value = '', - path = '', - ...varargs -) { - if ( name === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - const validValues = getSafeCookieValuesFn(); - if ( validValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(value, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - - const done = setCookieFn( - false, - name, - value, - '', - path, - safe.parseVarargs(varargs) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -} -function setCookieReload(name, value, path, ...args) { - setCookie(name, value, path, 'reload', '1', ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setCookieReload(...args); -}, -}; - - -scriptlets['trusted-set-cookie.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function getCookieFn( - name = '' -) { - const safe = safeSelf(); - for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { - const pos = s.indexOf('='); - if ( pos === -1 ) { continue; } - if ( s.slice(0, pos) !== name ) { continue; } - return s.slice(pos+1).trim(); - } -} -function setCookieFn( - trusted = false, - name = '', - value = '', - expires = '', - path = '', - options = {}, -) { - // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 - // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 - if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { - name = encodeURIComponent(name); - } - // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 - // The characters [",] are given a pass from the RFC requirements because - // apparently browsers do not follow the RFC to the letter. - if ( /[^ -:<-[\]-~]/.test(value) ) { - value = encodeURIComponent(value); - } - - const cookieBefore = getCookieFn(name); - if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } - if ( cookieBefore === value && options.reload ) { return; } - - const cookieParts = [ name, '=', value ]; - if ( expires !== '' ) { - cookieParts.push('; expires=', expires); - } - - if ( path === '' ) { path = '/'; } - else if ( path === 'none' ) { path = ''; } - if ( path !== '' && path !== '/' ) { return; } - if ( path === '/' ) { - cookieParts.push('; path=/'); - } - - if ( trusted ) { - if ( options.domain ) { - let domain = options.domain; - if ( /^\/.+\//.test(domain) ) { - const baseURL = new URL(document.baseURI); - const reDomain = new RegExp(domain.slice(1, -1)); - const match = reDomain.exec(baseURL.hostname); - domain = match ? match[0] : undefined; - } - if ( domain ) { - cookieParts.push(`; domain=${domain}`); - } - } - cookieParts.push('; Secure'); - } else if ( /^__(Host|Secure)-/.test(name) ) { - cookieParts.push('; Secure'); - } - - try { - document.cookie = cookieParts.join(''); - } catch { - } - - const done = getCookieFn(name) === value; - if ( done && options.reload ) { - window.location.reload(); - } - - return done; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function trustedSetCookie( - name = '', - value = '', - offsetExpiresSec = '', - path = '', - ...varargs -) { - if ( name === '' ) { return; } - - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const time = new Date(); - - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', time.getTime()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', time.toUTCString()); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', time.toISOString()); - } - - let expires = ''; - if ( offsetExpiresSec !== '' ) { - if ( offsetExpiresSec === '1day' ) { - time.setDate(time.getDate() + 1); - } else if ( offsetExpiresSec === '1year' ) { - time.setFullYear(time.getFullYear() + 1); - } else { - if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; } - time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10)); - } - expires = time.toUTCString(); - } - - const done = setCookieFn( - true, - name, - value, - expires, - path, - safe.parseVarargs(varargs) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetCookie(...args); -}, -}; - - -scriptlets['trusted-set-cookie-reload.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function getCookieFn( - name = '' -) { - const safe = safeSelf(); - for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { - const pos = s.indexOf('='); - if ( pos === -1 ) { continue; } - if ( s.slice(0, pos) !== name ) { continue; } - return s.slice(pos+1).trim(); - } -} -function setCookieFn( - trusted = false, - name = '', - value = '', - expires = '', - path = '', - options = {}, -) { - // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 - // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 - if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { - name = encodeURIComponent(name); - } - // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 - // The characters [",] are given a pass from the RFC requirements because - // apparently browsers do not follow the RFC to the letter. - if ( /[^ -:<-[\]-~]/.test(value) ) { - value = encodeURIComponent(value); - } - - const cookieBefore = getCookieFn(name); - if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } - if ( cookieBefore === value && options.reload ) { return; } - - const cookieParts = [ name, '=', value ]; - if ( expires !== '' ) { - cookieParts.push('; expires=', expires); - } - - if ( path === '' ) { path = '/'; } - else if ( path === 'none' ) { path = ''; } - if ( path !== '' && path !== '/' ) { return; } - if ( path === '/' ) { - cookieParts.push('; path=/'); - } - - if ( trusted ) { - if ( options.domain ) { - let domain = options.domain; - if ( /^\/.+\//.test(domain) ) { - const baseURL = new URL(document.baseURI); - const reDomain = new RegExp(domain.slice(1, -1)); - const match = reDomain.exec(baseURL.hostname); - domain = match ? match[0] : undefined; - } - if ( domain ) { - cookieParts.push(`; domain=${domain}`); - } - } - cookieParts.push('; Secure'); - } else if ( /^__(Host|Secure)-/.test(name) ) { - cookieParts.push('; Secure'); - } - - try { - document.cookie = cookieParts.join(''); - } catch { - } - - const done = getCookieFn(name) === value; - if ( done && options.reload ) { - window.location.reload(); - } - - return done; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function trustedSetCookie( - name = '', - value = '', - offsetExpiresSec = '', - path = '', - ...varargs -) { - if ( name === '' ) { return; } - - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const time = new Date(); - - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', time.getTime()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', time.toUTCString()); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', time.toISOString()); - } - - let expires = ''; - if ( offsetExpiresSec !== '' ) { - if ( offsetExpiresSec === '1day' ) { - time.setDate(time.getDate() + 1); - } else if ( offsetExpiresSec === '1year' ) { - time.setFullYear(time.getFullYear() + 1); - } else { - if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; } - time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10)); - } - expires = time.toUTCString(); - } - - const done = setCookieFn( - true, - name, - value, - expires, - path, - safe.parseVarargs(varargs) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -} -function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) { - trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetCookieReload(...args); -}, -}; - - -scriptlets['remove-cookie.js'] = { -aliases: ["cookie-remover.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function removeCookie( - needle = '', - ...varargs -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const reName = safe.patternToRegex(needle); - const extraArgs = safe.parseVarargs(varargs); - const throttle = (fn, ms = 500) => { - if ( throttle.timer !== undefined ) { return; } - throttle.timer = setTimeout(( ) => { - throttle.timer = undefined; - fn(); - }, ms); - }; - const baseURL = new URL(document.baseURI); - let targetDomain = extraArgs.domain; - if ( targetDomain && /^\/.+\//.test(targetDomain) ) { - const reDomain = new RegExp(targetDomain.slice(1, -1)); - const match = reDomain.exec(baseURL.hostname); - targetDomain = match ? match[0] : undefined; - } - const remove = ( ) => { - safe.String_split.call(document.cookie, ';').forEach(cookieStr => { - const pos = cookieStr.indexOf('='); - if ( pos === -1 ) { return; } - const cookieName = cookieStr.slice(0, pos).trim(); - if ( reName.test(cookieName) === false ) { return; } - const part1 = cookieName + '='; - const part2a = `; domain=${baseURL.hostname}`; - const part2b = `; domain=.${baseURL.hostname}`; - let part2c, part2d; - if ( targetDomain ) { - part2c = `; domain=${targetDomain}`; - part2d = `; domain=.${targetDomain}`; - } else if ( document.domain ) { - const domain = document.domain; - if ( domain !== baseURL.hostname ) { - part2c = `; domain=.${domain}`; - } - if ( domain.startsWith('www.') ) { - part2d = `; domain=${domain.replace('www', '')}`; - } - } - const part3 = '; path=/'; - const part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - document.cookie = part1 + part4; - document.cookie = part1 + part2a + part4; - document.cookie = part1 + part2b + part4; - document.cookie = part1 + part3 + part4; - document.cookie = part1 + part2a + part3 + part4; - document.cookie = part1 + part2b + part3 + part4; - if ( part2c !== undefined ) { - document.cookie = part1 + part2c + part3 + part4; - } - if ( part2d !== undefined ) { - document.cookie = part1 + part2d + part3 + part4; - } - }); - }; - remove(); - window.addEventListener('beforeunload', remove); - if ( typeof extraArgs.when !== 'string' ) { return; } - const supportedEventTypes = [ 'scroll', 'keydown' ]; - const eventTypes = safe.String_split.call(extraArgs.when, /\s/); - for ( const type of eventTypes ) { - if ( supportedEventTypes.includes(type) === false ) { continue; } - document.addEventListener(type, ( ) => { - throttle(remove); - }, { passive: true }); - } -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -removeCookie(...args); -}, -}; - - -scriptlets['set-local-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', - options = {} -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - } - - let modified = false; - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - modified = toRemove.length !== 0; - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - - const before = storage.getItem(key); - const after = `${value}`; - modified = after !== before; - if ( modified ) { - storage.setItem(key, after); - } - } - } catch { - } - - if ( modified && typeof options.reload === 'number' ) { - setTimeout(( ) => { window.location.reload(); }, options.reload); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function setLocalStorageItem(key = '', value = '', ...varargs) { - const safe = safeSelf(); - const options = safe.parseVarargs(varargs) - setLocalStorageItemFn('local', false, key, value, options); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setLocalStorageItem(...args); -}, -}; - - -scriptlets['set-session-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', - options = {} -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - } - - let modified = false; - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - modified = toRemove.length !== 0; - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - - const before = storage.getItem(key); - const after = `${value}`; - modified = after !== before; - if ( modified ) { - storage.setItem(key, after); - } - } - } catch { - } - - if ( modified && typeof options.reload === 'number' ) { - setTimeout(( ) => { window.location.reload(); }, options.reload); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function setSessionStorageItem(key = '', value = '', ...varargs) { - const safe = safeSelf(); - const options = safe.parseVarargs(varargs) - setLocalStorageItemFn('session', false, key, value, options); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -setSessionStorageItem(...args); -}, -}; - - -scriptlets['trusted-set-local-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', - options = {} -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - } - - let modified = false; - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - modified = toRemove.length !== 0; - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - - const before = storage.getItem(key); - const after = `${value}`; - modified = after !== before; - if ( modified ) { - storage.setItem(key, after); - } - } - } catch { - } - - if ( modified && typeof options.reload === 'number' ) { - setTimeout(( ) => { window.location.reload(); }, options.reload); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function trustedSetLocalStorageItem(key = '', value = '', ...varargs) { - const safe = safeSelf(); - const options = safe.parseVarargs(varargs) - setLocalStorageItemFn('local', true, key, value, options); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetLocalStorageItem(...args); -}, -}; - - -scriptlets['trusted-set-session-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - 'set', 'unset', - 'given', - ]; -} -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', - options = {} -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - } - - let modified = false; - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - modified = toRemove.length !== 0; - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - - const before = storage.getItem(key); - const after = `${value}`; - modified = after !== before; - if ( modified ) { - storage.setItem(key, after); - } - } - } catch { - } - - if ( modified && typeof options.reload === 'number' ) { - setTimeout(( ) => { window.location.reload(); }, options.reload); - } -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function trustedSetSessionStorageItem(key = '', value = '', ...varargs) { - const safe = safeSelf(); - const options = safe.parseVarargs(varargs) - setLocalStorageItemFn('session', true, key, value, options); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSetSessionStorageItem(...args); -}, -}; - - -scriptlets['abort-on-property-read.js'] = { -aliases: ["aopr.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function abortOnPropertyRead( - chain = '' -) { - if ( typeof chain !== 'string' ) { return; } - if ( chain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('abort-on-property-read', chain); - const exceptionToken = getExceptionTokenFn(); - const abort = function() { - safe.uboLog(logPrefix, 'Aborted'); - throw new ReferenceError(exceptionToken); - }; - const makeProxy = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - const desc = Object.getOwnPropertyDescriptor(owner, chain); - if ( !desc || desc.get !== abort ) { - Object.defineProperty(owner, chain, { - get: abort, - set: function(){} - }); - } - return; - } - const prop = chain.slice(0, pos); - let v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v ) { - makeProxy(v, chain); - return; - } - const desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.set !== undefined ) { return; } - Object.defineProperty(owner, prop, { - get: function() { return v; }, - set: function(a) { - v = a; - if ( a instanceof Object ) { - makeProxy(a, chain); - } - } - }); - }; - const owner = window; - makeProxy(owner, chain); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -abortOnPropertyRead(...args); -}, -}; - - -scriptlets['abort-on-property-write.js'] = { -aliases: ["aopw.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function abortOnPropertyWrite( - prop = '' -) { - if ( typeof prop !== 'string' ) { return; } - if ( prop === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('abort-on-property-write', prop); - const exceptionToken = getExceptionTokenFn(); - let owner = window; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - delete owner[prop]; - Object.defineProperty(owner, prop, { - set: function() { - safe.uboLog(logPrefix, 'Aborted'); - throw new ReferenceError(exceptionToken); - } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -abortOnPropertyWrite(...args); -}, -}; - - -scriptlets['adjust-setInterval.js'] = { -aliases: ["nano-setInterval-booster.js","nano-sib.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function adjustSetInterval( - needleArg = '', - delayArg = '', - boostArg = '' -) { - if ( typeof needleArg !== 'string' ) { return; } - const safe = safeSelf(); - const reNeedle = safe.patternToRegex(needleArg); - let delay = delayArg !== '*' ? parseInt(delayArg, 10) : -1; - if ( isNaN(delay) || isFinite(delay) === false ) { delay = 1000; } - let boost = parseFloat(boostArg); - boost = isNaN(boost) === false && isFinite(boost) - ? Math.min(Math.max(boost, 0.001), 50) - : 0.05; - self.setInterval = new Proxy(self.setInterval, { - apply: function(target, thisArg, args) { - const [ a, b ] = args; - if ( - (delay === -1 || b === delay) && - reNeedle.test(a.toString()) - ) { - args[1] = b * boost; - } - return target.apply(thisArg, args); - } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -adjustSetInterval(...args); -}, -}; - - -scriptlets['adjust-setTimeout.js'] = { -aliases: ["nano-setTimeout-booster.js","nano-stb.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function adjustSetTimeout( - needleArg = '', - delayArg = '', - boostArg = '' -) { - if ( typeof needleArg !== 'string' ) { return; } - const safe = safeSelf(); - const reNeedle = safe.patternToRegex(needleArg); - let delay = delayArg !== '*' ? parseInt(delayArg, 10) : -1; - if ( isNaN(delay) || isFinite(delay) === false ) { delay = 1000; } - let boost = parseFloat(boostArg); - boost = isNaN(boost) === false && isFinite(boost) - ? Math.min(Math.max(boost, 0.001), 50) - : 0.05; - self.setTimeout = new Proxy(self.setTimeout, { - apply: function(target, thisArg, args) { - const [ a, b ] = args; - if ( - (delay === -1 || b === delay) && - reNeedle.test(a.toString()) - ) { - args[1] = b * boost; - } - return target.apply(thisArg, args); - } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -adjustSetTimeout(...args); -}, -}; - - -scriptlets['prevent-refresh.js'] = { -aliases: ["refresh-defuser.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function preventRefresh( - delay = '' -) { - if ( typeof delay !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-refresh', delay); - const stop = content => { - window.stop(); - safe.uboLog(logPrefix, `Prevented "${content}"`); - }; - const defuse = ( ) => { - const meta = document.querySelector('meta[http-equiv="refresh" i][content]'); - if ( meta === null ) { return; } - const content = meta.getAttribute('content') || ''; - const ms = delay === '' - ? Math.max(parseFloat(content) || 0, 0) * 500 - : 0; - if ( ms === 0 ) { - stop(content); - } else { - setTimeout(( ) => { stop(content); }, ms); - } - }; - self.addEventListener('load', defuse, { capture: true, once: true }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -preventRefresh(...args); -}, -}; - - -scriptlets['remove-class.js'] = { -aliases: ["rc.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function removeClass( - rawToken = '', - rawSelector = '', - behavior = '' -) { - if ( typeof rawToken !== 'string' ) { return; } - if ( rawToken === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior); - const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); - const selector = tokens - .map(a => `${rawSelector}.${CSS.escape(a)}`) - .join(','); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); - } - const mustStay = /\bstay\b/.test(behavior); - let timer; - const rmclass = ( ) => { - timer = undefined; - try { - const nodes = document.querySelectorAll(selector); - for ( const node of nodes ) { - node.classList.remove(...tokens); - safe.uboLog(logPrefix, 'Removed class(es)'); - } - } catch { - } - if ( mustStay ) { return; } - if ( document.readyState !== 'complete' ) { return; } - observer.disconnect(); - }; - const mutationHandler = mutations => { - if ( timer !== undefined ) { return; } - let skip = true; - for ( let i = 0; i < mutations.length && skip; i++ ) { - const { type, addedNodes, removedNodes } = mutations[i]; - if ( type === 'attributes' ) { skip = false; } - for ( let j = 0; j < addedNodes.length && skip; j++ ) { - if ( addedNodes[j].nodeType === 1 ) { skip = false; break; } - } - for ( let j = 0; j < removedNodes.length && skip; j++ ) { - if ( removedNodes[j].nodeType === 1 ) { skip = false; break; } - } - } - if ( skip ) { return; } - timer = onIdleFn(rmclass, { timeout: 67 }); - }; - const observer = new MutationObserver(mutationHandler); - const start = ( ) => { - rmclass(); - observer.observe(document, { - attributes: true, - attributeFilter: [ 'class' ], - childList: true, - subtree: true, - }); - }; - runAt(( ) => { - start(); - }, /\bcomplete\b/.test(behavior) ? 'idle' : 'loading'); +fn: 'trustedEditInboundObject', +}; + + +scriptlets['edit-this-object.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'editThisObject', +}; + + +scriptlets['trusted-edit-this-object.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedEditThisObject', +}; + + +scriptlets['edit-object-on-getter.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'editObjectOnGetter', +}; + + +scriptlets['trusted-edit-object-on-getter.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedEditObjectOnGetter', +}; + + +scriptlets['edit-object-on-setter.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'editObjectOnSetter', +}; + + +scriptlets['trusted-edit-object-on-setter.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedEditObjectOnSetter', +}; + + +scriptlets['edit-element-object.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'editElementObject', +}; + + +scriptlets['trusted-edit-element-object.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedEditElementObject', +}; + + +scriptlets['json-edit-xhr-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonEditXhrResponse', +}; + + +scriptlets['trusted-json-edit-xhr-response.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonEditXhrResponse', +}; + + +scriptlets['json-edit-xhr-request.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonEditXhrRequest', +}; + + +scriptlets['trusted-json-edit-xhr-request.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonEditXhrRequest', +}; + + +scriptlets['json-edit-fetch-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonEditFetchResponse', +}; + + +scriptlets['trusted-json-edit-fetch-response.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonEditFetchResponse', +}; + + +scriptlets['json-edit-fetch-request.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonEditFetchRequest', +}; + + +scriptlets['trusted-json-edit-fetch-request.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonEditFetchRequest', +}; + + +scriptlets['jsonl-edit-xhr-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonlEditXhrResponse', +}; + + +scriptlets['trusted-jsonl-edit-xhr-response.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonlEditXhrResponse', +}; + + +scriptlets['jsonl-edit-fetch-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonlEditFetchResponse', +}; + + +scriptlets['trusted-jsonl-edit-fetch-response.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedJsonlEditFetchResponse', +}; + + +scriptlets['abort-on-stack-trace.js'] = { +aliases: ["aost.js"], + +requiresTrust: false, +fn: 'abortOnStackTrace', +}; + + +scriptlets['trusted-prune-inbound-object.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedPruneInboundObject', +}; + + +scriptlets['trusted-prune-outbound-object.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedPruneOutboundObject', +}; + + +scriptlets['json-prune.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonPrune', +}; + + +scriptlets['json-prune-fetch-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonPruneFetchResponse', +}; + + +scriptlets['json-prune-xhr-response.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'jsonPruneXhrResponse', +}; + + +scriptlets['evaldata-prune.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'evaldataPrune', +}; + + +scriptlets['noeval-if.js'] = { +aliases: ["prevent-eval-if.js"], + +requiresTrust: false, +fn: 'noEvalIf', +}; + + +scriptlets['prevent-addEventListener.js'] = { +aliases: ["addEventListener-defuser.js","aeld.js"], + +requiresTrust: false, +fn: 'preventAddEventListener', +}; + + +scriptlets['prevent-bab.js'] = { +aliases: ["bab-defuser.js","nobab.js"], + +requiresTrust: false, +fn: 'preventBab', +}; + + +scriptlets['prevent-clipboard-write.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'preventClipboardWrite', +}; + + +scriptlets['prevent-dialog.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'preventDialog', +}; + + +scriptlets['prevent-fetch.js'] = { +aliases: ["no-fetch-if.js"], + +requiresTrust: false, +fn: 'preventFetch', +}; + + +scriptlets['trusted-prevent-fetch.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedPreventFetch', +}; + + +scriptlets['freeze-element-property.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'freezeElementProperty', +}; + + +scriptlets['prevent-innerHTML.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'preventInnerHTML', +}; + + +scriptlets['prevent-navigation.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'preventNavigation', +}; + + +scriptlets['prevent-setTimeout.js'] = { +aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"], + +requiresTrust: false, +fn: 'preventSetTimeout', +}; + + +scriptlets['prevent-setInterval.js'] = { +aliases: ["no-setInterval-if.js","nosiif.js","setInterval-defuser.js"], + +requiresTrust: false, +fn: 'preventSetInterval', +}; + + +scriptlets['prevent-requestAnimationFrame.js'] = { +aliases: ["no-requestAnimationFrame-if.js","norafif.js"], + +requiresTrust: false, +fn: 'preventRequestAnimationFrame', +}; + + +scriptlets['set-constant.js'] = { +aliases: ["set.js"], + +requiresTrust: false, +fn: 'setConstant', +}; + + +scriptlets['trusted-set-constant.js'] = { +aliases: ["trusted-set.js"], + +requiresTrust: true, +fn: 'trustedSetConstant', +}; + + +scriptlets['trusted-replace-argument.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedReplaceArgument', +}; + + +scriptlets['spoof-css.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'spoofCSS', +}; + + +scriptlets['prevent-xhr.js'] = { +aliases: ["no-xhr-if.js"], + +requiresTrust: false, +fn: 'preventXhr', +}; + + +scriptlets['trusted-prevent-xhr.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'trustedPreventXhr', +}; + + +scriptlets['mpegdash-prune.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'mpegdashPrune', +}; + + +scriptlets['set-cookie.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'setCookie', +}; + + +scriptlets['set-cookie-reload.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'setCookieReload', +}; + + +scriptlets['trusted-set-cookie.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedSetCookie', +}; + + +scriptlets['trusted-set-cookie-reload.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedSetCookieReload', +}; + + +scriptlets['remove-cookie.js'] = { +aliases: ["cookie-remover.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'removeCookie', +}; + + +scriptlets['set-local-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'setLocalStorageItem', +}; + + +scriptlets['set-session-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'setSessionStorageItem', +}; + + +scriptlets['trusted-set-local-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedSetLocalStorageItem', +}; + + +scriptlets['trusted-set-session-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedSetSessionStorageItem', +}; + + +scriptlets['abort-on-property-read.js'] = { +aliases: ["aopr.js"], + +requiresTrust: false, +fn: 'abortOnPropertyRead', +}; + + +scriptlets['abort-on-property-write.js'] = { +aliases: ["aopw.js"], + +requiresTrust: false, +fn: 'abortOnPropertyWrite', +}; + + +scriptlets['adjust-setInterval.js'] = { +aliases: ["nano-setInterval-booster.js","nano-sib.js"], + +requiresTrust: false, +fn: 'adjustSetInterval', +}; + + +scriptlets['adjust-setTimeout.js'] = { +aliases: ["nano-setTimeout-booster.js","nano-stb.js"], + +requiresTrust: false, +fn: 'adjustSetTimeout', +}; + + +scriptlets['prevent-refresh.js'] = { +aliases: ["refresh-defuser.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'preventRefresh', +}; + + +scriptlets['remove-class.js'] = { +aliases: ["rc.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'removeClass', +}; + + +scriptlets['webrtc-if.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'webrtcIf', +}; + + +scriptlets['prevent-window-open.js'] = { +aliases: ["nowoif.js","no-window-open-if.js","window.open-defuser.js"], + +requiresTrust: false, +fn: 'noWindowOpenIf', +}; + + +scriptlets['close-window.js'] = { +aliases: ["window-close-if.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'closeWindow', +}; + + +scriptlets['window.name-defuser.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'windowNameDefuser', +}; + + +scriptlets['overlay-buster.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'overlayBuster', +}; + + +scriptlets['alert-buster.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'alertBuster', +}; + + +scriptlets['nowebrtc.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'noWebrtc', +}; + + +scriptlets['disable-newtab-links.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'disableNewtabLinks', +}; + + +scriptlets['xml-prune.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'xmlPrune', }; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -removeClass(...args); -}, + + +scriptlets['m3u-prune.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'm3uPrune', }; -scriptlets['webrtc-if.js'] = { +scriptlets['call-nothrow.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'callNothrow', +}; + + +scriptlets['remove-node-text.js'] = { +aliases: ["rmnt.js"], +world: 'ISOLATED', +requiresTrust: false, +fn: 'removeNodeText', +}; + + +scriptlets['prevent-canvas.js'] = { +aliases: [], + +requiresTrust: false, +fn: 'preventCanvas', +}; + + +scriptlets['multiup.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +fn: 'multiup', +}; + + +scriptlets['break-on-call.js'] = { aliases: [], requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { +fn: 'breakOnCall', +}; + + +scriptlets['trusted-replace-node-text.js'] = { +aliases: ["trusted-rpnt.js","replace-node-text.js","rpnt.js"], +world: 'ISOLATED', +requiresTrust: true, +fn: 'replaceNodeText', +}; + + +scriptlets['trusted-replace-fetch-response.js'] = { +aliases: ["trusted-rpfr.js"], + +requiresTrust: true, +fn: 'trustedReplaceFetchResponse', +}; + + +scriptlets['trusted-replace-xhr-response.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedReplaceXhrResponse', +}; + + +scriptlets['trusted-click-element.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +fn: 'trustedClickElement', +}; + + +scriptlets['trusted-replace-outbound-text.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedReplaceOutboundText', +}; + + +scriptlets['trusted-suppress-native-method.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedSuppressNativeMethod', +}; + + +scriptlets['trusted-prevent-dom-bypass.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedPreventDomBypass', +}; + + +scriptlets['trusted-override-element-method.js'] = { +aliases: [], + +requiresTrust: true, +fn: 'trustedOverrideElementMethod', +}; + + +// The calls come last, so the class dependencies, which are not hoisted, are declared by then. +export function run(scriptletGlobals, calls) { function safeSelf() { if ( safeSelf.safe ) { return safeSelf.safe; @@ -40442,1416 +982,1578 @@ function safeSelf() { } return safe; } -function webrtcIf( - good = '' -) { - if ( typeof good !== 'string' ) { return; } +function getRandomTokenFn() { const safe = safeSelf(); - const reGood = safe.patternToRegex(good); - const rtcName = window.RTCPeerConnection - ? 'RTCPeerConnection' - : (window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : ''); - if ( rtcName === '' ) { return; } - const log = console.log.bind(console); - const neuteredPeerConnections = new WeakSet(); - const isGoodConfig = function(instance, config) { - if ( neuteredPeerConnections.has(instance) ) { return false; } - if ( config instanceof Object === false ) { return true; } - if ( Array.isArray(config.iceServers) === false ) { return true; } - for ( const server of config.iceServers ) { - const urls = typeof server.urls === 'string' - ? [ server.urls ] - : server.urls; - if ( Array.isArray(urls) ) { - for ( const url of urls ) { - if ( reGood.test(url) ) { return true; } + return safe.String_fromCharCode(Date.now() % 26 + 97) + + safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); +} +function getExceptionTokenFn() { + const token = getRandomTokenFn(); + const oe = self.onerror; + self.onerror = function(msg, ...args) { + if ( typeof msg === 'string' && msg.includes(token) ) { return true; } + if ( oe instanceof Function ) { + return oe.call(this, msg, ...args); + } + }.bind(); + return token; +} +function trapPropertyFn(propChain, handler, options = {}) { + if ( propChain === '' ) { return; } + let owner = self; + let prop = propChain; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + owner = owner[prop.slice(0, pos)]; + if ( owner instanceof Object === false ) { return; } + prop = prop.slice(pos + 1); + } + const safe = safeSelf(); + if ( trapPropertyFn.db === undefined ) { + trapPropertyFn.db = new WeakMap(); + trapPropertyFn.entryFromContext = (owner, prop) => { + const handlers = trapPropertyFn.db.get(owner); + return handlers?.get(prop); + }; + trapPropertyFn.getter = (owner, prop) => { + const entry = trapPropertyFn.entryFromContext(owner, prop); + if ( entry === undefined ) { return; } + let r = entry.value; + for ( const desc of entry.stack ) { + try { r = desc.get(); } catch (e) { + if ( entry.canThrow ) { throw e; } } } - if ( typeof server.username === 'string' ) { - if ( reGood.test(server.username) ) { return true; } + return r; + }; + trapPropertyFn.setter = (owner, prop, value) => { + const entry = trapPropertyFn.entryFromContext(owner, prop); + if ( entry === undefined ) { return; } + entry.value = value; + for ( const desc of entry.stack ) { + try { desc.set(value); } catch (e) { + if ( entry.canThrow ) { throw e; } + } } - if ( typeof server.credential === 'string' ) { - if ( reGood.test(server.credential) ) { return true; } + }; + } + const { db } = trapPropertyFn; + const handlers = db.get(owner) || new Map(); + if ( handlers.size === 0 ) { + db.set(owner, handlers); + } + const entry = handlers.get(prop) || { + value: owner[prop], + stack: [], + }; + entry.stack.push(handler); + if ( entry.stack.length > 1 ) { return entry.value; } + Object.assign(entry, options); + handlers.set(prop, entry); + const desc = safe.Object_getOwnPropertyDescriptor(owner, prop); + if ( desc instanceof safe.Object ) { + if ( desc.get || desc.set ) { + entry.stack.push(desc); + } + } + try { + safe.Object_defineProperty(owner, prop, { + get() { + return trapPropertyFn.getter(owner, prop); + }, + set(value) { + trapPropertyFn.setter(owner, prop, value); + } + }); + } catch { + } + return entry.value; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} +function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); } - neuteredPeerConnections.add(instance); - return false; + } + return matched; +} +function generateContentFn(trusted, directive) { + const safe = safeSelf(); + const randomize = len => { + const chunks = []; + let textSize = 0; + do { + const s = safe.Math_random().toString(36).slice(2); + chunks.push(s); + textSize += s.length; + } + while ( textSize < len ); + return chunks.join(' ').slice(0, len); }; - const peerConnectionCtor = window[rtcName]; - const peerConnectionProto = peerConnectionCtor.prototype; - peerConnectionProto.createDataChannel = - new Proxy(peerConnectionProto.createDataChannel, { - apply: function(target, thisArg, args) { - if ( isGoodConfig(target, args[1]) === false ) { - log('uBO:', args[1]); - return Reflect.apply(target, thisArg, args.slice(0, 1)); - } - return Reflect.apply(target, thisArg, args); - }, - }); - window[rtcName] = - new Proxy(peerConnectionCtor, { - construct: function(target, args) { - if ( isGoodConfig(target, args[0]) === false ) { - log('uBO:', args[0]); - return Reflect.construct(target); - } - return Reflect.construct(target, args); + if ( directive === 'true' ) { + return randomize(10); + } + if ( directive === 'emptyObj' ) { + return '{}'; + } + if ( directive === 'emptyArr' ) { + return '[]'; + } + if ( directive === 'emptyStr' ) { + return ''; + } + if ( directive.startsWith('length:') ) { + const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); + if ( match === null ) { return ''; } + const min = parseInt(match[1], 10); + const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; + const len = safe.Math_min(min + extent * safe.Math_random(), 500000); + return randomize(len | 0); + } + if ( directive.startsWith('war:') ) { + if ( scriptletGlobals.warOrigin === undefined ) { return ''; } + return new Promise(resolve => { + const warOrigin = scriptletGlobals.warOrigin; + const warName = directive.slice(4); + const fullpath = [ warOrigin, '/', warName ]; + const warSecret = scriptletGlobals.warSecret; + if ( warSecret !== undefined ) { + fullpath.push('?secret=', warSecret); } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); + const warXHR = new safe.XMLHttpRequest(); + warXHR.responseType = 'text'; + warXHR.onloadend = ev => { + resolve(ev.target.responseText || ''); + }; + warXHR.open('GET', fullpath.join('')); + warXHR.send(); + }).catch(( ) => ''); + } + if ( directive.startsWith('join:') ) { + const parts = directive.slice(7) + .split(directive.slice(5, 7)) + .map(a => generateContentFn(trusted, a)); + return parts.some(a => a instanceof Promise) + ? Promise.all(parts).then(parts => parts.join('')) + : parts.join(''); + } + if ( trusted ) { + return directive; } -} catch { + return ''; } -webrtcIf(...args); -}, -}; - - -scriptlets['prevent-window-open.js'] = { -aliases: ["nowoif.js","no-window-open-if.js","window.open-defuser.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; +function onIdleFn(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; + return self.requestAnimationFrame(fn); +} +function offIdleFn(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); +} +function sleepFn(ms = 0) { + const nap = ( ) => { + return new Promise(resolve => { + self.requestAnimationFrame(resolve); + }); + } + const until = Date.now() + ms; + const sleep = async resolve => { + do { + await nap(); + } while ( Date.now() < until ); + resolve(); + }; + return new Promise(resolve => { sleep(resolve); }); +} +function lookupElementsFn(directive, until = 0) { + if ( lookupElementsFn.querySelectorEx === undefined ) { + lookupElementsFn.getShadowRoot = elem => { + if ( elem.openOrClosedShadowRoot ) { // Firefox + return elem.openOrClosedShadowRoot; } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); + if ( self.chrome?.dom?.openOrClosedShadowRoot ) { // Chromium + return self.chrome.dom.openOrClosedShadowRoot(elem); } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; + return elem.shadowRoot; + }; + lookupElementsFn.queryOrEvaluateSelector = (selector, context) => { + if ( selector.startsWith('xpath:') === false ) { + return Array.from(context.querySelectorAll(selector)); } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + const result = document.evaluate(selector.slice(6), context, null, 7, null); + const out = []; + if ( result.resultType === 7 ) { + for ( let i = 0; i < result.snapshotLength; i++ ) { + out[i] = result.snapshotItem(i); + } } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + return out; + } + lookupElementsFn.querySelectorEx = (selector, context = document) => { + const pos = selector.indexOf(' >>> '); + if ( pos === -1 ) { + return lookupElementsFn.queryOrEvaluateSelector(selector, context); + } + const outside = selector.slice(0, pos).trim(); + const inside = selector.slice(pos + 5).trim(); + const elems = lookupElementsFn.queryOrEvaluateSelector(outside, context); + const out = []; + for ( let i = 0; i < elems.length; i++ ) { + const shadowRoot = lookupElementsFn.getShadowRoot(elems[i]); + if ( Boolean(shadowRoot) === false ) { continue; } + lookupElementsFn.querySelectorEx(inside, shadowRoot).forEach(a => out.push(a)); } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + return out; + }; + lookupElementsFn.lookup = directive => { + const beVisible = directive.startsWith('when-visible:'); + const selector = beVisible ? directive.slice(13) : directive; + const elems = lookupElementsFn.querySelectorEx(selector); + if ( beVisible !== true ) { return elems; } + return elems.filter(a => a.checkVisibility({ + opacityProperty: true, + visibilityProperty: true, + })); + }; + lookupElementsFn.lookupAsync = details => { + const elems = lookupElementsFn.lookup(details.directive); + if ( elems.length || Date.now() >= details.until ) { + if ( details.observer ) { + details.observer.disconnect(); + details.observer = undefined; + } + if ( details.timer ) { + offIdleFn(details.timer); + details.timer = undefined; + } + return details.resolve(elems); } - try { - return new RegExp(match[1], match[2] || undefined); + if ( details.observer === undefined ) { + details.observer = new MutationObserver(( ) => { + lookupElementsFn.lookupAsync(details); + }); + details.observer.observe(document, { + attributes: true, + childList: true, + subtree: true, + }); } - catch { + if ( details.timer === undefined ) { + details.timer = onIdleFn(( ) => { + details.timer = undefined; + lookupElementsFn.lookupAsync(details); + }, { timeout: 151 }); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, + }; + } + if ( until === 0 ) { + return lookupElementsFn.lookup(directive); + } + return new Promise(resolve => { + lookupElementsFn.lookupAsync({ directive, until, resolve }); + }); +} +function runAt(fn, when) { + const intFromReadyState = state => { + const targets = { + 'loading': 1, 'asap': 1, + 'interactive': 2, 'end': 2, '2': 2, + 'complete': 3, 'idle': 3, '3': 3, + }; + const tokens = Array.isArray(state) ? state : [ state ]; + for ( const token of tokens ) { + const prop = `${token}`; + if ( Object.hasOwn(targets, prop) === false ) { continue; } + return targets[prop]; + } + return 0; }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const runAt = intFromReadyState(when); + if ( intFromReadyState(document.readyState) >= runAt ) { + fn(); return; + } + const onStateChange = ( ) => { + if ( intFromReadyState(document.readyState) < runAt ) { return; } + fn(); + safe.removeEventListener.apply(document, args); + }; + const safe = safeSelf(); + const args = [ 'readystatechange', onStateChange, { capture: true } ]; + safe.addEventListener.apply(document, args); +} +function runAtHtmlElementFn(fn) { + if ( document.documentElement ) { + fn(); + return; + } + const observer = new MutationObserver(( ) => { + observer.disconnect(); + fn(); + }); + observer.observe(document, { childList: true }); +} +function abortCurrentScriptFn( + target = '', + needle = '', + context = '' +) { + if ( typeof target !== 'string' ) { return; } + if ( target === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('abort-current-script', target, needle, context); + const reNeedle = safe.patternToRegex(needle); + const reContext = safe.patternToRegex(context); + const thisScript = document.currentScript; + const exceptionToken = getExceptionTokenFn(); + const scriptTexts = new WeakMap(); + const textContentGetter = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent').get; + const getScriptText = elem => { + let text = textContentGetter.call(elem); + if ( text.trim() !== '' ) { return text; } + if ( scriptTexts.has(elem) ) { return scriptTexts.get(elem); } + const [ , mime, content ] = /^data:([^,]*),(.+)$/.exec(elem.src.trim()) || + [ '', '', '' ]; + try { + switch ( true ) { + case mime.endsWith(';base64'): + text = self.atob(content); + break; + default: + text = self.decodeURIComponent(content); + break; + } + } catch { } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); + scriptTexts.set(elem, text); return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + const validate = ( ) => { + const e = document.currentScript; + if ( e instanceof HTMLScriptElement === false ) { return; } + if ( e === thisScript ) { return; } + if ( context !== '' && reContext.test(e.src) === false ) { return; } + if ( safe.logLevel > 1 && context !== '' ) { + safe.uboLog(logPrefix, `Matched src\n${e.src}`); + } + const scriptText = getScriptText(e); + if ( reNeedle.test(scriptText) === false ) { return; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched text\n${scriptText}`); + } + safe.uboLog(logPrefix, 'Aborted'); + throw new ReferenceError(exceptionToken); + }; + let currentValue = trapPropertyFn(target, { + get: function() { + validate(); + return currentValue; + }, + set: function(a) { + validate(); + currentValue = a; + } + }, { canThrow: true }); +} +function abortCurrentScript(...args) { + runAtHtmlElementFn(( ) => { + abortCurrentScriptFn(...args); + }); +} +function setAttrFn( + trusted = false, + logPrefix, + selector = '', + attr = '', + value = '', + options = {} +) { + if ( selector === '' ) { return; } + if ( attr === '' ) { return; } + + const safe = safeSelf(); + const copyFrom = trusted === false && /^\[.+\]$/.test(value) + ? value.slice(1, -1) + : ''; + + const extractValue = elem => copyFrom !== '' + ? elem.getAttribute(copyFrom) || '' + : value; + + const applySetAttr = ( ) => { + let elems; + try { + elems = document.querySelectorAll(selector); + } catch { + return false; + } + for ( const elem of elems ) { + const before = elem.getAttribute(attr); + const after = extractValue(elem); + if ( after === before ) { continue; } + if ( after !== '' && /^on/i.test(attr) ) { + if ( attr.toLowerCase() in elem ) { continue; } } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; + elem.setAttribute(attr, after); + safe.uboLog(logPrefix, `${attr}="${after}"`); + } + return true; + }; + + let observer, timer; + const onDomChanged = mutations => { + if ( timer !== undefined ) { return; } + let shouldWork = false; + for ( const mutation of mutations ) { + if ( mutation.addedNodes.length === 0 ) { continue; } + for ( const node of mutation.addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + shouldWork = true; break; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + if ( shouldWork ) { break; } + } + if ( shouldWork === false ) { return; } + timer = self.requestAnimationFrame(( ) => { + timer = undefined; + applySetAttr(); + }); + }; + + const start = ( ) => { + if ( applySetAttr() === false ) { return; } + observer = new MutationObserver(onDomChanged); + const root = document.documentElement; + if ( root instanceof self.Node === false ) { return; } + observer.observe(root, { subtree: true, childList: true }); + }; + runAt(( ) => { start(); }, options.runAt || 'idle'); } -function proxyApplyFn( - target = '', - handler = '', - options = {} +function setAttr( + selector = '', + attr = '', + value = '', + ...varargs ) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-attr', selector, attr, value); + const validValues = [ '', 'false', 'true' ]; + if ( validValues.includes(value.toLowerCase()) === false ) { + if ( /^\d+$/.test(value) ) { + const n = parseInt(value, 10); + if ( n >= 32768 ) { return; } + value = `${n}`; + } else if ( /^\[.+\]$/.test(value) === false ) { + return; } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; + const options = safe.parseVarargs(varargs); + setAttrFn(false, logPrefix, selector, attr, value, options); } -function noWindowOpenIf( - pattern = '', - delay = '', - decoy = '' +function trustedSetAttr( + selector = '', + attr = '', + value = '', + ...varargs ) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('no-window-open-if', pattern, delay, decoy); - const targetMatchResult = pattern.startsWith('!') === false; - if ( targetMatchResult === false ) { - pattern = pattern.slice(1); + const logPrefix = safe.makeLogPrefix('trusted-set-attr', selector, attr, value); + const options = safe.parseVarargs(varargs); + setAttrFn(true, logPrefix, selector, attr, value, options); +} +function removeAttr( + rawToken = '', + rawSelector = '', + behavior = '', + ...varargs +) { + if ( typeof rawToken !== 'string' ) { return; } + if ( rawToken === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('remove-attr', + rawToken, rawSelector, behavior, ...varargs + ); + const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); + const selector = tokens.map(a => { + const b = CSS.escape(a); + return rawSelector.includes(`[${b}]`) ? rawSelector : `${rawSelector}[${b}]`; + }).join(','); + const lazily = /\basap\b/.test(behavior) === false; + const options = safe.parseVarargs(varargs); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); } - const rePattern = safe.patternToRegex(pattern); - const autoRemoveAfter = (parseFloat(delay) || 0) * 1000; - const setTimeout = self.setTimeout; - const createDecoy = function(tag, urlProp, url) { - const decoyElem = document.createElement(tag); - decoyElem[urlProp] = url; - decoyElem.style.setProperty('height','1px', 'important'); - decoyElem.style.setProperty('position','fixed', 'important'); - decoyElem.style.setProperty('top','-1px', 'important'); - decoyElem.style.setProperty('width','1px', 'important'); - document.body.appendChild(decoyElem); - setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter); - return decoyElem; + const rmattrFromNode = node => { + for ( const attr of tokens ) { + if ( node.hasAttribute(attr) === false ) { continue; } + node.removeAttribute(attr); + safe.uboLog(logPrefix, `Removed attribute '${attr}'`); + } }; - const noopFunc = function(){}; - proxyApplyFn('open', function open(context) { - if ( pattern === 'debug' && safe.logLevel !== 0 ) { - debugger; // eslint-disable-line no-debugger - return context.reflect(); + const rmattr = nodes => { + for ( const node of nodes ?? document.querySelectorAll(selector) ) { + rmattrFromNode(node); } - const { callArgs } = context; - const haystack = callArgs.join(' '); - if ( rePattern.test(haystack) !== targetMatchResult ) { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Allowed (${callArgs.join(', ')})`); + }; + const rmAttrLazily = ( ) => { + if ( rmAttrLazily.timer !== undefined ) { return; } + rmAttrLazily.timer = onIdleFn(( ) => { + rmAttrLazily.timer = undefined; + rmattr(); + }, { timeout: 17 }); + }; + const mutationHandler = mutations => { + for ( const { addedNodes, removedNodes } of mutations ) { + for ( const node of addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + if ( lazily ) { return rmAttrLazily(); } + if ( node.matches(selector) ) { + rmattrFromNode(node); + } + if ( node.childElementCount ) { + rmattr(node.querySelectorAll(selector)); + } + } + if ( lazily ) { return; } + for ( const node of removedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + if ( node.matches(selector) ) { + rmattrFromNode(node); + } } - return context.reflect(); } - safe.uboLog(logPrefix, `Prevented (${callArgs.join(', ')})`); - if ( delay === '' ) { return null; } - if ( decoy === 'blank' ) { - callArgs[0] = 'about:blank'; - const r = context.reflect(); - setTimeout(( ) => { r.close(); }, autoRemoveAfter); - return r; + }; + const stop = ( ) => { + if ( start.observer ) { + start.observer.disconnect(); + start.observer = undefined; + } + if ( rmAttrLazily.timer ) { + offIdleFn(rmAttrLazily.timer); + rmAttrLazily.timer = undefined; } - const decoyElem = decoy === 'obj' - ? createDecoy('object', 'data', ...callArgs) - : createDecoy('iframe', 'src', ...callArgs); - let popup = decoyElem.contentWindow; - if ( typeof popup === 'object' && popup !== null ) { - Object.defineProperty(popup, 'closed', { value: false }); - } else { - popup = new Proxy(self, { - get: function(target, prop, ...args) { - if ( prop === 'closed' ) { return false; } - const r = Reflect.get(target, prop, ...args); - if ( typeof r === 'function' ) { return noopFunc; } - return r; - }, - set: function(...args) { - return Reflect.set(...args); - }, - }); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Quitting'); } - if ( safe.logLevel !== 0 ) { - popup = new Proxy(popup, { - get: function(target, prop, ...args) { - const r = Reflect.get(target, prop, ...args); - safe.uboLog(logPrefix, `popup / get ${prop} === ${r}`); - if ( typeof r === 'function' ) { - return (...args) => { return r.call(target, ...args); }; - } - return r; - }, - set: function(target, prop, value, ...args) { - safe.uboLog(logPrefix, `popup / set ${prop} = ${value}`); - return Reflect.set(target, prop, value, ...args); - }, - }); + }; + const start = ( ) => { + rmattr(); + if ( /\bstay\b/.test(behavior) === false ) { + if ( options.quitAfter === undefined ) { return; } } - return popup; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { + start.observer = new MutationObserver(mutationHandler); + start.observer.observe(document, { + attributes: true, + attributeFilter: tokens, + childList: true, + subtree: true, + }); + if ( options.quitAfter ) { + runAt(( ) => { + self.setTimeout(stop, options.quitAfter * 1000); + }, 'load'); + } + }; + runAt(( ) => { start(); }, safe.String_split.call(behavior, /\s+/)); } -noWindowOpenIf(...args); -}, -}; - - -scriptlets['close-window.js'] = { -aliases: ["window-close-if.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; +function trustedCreateHTML( + parentSelector, + htmlStr = '', + durationStr = '', + ...varargs +) { + if ( parentSelector === '' ) { return; } + if ( htmlStr === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-create-html', parentSelector, htmlStr, durationStr); + const extraArgs = safe.parseVarargs(varargs); + // We do not want to recursively create elements + self.trustedCreateHTML = true; + let ancestor = self.frameElement; + while ( ancestor ) { + const doc = ancestor.ownerDocument; + if ( doc === null ) { break; } + const win = doc.defaultView; + if ( win === null ) { break; } + if ( win.trustedCreateHTML ) { return; } + ancestor = ancestor.frameElement; } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, + const duration = parseInt(durationStr, 10); + const domParser = new DOMParser(); + const externalDoc = domParser.parseFromString(htmlStr, 'text/html'); + const toAppend = []; + while ( externalDoc.body.firstChild !== null ) { + toAppend.push(document.adoptNode(externalDoc.body.firstChild)); + } + if ( toAppend.length === 0 ) { return; } + const toRemove = []; + const remove = ( ) => { + for ( const node of toRemove ) { + if ( node.parentNode === null ) { continue; } + node.parentNode.removeChild(node); + } + safe.uboLog(logPrefix, 'Node(s) removed'); }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const appendOne = (target, nodes) => { + for ( const node of nodes ) { + target.append(node); + if ( isNaN(duration) ) { continue; } + toRemove.push(node); + } + }; + const append = ( ) => { + const targets = document.querySelectorAll(parentSelector); + if ( targets.length === 0 ) { return false; } + const limit = Math.min(targets.length, extraArgs.limit || 1) - 1; + for ( let i = 0; i < limit; i++ ) { + appendOne(targets[i], toAppend.map(a => a.cloneNode(true))); + } + appendOne(targets[limit], toAppend); + safe.uboLog(logPrefix, 'Node(s) appended'); + if ( toRemove.length === 0 ) { return true; } + setTimeout(remove, duration); + return true; + }; + const start = ( ) => { + if ( append() ) { return; } + const observer = new MutationObserver(( ) => { + if ( append() === false ) { return; } + observer.disconnect(); + }); + const observerOptions = { + childList: true, + subtree: true, + }; + if ( /[#.[]/.test(parentSelector) ) { + observerOptions.attributes = true; + if ( parentSelector.includes('[') === false ) { + observerOptions.attributeFilter = []; + if ( parentSelector.includes('#') ) { + observerOptions.attributeFilter.push('id'); + } + if ( parentSelector.includes('.') ) { + observerOptions.attributeFilter.push('class'); + } + } } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; + observer.observe(document, observerOptions); }; + runAt(start, extraArgs.runAt || 'loading'); +} +function urlSkip(url, blocked, steps) { try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + let redirectBlocked = false; + let urlout = url; + for ( const step of steps ) { + const urlin = urlout; + const c0 = step.charCodeAt(0); + // Extract from hash + if ( c0 === 0x23 && step === '#' ) { // # + const pos = urlin.indexOf('#'); + urlout = pos !== -1 ? urlin.slice(pos+1) : ''; + continue; } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + // Extract from URL parameter name at position i + if ( c0 === 0x26 ) { // & + const i = (parseInt(step.slice(1)) || 0) - 1; + if ( i < 0 ) { return; } + const url = new URL(urlin); + if ( i >= url.searchParams.size ) { return; } + const params = Array.from(url.searchParams.keys()); + urlout = decodeURIComponent(params[i]); + continue; } - }; - bc.postMessage('areyouready?'); + // Enforce https + if ( c0 === 0x2B && step === '+https' ) { // + + const s = urlin.replace(/^https?:\/\//, ''); + if ( /^[\w-]:\/\//.test(s) ) { return; } + urlout = `https://${s}`; + continue; + } + // Decode + if ( c0 === 0x2D ) { // - + // Base64 + if ( step === '-base64' ) { + urlout = self.atob(urlin); + continue; + } + // Safe Base64 + if ( step === '-safebase64' ) { + if ( urlSkip.safeBase64Replacer === undefined ) { + urlSkip.safeBase64Map = { '-': '+', '_': '/' }; + urlSkip.safeBase64Replacer = s => urlSkip.safeBase64Map[s]; + } + urlout = urlin.replace(/[-_]/g, urlSkip.safeBase64Replacer); + urlout = self.atob(urlout); + continue; + } + // URI component + if ( step === '-uricomponent' ) { + urlout = decodeURIComponent(urlin); + continue; + } + // Enable skip of blocked requests + if ( step === '-blocked' ) { + redirectBlocked = true; + continue; + } + } + // Regex extraction from first capture group + if ( c0 === 0x2F ) { // / + const re = new RegExp(step.slice(1, -1)); + const match = re.exec(urlin); + if ( match === null ) { return; } + if ( match.length <= 1 ) { return; } + urlout = match[1]; + continue; + } + // Extract from URL parameter + if ( c0 === 0x3F ) { // ? + urlout = (new URL(urlin)).searchParams.get(step.slice(1)); + if ( urlout === null ) { return; } + if ( urlout.includes(' ') ) { + urlout = urlout.replace(/ /g, '%20'); + } + continue; + } + // Unknown directive + return; + } + const urlfinal = new URL(urlout); + if ( urlfinal.protocol !== 'https:' ) { + if ( urlfinal.protocol !== 'http:' ) { return; } + } + if ( blocked && redirectBlocked !== true ) { return; } + return urlout; } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; } - return safe; } -function closeWindow( - arg1 = '' +function hrefSanitizer( + selector = '', + source = '' ) { - if ( typeof arg1 !== 'string' ) { return; } + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } const safe = safeSelf(); - let subject = ''; - if ( /^\/.*\/$/.test(arg1) ) { - subject = window.location.href; - } else if ( arg1 !== '' ) { - subject = `${window.location.pathname}${window.location.search}`; - } - try { - const re = safe.patternToRegex(arg1); - if ( re.test(subject) ) { - window.close(); + const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source); + if ( source === '' ) { source = 'text'; } + const sanitizeCopycats = (href, text) => { + let elems = []; + try { + elems = document.querySelectorAll(`a[href="${href}"`); } - } catch(ex) { - console.log(ex); - } -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -closeWindow(...args); -}, -}; - - -scriptlets['window.name-defuser.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function windowNameDefuser() { - if ( window === window.top ) { - window.name = ''; - } -}; -windowNameDefuser(...args); -}, -}; - - -scriptlets['overlay-buster.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function overlayBuster(allFrames) { - if ( allFrames === '' && window !== window.top ) { return; } - var tstart; - var ttl = 30000; - var delay = 0; - var delayStep = 50; - var buster = function() { - var docEl = document.documentElement, - bodyEl = document.body, - vw = Math.min(docEl.clientWidth, window.innerWidth), - vh = Math.min(docEl.clientHeight, window.innerHeight), - tol = Math.min(vw, vh) * 0.05, - el = document.elementFromPoint(vw/2, vh/2), - style, rect; - for (;;) { - if ( el === null || el.parentNode === null || el === bodyEl ) { + catch { + } + for ( const elem of elems ) { + elem.setAttribute('href', text); + } + return elems.length; + }; + const validateURL = text => { + if ( typeof text !== 'string' ) { return ''; } + if ( text === '' ) { return ''; } + if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; } + try { + const url = new URL(text, document.location); + return url.href; + } catch { + } + return ''; + }; + const extractURL = (elem, source) => { + if ( /^\[.*\]$/.test(source) ) { + return elem.getAttribute(source.slice(1,-1).trim()) || ''; + } + if ( source === 'text' ) { + return elem.textContent + .replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters + .replace(/[^\x21-\x7e]+$/, ''); // remove trailing invalid characters + } + const steps = source.replace(/(\S)\?/g, '\\1 ?').split(/\s+/); + const url = urlSkip(elem.href, false, steps); + if ( url === undefined ) { return; } + return url.replace(/ /g, '%20'); + }; + const sanitize = ( ) => { + let elems = []; + try { + elems = document.querySelectorAll(selector); + } + catch { + return false; + } + for ( const elem of elems ) { + if ( elem.localName !== 'a' ) { continue; } + if ( elem.hasAttribute('href') === false ) { continue; } + const href = elem.getAttribute('href'); + const text = extractURL(elem, source); + const hrefAfter = validateURL(text); + if ( hrefAfter === '' ) { continue; } + if ( hrefAfter === href ) { continue; } + elem.setAttribute('href', hrefAfter); + const count = sanitizeCopycats(href, hrefAfter); + safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`); + } + return true; + }; + let observer, timer; + const onDomChanged = mutations => { + if ( timer !== undefined ) { return; } + let shouldSanitize = false; + for ( const mutation of mutations ) { + if ( mutation.addedNodes.length === 0 ) { continue; } + for ( const node of mutation.addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + shouldSanitize = true; break; } - style = window.getComputedStyle(el); - if ( parseInt(style.zIndex, 10) >= 1000 || style.position === 'fixed' ) { - rect = el.getBoundingClientRect(); - if ( rect.left <= tol && rect.top <= tol && (vw - rect.right) <= tol && (vh - rect.bottom) < tol ) { - el.parentNode.removeChild(el); - tstart = Date.now(); - el = document.elementFromPoint(vw/2, vh/2); - bodyEl.style.setProperty('overflow', 'auto', 'important'); - docEl.style.setProperty('overflow', 'auto', 'important'); - continue; + if ( shouldSanitize ) { break; } + } + if ( shouldSanitize === false ) { return; } + timer = onIdleFn(( ) => { + timer = undefined; + sanitize(); + }); + }; + const start = ( ) => { + if ( sanitize() === false ) { return; } + observer = new MutationObserver(onDomChanged); + observer.observe(document.body, { + subtree: true, + childList: true, + }); + }; + runAt(( ) => { start(); }, 'interactive'); +} +class ArglistParser { + constructor(separatorChar = ',', mustQuote = false) { + this.separatorChar = this.actualSeparatorChar = separatorChar; + this.separatorCode = this.actualSeparatorCode = separatorChar.charCodeAt(0); + this.mustQuote = mustQuote; + this.quoteBeg = 0; this.quoteEnd = 0; + this.argBeg = 0; this.argEnd = 0; + this.separatorBeg = 0; this.separatorEnd = 0; + this.transform = false; + this.failed = false; + this.reWhitespaceStart = /^\s+/; + this.reWhitespaceEnd = /(?:^|\S)(\s+)$/; + this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; + this.reTrailingEscapeChars = /\\+$/; + } + nextArg(pattern, beg = 0) { + const len = pattern.length; + this.quoteBeg = beg + this.leftWhitespaceCount(pattern.slice(beg)); + this.failed = false; + const qc = pattern.charCodeAt(this.quoteBeg); + if ( qc === 0x22 /* " */ || qc === 0x27 /* ' */ || qc === 0x60 /* ` */ ) { + this.indexOfNextArgSeparator(pattern, qc); + if ( this.argEnd !== len ) { + this.quoteEnd = this.argEnd + 1; + this.separatorBeg = this.separatorEnd = this.quoteEnd; + this.separatorEnd += this.leftWhitespaceCount(pattern.slice(this.quoteEnd)); + if ( this.separatorEnd === len ) { return this; } + if ( pattern.charCodeAt(this.separatorEnd) === this.separatorCode ) { + this.separatorEnd += 1; + return this; } } - el = el.parentNode; } - if ( (Date.now() - tstart) < ttl ) { - delay = Math.min(delay + delayStep, 1000); - setTimeout(buster, delay); + this.indexOfNextArgSeparator(pattern, this.separatorCode); + this.separatorBeg = this.separatorEnd = this.argEnd; + if ( this.separatorBeg < len ) { + this.separatorEnd += 1; } - }; - var domReady = function(ev) { - if ( ev ) { - document.removeEventListener(ev.type, domReady); + this.argEnd -= this.rightWhitespaceCount(pattern.slice(0, this.separatorBeg)); + this.quoteEnd = this.argEnd; + if ( this.mustQuote ) { + this.failed = true; } - tstart = Date.now(); - setTimeout(buster, delay); - }; - if ( document.readyState === 'loading' ) { - document.addEventListener('DOMContentLoaded', domReady); - } else { - domReady(); + return this; } -}; -overlayBuster(...args); -}, -}; - - -scriptlets['alert-buster.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function alertBuster() { - window.alert = new Proxy(window.alert, { - apply: function(a) { - console.info(a); - }, - get(target, prop) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop); - }, - }); -}; -alertBuster(...args); -}, -}; - - -scriptlets['nowebrtc.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function noWebrtc() { - var rtcName = window.RTCPeerConnection ? 'RTCPeerConnection' : ( - window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : '' - ); - if ( rtcName === '' ) { return; } - var log = console.log.bind(console); - var pc = function(cfg) { - log('Document tried to create an RTCPeerConnection: %o', cfg); - }; - const noop = function() { - }; - pc.prototype = { - close: noop, - createDataChannel: noop, - createOffer: noop, - setRemoteDescription: noop, - toString: function() { - return '[object RTCPeerConnection]'; + normalizeArg(s, char = '') { + if ( char === '' ) { char = this.actualSeparatorChar; } + let out = ''; + let pos = 0; + while ( (pos = s.lastIndexOf(char)) !== -1 ) { + out = s.slice(pos) + out; + s = s.slice(0, pos); + const match = this.reTrailingEscapeChars.exec(s); + if ( match === null ) { continue; } + const tail = (match[0].length & 1) !== 0 + ? match[0].slice(0, -1) + : match[0]; + out = tail + out; + s = s.slice(0, -match[0].length); } - }; - var z = window[rtcName]; - window[rtcName] = pc.bind(window); - if ( z.prototype ) { - z.prototype.createDataChannel = function() { - return { - close: function() {}, - send: function() {} - }; - }.bind(null); + if ( out === '' ) { return s; } + return s + out; } -}; -noWebrtc(...args); -}, -}; - - -scriptlets['disable-newtab-links.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function disableNewtabLinks() { - document.addEventListener('click', ev => { - let target = ev.target; - while ( target !== null ) { - if ( target.localName === 'a' && target.hasAttribute('target') ) { - ev.stopPropagation(); - ev.preventDefault(); - break; + leftWhitespaceCount(s) { + const match = this.reWhitespaceStart.exec(s); + return match === null ? 0 : match[0].length; + } + rightWhitespaceCount(s) { + const match = this.reWhitespaceEnd.exec(s); + return match === null ? 0 : match[1].length; + } + indexOfNextArgSeparator(pattern, separatorCode) { + this.argBeg = this.argEnd = separatorCode !== this.separatorCode + ? this.quoteBeg + 1 + : this.quoteBeg; + this.transform = false; + if ( separatorCode !== this.actualSeparatorCode ) { + this.actualSeparatorCode = separatorCode; + this.actualSeparatorChar = String.fromCharCode(separatorCode); + } + while ( this.argEnd < pattern.length ) { + const pos = pattern.indexOf(this.actualSeparatorChar, this.argEnd); + if ( pos === -1 ) { + return (this.argEnd = pattern.length); } - target = target.parentNode; + if ( this.reOddTrailingEscape.test(pattern.slice(0, pos)) === false ) { + return (this.argEnd = pos); + } + this.transform = true; + this.argEnd = pos + 1; } - }, { capture: true }); -}; -disableNewtabLinks(...args); -}, -}; - - -scriptlets['xml-prune.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyToStringFn(proxiedFn, nativeFn) { - if ( proxyToStringFn.proxies === undefined ) { - proxyToStringFn.proxies = new WeakMap(); - proxyToStringFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyToStringFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; + } +} +class JSONPath { + static create(query) { + const jsonp = new JSONPath(); + jsonp.compile(query); + return jsonp; + } + static toJSON(obj, stringifier, ...args) { + return (stringifier || JSON.stringify)(obj, ...args) + .replace(/\//g, '\\/'); + } + static keys = Object.keys; + static entries = Object.entries; + static hasOwn = Object.hasOwn; + static Regex = RegExp; + get value() { + return this.#compiled && this.#compiled.rval; + } + set value(v) { + if ( this.#compiled === undefined ) { return; } + this.#compiled.rval = v; + } + get valid() { + return this.#compiled !== undefined; + } + compile(query) { + this.#compiled = undefined; + this.v2 = query.startsWith('v2:'); + if ( this.v2 ) { query = query.slice(3); } + const r = this.#compile(query, 0); + if ( r === undefined ) { return; } + if ( r.i !== query.length ) { + let val; + if ( query.startsWith('=', r.i) ) { + const match = this.#reRval.exec(query.slice(r.i)); + if ( match ) { + r.modify = match[1]; + val = match[2]; + } else { + val = query.slice(r.i+1); } - return proxyToStringFn.nativeToString.call(proxied); + } else if ( query.startsWith('+=', r.i) ) { + r.modify = '+'; + val = query.slice(r.i+2); } - }); - proxyToStringFn.proxies.set(proxiedToString, proxyToStringFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - proxyToStringFn.proxies.set(proxiedFn, nativeFn); -} -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); + try { r.rval = JSON.parse(val); } + catch { return; } } + r.v2 = this.v2; + this.#compiled = r; } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; + evaluate(root) { + if ( this.valid === false ) { return []; } + this.#root = { '$': root }; + const paths = this.#evaluate(this.#compiled.steps, []); + this.#root = null; + return paths; } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; + apply(root) { + if ( this.valid === false ) { return; } + const { rval } = this.#compiled; + this.#root = { '$': root }; + const paths = this.#evaluate(this.#compiled.steps, []); + let i = paths.length + if ( i === 0 ) { this.#root = null; return; } + while ( i-- ) { + const { obj, key } = this.#resolvePath(paths[i]); + if ( obj === undefined ) { continue; } + if ( rval !== undefined ) { + this.#modifyVal(obj, key); + } else if ( Array.isArray(obj) && typeof key === 'number' ) { + obj.splice(key, 1); + } else { + delete obj[key]; } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); + } + const result = this.#root['$'] ?? null; + this.#root = null; + return result; + } + dump() { + return JSON.stringify(this.#compiled); + } + toJSON(obj, ...args) { + return JSONPath.toJSON(obj, null, ...args) + } + get [Symbol.toStringTag]() { + return 'JSONPath'; + } + #UNDEFINED = 0; + #ROOT = 1; + #CURRENT = 2; + #CHILDREN = 3; + #DESCENDANTS = 4; + #QUANTIFIER = 5; + #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; + #reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/; + #reIndice = /^-?\d+/; + #reRval = /^=([a-z]+)\((.+)\)$/; + #reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/; + #root; + #compiled; + #compile(query, i) { + if ( query.length === 0 ) { return; } + const steps = []; + let c = query.charCodeAt(i); + if ( c === 0x24 /* $ */ ) { + steps.push({ mv: this.#ROOT }); + i += 1; + } else if ( c === 0x40 /* @ */ ) { + steps.push({ mv: this.#CURRENT }); + i += 1; + } else { + steps.push({ mv: i === 0 ? this.#ROOT : this.#CURRENT }); + } + let mv = this.#UNDEFINED; + for (;;) { + if ( i === query.length ) { break; } + c = query.charCodeAt(i); + if ( c === 0x20 /* whitespace */ ) { + i += 1; + continue; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; + // Dot accessor syntax + if ( c === 0x2E /* . */ ) { + if ( mv !== this.#UNDEFINED ) { return; } + if ( query.startsWith('..', i) ) { + mv = this.#DESCENDANTS; + i += 2; + } else { + mv = this.#CHILDREN; + i += 1; + } + continue; } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + if ( c === 0x3B /* ; */ ) { + if ( query.startsWith(';$', i) === false ) { return; } + steps.push( + { mv: this.#QUANTIFIER, min: 1, max: 1e6 }, + { mv: this.#ROOT } + ); + i += 2; + mv = this.#UNDEFINED; + continue; } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + if ( c === 0x7B /* { */ ) { + const match = this.#reQuantifier.exec(query.slice(i)); + if ( match === null ) { return; } + const comma = match[1].indexOf(','); + let min, max; + if ( comma === -1 ) { + min = max = parseInt(match[1]); + } else { + min = parseInt(match[1].slice(0, comma)) || 0; + max = parseInt(match[1].slice(comma+1)) || 1e6; + } + steps.push( + { mv: this.#QUANTIFIER, min, max }, + { mv: this.#ROOT } + ); + i += match[0].length; + mv = this.#UNDEFINED; + continue; } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + if ( c !== 0x5B /* [ */ ) { + if ( mv === this.#UNDEFINED ) { + const step = steps.at(-1); + if ( step === undefined ) { return; } + const j = this.#compileExpr(query, step, i); + if ( j ) { i = j; } + break; + } + const r = this.#consumeUnquotedIdentifier(query, i); + if ( r === undefined ) { return; } + steps.push({ mv, k: r.s }); + i = r.i; + mv = this.#UNDEFINED; + continue; } - try { - return new RegExp(match[1], match[2] || undefined); + // Bracket accessor syntax + if ( mv === this.#CHILDREN ) { return; } + if ( query.startsWith('[?', i) ) { + const not = query.charCodeAt(i+2) === 0x21 /* ! */ ? 1 : 0; + const j = i + 2 + not; + const r = this.#compile(query, j); + if ( r === undefined ) { return; } + if ( query.startsWith(']', r.i) === false ) { return; } + if ( not ) { r.steps.at(-1).not = true; } + steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); + i = r.i + 1; + mv = this.#UNDEFINED; + continue; } - catch { + if ( query.startsWith('[*]', i) ) { + mv ||= this.#CHILDREN; + steps.push({ mv, k: '*' }); + i += 3; + mv = this.#UNDEFINED; + continue; } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const r = this.#consumeIdentifier(query, i+1); + if ( r === undefined ) { return; } + mv ||= this.#CHILDREN; + steps.push({ mv, k: r.s }); + i = r.i + 1; + mv = this.#UNDEFINED; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; + if ( steps.length === 0 ) { return; } + if ( mv !== this.#UNDEFINED ) { return; } + return { steps, i }; + } + #evaluate(steps, pathin) { + let resultset = []; + if ( Array.isArray(steps) === false ) { return resultset; } + for ( const step of steps ) { + switch ( step.mv ) { + case this.#ROOT: + resultset = [ [ '$' ] ]; break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; + case this.#CURRENT: + if ( step.op ) { + const { obj, key } = this.#resolvePath(pathin); + if ( obj === undefined ) { return []; } + const outcome = this.#evaluateExpr(step, obj, key); + if ( outcome !== true ) { break; } + } + resultset = [ pathin ]; break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; + case this.#CHILDREN: + case this.#DESCENDANTS: { + if ( resultset.length === 0 ) { break; } + resultset = this.#getMatches(resultset, step); break; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } + case this.#QUANTIFIER: { + const { length } = resultset; + if ( length < step.min || length > step.max ) { return []; } + resultset = []; + break; } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} -function modifyXhrResponseFn( - propsToMatch = '', - modifierFn = '' -) { - if ( typeof propsToMatch !== 'string' ) { return; } - const safe = safeSelf(); - if ( modifyXhrResponseFn.xhrInstances === undefined ) { - modifyXhrResponseFn.xhrInstances = new WeakMap(); - } - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const NativeXMLHttpRequest = self.XMLHttpRequest; - const TrappedXMLHttpRequest = class XMLHttpRequest extends NativeXMLHttpRequest { - open(method, url, ...args) { - const haystack = { method, url }; - if ( propsToMatch === '' ) { - safe.uboLog(`modifyXhrResponseFn() / Called: ${safe.JSON_stringify(haystack, null, 2)}`); - } else if ( matchObjectPropertiesFn(propNeedles, haystack) ) { - modifyXhrResponseFn.xhrInstances.set(this, modifierFn); + default: + break; } - return super.open(method, url, ...args); - } - get response() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.response) - : super.response; - } - get responseText() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.responseText) - : super.responseText; - } - get responseXML() { - const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); - return modifierFn - ? modifierFn(this, super.responseXML) - : super.responseXML; - } - }; - proxyToStringFn(TrappedXMLHttpRequest.prototype.open, NativeXMLHttpRequest.prototype.open); - proxyToStringFn(TrappedXMLHttpRequest, NativeXMLHttpRequest); - self.XMLHttpRequest = TrappedXMLHttpRequest; -} -function xmlPrune( - selector = '', - selectorCheck = '', - urlPattern = '', - ...varargs -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('xml-prune', selector, selectorCheck, urlPattern); - const reUrl = safe.patternToRegex(urlPattern); - const extraArgs = safe.parseVarargs(varargs); - const queryAll = (xmlDoc, selector) => { - const isXpath = /^xpath\(.+\)$/.test(selector); - if ( isXpath === false ) { - return Array.from(xmlDoc.querySelectorAll(selector)); - } - const xpr = xmlDoc.evaluate( - selector.slice(6, -1), - xmlDoc, - null, - XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, - null - ); - const out = []; - for ( let i = 0; i < xpr.snapshotLength; i++ ) { - const node = xpr.snapshotItem(i); - out.push(node); } - return out; - }; - const pruneFromDoc = xmlDoc => { - try { - if ( selectorCheck !== '' && xmlDoc.querySelector(selectorCheck) === null ) { - return xmlDoc; + return resultset; + } + #getMatches(listin, step) { + const listout = []; + for ( const pathin of listin ) { + const { value: owner } = this.#resolvePath(pathin); + if ( owner === undefined ) { continue; } + if ( step.steps ) { + this.#getMatchesFromExpr(pathin, step, owner, listout); + continue; } - if ( extraArgs.logdoc ) { - const serializer = new XMLSerializer(); - safe.uboLog(logPrefix, `Document is\n\t${serializer.serializeToString(xmlDoc)}`); + const iter = this.#expandKey(owner, step.k); + if ( iter ) { + for ( const k of iter ) { + const outcome = this.#evaluateExpr(step, owner, k); + if ( outcome !== true ) { continue; } + listout.push([ ...pathin, k ]); + } } - const items = queryAll(xmlDoc, selector); - if ( items.length === 0 ) { return xmlDoc; } - safe.uboLog(logPrefix, `Removing ${items.length} items`); - for ( const item of items ) { - if ( item.nodeType === 1 ) { - item.remove(); - } else if ( item.nodeType === 2 ) { - item.ownerElement.removeAttribute(item.nodeName); + if ( step.mv !== this.#DESCENDANTS ) { continue; } + for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { + const iter = this.#expandKey(obj[key], step.k); + if ( iter === undefined ) { continue; } + for ( const k of iter ) { + const outcome = this.#evaluateExpr(step, obj[key], k); + if ( outcome !== true ) { continue; } + listout.push([ ...pathin, ...path, k ]); } - safe.uboLog(logPrefix, `${item.constructor.name}.${item.nodeName} removed`); } - } catch(ex) { - safe.uboErr(logPrefix, `Error: ${ex}`); } - return xmlDoc; - }; - const pruneFromText = text => { - if ( (/^\s*\s*$/.test(text)) === false ) { - return text; + return listout; + } + #expandKey(owner, k) { + if ( typeof owner !== 'object' || owner === null ) { return; } + if ( Array.isArray(k) ) { + const out = []; + for ( const a of k ) { + const iter = this.#expandKey(owner, a); + if ( iter === undefined ) { continue; } + out.push(...iter); + } + return out; } - try { - const xmlParser = new DOMParser(); - const xmlDoc = xmlParser.parseFromString(text, 'text/xml'); - pruneFromDoc(xmlDoc); - const serializer = new XMLSerializer(); - text = serializer.serializeToString(xmlDoc); - } catch { + if ( typeof k === 'number' ) { + if ( Array.isArray(owner) === false ) { return; } + return [ k >= 0 ? k : owner.length + k ]; } - return text; - }; - const urlFromArg = arg => { - if ( typeof arg === 'string' ) { return arg; } - if ( arg instanceof Request ) { return arg.url; } - return String(arg); - }; - self.fetch = new Proxy(self.fetch, { - apply: function(target, thisArg, args) { - const fetchPromise = Reflect.apply(target, thisArg, args); - if ( reUrl.test(urlFromArg(args[0])) === false ) { - return fetchPromise; - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(text => { - const responseAfter = new Response(pruneFromText(text), { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(( ) => - responseBefore - ); - }); + if ( k === '*' ) { + if ( Array.isArray(owner) ) { return owner.keys(); } + return JSONPath.keys(owner); } - }); - modifyXhrResponseFn(urlPattern, (xhr, before) => { - if ( before instanceof XMLDocument ) { - return pruneFromDoc(before); + if ( k instanceof JSONPath.Regex ) { + const out = []; + for ( const key of JSONPath.keys(owner) ) { + if ( k.test(key) === false ) { continue; } + out.push(key); + } + return out; } - if ( typeof before === 'string' ) { - return pruneFromText(before); + return [ k ]; + } + #getMatchesFromExpr(pathin, step, owner, out) { + const recursive = step.mv === this.#DESCENDANTS; + const v2 = this.#compiled.v2 || recursive || Array.isArray(owner); + for ( const { path } of this.#getDescendants(owner, recursive) ) { + const q = v2 ? [ ...pathin, ...path ] : pathin; + const r = this.#evaluate(step.steps, q); + if ( Boolean(r?.length) === false ) { continue; } + out.push(q); + if ( v2 === false ) { break; } } - return before; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); } -} catch { -} -xmlPrune(...args); -}, -}; - - -scriptlets['m3u-prune.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; + #getDescendants(v, recursive) { + const iterator = { + next() { + const n = this.stack.length; + if ( n === 0 ) { + this.value = undefined; + this.done = true; + return this; + } + const details = this.stack[n-1]; + const entry = details.keys.next(); + if ( entry.done ) { + this.stack.pop(); + this.path.pop(); + return this.next(); + } + this.path[n-1] = entry.value; + this.value = { + obj: details.obj, + key: entry.value, + path: this.path.slice(), + }; + const v = this.value.obj[this.value.key]; + if ( recursive ) { + if ( Array.isArray(v) ) { + this.stack.push({ obj: v, keys: v.keys() }); + } else if ( typeof v === 'object' && v !== null ) { + this.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); + } + } + return this; + }, + path: [], + value: undefined, + done: false, + stack: [], + [Symbol.iterator]() { return this; }, + }; + if ( Array.isArray(v) ) { + iterator.stack.push({ obj: v, keys: v.keys() }); + } else if ( typeof v === 'object' && v !== null ) { + iterator.stack.push({ obj: v, keys: JSONPath.keys(v).values() }); + } + return iterator; } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); + #consumeIdentifier(query, i) { + const keys = []; + let needIdentifier = true; + while ( i < query.length ) { + const c0 = query.charCodeAt(i); + if ( c0 === 0x5D /* ] */ ) { break; } + if ( c0 === 0x20 /* SPACE */ ) { + i += 1; + continue; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; + if ( c0 === 0x2C /* , */ ) { + if ( needIdentifier ) { return; } + i += 1; + needIdentifier = true; + continue; } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + if ( c0 === 0x22 /* " */ || c0 === 0x27 /* ' */ ) { + const r = this.#untilChar(query, c0, i+1); + if ( r === undefined ) { return; } + keys.push(r.s); + i = r.i; + needIdentifier = false; + continue; } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { + const match = this.#reIndice.exec(query.slice(i)); + if ( match === null ) { return; } + const indice = parseInt(query.slice(i), 10); + keys.push(indice); + i += match[0].length; + needIdentifier = false; + continue; } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + if ( this.v2 ) { return; } + const r = this.#consumeUnquotedIdentifier(query, i); + if ( r === undefined ) { return; } + keys.push(r.s); + i = r.i; + } + if ( needIdentifier ) { return; } + return { s: keys.length === 1 ? keys[0] : keys, i }; + } + #consumeUnquotedIdentifier(query, i) { + if ( query.charCodeAt(i) === 0x2F /* / */ ) { + const r = this.#untilChar(query, 0x2F, i+1); + if ( r === undefined ) { return; } + let re; + try { re = new JSONPath.Regex(r.s); } catch { return; } + return { s: re, i: r.i }; + } + const match = this.#reUnquotedIdentifier.exec(query.slice(i)); + if ( match === null ) { return; } + return { s: match[0], i: i + match[0].length }; + } + #untilChar(query, targetCharCode, i) { + const len = query.length; + const parts = []; + let beg = i, end = i; + for (;;) { + if ( end === len ) { return; } + const c = query.charCodeAt(end); + if ( c === targetCharCode ) { + parts.push(query.slice(beg, end)); + end += 1; + break; } - try { - return new RegExp(match[1], match[2] || undefined); + if ( c === 0x5C /* \ */ && (end+1) < len ) { + const d = query.charCodeAt(end+1); + if ( d === targetCharCode ) { + parts.push(query.slice(beg, end)); + end += 1; + beg = end; + } } - catch { + end += 1; + } + return { s: parts.join(''), i: end }; + } + #compileExpr(query, step, i) { + if ( query.startsWith('=/', i) ) { + const r = this.#untilChar(query, 0x2F /* / */, i+2); + if ( r === undefined ) { return i; } + const match = /^[i]/.exec(query.slice(r.i)); + try { + step.rval = new JSONPath.Regex(r.s, match && match[0] || undefined); + } catch { return; } + step.op = 're'; + if ( match ) { r.i += match[0].length; } + return r.i; + } + const match = this.#reExpr.exec(query.slice(i)); + if ( match === null ) { return; } + const op = match[1], rval = match[2]; + if ( rval.charCodeAt(0) === 0x27 /* ' */ ) { + const r = this.#untilChar(rval, 0x27, 1); + if ( r === undefined ) { return; } + step.rval = r.s; + step.op = op; + } else { + try { + step.rval = JSON.parse(rval); + step.op = op; + } catch { return; } + } + return i + match[0].length - 1; + } + #resolvePath(path) { + if ( path.length === 0 ) { return { value: this.#root }; } + const key = path.at(-1); + let obj = this.#root + for ( let i = 0, n = path.length-1; i < n; i++ ) { + obj = obj[path[i]]; + if ( obj instanceof Object === false ) { return {}; } + } + return { obj, key, value: obj[key] }; + } + #evaluateExpr(step, owner, k) { + if ( owner === undefined || owner === null ) { return; } + const hasOwn = owner[k] !== undefined || JSONPath.hasOwn(owner, k); + if ( step.op !== undefined && hasOwn === false ) { return; } + const target = step.not !== true; + const v = owner[k]; + switch ( step.op ) { + case '==': return (v === step.rval) === target; + case '!=': return (v !== step.rval) === target; + case '<': return (v < step.rval) === target; + case '<=': return (v <= step.rval) === target; + case '>': return (v > step.rval) === target; + case '>=': return (v >= step.rval) === target; + case '^=': return `${v}`.startsWith(step.rval) === target; + case '$=': return `${v}`.endsWith(step.rval) === target; + case '*=': return `${v}`.includes(step.rval) === target; + case 're': return step.rval.test(`${v}`); + default: break; + } + return hasOwn === target; + } + #modifyVal(obj, key) { + let { modify, rval } = this.#compiled; + if ( typeof rval === 'string' ) { + rval = rval.replace('${now}', `${Date.now()}`); + } + switch ( modify ) { + case undefined: + obj[key] = rval; + break; + case '+': { + if ( rval instanceof Object === false ) { return; } + const lval = obj[key]; + if ( lval instanceof Object === false ) { return; } + if ( Array.isArray(lval) ) { return; } + for ( const [ k, v ] of JSONPath.entries(rval) ) { + lval[k] = v; } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + break; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + case 'call': { + const entries = rval.slice(); + if ( entries.length < 2 ) { break; } + entries.forEach((a, i, aa) => { + if ( a === '${obj}' ) { aa[i] = obj; } + else if ( a === '${key}' ) { aa[i] = key; } + else if ( a === '${val}' ) { aa[i] = obj[key]; } + }); + const instance = entries[0] ?? self; + instance[entries[1]](...entries.slice(2)); + break; + } + case 'repl': { + const lval = obj[key]; + if ( typeof lval !== 'string' ) { return; } + if ( this.#compiled.re === undefined ) { + this.#compiled.re = null; + try { + this.#compiled.re = rval.regex !== undefined + ? new JSONPath.Regex(rval.regex, rval.flags) + : new JSONPath.Regex(rval.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); + } catch { } } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + if ( this.#compiled.re === null ) { return; } + obj[key] = lval.replace(this.#compiled.re, rval.replacement); + break; + } + default: + break; + } + } +} +function proxyToStringFn(proxiedFn, nativeFn) { + if ( proxyToStringFn.proxies === undefined ) { + proxyToStringFn.proxies = new WeakMap(); + proxyToStringFn.nativeToString = Function.prototype.toString; + const proxiedToString = new Proxy(Function.prototype.toString, { + apply(target, thisArg) { + let proxied = thisArg; + for(;;) { + const fn = proxyToStringFn.proxies.get(proxied); + if ( fn === undefined ) { break; } + proxied = fn; + } + return proxyToStringFn.nativeToString.call(proxied); } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; + }); + proxyToStringFn.proxies.set(proxiedToString, proxyToStringFn.nativeToString); + Function.prototype.toString = proxiedToString; } - return safe; + proxyToStringFn.proxies.set(proxiedFn, nativeFn); } function proxyApplyFn( target = '', @@ -41947,990 +2649,935 @@ function proxyApplyFn( return handler(proxyApplyFn.CtorContext.factory(target, args)); }; } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; + const proxiedTarget = new Proxy(fn, proxyDetails); + proxyApplyFn.proxies.set(proxiedTarget, fn); + context[prop] = proxiedTarget; +} +function proxyApplyConfig(config = '') { + try { + if ( typeof proxyApplyFn !== 'function' ) { return; } + config = JSON.parse(config); + if ( typeof config !== 'object' ) { return; } + Object.assign(proxyApplyFn, config); + } catch { + } } -function m3uPrune( - m3uPattern = '', - urlPattern = '' +function editOutboundObjectFn( + trusted = false, + propChain = '', + jsonq = '' ) { - if ( typeof m3uPattern !== 'string' ) { return; } + if ( propChain === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('m3u-prune', m3uPattern, urlPattern); - const toLog = []; - const regexFromArg = arg => { - if ( arg === '' ) { return /^/; } - const match = /^\/(.+)\/([gms]*)$/.exec(arg); - if ( match !== null ) { - let flags = match[2] || ''; - if ( flags.includes('m') ) { flags += 's'; } - return new RegExp(match[1], flags); - } - return new RegExp( - arg.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*+/g, '.*?') - ); - }; - const reM3u = regexFromArg(m3uPattern); - const reUrl = regexFromArg(urlPattern); - const pruneSpliceoutBlock = (lines, i) => { - if ( lines[i].startsWith('#EXT-X-CUE:TYPE="SpliceOut"') === false ) { - return false; - } - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; - if ( lines[i].startsWith('#EXT-X-ASSET:CAID') ) { - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; - } - if ( lines[i].startsWith('#EXT-X-SCTE35:') ) { - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; - } - if ( lines[i].startsWith('#EXT-X-CUE-IN') ) { - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-outbound-object`, + propChain, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + proxyApplyFn(propChain, function(context) { + const obj = context.reflect(); + const objAfter = jsonp.apply(obj); + if ( objAfter === undefined ) { return obj; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); } - if ( lines[i].startsWith('#EXT-X-SCTE35:') ) { - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; + return objAfter; + }); +} +function editOutboundObject(propChain = '', jsonq = '') { + editOutboundObjectFn(false, propChain, jsonq); +} +function trustedEditOutboundObject(propChain = '', jsonq = '') { + editOutboundObjectFn(true, propChain, jsonq); +} +function jsonEditFn(trusted = false, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit`, + jsonq, + ...varargs + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.parseVarargs(varargs); + const pattern = extraArgs.matches && safe.initPattern(extraArgs.matches); + proxyApplyFn('JSON.parse', function(context) { + const json = context.callArgs[0]; + const obj = context.reflect(); + if ( pattern && safe.testPattern(pattern, json) === false ) { return obj; } + const objAfter = jsonp.apply(obj); + if ( objAfter === undefined ) { return obj; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); } - return true; - }; - const pruneInfBlock = (lines, i) => { - if ( lines[i].startsWith('#EXTINF') === false ) { return false; } - if ( reM3u.test(lines[i+1]) === false ) { return false; } - toLog.push('Discarding', `\t${lines[i]}, \t${lines[i+1]}`); - lines[i] = lines[i+1] = undefined; i += 2; - if ( lines[i].startsWith('#EXT-X-DISCONTINUITY') ) { - toLog.push(`\t${lines[i]}`); - lines[i] = undefined; i += 1; + return objAfter; + }); +} +function jsonEdit(jsonq = '', ...varargs) { + jsonEditFn(false, jsonq, ...varargs); +} +function trustedJsonEdit(jsonq = '', ...varargs) { + jsonEditFn(true, jsonq, ...varargs); +} +function editInboundObjectFn( + trusted = false, + propChain = '', + argPosRaw = '', + jsonq = '', +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-inbound-object`, + propChain, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const argPos = parseInt(argPosRaw, 10); + if ( isNaN(argPos) ) { return; } + const getArgPos = args => { + if ( Array.isArray(args) === false ) { return; } + if ( argPos >= 0 ) { + if ( args.length <= argPos ) { return; } + return argPos; } - return true; + if ( args.length < -argPos ) { return; } + return args.length + argPos; }; - const pruner = text => { - if ( (/^\s*#EXTM3U/.test(text)) === false ) { return text; } - if ( m3uPattern === '' ) { - safe.uboLog(` Content:\n${text}`); - return text; - } - if ( reM3u.multiline ) { - reM3u.lastIndex = 0; - for (;;) { - const match = reM3u.exec(text); - if ( match === null ) { break; } - let discard = match[0]; - let before = text.slice(0, match.index); - if ( - /^[\n\r]+/.test(discard) === false && - /[\n\r]+$/.test(before) === false - ) { - const startOfLine = /[^\n\r]+$/.exec(before); - if ( startOfLine !== null ) { - before = before.slice(0, startOfLine.index); - discard = startOfLine[0] + discard; - } - } - let after = text.slice(match.index + match[0].length); - if ( - /[\n\r]+$/.test(discard) === false && - /^[\n\r]+/.test(after) === false - ) { - const endOfLine = /^[^\n\r]+/.exec(after); - if ( endOfLine !== null ) { - after = after.slice(endOfLine.index); - discard += discard + endOfLine[0]; - } - } - text = before.trim() + '\n' + after.trim(); - reM3u.lastIndex = before.length + 1; - toLog.push('Discarding', ...safe.String_split.call(discard, /\n+/).map(s => `\t${s}`)); - if ( reM3u.global === false ) { break; } - } - return text; + const editObj = obj => { + let clone; + try { + clone = safe.JSON_parse(safe.JSON_stringify(obj)); + } catch { } - const lines = safe.String_split.call(text, /\n\r|\n|\r/); - for ( let i = 0; i < lines.length; i++ ) { - if ( lines[i] === undefined ) { continue; } - if ( pruneSpliceoutBlock(lines, i) ) { continue; } - if ( pruneInfBlock(lines, i) ) { continue; } + if ( typeof clone !== 'object' || clone === null ) { return; } + const objAfter = jsonp.apply(clone); + if ( objAfter === undefined ) { return; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); } - return lines.filter(l => l !== undefined).join('\n'); - }; - const urlFromArg = arg => { - if ( typeof arg === 'string' ) { return arg; } - if ( arg instanceof Request ) { return arg.url; } - return String(arg); + return objAfter; }; - proxyApplyFn('fetch', async function fetch(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( reUrl.test(urlFromArg(args[0])) === false ) { return fetchPromise; } - const responseBefore = await fetchPromise; - const responseClone = responseBefore.clone(); - const textBefore = await responseClone.text(); - const textAfter = pruner(textBefore); - if ( textAfter === textBefore ) { return responseBefore; } - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - url: { value: responseBefore.url }, - type: { value: responseBefore.type }, - }); - if ( toLog.length !== 0 ) { - toLog.unshift(logPrefix); - safe.uboLog(toLog.join('\n')); - } - return responseAfter; - }) - self.XMLHttpRequest.prototype.open = new Proxy(self.XMLHttpRequest.prototype.open, { - apply: async (target, thisArg, args) => { - if ( reUrl.test(urlFromArg(args[1])) === false ) { - return Reflect.apply(target, thisArg, args); - } - thisArg.addEventListener('readystatechange', function() { - if ( thisArg.readyState !== 4 ) { return; } - const type = thisArg.responseType; - if ( type !== '' && type !== 'text' ) { return; } - const textin = thisArg.responseText; - const textout = pruner(textin); - if ( textout === textin ) { return; } - Object.defineProperty(thisArg, 'response', { value: textout }); - Object.defineProperty(thisArg, 'responseText', { value: textout }); - if ( toLog.length !== 0 ) { - toLog.unshift(logPrefix); - safe.uboLog(toLog.join('\n')); - } - }); - return Reflect.apply(target, thisArg, args); + proxyApplyFn(propChain, function(context) { + const i = getArgPos(context.callArgs); + if ( i !== undefined ) { + const obj = editObj(context.callArgs[i]); + if ( obj ) { + context.callArgs[i] = obj; + } } + return context.reflect(); }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { } -m3uPrune(...args); -}, -}; - - -scriptlets['call-nothrow.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; +function editInboundObject(propChain = '', argPos = '', jsonq = '') { + editInboundObjectFn(false, propChain, argPos, jsonq); +} +function trustedEditInboundObject(propChain = '', argPos = '', jsonq = '') { + editInboundObjectFn(true, propChain, argPos, jsonq); +} +function editThisObjectFn( + trusted = false, + propChain = '', + jsonq = '', + order = '' +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-this-object`, + propChain, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, + const editObj = objBefore => { + const objAfter = jsonp.apply(objBefore); + if ( objAfter === undefined ) { return; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel <= 1 ) { return; } + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + proxyApplyFn(propChain, function(context) { + const { thisArg } = context; + let r; + if ( order === 'after' ) { + r = context.reflect(); } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; + editObj(thisArg); + if ( order !== 'after' ) { + r = context.reflect(); + } + return r; + }); +} +function editThisObject(...args) { + editThisObjectFn(false, ...args); +} +function trustedEditThisObject(...args) { + editThisObjectFn(true, ...args); +} +function editObjectOnGetterFn( + trusted = false, + propChain = '', + jsonq = '', +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-object-on-getter`, + propChain, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const editObj = objBefore => { + const objAfter = jsonp.apply(objBefore); + if ( objAfter === undefined ) { return; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel <= 1 ) { return; } + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + let currentValue = trapPropertyFn(propChain, { + get: function() { + if ( currentValue instanceof Object ) { + editObj(currentValue); } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + return currentValue; + }, + set: function(a) { + currentValue = a; + } + }); } -function callNothrow( - chain = '' +function editObjectOnGetter(...args) { + editObjectOnGetterFn(false, ...args); +} +function trustedEditObjectOnGetter(...args) { + editObjectOnGetterFn(true, ...args); +} +function editObjectOnSetterFn( + trusted = false, + propChain = '', + jsonq = '', ) { - if ( typeof chain !== 'string' ) { return; } - if ( chain === '' ) { return; } + if ( propChain === '' ) { return; } const safe = safeSelf(); - const parts = safe.String_split.call(chain, '.'); - let owner = window, prop; - for (;;) { - prop = parts.shift(); - if ( parts.length === 0 ) { break; } - owner = owner[prop]; - if ( owner instanceof Object === false ) { return; } + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-object-on-setter`, + propChain, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); } - if ( prop === '' ) { return; } - const fn = owner[prop]; - if ( typeof fn !== 'function' ) { return; } - owner[prop] = new Proxy(fn, { - apply: function(...args) { - let r; - try { - r = Reflect.apply(...args); - } catch { - } - return r; + const editObj = objBefore => { + const objAfter = jsonp.apply(objBefore); + if ( objAfter === undefined ) { return; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel <= 1 ) { return; } + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(objAfter, null, 2)}`); + }; + let currentValue = trapPropertyFn(propChain, { + get: function() { + return currentValue; }, + set: function(a) { + currentValue = a; + if ( currentValue instanceof Object ) { + editObj(currentValue); + } + } }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); + if ( currentValue instanceof Object ) { + editObj(currentValue); } -} catch { } -callNothrow(...args); -}, -}; - - -scriptlets['remove-node-text.js'] = { -aliases: ["rmnt.js"], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; +function editObjectOnSetter(...args) { + editObjectOnSetterFn(false, ...args); +} +function trustedEditObjectOnSetter(...args) { + editObjectOnSetterFn(true, ...args); +} +async function editElementObjectFn( + trusted = false, + selector = '', + jsonq = '', + timeout = '' +) { + if ( selector === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}edit-element-object`, + selector, jsonq, timeout + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); + const process = elems => { + for ( const elem of elems ) { + const r = jsonp.apply(elem); + if ( r === undefined ) { continue; } + safe.uboLog(logPrefix, 'Edited'); + } }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); + const until = timeout !== '' ? Date.now() + parseInt(timeout, 10) : 0; + do { + const r = lookupElementsFn(selector, until); + if ( Array.isArray(r) ) { + process(r); + } else if ( r instanceof Promise ) { + const elems = await r; + process(elems); + } + await sleepFn(); + } while ( Date.now() < until ); } -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; +function editElementObject(...args) { + editElementObjectFn(false, ...args); +} +function trustedEditElementObject(...args) { + editElementObjectFn(true, ...args); +} +function jsonEditXhrResponseFn(trusted, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-xhr-response`, + jsonq + ); + const xhrInstances = new WeakMap(); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + xhrInstances.set(this, xhrDetails); } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { return innerResponse; } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; } - try { - return new RegExp(match[1], match[2] || undefined); + let obj; + if ( typeof innerResponse === 'object' ) { + obj = innerResponse; + } else if ( typeof innerResponse === 'string' ) { + try { obj = safe.JSON_parse(innerResponse); } catch { } } - catch { + if ( typeof obj !== 'object' || obj === null ) { + return (xhrDetails.response = innerResponse); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const objAfter = jsonp.apply(obj); + if ( objAfter === undefined ) { + return (xhrDetails.response = innerResponse); + } + safe.uboLog(logPrefix, 'Edited'); + const outerResponse = typeof innerResponse === 'string' + ? JSONPath.toJSON(objAfter, safe.JSON_stringify) + : objAfter; + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); +} +function jsonEditXhrResponse(jsonq = '', ...args) { + jsonEditXhrResponseFn(false, jsonq, ...args); +} +function trustedJsonEditXhrResponse(jsonq = '', ...args) { + jsonEditXhrResponseFn(true, jsonq, ...args); +} +function jsonEditXhrRequestFn(trusted, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-xhr-request`, + jsonq + ); + const xhrInstances = new WeakMap(); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + xhrInstances.set(this, xhrDetails); } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + return super.open(method, url, ...args); + } + send(body) { + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails ) { + body = this.#filterBody(body) || body; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; + super.send(body); + } + #filterBody(body) { + if ( typeof body !== 'string' ) { return; } + let data; + try { data = safe.JSON_parse(body); } + catch { } + if ( data instanceof Object === false ) { return; } + const objAfter = jsonp.apply(data); + if ( objAfter === undefined ) { return; } + body = safe.JSON_stringify(objAfter); + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${body}`); + } + return body; + } + }; +} +function jsonEditXhrRequest(jsonq = '', ...args) { + jsonEditXhrRequestFn(false, jsonq, ...args); +} +function trustedJsonEditXhrRequest(jsonq = '', ...args) { + jsonEditXhrRequestFn(true, jsonq, ...args); +} +function jsonEditFetchResponseFn(trusted, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-fetch-response`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); } - return safe; + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + proxyApplyFn('fetch', function(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.json().then(obj => { + if ( typeof obj !== 'object' ) { return responseBefore; } + const objAfter = jsonp.apply(obj); + if ( objAfter === undefined ) { return responseBefore; } + safe.uboLog(logPrefix, 'Edited'); + const responseAfter = Response.json(objAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }); +} +function jsonEditFetchResponse(jsonq = '', ...args) { + jsonEditFetchResponseFn(false, jsonq, ...args); +} +function trustedJsonEditFetchResponse(jsonq = '', ...args) { + jsonEditFetchResponseFn(true, jsonq, ...args); +} +function jsonEditFetchRequestFn(trusted, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-fetch-request`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const filterBody = body => { + if ( typeof body !== 'string' ) { return; } + let data; + try { data = safe.JSON_parse(body); } + catch { } + if ( data instanceof Object === false ) { return; } + const objAfter = jsonp.apply(data); + if ( objAfter === undefined ) { return; } + return safe.JSON_stringify(objAfter); + } + const proxyHandler = context => { + const args = context.callArgs; + const [ resource, options ] = args; + const bodyBefore = options?.body; + if ( Boolean(bodyBefore) === false ) { return context.reflect(); } + const bodyAfter = filterBody(bodyBefore); + if ( bodyAfter === undefined || bodyAfter === bodyBefore ) { + return context.reflect(); + } + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(resource, options); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return context.reflect(); } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${bodyAfter}`); + } + options.body = bodyAfter; + return context.reflect(); + }; + proxyApplyFn('fetch', proxyHandler); + proxyApplyFn('Request', proxyHandler); } -function getRandomTokenFn() { +function jsonEditFetchRequest(jsonq = '', ...args) { + jsonEditFetchRequestFn(false, jsonq, ...args); +} +function trustedJsonEditFetchRequest(jsonq = '', ...args) { + jsonEditFetchRequestFn(true, jsonq, ...args); +} +function jsonlEditFn(jsonp, text = '', jsonExtract) { const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); + const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; + const linesBefore = text.split('\n'); + const linesAfter = []; + for ( const lineBefore of linesBefore ) { + const match = jsonExtract.exec(lineBefore); + if ( match === null ) { + linesAfter.push(lineBefore); + continue; + } + const jsonBefore = match[1]; + let obj; + try { obj = safe.JSON_parse(jsonBefore); } catch { } + if ( typeof obj !== 'object' || obj === null ) { + linesAfter.push(lineBefore); + continue; + } + const objAfter = jsonp.apply(obj); + if ( objAfter === undefined ) { + linesAfter.push(lineBefore); + continue; + } + const jsonAfter = safe.JSON_stringify(objAfter); + const lineAfter = [ + lineBefore.slice(0, match.index), + jsonAfter, + lineBefore.slice(match.index + jsonBefore.length), + ].join(''); + linesAfter.push(lineAfter); + } + return linesAfter.join(lineSeparator); } -function replaceNodeTextFn( - nodeName = '', - pattern = '', - replacement = '', - ...varargs -) { +function jsonlEditXhrResponseFn(trusted, jsonq = '', ...varargs) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('replace-node-text.fn', ...Array.from(arguments)); - const reNodeName = safe.patternToRegex(nodeName, 'i', true); - const rePattern = safe.patternToRegex(pattern, 'gms'); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}jsonl-edit-xhr-response`, + jsonq + ); + const xhrInstances = new WeakMap(); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } const extraArgs = safe.parseVarargs(varargs); - const reIncludes = extraArgs.includes || extraArgs.condition - ? safe.patternToRegex(extraArgs.includes || extraArgs.condition, 'ms') - : null; - const reExcludes = extraArgs.excludes - ? safe.patternToRegex(extraArgs.excludes, 'ms') - : null; - const textContentFactory = (( ) => { - const out = { createScript: s => s }; - const { trustedTypes: tt } = self; - if ( tt instanceof Object ) { - if ( typeof tt.getPropertyType === 'function' ) { - if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { - return tt.createPolicy(getRandomTokenFn(), out); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const jsonExtract = extraArgs.jsonExtract + ? new RegExp(extraArgs.jsonExtract) + : /^(.*)$/; + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); } + xhrInstances.set(this, xhrDetails); } + return super.open(method, url, ...args); } - return out; - })(); - let sedCount = extraArgs.sedCount ?? Number.MAX_SAFE_INTEGER; - const handleNode = node => { - const before = node.textContent; - if ( reIncludes ) { - reIncludes.lastIndex = 0; - if ( safe.RegExp_test(reIncludes, before) === false ) { return; } - } - if ( reExcludes ) { - reExcludes.lastIndex = 0; - if ( safe.RegExp_test(reExcludes, before) ) { return; } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; + } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; + } + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; + } + if ( typeof innerResponse !== 'string' ) { + return (xhrDetails.response = innerResponse); + } + const outerResponse = jsonlEditFn(jsonp, innerResponse, jsonExtract); + if ( outerResponse !== innerResponse ) { + safe.uboLog(logPrefix, 'Pruned'); + } + return (xhrDetails.response = outerResponse); } - rePattern.lastIndex = 0; - if ( safe.RegExp_test(rePattern, before) === false ) { return; } - rePattern.lastIndex = 0; - const after = pattern !== '' - ? before.replace(rePattern, replacement) - : replacement; - node.textContent = node.nodeName === 'SCRIPT' - ? textContentFactory.createScript(after) - : after; - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Text before:\n${before.trim()}`); + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; } - safe.uboLog(logPrefix, `Text after:\n${after.trim()}`); - sedCount -= 1; }; - const handleTree = root => { - const treeWalker = document.createTreeWalker(root, - NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT - ); - const { currentScript } = document; - let count = 0; - for (;;) { - const node = treeWalker.nextNode(); - if ( node === null ) { break; } - count += 1; - if ( node === currentScript ) { continue; } - if ( reNodeName.test(node.nodeName) ) { - handleNode(node); - } else if ( node.nodeName === 'TEMPLATE' ) { - count += handleTree(node.content); - } else { - continue; +} +function jsonlEditXhrResponse(jsonq = '', ...args) { + jsonlEditXhrResponseFn(false, jsonq, ...args); +} +function trustedJsonlEditXhrResponse(jsonq = '', ...args) { + jsonlEditXhrResponseFn(true, jsonq, ...args); +} +function jsonlEditFetchResponseFn(trusted, jsonq = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const jsonExtract = extraArgs.jsonExtract + ? new RegExp(extraArgs.jsonExtract) + : /^(.*)$/; + const logall = jsonq === ''; + proxyApplyFn('fetch', function(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); } - if ( sedCount === 0 ) { break; } } - return count; - }; - if ( document.documentElement ) { - const count = handleTree(document.documentElement); - safe.uboLog(logPrefix, `${count} nodes present before installing mutation observer`); + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(textBefore => { + if ( typeof textBefore !== 'string' ) { return textBefore; } + if ( logall ) { + safe.uboLog(logPrefix, textBefore); + return responseBefore; + } + const textAfter = jsonlEditFn(jsonp, textBefore, jsonExtract); + if ( textAfter === textBefore ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }); +} +function jsonlEditFetchResponse(jsonq = '', ...args) { + jsonlEditFetchResponseFn(false, jsonq, ...args); +} +function trustedJsonlEditFetchResponse(jsonq = '', ...args) { + jsonlEditFetchResponseFn(true, jsonq, ...args); +} +function matchesStackTraceFn( + needleDetails, + logLevel = '' +) { + const safe = safeSelf(); + const exceptionToken = getExceptionTokenFn(); + const error = new safe.Error(exceptionToken); + const docURL = new URL(self.location.href); + docURL.hash = ''; + // Normalize stack trace + const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; + const lines = []; + for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { + if ( line.includes(exceptionToken) ) { continue; } + line = line.trim(); + const match = safe.RegExp_exec.call(reLine, line); + if ( match === null ) { continue; } + let url = match[2]; + if ( url.startsWith('(') ) { url = url.slice(1); } + if ( url === docURL.href ) { + url = 'inlineScript'; + } else if ( url.startsWith('') ) { + url = 'injectedScript'; + } + let fn = match[1] !== undefined + ? match[1].slice(0, -1) + : line.slice(0, match.index).trim(); + if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } + let rowcol = match[3]; + lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); } - const stay = Boolean(extraArgs.stay); - if ( sedCount === 0 && stay === false ) { return; } - const stop = (takeRecord = true) => { - const mutations = takeRecord ? observer.takeRecords() : []; - observer.disconnect(); - handleMutations(mutations); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Quitting'); + lines[0] = `stackDepth:${lines.length-1}`; + const stack = lines.join('\t'); + const r = needleDetails.matchAll !== true && + safe.testPattern(needleDetails, stack); + if ( + logLevel === 'all' || + logLevel === 'match' && r || + logLevel === 'nomatch' && !r + ) { + safe.uboLog(stack.replace(/\t/g, '\n')); + } + return r; +} +function abortOnStackTrace( + chain = '', + needle = '', + ...varargs +) { + if ( typeof chain !== 'string' ) { return; } + const safe = safeSelf(); + const needleDetails = safe.initPattern(needle, { canNegate: true }); + const extraArgs = safe.parseVarargs(varargs); + if ( needle === '' ) { extraArgs.log = 'all'; } + const makeProxy = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + let v = owner[chain]; + Object.defineProperty(owner, chain, { + get: function() { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { + throw new ReferenceError(getExceptionTokenFn()); + } + return v; + }, + set: function(a) { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { + throw new ReferenceError(getExceptionTokenFn()); + } + v = a; + }, + }); + return; } - }; - const handleMutations = mutations => { - for ( const mutation of mutations ) { - for ( const node of mutation.addedNodes ) { - if ( reNodeName.test(node.nodeName) ) { - handleNode(node); - } else if ( node.nodeName === 'TEMPLATE' ) { - handleTree(node.content); - } else { - continue; + const prop = chain.slice(0, pos); + let v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v ) { + makeProxy(v, chain); + return; + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.set !== undefined ) { return; } + Object.defineProperty(owner, prop, { + get: function() { return v; }, + set: function(a) { + v = a; + if ( a instanceof Object ) { + makeProxy(a, chain); } - if ( sedCount === 0 ) { return stop(false); } } - } + }); }; - const observer = new MutationObserver(handleMutations); - observer.observe(document, { childList: true, subtree: true }); - if ( stay ) { return; } - runAt(( ) => { - const quitAfter = extraArgs.quitAfter ?? 0; - if ( quitAfter === 0 ) { return stop(); } - setTimeout(( ) => { stop(); }, quitAfter); - }, 'interactive'); + const owner = window; + makeProxy(owner, chain); } -function removeNodeText( - nodeName, - includes, - ...extraArgs +function objectFindOwnerFn( + root, + path, + prune = false ) { - replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -removeNodeText(...args); -}, -}; - - -scriptlets['prevent-canvas.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + const safe = safeSelf(); + let owner = root; + let chain = path; + for (;;) { + if ( typeof owner !== 'object' || owner === null ) { return false; } + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + if ( prune === false ) { + return safe.Object_hasOwn(owner, chain); } - try { - return new RegExp(match[1], match[2] || undefined); + let modified = false; + if ( chain === '*' ) { + for ( const key in owner ) { + if ( safe.Object_hasOwn(owner, key) === false ) { continue; } + delete owner[key]; + modified = true; + } + } else if ( safe.Object_hasOwn(owner, chain) ) { + delete owner[chain]; + modified = true; } - catch { + return modified; + } + const prop = chain.slice(0, pos); + const next = chain.slice(pos + 1); + let found = false; + if ( prop === '[-]' && Array.isArray(owner) ) { + let i = owner.length; + while ( i-- ) { + if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } + owner.splice(i, 1); + found = true; } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + return found; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + if ( prop === '{-}' && owner instanceof Object ) { + for ( const key of Object.keys(owner) ) { + if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } + delete owner[key]; + found = true; } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + return found; + } + if ( + prop === '[]' && Array.isArray(owner) || + prop === '{}' && owner instanceof Object || + prop === '*' && owner instanceof Object + ) { + for ( const key of Object.keys(owner) ) { + if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } + found = true; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; + return found; + } + if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } + owner = owner[prop]; + chain = chain.slice(pos + 1); } - return safe; } -function preventCanvas( - contextType = '' +function objectPruneFn( + obj, + rawPrunePaths, + rawNeedlePaths, + stackNeedleDetails = { matchAll: true }, + extraArgs = {} ) { + if ( typeof rawPrunePaths !== 'string' ) { return; } const safe = safeSelf(); - const pattern = safe.initPattern(contextType, { canNegate: true }); - const proto = globalThis.HTMLCanvasElement.prototype; - proto.getContext = new Proxy(proto.getContext, { - apply(target, thisArg, args) { - if ( safe.testPattern(pattern, args[0]) ) { return null; } - return Reflect.apply(target, thisArg, args); + const prunePaths = rawPrunePaths !== '' + ? safe.String_split.call(rawPrunePaths, / +/) + : []; + const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' + ? safe.String_split.call(rawNeedlePaths, / +/) + : []; + if ( stackNeedleDetails.matchAll !== true ) { + if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { + return; } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); } -} catch { + if ( objectPruneFn.mustProcess === undefined ) { + objectPruneFn.mustProcess = (root, needlePaths) => { + for ( const needlePath of needlePaths ) { + if ( objectFindOwnerFn(root, needlePath) === false ) { + return false; + } + } + return true; + }; + } + if ( prunePaths.length === 0 ) { return; } + let outcome = 'nomatch'; + if ( objectPruneFn.mustProcess(obj, needlePaths) ) { + for ( const path of prunePaths ) { + if ( objectFindOwnerFn(obj, path, true) ) { + outcome = 'match'; + } + } + } + if ( outcome === 'match' ) { return obj; } } -preventCanvas(...args); -}, -}; - - -scriptlets['multiup.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { - -function multiup() { - const handler = ev => { - const target = ev.target; - if ( target.matches('button[link]') === false ) { return; } - const ancestor = target.closest('form'); - if ( ancestor === null ) { return; } - if ( ancestor !== target.parentElement ) { return; } - const link = (target.getAttribute('link') || '').trim(); - if ( link === '' ) { return; } - ev.preventDefault(); - ev.stopPropagation(); - document.location.href = link; - }; - document.addEventListener('click', handler, { capture: true }); -}; -multiup(...args); -}, -}; - - -scriptlets['break-on-call.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} +function trustedPruneInboundObject( + entryPoint = '', + argPos = '', + rawPrunePaths = '', + rawNeedlePaths = '', + ...varargs ) { + if ( entryPoint === '' ) { return; } let context = globalThis; - let prop = target; + let prop = entryPoint; for (;;) { const pos = prop.indexOf('.'); if ( pos === -1 ) { break; } @@ -42938,2922 +3585,3327 @@ function proxyApplyFn( if ( context instanceof Object === false ) { return; } prop = prop.slice(pos+1); } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } + if ( typeof context[prop] !== 'function' ) { return; } + const argIndex = parseInt(argPos); + if ( isNaN(argIndex) ) { return; } + if ( argIndex < 1 ) { return; } + const safe = safeSelf(); + const extraArgs = safe.parseVarargs(varargs); + const needlePaths = []; + if ( rawPrunePaths !== '' ) { + needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/)); } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); + if ( rawNeedlePaths !== '' ) { + needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/)); } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + const mustProcess = root => { + for ( const needlePath of needlePaths ) { + if ( objectFindOwnerFn(root, needlePath) === false ) { + return false; + } } + return true; }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; + context[prop] = new Proxy(context[prop], { + apply: function(target, thisArg, args) { + const targetArg = argIndex <= args.length + ? args[argIndex-1] + : undefined; + if ( targetArg instanceof Object && mustProcess(targetArg) ) { + let objBefore = targetArg; + if ( extraArgs.dontOverwrite ) { + try { + objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg)); + } catch { + objBefore = undefined; + } + } + if ( objBefore !== undefined ) { + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + args[argIndex-1] = objAfter || objBefore; + } + } + return Reflect.apply(target, thisArg, args); + }, + }); } -function breakOnCall(target) { - proxyApplyFn(target, function(context) { - debugger; // eslint-disable-line no-debugger - return context.reflect(); +function trustedPruneOutboundObject( + propChain = '', + rawPrunePaths = '', + rawNeedlePaths = '', + ...varargs +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const extraArgs = safe.parseVarargs(varargs); + proxyApplyFn(propChain, function(context) { + const objBefore = context.reflect(); + if ( objBefore instanceof Object === false ) { return objBefore; } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + { matchAll: true }, + extraArgs + ); + return objAfter || objBefore; }); -}; -breakOnCall(...args); -}, -}; - - -scriptlets['trusted-replace-node-text.js'] = { -aliases: ["trusted-rpnt.js","replace-node-text.js","rpnt.js"], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; +} +function jsonPrune( + rawPrunePaths = '', + rawNeedlePaths = '', + stackNeedle = '', + ...varargs +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('json-prune', rawPrunePaths, rawNeedlePaths, stackNeedle); + const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true }); + const extraArgs = safe.parseVarargs(varargs); + proxyApplyFn('JSON.parse', function(context) { + const objBefore = context.reflect(); + if ( rawPrunePaths === '' ) { + safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedleDetails, + extraArgs + ); + if ( objAfter === undefined ) { return objBefore; } + safe.uboLog(logPrefix, 'Pruned'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After pruning:\n${safe.JSON_stringify(objAfter, null, 2)}`); + } + return objAfter; + }); +} +function jsonPruneFetchResponse( + rawPrunePaths = '', + rawNeedlePaths = '', + ...varargs +) { const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); + const logPrefix = safe.makeLogPrefix('json-prune-fetch-response', rawPrunePaths, rawNeedlePaths); + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + const logall = rawPrunePaths === ''; + const applyHandler = function(target, thisArg, args) { + const fetchPromise = Reflect.apply(target, thisArg, args); + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.json().then(objBefore => { + if ( typeof objBefore !== 'object' ) { return responseBefore; } + if ( logall ) { + safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); + return responseBefore; + } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + if ( typeof objAfter !== 'object' ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = Response.json(objAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }; + self.fetch = new Proxy(self.fetch, { + apply: applyHandler + }); } -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; +function jsonPruneXhrResponse( + rawPrunePaths = '', + rawNeedlePaths = '', + ...varargs +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('json-prune-xhr-response', rawPrunePaths, rawNeedlePaths); + const xhrInstances = new WeakMap(); + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + let outcome = 'match'; + if ( propNeedles.size !== 0 ) { + if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { + outcome = 'nomatch'; + } } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); + if ( outcome === 'match' ) { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched optional "propsToMatch", "${extraArgs.propsToMatch}"`); + } + xhrInstances.set(this, xhrDetails); } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + let objBefore; + if ( typeof innerResponse === 'object' ) { + objBefore = innerResponse; + } else if ( typeof innerResponse === 'string' ) { + try { + objBefore = safe.JSON_parse(innerResponse); + } catch { + } } - try { - return new RegExp(match[1], match[2] || undefined); + if ( typeof objBefore !== 'object' ) { + return (xhrDetails.response = innerResponse); } - catch { + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + let outerResponse; + if ( typeof objAfter === 'object' ) { + outerResponse = typeof innerResponse === 'string' + ? safe.JSON_stringify(objAfter) + : objAfter; + safe.uboLog(logPrefix, 'Pruned'); + } else { + outerResponse = innerResponse; } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; } -function getRandomTokenFn() { +function evaldataPrune( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + proxyApplyFn('eval', function(context) { + const before = context.reflect(); + if ( typeof before !== 'object' ) { return before; } + if ( before === null ) { return null; } + const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); + return after || before; + }); +} +function noEvalIf( + needle = '' +) { + if ( typeof needle !== 'string' ) { return; } const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); + const logPrefix = safe.makeLogPrefix('noeval-if', needle); + const reNeedle = safe.patternToRegex(needle); + proxyApplyFn('eval', function(context) { + const { callArgs } = context; + const a = String(callArgs[0]); + if ( needle !== '' && reNeedle.test(a) ) { + safe.uboLog(logPrefix, 'Prevented:\n', a); + return; + } + if ( needle === '' || safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Not prevented:\n', a); + } + return context.reflect(); + }); } -function replaceNodeTextFn( - nodeName = '', +function preventAddEventListener( + type = '', pattern = '', - replacement = '', ...varargs ) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('replace-node-text.fn', ...Array.from(arguments)); - const reNodeName = safe.patternToRegex(nodeName, 'i', true); - const rePattern = safe.patternToRegex(pattern, 'gms'); const extraArgs = safe.parseVarargs(varargs); - const reIncludes = extraArgs.includes || extraArgs.condition - ? safe.patternToRegex(extraArgs.includes || extraArgs.condition, 'ms') - : null; - const reExcludes = extraArgs.excludes - ? safe.patternToRegex(extraArgs.excludes, 'ms') - : null; - const textContentFactory = (( ) => { - const out = { createScript: s => s }; - const { trustedTypes: tt } = self; - if ( tt instanceof Object ) { - if ( typeof tt.getPropertyType === 'function' ) { - if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { - return tt.createPolicy(getRandomTokenFn(), out); + const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); + const reType = safe.patternToRegex(type, undefined, true); + const rePattern = safe.patternToRegex(pattern); + const targetSelector = extraArgs.elements || undefined; + const elementMatches = elem => { + if ( targetSelector === 'window' ) { return elem === window; } + if ( targetSelector === 'document' ) { return elem === document; } + if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } + const elems = Array.from(document.querySelectorAll(targetSelector)); + return elems.includes(elem); + }; + const elementDetails = elem => { + if ( elem instanceof Window ) { return 'window'; } + if ( elem instanceof Document ) { return 'document'; } + if ( elem instanceof Element === false ) { return '?'; } + const parts = []; + // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 + const id = String(elem.id); + if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); } + for ( let i = 0; i < elem.classList.length; i++ ) { + parts.push(`.${CSS.escape(elem.classList.item(i))}`); + } + for ( let i = 0; i < elem.attributes.length; i++ ) { + const attr = elem.attributes.item(i); + if ( attr.name === 'id' ) { continue; } + if ( attr.name === 'class' ) { continue; } + parts.push(`[${CSS.escape(attr.name)}="${attr.value}"]`); + } + return parts.join(''); + }; + const shouldPrevent = (thisArg, type, handler) => { + const matchesType = safe.RegExp_test(reType, type); + const matchesHandler = safe.RegExp_test(rePattern, handler); + const matchesEither = matchesType || matchesHandler; + const matchesBoth = matchesType && matchesHandler; + if ( safe.logLevel > 1 && matchesEither ) { + debugger; // eslint-disable-line no-debugger + } + if ( matchesBoth && targetSelector !== undefined ) { + if ( elementMatches(thisArg) === false ) { return false; } + } + return matchesBoth; + }; + const proxyFn = function(context) { + const { callArgs, thisArg } = context; + let t, h; + try { + t = String(callArgs[0]); + if ( typeof callArgs[1] === 'function' ) { + h = String(safe.Function_toString(callArgs[1])); + } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { + if ( typeof callArgs[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(callArgs[1].handleEvent)); } + } else { + h = String(callArgs[1]); } + } catch { } - return out; - })(); - let sedCount = extraArgs.sedCount ?? Number.MAX_SAFE_INTEGER; - const handleNode = node => { - const before = node.textContent; - if ( reIncludes ) { - reIncludes.lastIndex = 0; - if ( safe.RegExp_test(reIncludes, before) === false ) { return; } + if ( type === '' && pattern === '' ) { + safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); + } else if ( shouldPrevent(thisArg, t, h) ) { + return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); } - if ( reExcludes ) { - reExcludes.lastIndex = 0; - if ( safe.RegExp_test(reExcludes, before) ) { return; } + return context.reflect(); + }; + const protect = owner => { + const { addEventListener } = owner; + Object.defineProperty(owner, 'addEventListener', { + set() { }, + get() { return addEventListener; } + }); + }; + runAt(( ) => { + proxyApplyFn('EventTarget.prototype.addEventListener', proxyFn); + if ( extraArgs.protect ) { protect(EventTarget.prototype); } + if ( Object.hasOwn(document, 'addEventListener') ) { + proxyApplyFn('document.addEventListener', proxyFn); + if ( extraArgs.protect ) { protect(document); } } - rePattern.lastIndex = 0; - if ( safe.RegExp_test(rePattern, before) === false ) { return; } - rePattern.lastIndex = 0; - const after = pattern !== '' - ? before.replace(rePattern, replacement) - : replacement; - node.textContent = node.nodeName === 'SCRIPT' - ? textContentFactory.createScript(after) - : after; - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Text before:\n${before.trim()}`); + if ( Object.hasOwn(window, 'addEventListener') ) { + proxyApplyFn('window.addEventListener', proxyFn); + if ( extraArgs.protect ) { protect(window); } } - safe.uboLog(logPrefix, `Text after:\n${after.trim()}`); - sedCount -= 1; + }, extraArgs.runAt); +} +function preventBab() { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-bab'); + const signatures = [ + [ 'blockadblock' ], + [ 'babasbm' ], + [ /getItem\('babn'\)/ ], + [ + 'getElementById', + 'String.fromCharCode', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'charAt', + 'DOMContentLoaded', + 'AdBlock', + 'addEventListener', + 'doScroll', + 'fromCharCode', + '<<2|r>>4', + 'sessionStorage', + 'clientWidth', + 'localStorage', + 'Math', + 'random', + ], + ]; + const check = function(s) { + if ( typeof s !== 'string' ) { return false; } + for ( const tokens of signatures ) { + let match = 0; + for ( const token of tokens ) { + const hit = token instanceof RegExp + ? token.test(s) + : s.includes(token); + if ( hit ) { match += 1; } + } + if ( (match / tokens.length) >= 0.8 ) { return true; } + } + return false; }; - const handleTree = root => { - const treeWalker = document.createTreeWalker(root, - NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT - ); - const { currentScript } = document; - let count = 0; - for (;;) { - const node = treeWalker.nextNode(); - if ( node === null ) { break; } - count += 1; - if ( node === currentScript ) { continue; } - if ( reNodeName.test(node.nodeName) ) { - handleNode(node); - } else if ( node.nodeName === 'TEMPLATE' ) { - count += handleTree(node.content); - } else { - continue; + proxyApplyFn('eval', function(context) { + const a = context.callArgs[0]; + if ( !check(a) ) { + return context.reflect(); + } + safe.uboLog(logPrefix, 'Prevented'); + if ( document.body ) { + document.body.style.removeProperty('visibility'); + } + const el = document.getElementById('babasbmsgx'); + if ( el ) { + el.parentNode.removeChild(el); + } + }); + proxyApplyFn('setTimeout', function(context) { + const { callArgs } = context; + const a = callArgs[0]; + if ( typeof a === 'string' && /\.bab_elementid.$/.test(a) ) { + callArgs[0] = ( ) => { }; + safe.uboLog(logPrefix, 'Prevented'); + } + return context.reflect(); + }); +} +function preventClipboardWrite(matches = '', ...varargs) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-clipboard-write'); + const pattern = safe.initPattern(matches); + const extraArgs = safe.parseVarargs(varargs); + const excludePattern = extraArgs.excludeMatches && + safe.initPattern(extraArgs.excludeMatches); + const htmlTemplate = [ + '
', + '${warning}\n', + '', + '
', + ].join(''); + const domAlert = clipboardText => { + const doc = document; + const domAlert = extraArgs.domAlert.replace(/\\n/g, '\n'); + let html; + if ( domAlert.includes('${text}') ) { + const code = doc.createElement('code'); + const styles = [ + 'background-color: #ddc', + 'display: inline-block', + 'font-family: monospace', + 'font-size: 100%', + 'max-height: 8em', + 'overflow: auto', + 'padding: 0.25em', + 'user-select: all', + 'width: 100%;', + 'word-break: break-all' + ]; + if ( Boolean(extraArgs.selectable ?? true) === false ) { + styles.push('user-select: none'); } - if ( sedCount === 0 ) { break; } + code.style = styles.join(';'); + code.textContent = clipboardText; + html = htmlTemplate.replace('${warning}', + domAlert.replace('${text}', code.outerHTML) + ); + } else { + html = htmlTemplate.replace('${warning}', domAlert); + } + if ( currentAlert ) { currentAlert.remove(); } + const domParser = new DOMParser(); + const fragment = domParser.parseFromString(html, 'text/html'); + currentAlert = fragment.querySelector('div'); + const button = currentAlert.querySelector('button'); + button.addEventListener('click', ( ) => { + if ( currentAlert === null ) { return; } + currentAlert.remove(); + currentAlert = null; + }); + currentAlert.setAttribute('popover', 'manual'); + doc.documentElement.append(currentAlert); + if ( typeof currentAlert.showPopover === 'function' ) { + currentAlert.showPopover(); } - return count; }; - if ( document.documentElement ) { - const count = handleTree(document.documentElement); - safe.uboLog(logPrefix, `${count} nodes present before installing mutation observer`); - } - const stay = Boolean(extraArgs.stay); - if ( sedCount === 0 && stay === false ) { return; } - const stop = (takeRecord = true) => { - const mutations = takeRecord ? observer.takeRecords() : []; - observer.disconnect(); - handleMutations(mutations); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Quitting'); + let currentAlert = null; + const prevent = text => { + if ( typeof text !== 'string' ) { return; } + text = text.trim(); + if ( safe.testPattern(pattern, text) !== true ) { return; } + if ( extraArgs.excludeMatches ) { + if ( safe.testPattern(excludePattern, text) ) { return; } + } + if ( extraArgs.domAlert ) { + domAlert(text); } + safe.uboLog(logPrefix, 'Prevented:\n\t', text); + return true; }; - const handleMutations = mutations => { - for ( const mutation of mutations ) { - for ( const node of mutation.addedNodes ) { - if ( reNodeName.test(node.nodeName) ) { - handleNode(node); - } else if ( node.nodeName === 'TEMPLATE' ) { - handleTree(node.content); - } else { - continue; - } - if ( sedCount === 0 ) { return stop(false); } + const installTraps = ( ) => { + proxyApplyFn('navigator.clipboard.writeText', async function(context) { + const text = `${context.callArgs[0]}`; + if ( prevent(text) ) { return; } + return context.reflect(); + }, { skipToString: true }); + proxyApplyFn('document.execCommand', function(context) { + const { callArgs } = context; + if ( callArgs[0] === 'copy' || callArgs[0] === 'cut' ) { + const text = document.getSelection()?.toString(); + if ( prevent(text) ) { return true; } } - } + return context.reflect(); + }, { skipToString: true }); }; - const observer = new MutationObserver(handleMutations); - observer.observe(document, { childList: true, subtree: true }); - if ( stay ) { return; } runAt(( ) => { - const quitAfter = extraArgs.quitAfter ?? 0; - if ( quitAfter === 0 ) { return stop(); } - setTimeout(( ) => { stop(); }, quitAfter); - }, 'interactive'); + self.document.addEventListener('mousemove', installTraps, { + once: true, + capture: true, + }); + }, 'interactive') } -function replaceNodeText( - nodeName, - pattern, - replacement, - ...extraArgs +function preventDialog( + selector = '', ) { - replaceNodeTextFn(nodeName, pattern, replacement, ...extraArgs); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -replaceNodeText(...args); -}, -}; - - -scriptlets['trusted-replace-fetch-response.js'] = { -aliases: ["trusted-rpfr.js"], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); + const logPrefix = safe.makeLogPrefix('prevent-dialog', selector); + const prevent = ( ) => { + debouncer = undefined; + const elems = document.querySelectorAll(`dialog${selector}`); + for ( const elem of elems ) { + if ( typeof elem.close !== 'function' ) { continue; } + if ( elem.open === false ) { continue; } + elem.close(); + safe.uboLog(logPrefix, 'Closed'); } - } - return needles; + }; + let debouncer; + const observer = new MutationObserver(( ) => { + if ( debouncer !== undefined ) { return; } + debouncer = requestAnimationFrame(prevent); + }); + observer.observe(document, { + attributes: true, + childList: true, + subtree: true, + }); } -function matchObjectPropertiesFn(propNeedles, ...objs) { +function preventFetchFn( + trusted = false, + propsToMatch = '', + responseBody = '', + responseType = '', + ...varargs +) { const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); + const setTimeout = self.setTimeout; + const scriptletName = `${trusted ? 'trusted-' : ''}prevent-fetch`; + const logPrefix = safe.makeLogPrefix( + scriptletName, + propsToMatch, + responseBody, + responseType + ); + const extraArgs = safe.parseVarargs(varargs); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const validResponseProps = { + ok: [ false, true ], + status: [ 403 ], + statusText: [ '', 'Not Found' ], + type: [ 'basic', 'cors', 'default', 'error', 'opaque' ], + }; + const responseProps = { + statusText: { value: 'OK' }, + }; + const responseHeaders = {}; + if ( /^\{.*\}$/.test(responseType) ) { + try { + Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => { + if ( p === 'headers' && trusted ) { + Object.assign(responseHeaders, v); + return; + } + if ( validResponseProps[p] === undefined ) { return; } + if ( validResponseProps[p].includes(v) === false ) { return; } + responseProps[p] = { value: v }; + }); + } + catch { } + } else if ( responseType !== '' ) { + if ( validResponseProps.type.includes(responseType) ) { + responseProps.type = { value: responseType }; } } - return matched; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); + proxyApplyFn('fetch', function fetch(context) { + const { callArgs } = context; + const details = collateFetchArgumentsFn(...callArgs); + if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { + const out = Array.from(Object.entries(details)).map(a => `${a[0]}:${a[1]}`); + safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); + } + if ( propsToMatch === '' && responseBody === '' ) { + return context.reflect(); + } + const matched = matchObjectPropertiesFn(propNeedles, details); + if ( matched === undefined || matched.length === 0 ) { + return context.reflect(); + } + return Promise.resolve(generateContentFn(trusted, responseBody)).then(text => { + safe.uboLog(logPrefix, `Prevented with response "${text}"`); + const headers = Object.assign({}, responseHeaders); + if ( headers['content-length'] === undefined ) { + headers['content-length'] = text.length; } - catch { + const response = new Response(text, { headers }); + const props = Object.assign( + { url: { value: details.url } }, + responseProps + ); + safe.Object_defineProperties(response, props); + if ( extraArgs.throttle ) { + return new Promise(resolve => { + setTimeout(( ) => { resolve(response); }, extraArgs.throttle); + }); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + return response; + }); + }); +} +function preventFetch(...args) { + preventFetchFn(false, ...args); +} +function trustedPreventFetch(...args) { + preventFetchFn(true, ...args); +} +function freezeElementProperty( + property = '', + selector = '', + pattern = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('freeze-element-property', property, selector, pattern); + const matcher = safe.initPattern(pattern, { canNegate: true }); + const owner = (( ) => { + if ( Object.hasOwn(HTMLScriptElement.prototype, property) ) { + return HTMLScriptElement.prototype; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; + if ( Object.hasOwn(HTMLElement.prototype, property) ) { + return HTMLElement.prototype; + } + if ( Object.hasOwn(Element.prototype, property) ) { + return Element.prototype; + } + if ( Object.hasOwn(Node.prototype, property) ) { + return Node.prototype; + } + return null; + })(); + if ( owner === null ) { return; } + const current = safe.Object_getOwnPropertyDescriptor(owner, property); + if ( current === undefined ) { return; } + const shouldPreventSet = (elem, a) => { + if ( selector !== '' ) { + if ( typeof elem.matches !== 'function' ) { return false; } + if ( elem.matches(selector) === false ) { return false; } + } + return safe.testPattern(matcher, `${a}`); }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + Object.defineProperty(owner, property, { + get: function() { + return current.get + ? current.get.call(this) + : current.value; + }, + set: function(a) { + if ( shouldPreventSet(this, a) ) { + safe.uboLog(logPrefix, 'Assignment prevented'); + } else if ( current.set ) { + current.set.call(this, a); } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Assigned:\n${a}`); } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + current.value = a; + }, + }); } -function collateFetchArgumentsFn(resource, options) { +function preventInnerHTML( + selector = '', + pattern = '' +) { + freezeElementProperty('innerHTML', selector, pattern); +} +function preventNavigation( + pattern = '' +) { const safe = safeSelf(); - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'url' - ]; - const out = {}; - if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { - collateFetchArgumentsFn.collateKnownProps = (src, out) => { - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; + const logPrefix = safe.makeLogPrefix('prevent-navigation', pattern); + const needle = pattern === 'location.href' ? self.location.href : pattern; + const matcher = safe.initPattern(needle, { canNegate: true }); + self.navigation.addEventListener('navigate', ev => { + if ( ev.userInitiated ) { return; } + const { url } = ev.destination; + if ( pattern === '' ) { + safe.uboLog(logPrefix, `Navigation to ${url}`); + return; + } + if ( safe.testPattern(matcher, url) ) { + ev.preventDefault(); + safe.uboLog(logPrefix, `Prevented navigation to ${url}`); + } + }); +} +class RangeParser { + constructor(s) { + this.not = s.charAt(0) === '!'; + if ( this.not ) { s = s.slice(1); } + if ( s === '' ) { return; } + const pos = s.indexOf('-'); + if ( pos !== 0 ) { + this.min = this.max = parseInt(s, 10) || 0; + } + if ( pos !== -1 ) { + this.max = parseInt(s.slice(pos + 1), 10) || Number.MAX_SAFE_INTEGER; + } } - if ( - typeof resource !== 'object' || - safe.Object_toString.call(resource) !== '[object Request]' - ) { - out.url = `${resource}`; - } else { - let clone; - try { - clone = safe.Request_clone.call(resource); - } catch { + unbound() { + return this.min === undefined && this.max === undefined; + } + test(v) { + const n = Math.min(Math.max(Number(v) || 0, 0), Number.MAX_SAFE_INTEGER); + if ( this.min === this.max ) { + return (this.min === undefined || n === this.min) !== this.not; + } + if ( this.min === undefined ) { + return (n <= this.max) !== this.not; + } + if ( this.max === undefined ) { + return (n >= this.min) !== this.not; + } + return (n >= this.min && n <= this.max) !== this.not; + } +} +function preventSetTimeout( + needleRaw = '', + delayRaw = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needleRaw, delayRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + const range = new RangeParser(delayRaw); + proxyApplyFn('setTimeout', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); + const b = callArgs[1]; + if ( needleRaw === '' && range.unbound() ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); + return context.reflect(); + } + if ( reNeedle.test(a) !== needleNot && range.test(b) ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); + } + return context.reflect(); + }); +} +function preventSetInterval( + needleRaw = '', + delayRaw = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-setInterval', needleRaw, delayRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + const range = new RangeParser(delayRaw); + proxyApplyFn('setInterval', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); + const b = callArgs[1]; + if ( needleRaw === '' && range.unbound() ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); + return context.reflect(); + } + if ( reNeedle.test(a) !== needleNot && range.test(b) ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); } - collateFetchArgumentsFn.collateKnownProps(clone || resource, out); - } - if ( typeof options === 'object' && options !== null ) { - collateFetchArgumentsFn.collateKnownProps(options, out); - } - return out; + return context.reflect(); + }); } -function replaceFetchResponseFn( - trusted = false, - pattern = '', - replacement = '', - propsToMatch = '', - ...varargs +function preventRequestAnimationFrame( + needleRaw = '' ) { - if ( trusted !== true ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('replace-fetch-response', pattern, replacement, propsToMatch); - if ( pattern === '*' ) { pattern = '.*'; } - const rePattern = safe.patternToRegex(pattern); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); - const extraArgs = safe.parseVarargs(varargs); - const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; - self.fetch = new Proxy(self.fetch, { - apply: function(target, thisArg, args) { - const fetchPromise = Reflect.apply(target, thisArg, args); - if ( pattern === '' ) { return fetchPromise; } - if ( propNeedles.size !== 0 ) { - const props = collateFetchArgumentsFn(...args); - const matched = matchObjectPropertiesFn(propNeedles, props); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(textBefore => { - if ( reIncludes && reIncludes.test(textBefore) === false ) { - return responseBefore; - } - const textAfter = textBefore.replace(rePattern, replacement); - if ( textAfter === textBefore ) { return responseBefore; } - safe.uboLog(logPrefix, 'Replaced'); - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, reason); - return fetchPromise; - }); + const logPrefix = safe.makeLogPrefix('prevent-requestAnimationFrame', needleRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + proxyApplyFn('requestAnimationFrame', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); + if ( needleRaw === '' ) { + safe.uboLog(logPrefix, `Called:\n${a}`); + } else if ( reNeedle.test(a) !== needleNot ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}`); } + return context.reflect(); }); } -function trustedReplaceFetchResponse(...args) { - replaceFetchResponseFn(true, ...args); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedReplaceFetchResponse(...args); -}, -}; - - -scriptlets['trusted-replace-xhr-response.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } +function parseReplaceFn(s) { + if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } + const parser = new ArglistParser('/'); + parser.nextArg(s, 1); + let pattern = s.slice(parser.argBeg, parser.argEnd); + if ( parser.transform ) { + pattern = parser.normalizeArg(pattern); } - return needles; -} -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; + if ( pattern === '' ) { return; } + parser.nextArg(s, parser.separatorEnd); + let replacement = s.slice(parser.argBeg, parser.argEnd); + if ( parser.separatorEnd === parser.separatorBeg ) { return; } + if ( parser.transform ) { + replacement = parser.normalizeArg(replacement); } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; + const flags = s.slice(parser.separatorEnd); try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); + return { re: new RegExp(pattern, flags), replacement }; } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; } - return safe; } -function matchObjectPropertiesFn(propNeedles, ...objs) { +function validateConstantFn(trusted, raw, extraArgs = {}) { const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); + let value; + if ( raw === 'undefined' ) { + value = undefined; + } else if ( raw === 'false' ) { + value = false; + } else if ( raw === 'true' ) { + value = true; + } else if ( raw === 'null' ) { + value = null; + } else if ( raw === "''" || raw === '' ) { + value = ''; + } else if ( raw === '[]' || raw === 'emptyArr' ) { + value = []; + } else if ( raw === '{}' || raw === 'emptyObj' ) { + value = {}; + } else if ( raw === 'noopFunc' ) { + value = function(){}; + } else if ( raw === 'trueFunc' ) { + value = function(){ return true; }; + } else if ( raw === 'falseFunc' ) { + value = function(){ return false; }; + } else if ( raw === 'throwFunc' ) { + value = function(){ throw ''; }; + } else if ( /^-?\d+$/.test(raw) ) { + value = parseInt(raw); + if ( isNaN(raw) ) { return; } + if ( Math.abs(raw) > 0x7FFF ) { return; } + } else if ( trusted ) { + if ( raw.startsWith('json:') ) { + try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } + } else if ( raw.startsWith('{') && raw.endsWith('}') ) { + try { value = safe.JSON_parse(raw).value; } catch { return; } } + } else { + return; } - return matched; + if ( extraArgs.as !== undefined ) { + if ( extraArgs.as === 'function' ) { + return ( ) => value; + } else if ( extraArgs.as === 'callback' ) { + return ( ) => (( ) => value); + } else if ( extraArgs.as === 'resolved' ) { + return Promise.resolve(value); + } else if ( extraArgs.as === 'rejected' ) { + return Promise.reject(value); + } + } + return value; } -function trustedReplaceXhrResponse( - pattern = '', - replacement = '', - propsToMatch = '', +function setConstantFn( + trusted = false, + chain = '', + rawValue = '', ...varargs ) { + if ( chain === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-xhr-response', pattern, replacement, propsToMatch); - const xhrInstances = new WeakMap(); - if ( pattern === '*' ) { pattern = '.*'; } - const rePattern = safe.patternToRegex(pattern); - const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue); const extraArgs = safe.parseVarargs(varargs); - const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const outerXhr = this; - const xhrDetails = { method, url }; - let outcome = 'match'; - if ( propNeedles.size !== 0 ) { - if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { - outcome = 'nomatch'; - } - } - if ( outcome === 'match' ) { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch"`); - } - xhrInstances.set(outerXhr, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - if ( typeof innerResponse !== 'string' ) { - return (xhrDetails.response = innerResponse); - } - if ( reIncludes && reIncludes.test(innerResponse) === false ) { - return (xhrDetails.response = innerResponse); - } - const textBefore = innerResponse; - const textAfter = textBefore.replace(rePattern, replacement); - if ( textAfter !== textBefore ) { - safe.uboLog(logPrefix, 'Match'); - } - return (xhrDetails.response = textAfter); - } - get responseText() { - const response = this.response; - if ( typeof response !== 'string' ) { - return super.responseText; - } - return response; - } - }; -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedReplaceXhrResponse(...args); -}, -}; - - -scriptlets['trusted-click-element.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function runAtHtmlElementFn(fn) { - if ( document.documentElement ) { - fn(); - return; - } - const observer = new MutationObserver(( ) => { - observer.disconnect(); - fn(); - }); - observer.observe(document, { childList: true }); -} -function onIdleFn(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); -} -function offIdleFn(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); -} -function lookupElementsFn(directive, until = 0) { - if ( lookupElementsFn.querySelectorEx === undefined ) { - lookupElementsFn.getShadowRoot = elem => { - if ( elem.openOrClosedShadowRoot ) { // Firefox - return elem.openOrClosedShadowRoot; - } - if ( self.chrome?.dom?.openOrClosedShadowRoot ) { // Chromium - return self.chrome.dom.openOrClosedShadowRoot(elem); - } - return elem.shadowRoot; + function setConstant(chain, rawValue) { + const trappedProp = (( ) => { + const pos = chain.lastIndexOf('.'); + if ( pos === -1 ) { return chain; } + return chain.slice(pos+1); + })(); + const cloakFunc = fn => { + safe.Object_defineProperty(fn, 'name', { value: trappedProp }); + return new Proxy(fn, { + defineProperty(target, prop) { + if ( prop !== 'toString' ) { + return Reflect.defineProperty(...arguments); + } + return true; + }, + deleteProperty(target, prop) { + if ( prop !== 'toString' ) { + return Reflect.deleteProperty(...arguments); + } + return true; + }, + get(target, prop) { + if ( prop === 'toString' ) { + return function() { + return `function ${trappedProp}() { [native code] }`; + }.bind(null); + } + return Reflect.get(...arguments); + }, + }); }; - lookupElementsFn.queryOrEvaluateSelector = (selector, context) => { - if ( selector.startsWith('xpath:') === false ) { - return Array.from(context.querySelectorAll(selector)); - } - const result = document.evaluate(selector.slice(6), context, null, 7, null); - const out = []; - if ( result.resultType === 7 ) { - for ( let i = 0; i < result.snapshotLength; i++ ) { - out[i] = result.snapshotItem(i); - } - } - return out; + if ( trappedProp === '' ) { return; } + const thisScript = document.currentScript; + let normalValue = validateConstantFn(trusted, rawValue, extraArgs); + if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { + normalValue = cloakFunc(normalValue); } - lookupElementsFn.querySelectorEx = (selector, context = document) => { - const pos = selector.indexOf(' >>> '); - if ( pos === -1 ) { - return lookupElementsFn.queryOrEvaluateSelector(selector, context); - } - const outside = selector.slice(0, pos).trim(); - const inside = selector.slice(pos + 5).trim(); - const elems = lookupElementsFn.queryOrEvaluateSelector(outside, context); - const out = []; - for ( let i = 0; i < elems.length; i++ ) { - const shadowRoot = lookupElementsFn.getShadowRoot(elems[i]); - if ( Boolean(shadowRoot) === false ) { continue; } - lookupElementsFn.querySelectorEx(inside, shadowRoot).forEach(a => out.push(a)); + let aborted = false; + const mustAbort = function(v) { + if ( trusted ) { return false; } + if ( aborted ) { return true; } + aborted = + (v !== undefined && v !== null) && + (normalValue !== undefined && normalValue !== null) && + (typeof v !== typeof normalValue); + if ( aborted ) { + safe.uboLog(logPrefix, `Aborted because value set to ${v}`); } - return out; - }; - lookupElementsFn.lookup = directive => { - const beVisible = directive.startsWith('when-visible:'); - const selector = beVisible ? directive.slice(13) : directive; - const elems = lookupElementsFn.querySelectorEx(selector); - if ( beVisible !== true ) { return elems; } - return elems.filter(a => a.checkVisibility({ - opacityProperty: true, - visibilityProperty: true, - })); + return aborted; }; - lookupElementsFn.lookupAsync = details => { - const elems = lookupElementsFn.lookup(details.directive); - if ( elems.length || Date.now() >= details.until ) { - if ( details.observer ) { - details.observer.disconnect(); - details.observer = undefined; + // https://github.com/uBlockOrigin/uBlock-issues/issues/156 + // Support multiple trappers for the same property. + const trapProp = function(owner, prop, configurable, handler) { + if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; } + const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop); + let prevGetter, prevSetter; + if ( odesc instanceof safe.Object ) { + owner[prop] = normalValue; + if ( odesc.get instanceof Function ) { + prevGetter = odesc.get; } - if ( details.timer ) { - offIdleFn(details.timer); - details.timer = undefined; + if ( odesc.set instanceof Function ) { + prevSetter = odesc.set; } - return details.resolve(elems); } - if ( details.observer === undefined ) { - details.observer = new MutationObserver(( ) => { - lookupElementsFn.lookupAsync(details); + try { + safe.Object_defineProperty(owner, prop, { + configurable, + get() { + if ( prevGetter !== undefined ) { + prevGetter(); + } + return handler.getter(); + }, + set(a) { + if ( prevSetter !== undefined ) { + prevSetter(a); + } + handler.setter(a); + } }); - details.observer.observe(document, { - attributes: true, - childList: true, - subtree: true, + safe.uboLog(logPrefix, 'Trap installed'); + } catch(ex) { + safe.uboErr(logPrefix, ex); + } + }; + const trapChain = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + trapProp(owner, chain, false, { + v: undefined, + init: function(v) { + if ( mustAbort(v) ) { return false; } + this.v = v; + return true; + }, + getter: function() { + if ( document.currentScript === thisScript ) { + return this.v; + } + safe.uboLog(logPrefix, 'Property read'); + return normalValue; + }, + setter: function(a) { + if ( mustAbort(a) === false ) { return; } + normalValue = a; + } }); + return; } - if ( details.timer === undefined ) { - details.timer = onIdleFn(( ) => { - details.timer = undefined; - lookupElementsFn.lookupAsync(details); - }, { timeout: 151 }); + const prop = chain.slice(0, pos); + const v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) { + trapChain(v, chain); + return; } + trapProp(owner, prop, true, { + v: undefined, + init: function(v) { + this.v = v; + return true; + }, + getter: function() { + return this.v; + }, + setter: function(a) { + this.v = a; + if ( a instanceof safe.Object ) { + trapChain(a, chain); + } + } + }); }; + trapChain(window, chain); } - if ( until === 0 ) { - return lookupElementsFn.lookup(directive); - } - return new Promise(resolve => { - lookupElementsFn.lookupAsync({ directive, until, resolve }); - }); + runAt(( ) => { + setConstant(chain, rawValue); + }, extraArgs.runAt); } -function getAllLocalStorageFn(which = 'localStorage') { - const storage = self[which]; - const out = []; - for ( let i = 0; i < storage.length; i++ ) { - const key = storage.key(i); - const value = storage.getItem(key); - return { key, value }; - } - return out; +function setConstant( + ...args +) { + setConstantFn(false, ...args); } -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; +function trustedSetConstant( + ...args +) { + setConstantFn(true, ...args); +} +function trustedReplaceArgument( + propChain = '', + argposRaw = '', + argraw = '', + ...varargs +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); + const argoffset = parseInt(argposRaw, 10) || 0; + const extraArgs = safe.parseVarargs(varargs); + let replacer; + if ( argraw.startsWith('repl:/') ) { + const parsed = parseReplaceFn(argraw.slice(5)); + if ( parsed === undefined ) { return; } + replacer = arg => `${arg}`.replace(replacer.re, replacer.replacement); + Object.assign(replacer, parsed); + } else if ( argraw.startsWith('add:') ) { + const delta = parseFloat(argraw.slice(4)); + if ( isNaN(delta) ) { return; } + replacer = arg => Number(arg) + delta; + } else { + const value = validateConstantFn(true, argraw, extraArgs); + replacer = ( ) => value; + } + const reCondition = extraArgs.condition + ? safe.patternToRegex(`${extraArgs.condition}`) + : /^/; + const getArg = context => { + if ( argposRaw === 'this' ) { return context.thisArg; } + const { callArgs } = context; + const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; + if ( argpos < 0 || argpos >= callArgs.length ) { return; } + context.private = { argpos }; + return callArgs[argpos]; + }; + const setArg = (context, value) => { + if ( argposRaw === 'this' ) { + if ( value !== context.thisArg ) { + context.thisArg = value; } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + } else if ( context.private ) { + context.callArgs[context.private.argpos] = value; + } + }; + proxyApplyFn(propChain, function(context) { + if ( argposRaw === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${context.callArgs.join('\n')}`); + return context.reflect(); + } + const argBefore = getArg(context); + if ( extraArgs.condition !== undefined ) { + if ( safe.RegExp_test(reCondition, argBefore) === false ) { + return context.reflect(); } - return { pattern, expect }; + } + const argAfter = replacer(argBefore); + if ( argAfter !== argBefore ) { + setArg(context, argAfter); + safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`); + } + return context.reflect(); + }); +} +function spoofCSS( + selector, + ...args +) { + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase()); + const propToValueMap = new Map(); + const privatePropToValueMap = new Map(); + for ( let i = 0; i < args.length; i += 2 ) { + const prop = toCamelCase(args[i+0]); + if ( prop === '' ) { break; } + const value = args[i+1]; + if ( typeof value !== 'string' ) { break; } + if ( prop.charCodeAt(0) === 0x5F /* _ */ ) { + privatePropToValueMap.set(prop, value); + } else { + propToValueMap.set(prop, value); + } + } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('spoof-css', selector, ...args); + const instanceProperties = [ 'cssText', 'length', 'parentRule' ]; + const spoofStyle = (prop, real) => { + const normalProp = toCamelCase(prop); + const shouldSpoof = propToValueMap.has(normalProp); + const value = shouldSpoof ? propToValueMap.get(normalProp) : real; + if ( shouldSpoof ) { + safe.uboLog(logPrefix, `Spoofing ${prop} to ${value}`); + } + return value; + }; + const cloackFunc = (fn, thisArg, name) => { + const trap = fn.bind(thisArg); + Object.defineProperty(trap, 'name', { value: name }); + Object.defineProperty(trap, 'toString', { + value: ( ) => `function ${name}() { [native code] }` + }); + return trap; + }; + self.getComputedStyle = new Proxy(self.getComputedStyle, { + apply: function(target, thisArg, args) { + // eslint-disable-next-line no-debugger + if ( privatePropToValueMap.has('_debug') ) { debugger; } + const style = Reflect.apply(target, thisArg, args); + const targetElements = new WeakSet(document.querySelectorAll(selector)); + if ( targetElements.has(args[0]) === false ) { return style; } + const proxiedStyle = new Proxy(style, { + get(target, prop) { + if ( typeof target[prop] === 'function' ) { + if ( prop === 'getPropertyValue' ) { + return cloackFunc(function getPropertyValue(prop) { + return spoofStyle(prop, target[prop]); + }, target, 'getPropertyValue'); + } + return cloackFunc(target[prop], target, prop); + } + if ( instanceProperties.includes(prop) ) { + return Reflect.get(target, prop); + } + return spoofStyle(prop, Reflect.get(target, prop)); + }, + getOwnPropertyDescriptor(target, prop) { + if ( propToValueMap.has(prop) ) { + return { + configurable: true, + enumerable: true, + value: propToValueMap.get(prop), + writable: true, + }; + } + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + }); + return proxiedStyle; }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + get(target, prop) { + if ( prop === 'toString' ) { + return target.toString.bind(target); } - return haystack.includes(details.pattern) === details.expect; + return Reflect.get(target, prop); }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + }); + Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, { + apply: function(target, thisArg, args) { + // eslint-disable-next-line no-debugger + if ( privatePropToValueMap.has('_debug') ) { debugger; } + const rect = Reflect.apply(target, thisArg, args); + const targetElements = new WeakSet(document.querySelectorAll(selector)); + if ( targetElements.has(thisArg) === false ) { return rect; } + let { x, y, height, width } = rect; + if ( privatePropToValueMap.has('_rectx') ) { + x = parseFloat(privatePropToValueMap.get('_rectx')); } - try { - return new RegExp(match[1], match[2] || undefined); + if ( privatePropToValueMap.has('_recty') ) { + y = parseFloat(privatePropToValueMap.get('_recty')); } - catch { + if ( privatePropToValueMap.has('_rectw') ) { + width = parseFloat(privatePropToValueMap.get('_rectw')); + } else if ( propToValueMap.has('width') ) { + width = parseFloat(propToValueMap.get('width')); } - return /^/; + if ( privatePropToValueMap.has('_recth') ) { + height = parseFloat(privatePropToValueMap.get('_recth')); + } else if ( propToValueMap.has('height') ) { + height = parseFloat(propToValueMap.get('height')); + } + return new self.DOMRect(x, y, width, height); }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); + get(target, prop) { + if ( prop === 'toString' ) { + return target.toString.bind(target); + } + return Reflect.get(target, prop); }, + }); +} +function modifyXhrResponseFn( + propsToMatch = '', + modifierFn = '' +) { + if ( typeof propsToMatch !== 'string' ) { return; } + const safe = safeSelf(); + if ( modifyXhrResponseFn.xhrInstances === undefined ) { + modifyXhrResponseFn.xhrInstances = new WeakMap(); + } + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const NativeXMLHttpRequest = self.XMLHttpRequest; + const TrappedXMLHttpRequest = class XMLHttpRequest extends NativeXMLHttpRequest { + open(method, url, ...args) { + const haystack = { method, url }; + if ( propsToMatch === '' ) { + safe.uboLog(`modifyXhrResponseFn() / Called: ${safe.JSON_stringify(haystack, null, 2)}`); + } else if ( matchObjectPropertiesFn(propNeedles, haystack) ) { + modifyXhrResponseFn.xhrInstances.set(this, modifierFn); + } + return super.open(method, url, ...args); + } + get response() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.response) + : super.response; + } + get responseText() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.responseText) + : super.responseText; + } + get responseXML() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.responseXML) + : super.responseXML; + } }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + proxyToStringFn(TrappedXMLHttpRequest.prototype.open, NativeXMLHttpRequest.prototype.open); + proxyToStringFn(TrappedXMLHttpRequest, NativeXMLHttpRequest); + self.XMLHttpRequest = TrappedXMLHttpRequest; +} +function preventXhrFn( + trusted = false, + propsToMatch = '', + directive = '' +) { + if ( typeof propsToMatch !== 'string' ) { return; } + const safe = safeSelf(); + const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr'; + const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive); + const xhrInstances = new WeakMap(); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const warOrigin = scriptletGlobals.warOrigin; + const safeDispatchEvent = (xhr, type) => { + try { + xhr.dispatchEvent(new Event(type)); + } catch { } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + proxyApplyFn('XMLHttpRequest.prototype.open', function(context) { + const { thisArg, callArgs } = context; + xhrInstances.delete(thisArg); + const [ method, url, ...args ] = callArgs; + if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { + return context.reflect(); + } + const haystack = { method, url }; + if ( propsToMatch === '' && directive === '' ) { + safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); + return context.reflect(); + } + if ( matchObjectPropertiesFn(propNeedles, haystack) ) { + const xhrDetails = Object.assign(haystack, { + xhr: thisArg, + defer: args.length === 0 || !!args[0], + directive, + headers: { + 'date': '', + 'content-type': '', + 'content-length': '', + }, + url: haystack.url, + props: { + response: { value: '' }, + responseText: { value: '' }, + responseXML: { value: null }, + }, + }); + xhrInstances.set(thisArg, xhrDetails); + } + return context.reflect(); + }); + proxyApplyFn('XMLHttpRequest.prototype.send', function(context) { + const { thisArg } = context; + const xhrDetails = xhrInstances.get(thisArg); + if ( xhrDetails === undefined ) { + return context.reflect(); + } + xhrDetails.headers['date'] = (new Date()).toUTCString(); + let xhrText = ''; + switch ( thisArg.responseType ) { + case 'arraybuffer': + xhrDetails.props.response.value = new ArrayBuffer(0); + xhrDetails.headers['content-type'] = 'application/octet-stream'; + break; + case 'blob': + xhrDetails.props.response.value = new Blob([]); + xhrDetails.headers['content-type'] = 'application/octet-stream'; + break; + case 'document': { + const parser = new DOMParser(); + const doc = parser.parseFromString('', 'text/html'); + xhrDetails.props.response.value = doc; + xhrDetails.props.responseXML.value = doc; + xhrDetails.headers['content-type'] = 'text/html'; + break; + } + case 'json': + xhrDetails.props.response.value = {}; + xhrDetails.props.responseText.value = '{}'; + xhrDetails.headers['content-type'] = 'application/json'; + break; + default: { + if ( directive === '' ) { break; } + xhrText = generateContentFn(trusted, xhrDetails.directive); + if ( xhrText instanceof Promise ) { + xhrText = xhrText.then(text => { + xhrDetails.props.response.value = text; + xhrDetails.props.responseText.value = text; + }); + } else { + xhrDetails.props.response.value = xhrText; + xhrDetails.props.responseText.value = xhrText; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + xhrDetails.headers['content-type'] = 'text/plain'; + break; + } + } + if ( xhrDetails.defer === false ) { + xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length; + Object.defineProperties(xhrDetails.xhr, { + readyState: { value: 4 }, + responseURL: { value: xhrDetails.url }, + status: { value: 200 }, + statusText: { value: 'OK' }, + }); + Object.defineProperties(xhrDetails.xhr, xhrDetails.props); + return; + } + Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 1, configurable: true }, + responseURL: { value: xhrDetails.url }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + xhrDetails.headers['content-length'] = `${details.props.response.value}`.length; + Object.defineProperties(details.xhr, { + readyState: { value: 2, configurable: true }, + status: { value: 200 }, + statusText: { value: 'OK' }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 3, configurable: true }, + }); + Object.defineProperties(details.xhr, details.props); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 4 }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + safeDispatchEvent(details.xhr, 'load'); + safeDispatchEvent(details.xhr, 'loadend'); + safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); + }); + }); + proxyApplyFn('XMLHttpRequest.prototype.getResponseHeader', function(context) { + const { thisArg } = context; + const xhrDetails = xhrInstances.get(thisArg); + if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { + return context.reflect(); + } + const headerName = `${context.callArgs[0]}`; + const value = xhrDetails.headers[headerName.toLowerCase()]; + if ( value !== undefined && value !== '' ) { return value; } + return null; + }); + proxyApplyFn('XMLHttpRequest.prototype.getAllResponseHeaders', function(context) { + const { thisArg } = context; + const xhrDetails = xhrInstances.get(thisArg); + if ( xhrDetails === undefined || thisArg.readyState < thisArg.HEADERS_RECEIVED ) { + return context.reflect(); + } + const out = []; + for ( const [ name, value ] of Object.entries(xhrDetails.headers) ) { + if ( !value ) { continue; } + out.push(`${name}: ${value}`); + } + if ( out.length !== 0 ) { out.push(''); } + return out.join('\r\n'); + }); } -function getAllCookiesFn() { - const safe = safeSelf(); - return safe.String_split.call(document.cookie, /\s*;\s*/).map(s => { - const pos = s.indexOf('='); - if ( pos === 0 ) { return; } - if ( pos === -1 ) { return `${s.trim()}=`; } - const key = s.slice(0, pos).trim(); - const value = s.slice(pos+1).trim(); - return { key, value }; - }).filter(s => s !== undefined); +function preventXhr(...args) { + preventXhrFn(false, ...args); } -function trustedClickElement( - selectors = '', - extraMatch = '', - delay = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-click-element', selectors, extraMatch, delay); - - if ( extraMatch !== '' ) { - const assertions = safe.String_split.call(extraMatch, ',').map(s => { - const pos1 = s.indexOf(':'); - const s1 = pos1 !== -1 ? s.slice(0, pos1) : s; - const not = s1.startsWith('!'); - const type = not ? s1.slice(1) : s1; - const s2 = pos1 !== -1 ? s.slice(pos1+1).trim() : ''; - if ( s2 === '' ) { return; } - const out = { not, type }; - const match = /^\/(.+)\/(i?)$/.exec(s2); - if ( match !== null ) { - out.re = new RegExp(match[1], match[2] || undefined); - return out; - } - const pos2 = s2.indexOf('='); - const key = pos2 !== -1 ? s2.slice(0, pos2).trim() : s2; - const value = pos2 !== -1 ? s2.slice(pos2+1).trim() : ''; - out.re = new RegExp(`^${safe.escapeRegexChars(key)}=${safe.escapeRegexChars(value)}`); - return out; - }).filter(details => details !== undefined); - const allCookies = assertions.some(o => o.type === 'cookie') - ? getAllCookiesFn() - : []; - const allStorageItems = assertions.some(o => o.type === 'localStorage') - ? getAllLocalStorageFn() - : []; - const hasNeedle = (haystack, needle) => { - for ( const { key, value } of haystack ) { - if ( needle.test(`${key}=${value}`) ) { return true; } - } - return false; - }; - for ( const { not, type, re } of assertions ) { - switch ( type ) { - case 'cookie': - if ( hasNeedle(allCookies, re) === not ) { return; } - break; - case 'localStorage': - if ( hasNeedle(allStorageItems, re) === not ) { return; } - break; - } +function trustedPreventXhr(...args) { + preventXhrFn(true, ...args); +} +function mpegdashPrune( + selector = '', + propsToMatch = '' +) { + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('mpegdash-prune', selector, propsToMatch); + const queryAll = (xmlDoc, selector) => { + if ( selector.startsWith('xpath:') === false ) { + return Array.from(xmlDoc.querySelectorAll(selector)); } - } - - const steps = (( ) => { - const steps = /^[;|]/.test(selectors) - ? safe.String_split.call(selectors.slice(1), selectors.charAt(0)) - : safe.String_split.call(selectors, ','); - return steps.map(a => { - a = a.trim(); - if ( /^\d+$/.test(a) ) { return parseInt(a, 10); } - return a; - }); - })(); - if ( steps.length === 0 ) { return; } - const clickDelay = parseInt(delay, 10) || 1; - for ( let i = steps.length-1; i > 0; i-- ) { - if ( typeof steps[i] !== 'string' ) { continue; } - if ( typeof steps[i-1] !== 'string' ) { continue; } - steps.splice(i, 0, clickDelay); - } - if ( steps.length === 1 && delay !== '' ) { - steps.unshift(clickDelay); - } - if ( typeof steps.at(-1) !== 'number' ) { - steps.push(11000); - } - - const timeout = steps.pop(); - - const waitForTime = ms => { - return new Promise(resolve => { - safe.uboLog(logPrefix, `Waiting for ${ms} ms`); - waitForTime.timer = setTimeout(( ) => { - waitForTime.timer = undefined; - resolve(); - }, ms); - }); + const xpr = xmlDoc.evaluate( + selector.slice(6), + xmlDoc, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null + ); + const out = []; + for ( let i = 0; i < xpr.snapshotLength; i++ ) { + const node = xpr.snapshotItem(i); + out.push(node); + } + return out; }; - - const waitForElement = directive => { - safe.uboLog(logPrefix, `Waiting for ${directive}`); - return lookupElementsFn(directive, Date.now() + timeout).then(elems => { - if ( elems.length === 0 ) { return false; } - elems[0].click(); - safe.uboLog(logPrefix, `Clicked ${directive}`); - return true; - }); + const rePTparse = /^PT(\d+D)?(\d+H)?(\d+M)?([\d.]+S)?$/; + const secondsPerDay = 24 * 60 * 60; + const secondsPerHour = 60 * 60; + const secondsPerMinute = 60; + const secondsFromPT = pt => { + const match = rePTparse.exec(pt); + if ( match === null ) { return; } + let seconds = 0; + if ( match[1] ) { + const d = parseFloat(match[1].slice(0, -1)); + if ( isNaN(d) ) { return; } + seconds += d * secondsPerDay; + } + if ( match[2] ) { + const h = parseFloat(match[2].slice(0, -1)); + if ( isNaN(h) ) { return; } + seconds += h * secondsPerHour; + } + if ( match[3] ) { + const m = parseFloat(match[3].slice(0, -1)); + if ( isNaN(m) ) { return; } + seconds += m * secondsPerMinute; + } + if ( match[4] ) { + const s = parseFloat(match[4].slice(0, -1)); + if ( isNaN(s) ) { return; } + seconds += s; + } + return seconds; }; - - const process = async ( ) => { - while ( steps.length !== 0 ) { - const step = steps.shift(); - if ( step === undefined ) { break; } - if ( typeof step === 'number' ) { - await waitForTime(step); - if ( step === 1 ) { continue; } - continue; - } - if ( step.startsWith('!') ) { continue; } - const clicked = await waitForElement(step); - if ( clicked ) { continue; } - safe.uboLog(logPrefix, `Timed out waiting on ${step}`); - break; + const ptFromSeconds = seconds => { + const parts = [ 'PT' ]; + const d = Math.floor(seconds / secondsPerDay); + if ( d ) { + parts.push(`${d}D`); + seconds -= d * secondsPerDay; + } + const h = Math.floor(seconds / secondsPerHour); + if ( h ) { + parts.push(`${h}H`); + seconds -= h * secondsPerHour; + } + const m = Math.floor(seconds / secondsPerMinute); + if ( m ) { + parts.push(`${m}M`); + seconds -= m * secondsPerMinute; } + parts.push(`${seconds}S`); + return parts.join(''); }; - - runAtHtmlElementFn(process); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedClickElement(...args); -}, -}; - - -scriptlets['trusted-replace-outbound-text.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + const fixTimeAttributes = xmlDoc => { + try { + const periods = queryAll(xmlDoc, 'MPD > Period'); + if ( periods.length === 0 ) { return; } + let seconds = 0; + for ( const period of periods ) { + const startAttrBefore = period.getAttribute('start'); + const durAttr = period.getAttribute('duration'); + if ( startAttrBefore === null || durAttr === null ) { continue; } + const startAttrAfter = ptFromSeconds(seconds); + period.setAttribute('start', startAttrAfter); + if ( period.hasAttribute('id') ) { + const idAttr = period.getAttribute('id'); + period.setAttribute('id', idAttr.replace(startAttrBefore, startAttrAfter)); + } + seconds += secondsFromPT(durAttr); } - try { - return new RegExp(match[1], match[2] || undefined); + const mpds = queryAll(xmlDoc, 'MPD[mediaPresentationDuration]'); + if ( mpds.length !== 1 ) { return; } + mpds[0].setAttribute('mediaPresentationDuration', ptFromSeconds(seconds)); + } catch { + } + }; + const pruneFromDoc = xmlDoc => { + try { + if ( selector === '' ) { + const serializer = new XMLSerializer(); + safe.uboLog(logPrefix, `Document is\n\t${serializer.serializeToString(xmlDoc)}`); } - catch { + const items = queryAll(xmlDoc, selector); + if ( items.length === 0 ) { return xmlDoc; } + safe.uboLog(logPrefix, `Patching ${items.length} items`); + for ( const item of items ) { + if ( item.nodeType !== 1 ) { continue; } + item.setAttribute('duration', 'PT0S'); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, + fixTimeAttributes(xmlDoc); + } catch(ex) { + safe.uboErr(logPrefix, `Error: ${ex}`); + } + return xmlDoc; }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const pruneFromText = text => { + if ( (/^\s*\s*$/.test(text)) === false ) { + return text; + } + try { + const xmlParser = new DOMParser(); + const xmlDoc = xmlParser.parseFromString(text, 'text/xml'); + pruneFromDoc(xmlDoc); + const serializer = new XMLSerializer(); + text = serializer.serializeToString(xmlDoc); + } catch { } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; + modifyXhrResponseFn(propsToMatch, (xhr, before) => { + if ( before instanceof XMLDocument ) { + return pruneFromDoc(before); + } + if ( typeof before === 'string' ) { + return pruneFromText(before); + } + return before; + }); +} +function getSafeCookieValuesFn() { + return [ + 'accept', 'reject', + 'accepted', 'rejected', 'notaccepted', + 'allow', 'disallow', 'deny', + 'allowed', 'denied', + 'approved', 'disapproved', + 'checked', 'unchecked', + 'dismiss', 'dismissed', + 'enable', 'disable', + 'enabled', 'disabled', + 'essential', 'nonessential', + 'forbidden', 'forever', + 'hide', 'hidden', + 'necessary', 'required', + 'ok', + 'on', 'off', + 'true', 't', 'false', 'f', + 'yes', 'y', 'no', 'n', + 'all', 'none', 'functional', + 'granted', 'done', + 'decline', 'declined', + 'closed', 'next', 'mandatory', + 'disagree', 'agree', + 'set', 'unset', + 'given', + ]; +} +function getAllCookiesFn() { + const safe = safeSelf(); + return safe.String_split.call(document.cookie, /\s*;\s*/).map(s => { + const pos = s.indexOf('='); + if ( pos === 0 ) { return; } + if ( pos === -1 ) { return `${s.trim()}=`; } + const key = s.slice(0, pos).trim(); + const value = s.slice(pos+1).trim(); + return { key, value }; + }).filter(s => s !== undefined); +} +function getCookieFn( + name = '' +) { + const safe = safeSelf(); + for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { + const pos = s.indexOf('='); + if ( pos === -1 ) { continue; } + if ( s.slice(0, pos) !== name ) { continue; } + return s.slice(pos+1).trim(); } - return safe; } -function proxyApplyFn( - target = '', - handler = '', - options = {} +function setCookieFn( + trusted = false, + name = '', + value = '', + expires = '', + path = '', + options = {}, ) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); + // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 + // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 + if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { + name = encodeURIComponent(name); } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; + // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 + // The characters [",] are given a pass from the RFC requirements because + // apparently browsers do not follow the RFC to the letter. + if ( /[^ -:<-[\]-~]/.test(value) ) { + value = encodeURIComponent(value); + } + + const cookieBefore = getCookieFn(name); + if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } + if ( cookieBefore === value && options.reload ) { return; } + + const cookieParts = [ name, '=', value ]; + if ( expires !== '' ) { + cookieParts.push('; expires=', expires); + } + + if ( path === '' ) { path = '/'; } + else if ( path === 'none' ) { path = ''; } + if ( path !== '' && path !== '/' ) { return; } + if ( path === '/' ) { + cookieParts.push('; path=/'); + } + + if ( trusted ) { + if ( options.domain ) { + let domain = options.domain; + if ( /^\/.+\//.test(domain) ) { + const baseURL = new URL(document.baseURI); + const reDomain = new RegExp(domain.slice(1, -1)); + const match = reDomain.exec(baseURL.hostname); + domain = match ? match[0] : undefined; } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); + if ( domain ) { + cookieParts.push(`; domain=${domain}`); } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; } + cookieParts.push('; Secure'); + } else if ( /^__(Host|Secure)-/.test(name) ) { + cookieParts.push('; Secure'); } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); + + try { + document.cookie = cookieParts.join(''); + } catch { } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + + const done = getCookieFn(name) === value; + if ( done && options.reload ) { + window.location.reload(); + } + + return done; +} +function setCookie( + name = '', + value = '', + path = '', + ...varargs +) { + if ( name === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); + const normalized = value.toLowerCase(); + const match = /^("?)(.+)\1$/.exec(normalized); + const unquoted = match && match[2] || normalized; + const validValues = getSafeCookieValuesFn(); + if ( validValues.includes(unquoted) === false ) { + if ( /^-?\d+$/.test(unquoted) === false ) { return; } + const n = parseInt(value, 10) || 0; + if ( n < -32767 || n > 32767 ) { return; } + } + + const done = setCookieFn( + false, + name, + value, + '', + path, + safe.parseVarargs(varargs) + ); + + if ( done ) { + safe.uboLog(logPrefix, 'Done'); + } +} +function setCookieReload(name, value, path, ...args) { + setCookie(name, value, path, 'reload', '1', ...args); +} +function trustedSetCookie( + name = '', + value = '', + offsetExpiresSec = '', + path = '', + ...varargs +) { + if ( name === '' ) { return; } + + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); + const time = new Date(); + + if ( value.includes('$now$') ) { + value = value.replaceAll('$now$', time.getTime()); + } + if ( value.includes('$currentDate$') ) { + value = value.replaceAll('$currentDate$', time.toUTCString()); + } + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', time.toISOString()); + } + + let expires = ''; + if ( offsetExpiresSec !== '' ) { + if ( offsetExpiresSec === '1day' ) { + time.setDate(time.getDate() + 1); + } else if ( offsetExpiresSec === '1year' ) { + time.setFullYear(time.getFullYear() + 1); + } else { + if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; } + time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10)); } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; + expires = time.toUTCString(); + } + + const done = setCookieFn( + true, + name, + value, + expires, + path, + safe.parseVarargs(varargs) + ); + + if ( done ) { + safe.uboLog(logPrefix, 'Done'); } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; } -function trustedReplaceOutboundText( - propChain = '', - rawPattern = '', - rawReplacement = '', +function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) { + trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args); +} +function removeCookie( + needle = '', ...varargs ) { - if ( propChain === '' ) { return; } + if ( typeof needle !== 'string' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, rawPattern, rawReplacement, ...varargs); - const rePattern = safe.patternToRegex(rawPattern); - const replacement = rawReplacement.startsWith('json:') - ? safe.JSON_parse(rawReplacement.slice(5)) - : rawReplacement; + const reName = safe.patternToRegex(needle); const extraArgs = safe.parseVarargs(varargs); - const reCondition = safe.patternToRegex(extraArgs.condition || ''); - proxyApplyFn(propChain, function(context) { - const encodedTextBefore = context.reflect(); - let textBefore = encodedTextBefore; - if ( extraArgs.encoding === 'base64' ) { - try { textBefore = self.atob(encodedTextBefore); } - catch { return encodedTextBefore; } + const throttle = (fn, ms = 500) => { + if ( throttle.timer !== undefined ) { return; } + throttle.timer = setTimeout(( ) => { + throttle.timer = undefined; + fn(); + }, ms); + }; + const baseURL = new URL(document.baseURI); + let targetDomain = extraArgs.domain; + if ( targetDomain && /^\/.+\//.test(targetDomain) ) { + const reDomain = new RegExp(targetDomain.slice(1, -1)); + const match = reDomain.exec(baseURL.hostname); + targetDomain = match ? match[0] : undefined; + } + const remove = ( ) => { + safe.String_split.call(document.cookie, ';').forEach(cookieStr => { + const pos = cookieStr.indexOf('='); + if ( pos === -1 ) { return; } + const cookieName = cookieStr.slice(0, pos).trim(); + if ( reName.test(cookieName) === false ) { return; } + const part1 = cookieName + '='; + const part2a = `; domain=${baseURL.hostname}`; + const part2b = `; domain=.${baseURL.hostname}`; + let part2c, part2d; + if ( targetDomain ) { + part2c = `; domain=${targetDomain}`; + part2d = `; domain=.${targetDomain}`; + } else if ( document.domain ) { + const domain = document.domain; + if ( domain !== baseURL.hostname ) { + part2c = `; domain=.${domain}`; + } + if ( domain.startsWith('www.') ) { + part2d = `; domain=${domain.replace('www', '')}`; + } + } + const part3 = '; path=/'; + const part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.cookie = part1 + part4; + document.cookie = part1 + part2a + part4; + document.cookie = part1 + part2b + part4; + document.cookie = part1 + part3 + part4; + document.cookie = part1 + part2a + part3 + part4; + document.cookie = part1 + part2b + part3 + part4; + if ( part2c !== undefined ) { + document.cookie = part1 + part2c + part3 + part4; + } + if ( part2d !== undefined ) { + document.cookie = part1 + part2d + part3 + part4; + } + }); + }; + remove(); + window.addEventListener('beforeunload', remove); + if ( typeof extraArgs.when !== 'string' ) { return; } + const supportedEventTypes = [ 'scroll', 'keydown' ]; + const eventTypes = safe.String_split.call(extraArgs.when, /\s/); + for ( const type of eventTypes ) { + if ( supportedEventTypes.includes(type) === false ) { continue; } + document.addEventListener(type, ( ) => { + throttle(remove); + }, { passive: true }); + } +} +function getAllLocalStorageFn(which = 'localStorage') { + const storage = self[which]; + const out = []; + for ( let i = 0; i < storage.length; i++ ) { + const key = storage.key(i); + const value = storage.getItem(key); + return { key, value }; + } + return out; +} +function setLocalStorageItemFn( + which = 'local', + trusted = false, + key = '', + value = '', + options = {} +) { + if ( key === '' ) { return; } + + // For increased compatibility with AdGuard + if ( value === 'emptyArr' ) { + value = '[]'; + } else if ( value === 'emptyObj' ) { + value = '{}'; + } + + const trustedValues = [ + '', + 'undefined', 'null', + '{}', '[]', '""', + '$remove$', + ...getSafeCookieValuesFn(), + ]; + + if ( trusted ) { + if ( value.includes('$now$') ) { + value = value.replaceAll('$now$', Date.now()); } - if ( rawPattern === '' ) { - safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore); - return encodedTextBefore; + if ( value.includes('$currentDate$') ) { + value = value.replaceAll('$currentDate$', `${Date()}`); } - reCondition.lastIndex = 0; - if ( reCondition.test(textBefore) === false ) { return encodedTextBefore; } - const textAfter = textBefore.replace(rePattern, replacement); - if ( textAfter === textBefore ) { return encodedTextBefore; } - safe.uboLog(logPrefix, 'Matched and replaced'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Modified decoded outbound text:\n', textAfter); + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); } - let encodedTextAfter = textAfter; - if ( extraArgs.encoding === 'base64' ) { - encodedTextAfter = self.btoa(textAfter); + } else { + const normalized = value.toLowerCase(); + const match = /^("?)(.+)\1$/.exec(normalized); + const unquoted = match && match[2] || normalized; + if ( trustedValues.includes(unquoted) === false ) { + if ( /^-?\d+$/.test(unquoted) === false ) { return; } + const n = parseInt(unquoted, 10) || 0; + if ( n < -32767 || n > 32767 ) { return; } } - return encodedTextAfter; - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); } -} catch { -} -trustedReplaceOutboundText(...args); -}, -}; - -scriptlets['trusted-suppress-native-method.js'] = { -aliases: [], + let modified = false; -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function proxyApplyFn( - target = '', - handler = '', - options = {} -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; + try { + const storage = self[`${which}Storage`]; + if ( value === '$remove$' ) { + const safe = safeSelf(); + const pattern = safe.patternToRegex(key, undefined, true ); + const toRemove = []; + for ( let i = 0, n = storage.length; i < n; i++ ) { + const key = storage.key(i); + if ( pattern.test(key) ) { toRemove.push(key); } } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; + modified = toRemove.length !== 0; + for ( const key of toRemove ) { + storage.removeItem(key); } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); + } else { + + const before = storage.getItem(key); + const after = `${value}`; + modified = after !== before; + if ( modified ) { + storage.setItem(key, after); } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; - } - return proxyApplyFn.nativeToString.call(proxied); - } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; } + } catch { } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; + + if ( modified && typeof options.reload === 'number' ) { + setTimeout(( ) => { window.location.reload(); }, options.reload); } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; } -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); +function setLocalStorageItem(key = '', value = '', ...varargs) { + const safe = safeSelf(); + const options = safe.parseVarargs(varargs) + setLocalStorageItemFn('local', false, key, value, options); +} +function setSessionStorageItem(key = '', value = '', ...varargs) { + const safe = safeSelf(); + const options = safe.parseVarargs(varargs) + setLocalStorageItemFn('session', false, key, value, options); +} +function trustedSetLocalStorageItem(key = '', value = '', ...varargs) { + const safe = safeSelf(); + const options = safe.parseVarargs(varargs) + setLocalStorageItemFn('local', true, key, value, options); +} +function trustedSetSessionStorageItem(key = '', value = '', ...varargs) { + const safe = safeSelf(); + const options = safe.parseVarargs(varargs) + setLocalStorageItemFn('session', true, key, value, options); +} +function replaceNodeTextFn( + nodeName = '', + pattern = '', + replacement = '', + ...varargs +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('replace-node-text.fn', ...Array.from(arguments)); + const reNodeName = safe.patternToRegex(nodeName, 'i', true); + const rePattern = safe.patternToRegex(pattern, 'gms'); + const extraArgs = safe.parseVarargs(varargs); + const reIncludes = extraArgs.includes || extraArgs.condition + ? safe.patternToRegex(extraArgs.includes || extraArgs.condition, 'ms') + : null; + const reExcludes = extraArgs.excludes + ? safe.patternToRegex(extraArgs.excludes, 'ms') + : null; + const textContentFactory = (( ) => { + const out = { createScript: s => s }; + const { trustedTypes: tt } = self; + if ( tt instanceof Object ) { + if ( typeof tt.getPropertyType === 'function' ) { + if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { + return tt.createPolicy(getRandomTokenFn(), out); + } } - try { - return new RegExp(match[1], match[2] || undefined); + } + return out; + })(); + let sedCount = extraArgs.sedCount ?? Number.MAX_SAFE_INTEGER; + const handleNode = node => { + const before = node.textContent; + if ( reIncludes ) { + reIncludes.lastIndex = 0; + if ( safe.RegExp_test(reIncludes, before) === false ) { return; } + } + if ( reExcludes ) { + reExcludes.lastIndex = 0; + if ( safe.RegExp_test(reExcludes, before) ) { return; } + } + rePattern.lastIndex = 0; + if ( safe.RegExp_test(rePattern, before) === false ) { return; } + rePattern.lastIndex = 0; + const after = pattern !== '' + ? before.replace(rePattern, replacement) + : replacement; + node.textContent = node.nodeName === 'SCRIPT' + ? textContentFactory.createScript(after) + : after; + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Text before:\n${before.trim()}`); + } + safe.uboLog(logPrefix, `Text after:\n${after.trim()}`); + sedCount -= 1; + }; + const handleTree = root => { + const treeWalker = document.createTreeWalker(root, + NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT + ); + const { currentScript } = document; + let count = 0; + for (;;) { + const node = treeWalker.nextNode(); + if ( node === null ) { break; } + count += 1; + if ( node === currentScript ) { continue; } + if ( reNodeName.test(node.nodeName) ) { + handleNode(node); + } else if ( node.nodeName === 'TEMPLATE' ) { + count += handleTree(node.content); + } else { + continue; } - catch { + if ( sedCount === 0 ) { break; } + } + return count; + }; + if ( document.documentElement ) { + const count = handleTree(document.documentElement); + safe.uboLog(logPrefix, `${count} nodes present before installing mutation observer`); + } + const stay = Boolean(extraArgs.stay); + if ( sedCount === 0 && stay === false ) { return; } + const stop = (takeRecord = true) => { + const mutations = takeRecord ? observer.takeRecords() : []; + observer.disconnect(); + handleMutations(mutations); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Quitting'); + } + }; + const handleMutations = mutations => { + for ( const mutation of mutations ) { + for ( const node of mutation.addedNodes ) { + if ( reNodeName.test(node.nodeName) ) { + handleNode(node); + } else if ( node.nodeName === 'TEMPLATE' ) { + handleTree(node.content); + } else { + continue; + } + if ( sedCount === 0 ) { return stop(false); } } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, + } }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const observer = new MutationObserver(handleMutations); + observer.observe(document, { childList: true, subtree: true }); + if ( stay ) { return; } + runAt(( ) => { + const quitAfter = extraArgs.quitAfter ?? 0; + if ( quitAfter === 0 ) { return stop(); } + setTimeout(( ) => { stop(); }, quitAfter); + }, 'interactive'); +} +function replaceFetchResponseFn( + trusted = false, + pattern = '', + replacement = '', + propsToMatch = '', + ...varargs +) { + if ( trusted !== true ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('replace-fetch-response', pattern, replacement, propsToMatch); + if ( pattern === '*' ) { pattern = '.*'; } + const rePattern = safe.patternToRegex(pattern); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const extraArgs = safe.parseVarargs(varargs); + const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; + self.fetch = new Proxy(self.fetch, { + apply: function(target, thisArg, args) { + const fetchPromise = Reflect.apply(target, thisArg, args); + if ( pattern === '' ) { return fetchPromise; } + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(textBefore => { + if ( reIncludes && reIncludes.test(textBefore) === false ) { + return responseBefore; + } + const textAfter = textBefore.replace(rePattern, replacement); + if ( textAfter === textBefore ) { return responseBefore; } + safe.uboLog(logPrefix, 'Replaced'); + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, reason); + return fetchPromise; + }); } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; + }); +} +function abortOnPropertyRead( + chain = '' +) { + if ( typeof chain !== 'string' ) { return; } + if ( chain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('abort-on-property-read', chain); + const exceptionToken = getExceptionTokenFn(); + const abort = function() { + safe.uboLog(logPrefix, 'Aborted'); + throw new ReferenceError(exceptionToken); }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); + const makeProxy = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + const desc = Object.getOwnPropertyDescriptor(owner, chain); + if ( !desc || desc.get !== abort ) { + Object.defineProperty(owner, chain, { + get: abort, + set: function(){} + }); } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; + return; + } + const prop = chain.slice(0, pos); + let v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v ) { + makeProxy(v, chain); + return; + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.set !== undefined ) { return; } + Object.defineProperty(owner, prop, { + get: function() { return v; }, + set: function(a) { + v = a; + if ( a instanceof Object ) { + makeProxy(a, chain); + } } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; + }); + }; + const owner = window; + makeProxy(owner, chain); +} +function abortOnPropertyWrite( + prop = '' +) { + if ( typeof prop !== 'string' ) { return; } + if ( prop === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('abort-on-property-write', prop); + const exceptionToken = getExceptionTokenFn(); + let owner = window; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + owner = owner[prop.slice(0, pos)]; + if ( owner instanceof Object === false ) { return; } + prop = prop.slice(pos + 1); } - return safe; + delete owner[prop]; + Object.defineProperty(owner, prop, { + set: function() { + safe.uboLog(logPrefix, 'Aborted'); + throw new ReferenceError(exceptionToken); + } + }); } -function getRandomTokenFn() { +function adjustSetInterval( + needleArg = '', + delayArg = '', + boostArg = '' +) { + if ( typeof needleArg !== 'string' ) { return; } const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); + const reNeedle = safe.patternToRegex(needleArg); + let delay = delayArg !== '*' ? parseInt(delayArg, 10) : -1; + if ( isNaN(delay) || isFinite(delay) === false ) { delay = 1000; } + let boost = parseFloat(boostArg); + boost = isNaN(boost) === false && isFinite(boost) + ? Math.min(Math.max(boost, 0.001), 50) + : 0.05; + self.setInterval = new Proxy(self.setInterval, { + apply: function(target, thisArg, args) { + const [ a, b ] = args; + if ( + (delay === -1 || b === delay) && + reNeedle.test(a.toString()) + ) { + args[1] = b * boost; + } + return target.apply(thisArg, args); + } + }); } -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); +function adjustSetTimeout( + needleArg = '', + delayArg = '', + boostArg = '' +) { + if ( typeof needleArg !== 'string' ) { return; } + const safe = safeSelf(); + const reNeedle = safe.patternToRegex(needleArg); + let delay = delayArg !== '*' ? parseInt(delayArg, 10) : -1; + if ( isNaN(delay) || isFinite(delay) === false ) { delay = 1000; } + let boost = parseFloat(boostArg); + boost = isNaN(boost) === false && isFinite(boost) + ? Math.min(Math.max(boost, 0.001), 50) + : 0.05; + self.setTimeout = new Proxy(self.setTimeout, { + apply: function(target, thisArg, args) { + const [ a, b ] = args; + if ( + (delay === -1 || b === delay) && + reNeedle.test(a.toString()) + ) { + args[1] = b * boost; + } + return target.apply(thisArg, args); } - }.bind(); - return token; + }); } -function matchesStackTraceFn( - needleDetails, - logLevel = '' +function preventRefresh( + delay = '' ) { + if ( typeof delay !== 'string' ) { return; } const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; + const logPrefix = safe.makeLogPrefix('prevent-refresh', delay); + const stop = content => { + window.stop(); + safe.uboLog(logPrefix, `Prevented "${content}"`); + }; + const defuse = ( ) => { + const meta = document.querySelector('meta[http-equiv="refresh" i][content]'); + if ( meta === null ) { return; } + const content = meta.getAttribute('content') || ''; + const ms = delay === '' + ? Math.max(parseFloat(content) || 0, 0) * 500 + : 0; + if ( ms === 0 ) { + stop(content); + } else { + setTimeout(( ) => { stop(content); }, ms); } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); + }; + self.addEventListener('load', defuse, { capture: true, once: true }); +} +function removeClass( + rawToken = '', + rawSelector = '', + behavior = '' +) { + if ( typeof rawToken !== 'string' ) { return; } + if ( rawToken === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior); + const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); + const selector = tokens + .map(a => `${rawSelector}.${CSS.escape(a)}`) + .join(','); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); } - return r; + const mustStay = /\bstay\b/.test(behavior); + let timer; + const rmclass = ( ) => { + timer = undefined; + try { + const nodes = document.querySelectorAll(selector); + for ( const node of nodes ) { + node.classList.remove(...tokens); + safe.uboLog(logPrefix, 'Removed class(es)'); + } + } catch { + } + if ( mustStay ) { return; } + if ( document.readyState !== 'complete' ) { return; } + observer.disconnect(); + }; + const mutationHandler = mutations => { + if ( timer !== undefined ) { return; } + let skip = true; + for ( let i = 0; i < mutations.length && skip; i++ ) { + const { type, addedNodes, removedNodes } = mutations[i]; + if ( type === 'attributes' ) { skip = false; } + for ( let j = 0; j < addedNodes.length && skip; j++ ) { + if ( addedNodes[j].nodeType === 1 ) { skip = false; break; } + } + for ( let j = 0; j < removedNodes.length && skip; j++ ) { + if ( removedNodes[j].nodeType === 1 ) { skip = false; break; } + } + } + if ( skip ) { return; } + timer = onIdleFn(rmclass, { timeout: 67 }); + }; + const observer = new MutationObserver(mutationHandler); + const start = ( ) => { + rmclass(); + observer.observe(document, { + attributes: true, + attributeFilter: [ 'class' ], + childList: true, + subtree: true, + }); + }; + runAt(( ) => { + start(); + }, /\bcomplete\b/.test(behavior) ? 'idle' : 'loading'); +} +function webrtcIf( + good = '' +) { + if ( typeof good !== 'string' ) { return; } + const safe = safeSelf(); + const reGood = safe.patternToRegex(good); + const rtcName = window.RTCPeerConnection + ? 'RTCPeerConnection' + : (window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : ''); + if ( rtcName === '' ) { return; } + const log = console.log.bind(console); + const neuteredPeerConnections = new WeakSet(); + const isGoodConfig = function(instance, config) { + if ( neuteredPeerConnections.has(instance) ) { return false; } + if ( config instanceof Object === false ) { return true; } + if ( Array.isArray(config.iceServers) === false ) { return true; } + for ( const server of config.iceServers ) { + const urls = typeof server.urls === 'string' + ? [ server.urls ] + : server.urls; + if ( Array.isArray(urls) ) { + for ( const url of urls ) { + if ( reGood.test(url) ) { return true; } + } + } + if ( typeof server.username === 'string' ) { + if ( reGood.test(server.username) ) { return true; } + } + if ( typeof server.credential === 'string' ) { + if ( reGood.test(server.credential) ) { return true; } + } + } + neuteredPeerConnections.add(instance); + return false; + }; + const peerConnectionCtor = window[rtcName]; + const peerConnectionProto = peerConnectionCtor.prototype; + peerConnectionProto.createDataChannel = + new Proxy(peerConnectionProto.createDataChannel, { + apply: function(target, thisArg, args) { + if ( isGoodConfig(target, args[1]) === false ) { + log('uBO:', args[1]); + return Reflect.apply(target, thisArg, args.slice(0, 1)); + } + return Reflect.apply(target, thisArg, args); + }, + }); + window[rtcName] = + new Proxy(peerConnectionCtor, { + construct: function(target, args) { + if ( isGoodConfig(target, args[0]) === false ) { + log('uBO:', args[0]); + return Reflect.construct(target); + } + return Reflect.construct(target, args); + } + }); } -function trustedSuppressNativeMethod( - methodPath = '', - signature = '', - how = '', - stack = '' +function noWindowOpenIf( + pattern = '', + delay = '', + decoy = '' ) { - if ( methodPath === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how, stack); - const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => { - if ( /^".*"$/.test(v) ) { - return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; - } - if ( /^\/.+\/$/.test(v) ) { - return { type: 'pattern', re: safe.patternToRegex(v) }; - } - if ( v === 'false' ) { - return { type: 'exact', value: false }; - } - if ( v === 'true' ) { - return { type: 'exact', value: true }; - } - if ( v === 'null' ) { - return { type: 'exact', value: null }; - } - if ( v === 'undefined' ) { - return { type: 'exact', value: undefined }; - } - }); - const stackNeedle = safe.initPattern(stack, { canNegate: true }); - proxyApplyFn(methodPath, function(context) { - const { callArgs } = context; - if ( signature === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); + const logPrefix = safe.makeLogPrefix('no-window-open-if', pattern, delay, decoy); + const targetMatchResult = pattern.startsWith('!') === false; + if ( targetMatchResult === false ) { + pattern = pattern.slice(1); + } + const rePattern = safe.patternToRegex(pattern); + const autoRemoveAfter = (parseFloat(delay) || 0) * 1000; + const setTimeout = self.setTimeout; + const createDecoy = function(tag, urlProp, url) { + const decoyElem = document.createElement(tag); + decoyElem[urlProp] = url; + decoyElem.style.setProperty('height','1px', 'important'); + decoyElem.style.setProperty('position','fixed', 'important'); + decoyElem.style.setProperty('top','-1px', 'important'); + decoyElem.style.setProperty('width','1px', 'important'); + document.body.appendChild(decoyElem); + setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter); + return decoyElem; + }; + const noopFunc = function(){}; + proxyApplyFn('open', function open(context) { + if ( pattern === 'debug' && safe.logLevel !== 0 ) { + debugger; // eslint-disable-line no-debugger return context.reflect(); } - for ( let i = 0; i < signatureArgs.length; i++ ) { - const signatureArg = signatureArgs[i]; - if ( signatureArg === undefined ) { continue; } - const targetArg = i < callArgs.length ? callArgs[i] : undefined; - if ( signatureArg.type === 'exact' ) { - if ( targetArg !== signatureArg.value ) { - return context.reflect(); - } - } - if ( signatureArg.type === 'pattern' ) { - if ( safe.RegExp_test(signatureArg.re, targetArg) === false ) { - return context.reflect(); - } + const { callArgs } = context; + const haystack = callArgs.join(' '); + if ( rePattern.test(haystack) !== targetMatchResult ) { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Allowed (${callArgs.join(', ')})`); } + return context.reflect(); } - if ( stackNeedle.matchAll !== true ) { - const logLevel = safe.logLevel > 1 ? 'all' : ''; - if ( matchesStackTraceFn(stackNeedle, logLevel) === false ) { - return context.reflect(); - } + safe.uboLog(logPrefix, `Prevented (${callArgs.join(', ')})`); + if ( delay === '' ) { return null; } + if ( decoy === 'blank' ) { + callArgs[0] = 'about:blank'; + const r = context.reflect(); + setTimeout(( ) => { r.close(); }, autoRemoveAfter); + return r; } - if ( how === 'debug' ) { - debugger; // eslint-disable-line no-debugger - return context.reflect(); + const decoyElem = decoy === 'obj' + ? createDecoy('object', 'data', ...callArgs) + : createDecoy('iframe', 'src', ...callArgs); + let popup = decoyElem.contentWindow; + if ( typeof popup === 'object' && popup !== null ) { + Object.defineProperty(popup, 'closed', { value: false }); + } else { + popup = new Proxy(self, { + get: function(target, prop, ...args) { + if ( prop === 'closed' ) { return false; } + const r = Reflect.get(target, prop, ...args); + if ( typeof r === 'function' ) { return noopFunc; } + return r; + }, + set: function(...args) { + return Reflect.set(...args); + }, + }); } - safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`); - if ( how === 'abort' ) { - throw new ReferenceError(); + if ( safe.logLevel !== 0 ) { + popup = new Proxy(popup, { + get: function(target, prop, ...args) { + const r = Reflect.get(target, prop, ...args); + safe.uboLog(logPrefix, `popup / get ${prop} === ${r}`); + if ( typeof r === 'function' ) { + return (...args) => { return r.call(target, ...args); }; + } + return r; + }, + set: function(target, prop, value, ...args) { + safe.uboLog(logPrefix, `popup / set ${prop} = ${value}`); + return Reflect.set(target, prop, value, ...args); + }, + }); } - }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { -} -trustedSuppressNativeMethod(...args); -}, -}; - - -scriptlets['trusted-prevent-dom-bypass.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + return popup; + }); +} +function closeWindow( + arg1 = '' +) { + if ( typeof arg1 !== 'string' ) { return; } + const safe = safeSelf(); + let subject = ''; + if ( /^\/.*\/$/.test(arg1) ) { + subject = window.location.href; + } else if ( arg1 !== '' ) { + subject = `${window.location.pathname}${window.location.search}`; + } + try { + const re = safe.patternToRegex(arg1); + if ( re.test(subject) ) { + window.close(); + } + } catch(ex) { + console.log(ex); + } +} +function windowNameDefuser() { + if ( window === window.top ) { + window.name = ''; + } +} +function overlayBuster(allFrames) { + if ( allFrames === '' && window !== window.top ) { return; } + var tstart; + var ttl = 30000; + var delay = 0; + var delayStep = 50; + var buster = function() { + var docEl = document.documentElement, + bodyEl = document.body, + vw = Math.min(docEl.clientWidth, window.innerWidth), + vh = Math.min(docEl.clientHeight, window.innerHeight), + tol = Math.min(vw, vh) * 0.05, + el = document.elementFromPoint(vw/2, vh/2), + style, rect; + for (;;) { + if ( el === null || el.parentNode === null || el === bodyEl ) { + break; } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + style = window.getComputedStyle(el); + if ( parseInt(style.zIndex, 10) >= 1000 || style.position === 'fixed' ) { + rect = el.getBoundingClientRect(); + if ( rect.left <= tol && rect.top <= tol && (vw - rect.right) <= tol && (vh - rect.bottom) < tol ) { + el.parentNode.removeChild(el); + tstart = Date.now(); + el = document.elementFromPoint(vw/2, vh/2); + bodyEl.style.setProperty('overflow', 'auto', 'important'); + docEl.style.setProperty('overflow', 'auto', 'important'); + continue; + } } - return haystack.includes(details.pattern) === details.expect; + el = el.parentNode; + } + if ( (Date.now() - tstart) < ttl ) { + delay = Math.min(delay + delayStep, 1000); + setTimeout(buster, delay); + } + }; + var domReady = function(ev) { + if ( ev ) { + document.removeEventListener(ev.type, domReady); + } + tstart = Date.now(); + setTimeout(buster, delay); + }; + if ( document.readyState === 'loading' ) { + document.addEventListener('DOMContentLoaded', domReady); + } else { + domReady(); + } +} +function alertBuster() { + window.alert = new Proxy(window.alert, { + apply: function(a) { + console.info(a); }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { + get(target, prop) { + if ( prop === 'toString' ) { + return target.toString.bind(target); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); + return Reflect.get(target, prop); }, + }); +} +function noWebrtc() { + var rtcName = window.RTCPeerConnection ? 'RTCPeerConnection' : ( + window.webkitRTCPeerConnection ? 'webkitRTCPeerConnection' : '' + ); + if ( rtcName === '' ) { return; } + var log = console.log.bind(console); + var pc = function(cfg) { + log('Document tried to create an RTCPeerConnection: %o', cfg); }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + const noop = function() { + }; + pc.prototype = { + close: noop, + createDataChannel: noop, + createOffer: noop, + setRemoteDescription: noop, + toString: function() { + return '[object RTCPeerConnection]'; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; + var z = window[rtcName]; + window[rtcName] = pc.bind(window); + if ( z.prototype ) { + z.prototype.createDataChannel = function() { + return { + close: function() {}, + send: function() {} + }; + }.bind(null); + } +} +function disableNewtabLinks() { + document.addEventListener('click', ev => { + let target = ev.target; + while ( target !== null ) { + if ( target.localName === 'a' && target.hasAttribute('target') ) { + ev.stopPropagation(); + ev.preventDefault(); break; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + target = target.parentNode; + } + }, { capture: true }); } -function proxyApplyFn( - target = '', - handler = '', - options = {} +function xmlPrune( + selector = '', + selectorCheck = '', + urlPattern = '', + ...varargs ) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('xml-prune', selector, selectorCheck, urlPattern); + const reUrl = safe.patternToRegex(urlPattern); + const extraArgs = safe.parseVarargs(varargs); + const queryAll = (xmlDoc, selector) => { + const isXpath = /^xpath\(.+\)$/.test(selector); + if ( isXpath === false ) { + return Array.from(xmlDoc.querySelectorAll(selector)); + } + const xpr = xmlDoc.evaluate( + selector.slice(6, -1), + xmlDoc, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null + ); + const out = []; + for ( let i = 0; i < xpr.snapshotLength; i++ ) { + const node = xpr.snapshotItem(i); + out.push(node); + } + return out; + }; + const pruneFromDoc = xmlDoc => { + try { + if ( selectorCheck !== '' && xmlDoc.querySelector(selectorCheck) === null ) { + return xmlDoc; } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; + if ( extraArgs.logdoc ) { + const serializer = new XMLSerializer(); + safe.uboLog(logPrefix, `Document is\n\t${serializer.serializeToString(xmlDoc)}`); } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; + const items = queryAll(xmlDoc, selector); + if ( items.length === 0 ) { return xmlDoc; } + safe.uboLog(logPrefix, `Removing ${items.length} items`); + for ( const item of items ) { + if ( item.nodeType === 1 ) { + item.remove(); + } else if ( item.nodeType === 2 ) { + item.ownerElement.removeAttribute(item.nodeName); + } + safe.uboLog(logPrefix, `${item.constructor.name}.${item.nodeName} removed`); } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); + } catch(ex) { + safe.uboErr(logPrefix, `Error: ${ex}`); + } + return xmlDoc; + }; + const pruneFromText = text => { + if ( (/^\s*\s*$/.test(text)) === false ) { + return text; + } + try { + const xmlParser = new DOMParser(); + const xmlDoc = xmlParser.parseFromString(text, 'text/xml'); + pruneFromDoc(xmlDoc); + const serializer = new XMLSerializer(); + text = serializer.serializeToString(xmlDoc); + } catch { + } + return text; + }; + const urlFromArg = arg => { + if ( typeof arg === 'string' ) { return arg; } + if ( arg instanceof Request ) { return arg.url; } + return String(arg); + }; + self.fetch = new Proxy(self.fetch, { + apply: function(target, thisArg, args) { + const fetchPromise = Reflect.apply(target, thisArg, args); + if ( reUrl.test(urlFromArg(args[0])) === false ) { + return fetchPromise; } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(text => { + const responseAfter = new Response(pruneFromText(text), { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(( ) => + responseBefore + ); + }); + } + }); + modifyXhrResponseFn(urlPattern, (xhr, before) => { + if ( before instanceof XMLDocument ) { + return pruneFromDoc(before); + } + if ( typeof before === 'string' ) { + return pruneFromText(before); + } + return before; + }); +} +function m3uPrune( + m3uPattern = '', + urlPattern = '' +) { + if ( typeof m3uPattern !== 'string' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('m3u-prune', m3uPattern, urlPattern); + const toLog = []; + const regexFromArg = arg => { + if ( arg === '' ) { return /^/; } + const match = /^\/(.+)\/([gms]*)$/.exec(arg); + if ( match !== null ) { + let flags = match[2] || ''; + if ( flags.includes('m') ) { flags += 's'; } + return new RegExp(match[1], flags); + } + return new RegExp( + arg.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*+/g, '.*?') + ); + }; + const reM3u = regexFromArg(m3uPattern); + const reUrl = regexFromArg(urlPattern); + const pruneSpliceoutBlock = (lines, i) => { + if ( lines[i].startsWith('#EXT-X-CUE:TYPE="SpliceOut"') === false ) { + return false; + } + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + if ( lines[i].startsWith('#EXT-X-ASSET:CAID') ) { + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + } + if ( lines[i].startsWith('#EXT-X-SCTE35:') ) { + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + } + if ( lines[i].startsWith('#EXT-X-CUE-IN') ) { + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + } + if ( lines[i].startsWith('#EXT-X-SCTE35:') ) { + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + } + return true; + }; + const pruneInfBlock = (lines, i) => { + if ( lines[i].startsWith('#EXTINF') === false ) { return false; } + if ( reM3u.test(lines[i+1]) === false ) { return false; } + toLog.push('Discarding', `\t${lines[i]}, \t${lines[i+1]}`); + lines[i] = lines[i+1] = undefined; i += 2; + if ( lines[i].startsWith('#EXT-X-DISCONTINUITY') ) { + toLog.push(`\t${lines[i]}`); + lines[i] = undefined; i += 1; + } + return true; + }; + const pruner = text => { + if ( (/^\s*#EXTM3U/.test(text)) === false ) { return text; } + if ( m3uPattern === '' ) { + safe.uboLog(` Content:\n${text}`); + return text; + } + if ( reM3u.multiline ) { + reM3u.lastIndex = 0; + for (;;) { + const match = reM3u.exec(text); + if ( match === null ) { break; } + let discard = match[0]; + let before = text.slice(0, match.index); + if ( + /^[\n\r]+/.test(discard) === false && + /[\n\r]+$/.test(before) === false + ) { + const startOfLine = /[^\n\r]+$/.exec(before); + if ( startOfLine !== null ) { + before = before.slice(0, startOfLine.index); + discard = startOfLine[0] + discard; } - return proxyApplyFn.nativeToString.call(proxied); + } + let after = text.slice(match.index + match[0].length); + if ( + /[\n\r]+$/.test(discard) === false && + /^[\n\r]+/.test(after) === false + ) { + const endOfLine = /^[^\n\r]+/.exec(after); + if ( endOfLine !== null ) { + after = after.slice(endOfLine.index); + discard += discard + endOfLine[0]; + } + } + text = before.trim() + '\n' + after.trim(); + reM3u.lastIndex = before.length + 1; + toLog.push('Discarding', ...safe.String_split.call(discard, /\n+/).map(s => `\t${s}`)); + if ( reM3u.global === false ) { break; } + } + return text; + } + const lines = safe.String_split.call(text, /\n\r|\n|\r/); + for ( let i = 0; i < lines.length; i++ ) { + if ( lines[i] === undefined ) { continue; } + if ( pruneSpliceoutBlock(lines, i) ) { continue; } + if ( pruneInfBlock(lines, i) ) { continue; } + } + return lines.filter(l => l !== undefined).join('\n'); + }; + const urlFromArg = arg => { + if ( typeof arg === 'string' ) { return arg; } + if ( arg instanceof Request ) { return arg.url; } + return String(arg); + }; + proxyApplyFn('fetch', async function fetch(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( reUrl.test(urlFromArg(args[0])) === false ) { return fetchPromise; } + const responseBefore = await fetchPromise; + const responseClone = responseBefore.clone(); + const textBefore = await responseClone.text(); + const textAfter = pruner(textBefore); + if ( textAfter === textBefore ) { return responseBefore; } + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + url: { value: responseBefore.url }, + type: { value: responseBefore.type }, + }); + if ( toLog.length !== 0 ) { + toLog.unshift(logPrefix); + safe.uboLog(toLog.join('\n')); + } + return responseAfter; + }) + self.XMLHttpRequest.prototype.open = new Proxy(self.XMLHttpRequest.prototype.open, { + apply: async (target, thisArg, args) => { + if ( reUrl.test(urlFromArg(args[1])) === false ) { + return Reflect.apply(target, thisArg, args); + } + thisArg.addEventListener('readystatechange', function() { + if ( thisArg.readyState !== 4 ) { return; } + const type = thisArg.responseType; + if ( type !== '' && type !== 'text' ) { return; } + const textin = thisArg.responseText; + const textout = pruner(textin); + if ( textout === textin ) { return; } + Object.defineProperty(thisArg, 'response', { value: textout }); + Object.defineProperty(thisArg, 'responseText', { value: textout }); + if ( toLog.length !== 0 ) { + toLog.unshift(logPrefix); + safe.uboLog(toLog.join('\n')); } }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + return Reflect.apply(target, thisArg, args); } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; + }); } -function trustedPreventDomBypass( - methodPath = '', - targetProp = '' +function callNothrow( + chain = '' ) { - if ( methodPath === '' ) { return; } + if ( typeof chain !== 'string' ) { return; } + if ( chain === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-prevent-dom-bypass', methodPath, targetProp); - proxyApplyFn(methodPath, function(context) { - const elems = new Set(context.callArgs.filter(e => e instanceof HTMLElement)); - const r = context.reflect(); - if ( elems.length === 0 ) { return r; } - for ( const elem of elems ) { + const parts = safe.String_split.call(chain, '.'); + let owner = window, prop; + for (;;) { + prop = parts.shift(); + if ( parts.length === 0 ) { break; } + owner = owner[prop]; + if ( owner instanceof Object === false ) { return; } + } + if ( prop === '' ) { return; } + const fn = owner[prop]; + if ( typeof fn !== 'function' ) { return; } + owner[prop] = new Proxy(fn, { + apply: function(...args) { + let r; try { - if ( `${elem.contentWindow}` !== '[object Window]' ) { continue; } - if ( elem.contentWindow.location.href !== 'about:blank' ) { - if ( elem.contentWindow.location.href !== self.location.href ) { - continue; - } - } - if ( targetProp !== '' ) { - let me = self, it = elem.contentWindow; - let chain = targetProp; - for (;;) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { break; } - const prop = chain.slice(0, pos); - me = me[prop]; it = it[prop]; - chain = chain.slice(pos+1); - } - it[chain] = me[chain]; - } else { - Object.defineProperty(elem, 'contentWindow', { value: self }); - } - safe.uboLog(logPrefix, 'Bypass prevented'); + r = Reflect.apply(...args); } catch { } - } - return r; + return r; + }, }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { } -trustedPreventDomBypass(...args); -}, -}; - - -scriptlets['trusted-override-element-method.js'] = { -aliases: [], - -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { -function validateConstantFn(trusted, raw, extraArgs = {}) { +function removeNodeText( + nodeName, + includes, + ...extraArgs +) { + replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs); +} +function preventCanvas( + contextType = '' +) { const safe = safeSelf(); - let value; - if ( raw === 'undefined' ) { - value = undefined; - } else if ( raw === 'false' ) { - value = false; - } else if ( raw === 'true' ) { - value = true; - } else if ( raw === 'null' ) { - value = null; - } else if ( raw === "''" || raw === '' ) { - value = ''; - } else if ( raw === '[]' || raw === 'emptyArr' ) { - value = []; - } else if ( raw === '{}' || raw === 'emptyObj' ) { - value = {}; - } else if ( raw === 'noopFunc' ) { - value = function(){}; - } else if ( raw === 'trueFunc' ) { - value = function(){ return true; }; - } else if ( raw === 'falseFunc' ) { - value = function(){ return false; }; - } else if ( raw === 'throwFunc' ) { - value = function(){ throw ''; }; - } else if ( /^-?\d+$/.test(raw) ) { - value = parseInt(raw); - if ( isNaN(raw) ) { return; } - if ( Math.abs(raw) > 0x7FFF ) { return; } - } else if ( trusted ) { - if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } - } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch { return; } - } - } else { - return; - } - if ( extraArgs.as !== undefined ) { - if ( extraArgs.as === 'function' ) { - return ( ) => value; - } else if ( extraArgs.as === 'callback' ) { - return ( ) => (( ) => value); - } else if ( extraArgs.as === 'resolved' ) { - return Promise.resolve(value); - } else if ( extraArgs.as === 'rejected' ) { - return Promise.reject(value); + const pattern = safe.initPattern(contextType, { canNegate: true }); + const proto = globalThis.HTMLCanvasElement.prototype; + proto.getContext = new Proxy(proto.getContext, { + apply(target, thisArg, args) { + if ( safe.testPattern(pattern, args[0]) ) { return null; } + return Reflect.apply(target, thisArg, args); } - } - return value; + }); } -function safeSelf() { - if ( safeSelf.safe ) { - return safeSelf.safe; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toString': Function.prototype.call.bind(self.Function.prototype.toString), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'Object_toString': Object.prototype.toString, - 'RegExp': self.RegExp, - 'RegExp_test': Function.prototype.call.bind(self.RegExp.prototype.test), - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parse': Function.prototype.call.bind(self.JSON.parse, self.JSON), - 'JSON_stringify': Function.prototype.call.bind(self.JSON.stringify, self.JSON), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; +function multiup() { + const handler = ev => { + const target = ev.target; + if ( target.matches('button[link]') === false ) { return; } + const ancestor = target.closest('form'); + if ( ancestor === null ) { return; } + if ( ancestor !== target.parentElement ) { return; } + const link = (target.getAttribute('link') || '').trim(); + if ( link === '' ) { return; } + ev.preventDefault(); + ev.stopPropagation(); + document.location.href = link; + }; + document.addEventListener('click', handler, { capture: true }); +} +function breakOnCall(target) { + proxyApplyFn(target, function(context) { + debugger; // eslint-disable-line no-debugger + return context.reflect(); + }); +} +function replaceNodeText( + nodeName, + pattern, + replacement, + ...extraArgs +) { + replaceNodeTextFn(nodeName, pattern, replacement, ...extraArgs); +} +function trustedReplaceFetchResponse(...args) { + replaceFetchResponseFn(true, ...args); +} +function trustedReplaceXhrResponse( + pattern = '', + replacement = '', + propsToMatch = '', + ...varargs +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-replace-xhr-response', pattern, replacement, propsToMatch); + const xhrInstances = new WeakMap(); + if ( pattern === '*' ) { pattern = '.*'; } + const rePattern = safe.patternToRegex(pattern); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const extraArgs = safe.parseVarargs(varargs); + const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const outerXhr = this; + const xhrDetails = { method, url }; + let outcome = 'match'; + if ( propNeedles.size !== 0 ) { + if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { + outcome = 'nomatch'; + } } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); + if ( outcome === 'match' ) { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch"`); + } + xhrInstances.set(outerXhr, xhrDetails); } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test(details.re, haystack) === details.expect; + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + if ( typeof innerResponse !== 'string' ) { + return (xhrDetails.response = innerResponse); } - try { - return new RegExp(match[1], match[2] || undefined); + if ( reIncludes && reIncludes.test(innerResponse) === false ) { + return (xhrDetails.response = innerResponse); } - catch { + const textBefore = innerResponse; + const textAfter = textBefore.replace(rePattern, replacement); + if ( textAfter !== textBefore ) { + safe.uboLog(logPrefix, 'Match'); } - return /^/; - }, - parseVarargs(varargs) { - const entries = varargs.reduce((out, v, i, a) => { - if ( i & 1 ) { return out; } - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - return out; - }, []); - return this.Object_fromEntries(entries); - }, - }; - safeSelf.safe = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } + return (xhrDetails.response = textAfter); + } + get responseText() { + const response = this.response; + if ( typeof response !== 'string' ) { + return super.responseText; + } + return response; } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); +} +function trustedClickElement( + selectors = '', + extraMatch = '', + delay = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-click-element', selectors, extraMatch, delay); + + if ( extraMatch !== '' ) { + const assertions = safe.String_split.call(extraMatch, ',').map(s => { + const pos1 = s.indexOf(':'); + const s1 = pos1 !== -1 ? s.slice(0, pos1) : s; + const not = s1.startsWith('!'); + const type = not ? s1.slice(1) : s1; + const s2 = pos1 !== -1 ? s.slice(pos1+1).trim() : ''; + if ( s2 === '' ) { return; } + const out = { not, type }; + const match = /^\/(.+)\/(i?)$/.exec(s2); + if ( match !== null ) { + out.re = new RegExp(match[1], match[2] || undefined); + return out; } - bcBuffer.push({ type, text }); + const pos2 = s2.indexOf('='); + const key = pos2 !== -1 ? s2.slice(0, pos2).trim() : s2; + const value = pos2 !== -1 ? s2.slice(pos2+1).trim() : ''; + out.re = new RegExp(`^${safe.escapeRegexChars(key)}=${safe.escapeRegexChars(value)}`); + return out; + }).filter(details => details !== undefined); + const allCookies = assertions.some(o => o.type === 'cookie') + ? getAllCookiesFn() + : []; + const allStorageItems = assertions.some(o => o.type === 'localStorage') + ? getAllLocalStorageFn() + : []; + const hasNeedle = (haystack, needle) => { + for ( const { key, value } of haystack ) { + if ( needle.test(`${key}=${value}`) ) { return true; } + } + return false; }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; + for ( const { not, type, re } of assertions ) { + switch ( type ) { + case 'cookie': + if ( hasNeedle(allCookies, re) === not ) { return; } break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; + case 'localStorage': + if ( hasNeedle(allStorageItems, re) === not ) { return; } break; } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; + } + } + + const steps = (( ) => { + const steps = /^[;|]/.test(selectors) + ? safe.String_split.call(selectors.slice(1), selectors.charAt(0)) + : safe.String_split.call(selectors, ','); + return steps.map(a => { + a = a.trim(); + if ( /^\d+$/.test(a) ) { return parseInt(a, 10); } + return a; + }); + })(); + if ( steps.length === 0 ) { return; } + const clickDelay = parseInt(delay, 10) || 1; + for ( let i = steps.length-1; i > 0; i-- ) { + if ( typeof steps[i] !== 'string' ) { continue; } + if ( typeof steps[i-1] !== 'string' ) { continue; } + steps.splice(i, 0, clickDelay); + } + if ( steps.length === 1 && delay !== '' ) { + steps.unshift(clickDelay); + } + if ( typeof steps.at(-1) !== 'number' ) { + steps.push(11000); + } + + const timeout = steps.pop(); + + const waitForTime = ms => { + return new Promise(resolve => { + safe.uboLog(logPrefix, `Waiting for ${ms} ms`); + waitForTime.timer = setTimeout(( ) => { + waitForTime.timer = undefined; + resolve(); + }, ms); + }); + }; + + const waitForElement = directive => { + safe.uboLog(logPrefix, `Waiting for ${directive}`); + return lookupElementsFn(directive, Date.now() + timeout).then(elems => { + if ( elems.length === 0 ) { return false; } + elems[0].click(); + safe.uboLog(logPrefix, `Clicked ${directive}`); + return true; + }); + }; + + const process = async ( ) => { + while ( steps.length !== 0 ) { + const step = steps.shift(); + if ( step === undefined ) { break; } + if ( typeof step === 'number' ) { + await waitForTime(step); + if ( step === 1 ) { continue; } + continue; + } + if ( step.startsWith('!') ) { continue; } + const clicked = await waitForElement(step); + if ( clicked ) { continue; } + safe.uboLog(logPrefix, `Timed out waiting on ${step}`); + break; + } + }; + + runAtHtmlElementFn(process); } -function proxyApplyFn( - target = '', - handler = '', - options = {} +function trustedReplaceOutboundText( + propChain = '', + rawPattern = '', + rawReplacement = '', + ...varargs ) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, rawPattern, rawReplacement, ...varargs); + const rePattern = safe.patternToRegex(rawPattern); + const replacement = rawReplacement.startsWith('json:') + ? safe.JSON_parse(rawReplacement.slice(5)) + : rawReplacement; + const extraArgs = safe.parseVarargs(varargs); + const reCondition = safe.patternToRegex(extraArgs.condition || ''); + proxyApplyFn(propChain, function(context) { + const encodedTextBefore = context.reflect(); + let textBefore = encodedTextBefore; + if ( extraArgs.encoding === 'base64' ) { + try { textBefore = self.atob(encodedTextBefore); } + catch { return encodedTextBefore; } + } + if ( rawPattern === '' ) { + safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore); + return encodedTextBefore; + } + reCondition.lastIndex = 0; + if ( reCondition.test(textBefore) === false ) { return encodedTextBefore; } + const textAfter = textBefore.replace(rePattern, replacement); + if ( textAfter === textBefore ) { return encodedTextBefore; } + safe.uboLog(logPrefix, 'Matched and replaced'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Modified decoded outbound text:\n', textAfter); + } + let encodedTextAfter = textAfter; + if ( extraArgs.encoding === 'base64' ) { + encodedTextAfter = self.btoa(textAfter); + } + return encodedTextAfter; + }); +} +function trustedSuppressNativeMethod( + methodPath = '', + signature = '', + how = '', + stack = '' +) { + if ( methodPath === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how, stack); + const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => { + if ( /^".*"$/.test(v) ) { + return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; + } + if ( /^\/.+\/$/.test(v) ) { + return { type: 'pattern', re: safe.patternToRegex(v) }; + } + if ( v === 'false' ) { + return { type: 'exact', value: false }; + } + if ( v === 'true' ) { + return { type: 'exact', value: true }; + } + if ( v === 'null' ) { + return { type: 'exact', value: null }; + } + if ( v === 'undefined' ) { + return { type: 'exact', value: undefined }; + } + }); + const stackNeedle = safe.initPattern(stack, { canNegate: true }); + proxyApplyFn(methodPath, function(context) { + const { callArgs } = context; + if ( signature === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); + return context.reflect(); + } + for ( let i = 0; i < signatureArgs.length; i++ ) { + const signatureArg = signatureArgs[i]; + if ( signatureArg === undefined ) { continue; } + const targetArg = i < callArgs.length ? callArgs[i] : undefined; + if ( signatureArg.type === 'exact' ) { + if ( targetArg !== signatureArg.value ) { + return context.reflect(); + } } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; + if ( signatureArg.type === 'pattern' ) { + if ( safe.RegExp_test(signatureArg.re, targetArg) === false ) { + return context.reflect(); + } } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); + } + if ( stackNeedle.matchAll !== true ) { + const logLevel = safe.logLevel > 1 ? 'all' : ''; + if ( matchesStackTraceFn(stackNeedle, logLevel) === false ) { + return context.reflect(); } - }; - proxyApplyFn.isCtor = new Map(); - proxyApplyFn.proxies = new WeakMap(); - if ( (options.skipToString || proxyApplyFn.skipToString) !== true ) { - proxyApplyFn.nativeToString = Function.prototype.toString; - const proxiedToString = new Proxy(Function.prototype.toString, { - apply(target, thisArg) { - let proxied = thisArg; - for(;;) { - const fn = proxyApplyFn.proxies.get(proxied); - if ( fn === undefined ) { break; } - proxied = fn; + } + if ( how === 'debug' ) { + debugger; // eslint-disable-line no-debugger + return context.reflect(); + } + safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`); + if ( how === 'abort' ) { + throw new ReferenceError(); + } + }); +} +function trustedPreventDomBypass( + methodPath = '', + targetProp = '' +) { + if ( methodPath === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-prevent-dom-bypass', methodPath, targetProp); + proxyApplyFn(methodPath, function(context) { + const elems = new Set(context.callArgs.filter(e => e instanceof HTMLElement)); + const r = context.reflect(); + if ( elems.length === 0 ) { return r; } + for ( const elem of elems ) { + try { + if ( `${elem.contentWindow}` !== '[object Window]' ) { continue; } + if ( elem.contentWindow.location.href !== 'about:blank' ) { + if ( elem.contentWindow.location.href !== self.location.href ) { + continue; } - return proxyApplyFn.nativeToString.call(proxied); } - }); - proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString); - Function.prototype.toString = proxiedToString; - } - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + if ( targetProp !== '' ) { + let me = self, it = elem.contentWindow; + let chain = targetProp; + for (;;) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { break; } + const prop = chain.slice(0, pos); + me = me[prop]; it = it[prop]; + chain = chain.slice(pos+1); + } + it[chain] = me[chain]; + } else { + Object.defineProperty(elem, 'contentWindow', { value: self }); + } + safe.uboLog(logPrefix, 'Bypass prevented'); + } catch { + } } - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - const proxiedTarget = new Proxy(fn, proxyDetails); - proxyApplyFn.proxies.set(proxiedTarget, fn); - context[prop] = proxiedTarget; + return r; + }); } function trustedOverrideElementMethod( methodPath = '', @@ -45887,19 +6939,12 @@ function trustedOverrideElementMethod( } return validateConstantFn(true, disposition, extraArgs); }); -}; -try { - const key = Symbol.for('safeSelf.1.75.1b1'); - safeSelf.safe = globalThis[key]; - if ( safeSelf.safe === undefined ) { - Object.defineProperty(globalThis, key, { value: safeSelf() }); - } -} catch { } -trustedOverrideElementMethod(...args); -}, -}; - +const table = { abortCurrentScript, setAttr, trustedSetAttr, removeAttr, trustedCreateHTML, hrefSanitizer, proxyApplyConfig, editOutboundObject, trustedEditOutboundObject, jsonEdit, trustedJsonEdit, editInboundObject, trustedEditInboundObject, editThisObject, trustedEditThisObject, editObjectOnGetter, trustedEditObjectOnGetter, editObjectOnSetter, trustedEditObjectOnSetter, editElementObject, trustedEditElementObject, jsonEditXhrResponse, trustedJsonEditXhrResponse, jsonEditXhrRequest, trustedJsonEditXhrRequest, jsonEditFetchResponse, trustedJsonEditFetchResponse, jsonEditFetchRequest, trustedJsonEditFetchRequest, jsonlEditXhrResponse, trustedJsonlEditXhrResponse, jsonlEditFetchResponse, trustedJsonlEditFetchResponse, abortOnStackTrace, trustedPruneInboundObject, trustedPruneOutboundObject, jsonPrune, jsonPruneFetchResponse, jsonPruneXhrResponse, evaldataPrune, noEvalIf, preventAddEventListener, preventBab, preventClipboardWrite, preventDialog, preventFetch, trustedPreventFetch, freezeElementProperty, preventInnerHTML, preventNavigation, preventSetTimeout, preventSetInterval, preventRequestAnimationFrame, setConstant, trustedSetConstant, trustedReplaceArgument, spoofCSS, preventXhr, trustedPreventXhr, mpegdashPrune, setCookie, setCookieReload, trustedSetCookie, trustedSetCookieReload, removeCookie, setLocalStorageItem, setSessionStorageItem, trustedSetLocalStorageItem, trustedSetSessionStorageItem, abortOnPropertyRead, abortOnPropertyWrite, adjustSetInterval, adjustSetTimeout, preventRefresh, removeClass, webrtcIf, noWindowOpenIf, closeWindow, windowNameDefuser, overlayBuster, alertBuster, noWebrtc, disableNewtabLinks, xmlPrune, m3uPrune, callNothrow, removeNodeText, preventCanvas, multiup, breakOnCall, replaceNodeText, trustedReplaceFetchResponse, trustedReplaceXhrResponse, trustedClickElement, trustedReplaceOutboundText, trustedSuppressNativeMethod, trustedPreventDomBypass, trustedOverrideElementMethod }; +for (const [name, ...args] of calls) { + try { table[name](...args); } catch {} +} +} export default scriptlets;