"use strict"; globalThis.__obscura_errors = []; globalThis.addEventListener = globalThis.addEventListener || function(){}; globalThis.onunhandledrejection = function(e) { if (e?.preventDefault) e.preventDefault(); }; globalThis.onerror = function(msg, src, line, col, error) { globalThis.__obscura_errors.push({msg: String(msg), src: String(src||""), line, error: String(error||"")}); }; globalThis.__windowListeners = {}; globalThis.addEventListener = function(type, fn) { if (!globalThis.__windowListeners[type]) globalThis.__windowListeners[type] = []; globalThis.__windowListeners[type].push(fn); }; globalThis.removeEventListener = function(type, fn) { if (globalThis.__windowListeners[type]) { globalThis.__windowListeners[type] = globalThis.__windowListeners[type].filter(h => h !== fn); } }; globalThis.dispatchEvent = function(event) { if (!event) return true; const handlers = globalThis.__windowListeners[event.type] || []; for (const h of handlers) { try { h.call(globalThis, event); } catch(e) { console.error(e); } } return !event.defaultPrevented; }; const _dom = (cmd, a1, a2) => Deno.core.ops.op_dom(cmd, String(a1 ?? ""), String(a2 ?? "")); const _nativeFns = new Set(); const _origToString = Function.prototype.toString; Function.prototype.toString = function() { if (_nativeFns.has(this)) { return `function ${this.name || ''}() { [native code] }`; } return _origToString.call(this); }; function _markNative(fn) { if (typeof fn === 'function') _nativeFns.add(fn); return fn; } _nativeFns.add(Function.prototype.toString); [Error, TypeError, ReferenceError, SyntaxError, RangeError, URIError, EvalError].forEach(E => { try { Object.defineProperty(E.prototype, 'name', { value: E.name, writable: true, enumerable: false, configurable: false, }); } catch(e) {} }); const _stackCache = new WeakMap(); const _origStackDesc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack'); if (_origStackDesc && _origStackDesc.get) { Object.defineProperty(Error.prototype, 'stack', { configurable: false, enumerable: false, get: function() { if (!_stackCache.has(this)) _stackCache.set(this, _origStackDesc.get.call(this)); return _stackCache.get(this); } }); } let _fpSeed = 0; function _fpRand(salt) { let h = (_fpSeed ^ (salt || 0)) | 0; h = Math.imul(h ^ (h >>> 16), 0x45d9f3b); h = Math.imul(h ^ (h >>> 13), 0x45d9f3b); return ((h ^ (h >>> 16)) >>> 0) / 0xFFFFFFFF; } function _fpNoise(x, y, channel) { return (_fpRand(x * 7919 + y * 6271 + channel * 8923) - 0.5) * 4; } var _fpCache = null; function _getFp() { if (_fpCache) return _fpCache; const gpuPool = [ 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (NVIDIA, NVIDIA GeForce RTX 2070 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (AMD, AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1080 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (AMD, AMD Radeon RX 5700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)', 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)', ]; const gpuVendorPool = [ 'Google Inc. (NVIDIA)','Google Inc. (NVIDIA)','Google Inc. (NVIDIA)', 'Google Inc. (Intel)','Google Inc. (Intel)', 'Google Inc. (AMD)','Google Inc. (AMD)', 'Google Inc. (NVIDIA)','Google Inc. (NVIDIA)', 'Google Inc. (Intel)','Google Inc. (AMD)','Google Inc. (NVIDIA)', ]; const idx = Math.floor(_fpRand(42) * gpuPool.length); const screenPool = [[1920,1080],[2560,1440],[1366,768],[1536,864],[1440,900],[1680,1050],[1280,720],[3840,2160]]; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; let cfp = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg'; for (let i = 0; i < 40; i++) cfp += chars[Math.floor(_fpRand(500 + i) * 64)]; cfp += '=='; _fpCache = { gpu: gpuPool[idx], gpuVendor: gpuVendorPool[idx], audioBaseLatency: 0.002 + _fpRand(100) * 0.008, audioSampleRate: [44100, 48000][Math.floor(_fpRand(101) * 2)], compThreshold: -24 + (_fpRand(102) - 0.5) * 4, compKnee: 30 + (_fpRand(103) - 0.5) * 4, compRatio: 12 + (_fpRand(104) - 0.5) * 4, batteryLevel: 0.5 + _fpRand(200) * 0.5, batteryCharging: _fpRand(201) > 0.3, screen: screenPool[Math.floor(_fpRand(300) * screenPool.length)], canvasFingerprint: cfp, }; return _fpCache; } function _fp(key) { return _getFp()[key]; } globalThis._eventRegistry = globalThis._eventRegistry || {}; globalThis._formValues = globalThis._formValues || {}; globalThis._formChecked = globalThis._formChecked || {}; const _eventRegistry = globalThis._eventRegistry; const _formValues = globalThis._formValues; const _formChecked = globalThis._formChecked; const _domParse = (cmd, a1, a2) => { try { return JSON.parse(_dom(cmd, a1, a2)); } catch { return null; } }; const _consoleFn = (level, args) => { try { Deno.core.ops.op_console_msg(level, args.map(a => { if (a === null) return "null"; if (a === undefined) return "undefined"; if (a instanceof Error) return a.stack || a.message || String(a); if (typeof a === "object") { try { const s = JSON.stringify(a); return s === "{}" && a.message ? a.message : s; } catch { return String(a); } } return String(a); }).join(" ")); } catch {} }; globalThis.console = { log: (...a) => _consoleFn("log", a), warn: (...a) => _consoleFn("warn", a), error: (...a) => _consoleFn("error", a), info: (...a) => _consoleFn("log", a), debug: () => {}, dir: () => {}, trace: () => {}, table: () => {}, group: () => {}, groupEnd: () => {}, groupCollapsed: () => {}, time: () => {}, timeEnd: () => {}, timeLog: () => {}, count: () => {}, countReset: () => {}, clear: () => {}, assert: (c, ...a) => { if (!c) _consoleFn("error", ["Assertion failed:", ...a]); }, }; let _tid = 0; const _clearedTimers = new Set(); const _intervals = new Set(); const _scheduleAfter = (delay, fn) => { const d = Math.max(0, Number(delay) || 0); if (d === 0) Promise.resolve().then(fn); else Deno.core.ops.op_sleep(d).then(fn); }; globalThis.setTimeout = (fn, delay = 0, ...args) => { if (typeof fn !== "function") return ++_tid; const id = ++_tid; _scheduleAfter(delay, () => { if (_clearedTimers.has(id)) return; try { fn(...args); } catch(e) { console.error("Timer error:", e); } }); return id; }; globalThis.clearTimeout = (id) => { _clearedTimers.add(id); }; globalThis.setInterval = (fn, delay = 0, ...args) => { if (typeof fn !== "function") return ++_tid; const id = ++_tid; _intervals.add(id); const tick = () => { if (!_intervals.has(id)) return; try { fn(...args); } catch(e) { console.error("Interval error:", e); } if (!_intervals.has(id)) return; _scheduleAfter(delay, tick); }; _scheduleAfter(delay, tick); return id; }; globalThis.clearInterval = (id) => { _intervals.delete(id); _clearedTimers.add(id); }; globalThis.requestAnimationFrame = (fn) => setTimeout(fn, 0); globalThis.cancelAnimationFrame = globalThis.clearTimeout; globalThis.queueMicrotask = globalThis.queueMicrotask || ((fn) => Promise.resolve().then(fn)); class MessageChannel { constructor() { this.port1 = { onmessage: null, postMessage: () => {}, close() {}, addEventListener() {}, removeEventListener() {} }; this.port2 = { onmessage: null, postMessage: () => {}, close() {}, addEventListener() {}, removeEventListener() {} }; this.port1.postMessage = (data) => { Promise.resolve().then(() => { if (this.port2.onmessage) this.port2.onmessage({ data }); }); }; this.port2.postMessage = (data) => { Promise.resolve().then(() => { if (this.port1.onmessage) this.port1.onmessage({ data }); }); }; } } globalThis.MessageChannel = MessageChannel; globalThis.MessagePort = class MessagePort { constructor(){} postMessage(){} close(){} addEventListener(){} removeEventListener(){} }; class CSSStyleDeclaration { constructor() { this._props = {}; } setProperty(name, value) { this._props[name] = String(value); } removeProperty(name) { const old = this._props[name]; delete this._props[name]; return old || ""; } getPropertyValue(name) { return this._props[name] || ""; } get cssText() { return Object.entries(this._props).map(([k,v]) => `${k}: ${v}`).join("; "); } set cssText(v) { this._props = {}; if(v) v.split(";").forEach(p => { const [k,...rest]=p.split(":"); if(k&&rest.length) this._props[k.trim()]=rest.join(":").trim(); }); } get length() { return Object.keys(this._props).length; } item(i) { return Object.keys(this._props)[i] || ""; } } const _styleProxy = (decl) => new Proxy(decl, { get(t, p) { if (typeof p === "symbol" || p in t) return t[p]; if (typeof p === "string") return t._props[p] || ""; return undefined; }, set(t, p, v) { if (typeof p === "string") { t._props[p] = String(v); return true; } t[p] = v; return true; } }); class Node { static ELEMENT_NODE = 1; static TEXT_NODE = 3; static COMMENT_NODE = 8; static DOCUMENT_NODE = 9; static DOCUMENT_FRAGMENT_NODE = 11; constructor(nid) { this._nid = nid; } get nodeType() { return +_dom("node_type", this._nid); } get nodeName() { return _domParse("node_name", this._nid) || ""; } get ownerDocument() { return globalThis.document; } get textContent() { return _domParse("text_content", this._nid) ?? ""; } set textContent(v) { const oldChildren = _domParse("child_nodes", this._nid) || []; for (const c of oldChildren) _dom("remove_child", c); let added = []; if (v != null && v !== "") { const tn = +_dom("create_text_node", String(v)); _dom("append_child", this._nid, tn); added = [tn]; } // Real MutationObserver fires childList for the children swap. // Without this React 18+ hydration mismatch detection and many polling // libs (intersection-driven lazy load, content sync) silently stall. if (globalThis.__mutationObservers?.length) { globalThis.__notifyMutation('childList', this._nid, added, oldChildren); } } get nodeValue() { const t = this.nodeType; if (t === 3 || t === 8) return _domParse("text_content", this._nid) ?? ""; return null; } set nodeValue(v) { const t = this.nodeType; if (t === 3 || t === 8) _dom("set_text_content", this._nid, String(v ?? "")); } get parentNode() { return _wrap(+_dom("parent_node", this._nid)); } get parentElement() { const p = this.parentNode; return p && p.nodeType === 1 ? p : null; } get childNodes() { const ids = _domParse("child_nodes", this._nid) || []; const list = ids.map(_wrap).filter(Boolean); list.item = (i) => list[i] || null; return list; } get firstChild() { return _wrap(+_dom("first_child", this._nid)); } get lastChild() { return _wrap(+_dom("last_child", this._nid)); } get nextSibling() { return _wrap(+_dom("next_sibling", this._nid)); } get previousSibling() { return _wrap(+_dom("prev_sibling", this._nid)); } appendChild(c) { if (!c) return c; _dom("append_child", this._nid, c._nid); if (globalThis.__mutationObservers?.length) globalThis.__notifyMutation('childList', this._nid, [c._nid], []); if (c instanceof Element && c.tagName === 'SCRIPT') { const scriptType = c.getAttribute('type') || ''; if (scriptType && scriptType !== 'text/javascript' && scriptType !== 'application/javascript') { return c; } const src = c.getAttribute('src'); if (src) { const fullUrl = src.startsWith('http') ? src : new URL(src, globalThis.location?.href || 'http://localhost/').href; const pageOrigin = (function() { try { return new URL(globalThis.location?.href || "about:blank").origin; } catch(e) { return ""; } })(); (async () => { try { const raw = await Deno.core.ops.op_fetch_url(fullUrl, "GET", "{}", "", pageOrigin, "no-cors"); const parsed = JSON.parse(raw); if (parsed.body) { try { (0, eval)(parsed.body); } catch(e) { console.error('Dynamic script error (' + fullUrl + '):', e.message); } } if (typeof c.onload === 'function') try { c.onload(new Event('load')); } catch(e) {} try { c.dispatchEvent(new Event('load')); } catch(e) {} } catch(e) { console.error('Dynamic script fetch error:', e.message); if (typeof c.onerror === 'function') try { c.onerror(e); } catch(ex) {} } })(); } else { const code = c.textContent; if (code) { try { (0, eval)(code); } catch(e) { console.error('Dynamic inline script error:', e.message); } } } } return c; } removeChild(c) { if (!c) return c; _dom("remove_child", c._nid); if (globalThis.__mutationObservers?.length) globalThis.__notifyMutation('childList', this._nid, [], [c._nid]); return c; } replaceChild(newChild, oldChild) { if (!oldChild || !newChild) return oldChild; _dom("insert_before", newChild._nid, oldChild._nid); _dom("remove_child", oldChild._nid); return oldChild; } insertBefore(n, ref) { if (!n) return n; if (!ref) { this.appendChild(n); return n; } _dom("insert_before", n._nid, ref._nid); return n; } contains(o) { return o ? _dom("contains", this._nid, o._nid) === "true" : false; } hasChildNodes() { return _dom("has_child_nodes", this._nid) === "true"; } cloneNode(deep) { const t = this.nodeType; if (t === 1) { if (deep) { const wrapper = document.createElement('div'); wrapper.innerHTML = _domParse("outer_html", this._nid) || ""; const clone = wrapper.firstChild; return clone; } const el = document.createElement(this.nodeName.toLowerCase()); const html = _domParse("outer_html", this._nid) || ""; const attrMatch = html.match(/^<[a-zA-Z][^\s>]*([\s\S]*?)>/); if (attrMatch && attrMatch[1]) { const attrStr = attrMatch[1].trim(); const re = /([a-zA-Z_:][a-zA-Z0-9_.:-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g; let m; while ((m = re.exec(attrStr)) !== null) { const name = m[1]; const val = m[2] !== undefined ? m[2] : m[3] !== undefined ? m[3] : m[4] || ""; if (name !== this.nodeName.toLowerCase()) el.setAttribute(name, val); } } return el; } if (t === 3) return document.createTextNode(this.textContent); if (t === 8) return document.createComment(this.nodeValue || ""); return null; } compareDocumentPosition(other) { if (!other) return 0; if (this._nid === other._nid) return 0; if (this.contains(other)) return 16 | 4; // DOCUMENT_POSITION_CONTAINED_BY | FOLLOWING if (other.contains && other.contains(this)) return 8 | 2; // DOCUMENT_POSITION_CONTAINS | PRECEDING return 4; // DOCUMENT_POSITION_FOLLOWING } getRootNode() { return globalThis.document; } normalize() {} // no-op isEqualNode(other) { if (!other) return false; if (this._nid === other._nid) return true; if (this.nodeType !== other.nodeType) return false; if (this.nodeName !== other.nodeName) return false; if (this.nodeValue !== other.nodeValue) return false; const a = this.attributes ? this.attributes : null; const b = other.attributes ? other.attributes : null; if ((a && a.length) || (b && b.length)) { if (!a || !b || a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (other.getAttribute(a[i].name) !== a[i].value) return false; } } const cA = this.childNodes || []; const cB = other.childNodes || []; if (cA.length !== cB.length) return false; for (let i = 0; i < cA.length; i++) { if (!cA[i].isEqualNode(cB[i])) return false; } return true; } isSameNode(other) { return other && this._nid === other._nid; } addEventListener() {} removeEventListener() {} dispatchEvent() { return true; } } class CharacterData extends Node { get data() { return _domParse("text_content", this._nid) ?? ""; } set data(v) { const oldValue = _domParse("text_content", this._nid) ?? ""; _dom("set_text_content", this._nid, String(v ?? "")); if (globalThis.__mutationObservers?.length) { globalThis.__notifyMutation('characterData', this._nid, [], [], null, oldValue); } } get length() { return this.data.length; } substringData(offset, count) { return this.data.substring(offset, offset + count); } appendData(s) { this.data += s; } insertData(offset, s) { const d = this.data; this.data = d.slice(0, offset) + s + d.slice(offset); } deleteData(offset, count) { const d = this.data; this.data = d.slice(0, offset) + d.slice(offset + count); } replaceData(offset, count, s) { const d = this.data; this.data = d.slice(0, offset) + s + d.slice(offset + count); } } class Text extends CharacterData { get nodeName() { return "#text"; } get nodeType() { return 3; } get wholeText() { return this.data; } splitText(offset) { const d = this.data; const tail = d.substring(offset); this.data = d.substring(0, offset); const newNid = +_dom("create_text_node", tail); const parent = this.parentNode; if (parent) { const ref = this.nextSibling; parent.insertBefore(_wrap(newNid), ref); } return _wrap(newNid); } cloneNode() { return document.createTextNode(this.data); } } class Comment extends CharacterData { get nodeName() { return "#comment"; } get nodeType() { return 8; } cloneNode() { return document.createComment(this.data); } } class Element extends Node { constructor(nid) { super(nid); this._style = _styleProxy(new CSSStyleDeclaration()); } get tagName() { return _domParse("tag_name", this._nid) || ""; } get localName() { return (this.tagName || "").toLowerCase(); } get id() { return this.getAttribute("id") || ""; } set id(v) { this.setAttribute("id", v); } get className() { return this.getAttribute("class") || ""; } set className(v) { this.setAttribute("class", v); } get namespaceURI() { const tag = this.localName; if (tag === "svg" || this._ns === "http://www.w3.org/2000/svg") return "http://www.w3.org/2000/svg"; return "http://www.w3.org/1999/xhtml"; } get innerHTML() { return _domParse("inner_html", this._nid) ?? ""; } set innerHTML(v) { if (this.localName === 'template') { this.content.innerHTML = v; return; } // Capture the children that are about to be replaced so we can deliver // them as `removedNodes` in the MutationObserver record. Without this, // libraries that mutate via `innerHTML =` (jQuery's `.html(s)`, React // `dangerouslySetInnerHTML`, vue-style content swaps) silently bypass // every MutationObserver subscriber and downstream hydration / polling // logic stalls. let oldChildren = []; let newChildren = []; if (globalThis.__mutationObservers?.length) { oldChildren = _domParse("child_nodes", this._nid) || []; } _dom("set_inner_html", this._nid, String(v ?? "")); if (globalThis.__mutationObservers?.length) { newChildren = _domParse("child_nodes", this._nid) || []; globalThis.__notifyMutation('childList', this._nid, newChildren, oldChildren); } } get outerHTML() { return _domParse("outer_html", this._nid) ?? ""; } get innerText() { return this.textContent; } set innerText(v) { this.textContent = v; } get children() { const ids = _domParse("element_children", this._nid) || []; return ids.map(_wrapEl).filter(Boolean); } get content() { if (this.localName !== 'template') return undefined; if (!this._templateContent) this._templateContent = document.createDocumentFragment(); return this._templateContent; } get childElementCount() { return this.children.length; } get firstElementChild() { return this.children[0] || null; } get lastElementChild() { const ch = this.children; return ch[ch.length-1] || null; } get nextElementSibling() { let s = this.nextSibling; while(s && s.nodeType !== 1) s = s.nextSibling; return s; } get previousElementSibling() { let s = this.previousSibling; while(s && s.nodeType !== 1) s = s.previousSibling; return s; } get classList() { const el = this; const obj = { add: (...c) => { const s = new Set((el.className||"").split(/\s+/).filter(Boolean)); c.forEach(x=>s.add(x)); el.className=[...s].join(" "); }, remove: (...c) => { const s = new Set((el.className||"").split(/\s+/).filter(Boolean)); c.forEach(x=>s.delete(x)); el.className=[...s].join(" "); }, contains: c => (el.className||"").split(/\s+/).includes(c), toggle: (c, force) => { const has = obj.contains(c); if(force===true||(!has&&force!==false)){obj.add(c);return true;} obj.remove(c); return false; }, get length() { return (el.className||"").split(/\s+/).filter(Boolean).length; }, item: i => (el.className||"").split(/\s+/).filter(Boolean)[i] || null, forEach: (cb) => (el.className||"").split(/\s+/).filter(Boolean).forEach(cb), toString: () => el.className || "", }; return obj; } get style() { return this._style; } set style(v) { if (typeof v === "string") this._style.cssText = v; } getAttribute(n) { return _domParse("get_attribute", this._nid, n); } setAttribute(n, v) { _dom("set_attribute", this._nid, n + "\0" + String(v)); if (globalThis.__mutationObservers?.length) globalThis.__notifyMutation('attributes', this._nid, [], [], n); } setAttributeNS(ns, n, v) { this.setAttribute(n, v); } // Simplified NS handling removeAttribute(n) { _dom("remove_attribute", this._nid, n); } removeAttributeNS(ns, n) { this.removeAttribute(n); } hasAttribute(n) { return this.getAttribute(n) !== null; } hasAttributes() { return true; } // Simplified get attributes() { const el = this; const names = _domParse("attribute_names", el._nid) || []; const list = names.map((name) => ({ name, localName: name, value: el.getAttribute(name) ?? "", namespaceURI: null, prefix: null, specified: true, ownerElement: el, nodeName: name, nodeValue: el.getAttribute(name) ?? "", nodeType: 2, })); list.length = names.length; list.getNamedItem = (n) => names.includes(n) ? list[names.indexOf(n)] : null; list.setNamedItem = (a) => { if (a && a.name) el.setAttribute(a.name, a.value); return a; }; list.removeNamedItem = (n) => { const a = list.getNamedItem(n); if (a) el.removeAttribute(n); return a; }; list.item = (i) => list[i] || null; for (let i = 0; i < names.length; i++) { Object.defineProperty(list, names[i], { value: list[i], configurable: true, enumerable: false }); } return list; } getAttributeNS(ns, n) { return this.getAttribute(n); } querySelector(s) { return _wrapEl(+_dom("query_selector_scoped", this._nid, s)); } querySelectorAll(s) { const ids = _domParse("query_selector_all_scoped", this._nid, s) || []; const list = ids.map(_wrapEl).filter(Boolean); list.item = (i) => list[i] || null; list.forEach = Array.prototype.forEach.bind(list); return list; } getElementsByTagName(t) { return this.querySelectorAll(t); } getElementsByClassName(c) { return this.querySelectorAll("." + c); } matches(s) { const parent = this.parentNode; if (!parent || !parent.querySelectorAll) return false; const matches = parent.querySelectorAll(s); for (let i = 0; i < matches.length; i++) { if (matches[i]._nid === this._nid) return true; } return false; } closest(s) { let el = this; while (el) { if (el.nodeType === 1 && el.matches && el.matches(s)) return el; el = el.parentNode; } return null; } insertAdjacentHTML(position, html) { const parent = this.parentNode; switch (position) { case 'beforebegin': if (parent) { const tmp = document.createElement('div'); tmp.innerHTML = html; const children = tmp.childNodes; for (let i = 0; i < children.length; i++) parent.insertBefore(children[i], this); } break; case 'afterbegin': { const tmp = document.createElement('div'); tmp.innerHTML = html; const children = tmp.childNodes; const first = this.firstChild; for (let i = children.length - 1; i >= 0; i--) this.insertBefore(children[i], first); } break; case 'beforeend': { const tmp = document.createElement('div'); tmp.innerHTML = html; const children = tmp.childNodes; for (let i = 0; i < children.length; i++) this.appendChild(children[i]); } break; case 'afterend': if (parent) { const tmp = document.createElement('div'); tmp.innerHTML = html; const children = tmp.childNodes; const next = this.nextSibling; for (let i = 0; i < children.length; i++) parent.insertBefore(children[i], next); } break; } } addEventListener(type, handler, opts) { const key = this._nid; if (!_eventRegistry[key]) _eventRegistry[key] = {}; if (!_eventRegistry[key][type]) _eventRegistry[key][type] = []; _eventRegistry[key][type].push(handler); } removeEventListener(type, handler) { const key = this._nid; if (_eventRegistry[key] && _eventRegistry[key][type]) { _eventRegistry[key][type] = _eventRegistry[key][type].filter(h => h !== handler); } } dispatchEvent(event) { if (!event) return true; if (!event.target) event.target = this; event.currentTarget = this; const handlers = (_eventRegistry[this._nid] || {})[event.type] || []; for (const h of handlers) { try { h.call(this, event); } catch(e) { console.error(e); } if (event._immediatePropagationStopped) break; } if (event.bubbles && !event._propagationStopped && this.parentNode) { this.parentNode.dispatchEvent(event); } return !event.defaultPrevented; } click() { const cancelled = !this.dispatchEvent(new MouseEvent("click", {bubbles: true, cancelable: true})); if (!cancelled) { const link = this.tagName === 'A' ? this : (this.closest ? this.closest('a[href]') : null); if (link) { const href = link.getAttribute('href'); if (href && !href.startsWith('#') && !href.startsWith('javascript:')) { location.assign(href); return; } } const type = (this.getAttribute('type') || '').toLowerCase(); if (type === 'submit' || (this.localName === 'button' && type !== 'button' && type !== 'reset')) { const form = this.closest ? this.closest('form') : null; if (form && typeof form.submit === 'function') { form.submit(this); } } } } focus() { globalThis.__obscura_focused = this; globalThis.__obscura_click_target = this; } blur() { if (globalThis.__obscura_focused === this) globalThis.__obscura_focused = null; } get value() { if (_formValues[this._nid] !== undefined) return _formValues[this._nid]; const tag = this.localName; if (tag === 'textarea') return this.textContent; return this.getAttribute("value") || ""; } set value(v) { _formValues[this._nid] = String(v); const tag = this.localName; if (tag === 'textarea') { this.textContent = String(v); } } get checked() { if (_formChecked[this._nid] !== undefined) return _formChecked[this._nid]; return this.hasAttribute("checked"); } set checked(v) { _formChecked[this._nid] = !!v; } get selected() { if (this._selected !== undefined) return this._selected; return this.hasAttribute("selected"); } set selected(v) { this._selected = !!v; } get disabled() { return this.hasAttribute("disabled"); } set disabled(v) { if (v) this.setAttribute("disabled", ""); else this.removeAttribute("disabled"); } get type() { return this.getAttribute("type") || (this.localName === "input" ? "text" : ""); } set type(v) { this.setAttribute("type", v); } get name() { return this.getAttribute("name") || ""; } set name(v) { this.setAttribute("name", v); } get placeholder() { return this.getAttribute("placeholder") || ""; } set placeholder(v) { this.setAttribute("placeholder", v); } get href() { return this.getAttribute("href") || ""; } set href(v) { this.setAttribute("href", v); } get src() { return this.getAttribute("src") || ""; } set src(v) { this.setAttribute("src", v); if (this.localName === 'iframe' && v && v !== 'about:blank') { this._loadIframeSrc(v); } } _loadIframeSrc(url) { let fullUrl = url; if (!url.includes('://')) { try { fullUrl = new URL(url, _domParse("document_url") || "about:blank").href; } catch(e) {} } const el = this; fetch(fullUrl, {mode: 'no-cors'}).then(async resp => { if (resp.ok || resp.type === 'opaque') { const html = await resp.text(); el._iframeDoc = new _IframeDocument(html, fullUrl, el); el._iframeWin = new _IframeWindow(el._iframeDoc, fullUrl); } else { el._iframeDoc = new _IframeDocument('', fullUrl, el); el._iframeWin = new _IframeWindow(el._iframeDoc, fullUrl); } _registerIframe(el); if (typeof el.onload === 'function') { try { el.onload(); } catch(e) {} } else { var onloadAttr = el.getAttribute('onload'); if (onloadAttr) try { (0, eval)(onloadAttr); } catch(e) {} } }).catch(() => { el._iframeDoc = new _IframeDocument('', fullUrl, el); el._iframeWin = new _IframeWindow(el._iframeDoc, fullUrl); _registerIframe(el); if (typeof el.onload === 'function') try { el.onload(); } catch(e) {} }); } get contentDocument() { if (this.localName !== 'iframe') return undefined; if (this._iframeDoc) { const pageOrigin = (function(){ try { return new URL(_domParse("document_url")).origin; } catch(e) { return ''; } })(); const iframeOrigin = (function(url){ try { return new URL(url).origin; } catch(e) { return ''; } })(this.src); if (pageOrigin === iframeOrigin || this.src === '' || this.src === 'about:blank' || !this.src.includes('://')) { return this._iframeDoc; } return null; // Cross-origin: blocked } if (!this._iframeDoc) { this._iframeDoc = new _IframeDocument('', 'about:blank', this); this._iframeWin = new _IframeWindow(this._iframeDoc, 'about:blank'); } return this._iframeDoc; } get contentWindow() { if (this.localName !== 'iframe') return undefined; if (!this._iframeWin) { this.contentDocument; // side effect: creates _iframeDoc + _iframeWin } return this._iframeWin; } get action() { const action = this.getAttribute("action") || _domParse("document_url") || ""; try { return new URL(action, _domParse("document_url") || "about:blank").href; } catch(e) { return action; } } set action(v) { this.setAttribute("action", v); } get method() { return this.getAttribute("method") || "get"; } set method(v) { this.setAttribute("method", v); } get form() { let p = this.parentNode; while (p && p.localName !== 'form') p = p.parentNode; return p; } get options() { if (this.localName !== 'select') return []; return this.querySelectorAll('option'); } get selectedIndex() { const opts = this.options; for (let i = 0; i < opts.length; i++) { if (opts[i].selected || opts[i].hasAttribute('selected')) return i; } return -1; } set selectedIndex(v) { const opts = this.options; for (let i = 0; i < opts.length; i++) { opts[i]._selected = (i === v); } } submit(submitter) { const cancelled = !this.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); if (cancelled) return; const pairs = []; const fields = this.querySelectorAll('input, select, textarea'); for (let i = 0; i < fields.length; i++) { const f = fields[i]; const name = f.getAttribute('name'); if (!name) continue; if (f.getAttribute('disabled') !== null) continue; const tag = f.localName; const type = (f.getAttribute('type') || '').toLowerCase(); if ((type === 'checkbox' || type === 'radio') && !f.checked) continue; if (type === 'file' || type === 'reset') continue; if (type === 'button') continue; if (type === 'submit' || tag === 'button') { if (submitter && f !== submitter) continue; if (!submitter) continue; // default submit: don't include submit button value } let val; if (tag === 'select') { const opt = f.querySelector('option[selected]') || f.querySelector('option'); val = opt ? (opt.getAttribute('value') !== null ? opt.getAttribute('value') : opt.textContent) : ''; } else if (tag === 'textarea') { val = f.value || f.textContent || ''; } else { val = f.value !== undefined ? f.value : (f.getAttribute('value') || ''); } const enc = (s) => encodeURIComponent(s).replace(/%20/g, '+').replace(/!/g, '%21'); pairs.push(enc(name) + '=' + enc(val)); } const action = this.getAttribute('action') || ''; const method = (this.getAttribute('method') || 'GET').toUpperCase(); const baseUrl = globalThis.location?.href || 'about:blank'; let targetUrl; try { targetUrl = new URL(action, baseUrl).href; } catch(e) { targetUrl = action; } const encoded = pairs.join('&'); if (method === 'POST') { Deno.core.ops.op_navigate(targetUrl, 'POST', encoded); } else { const sep = targetUrl.includes('?') ? '&' : '?'; Deno.core.ops.op_navigate(targetUrl + (encoded ? sep + encoded : ''), 'GET', ''); } } reset() { this.dispatchEvent(new Event('reset', { bubbles: true })); } get dataset() { const el = this; return new Proxy({}, { get(_, k) { if(typeof k!=="string")return undefined; return el.getAttribute("data-"+k.replace(/([A-Z])/g,"-$1").toLowerCase()); }, set(_, k, v) { el.setAttribute("data-"+k.replace(/([A-Z])/g,"-$1").toLowerCase(), v); return true; }, }); } get offsetWidth() { return 100; } get offsetHeight() { return 20; } get offsetTop() { return 0; } get offsetLeft() { return 0; } get clientWidth() { return 100; } get clientHeight() { return 20; } get scrollWidth() { return 100; } get scrollHeight() { return 20; } get scrollTop() { return 0; } set scrollTop(v) {} get scrollLeft() { return 0; } set scrollLeft(v) {} getBoundingClientRect() { globalThis.__obscura_click_target = this; // No layout engine, but Playwright's actionability polling needs each // element to occupy a stable, distinct rect so hit-testing can pick the // right one (issue #45). Synthesize a deterministic position from the // node id: every nid maps to a unique cell in a 12-column grid, sized // to fit a 1280x720 viewport. Stable across reads, different per node. const VW = 1280, VH = 720, COLS = 12, CW = 100, CH = 20, GX = 110, GY = 30; const rowsPerScreen = Math.max(1, Math.floor((VH - 10) / GY)); const cell = this._nid | 0; const col = ((cell * 7) | 0) % COLS; const row = (((cell * 13) | 0) >> 0) % rowsPerScreen; const x = 10 + col * GX; const y = 10 + row * GY; return { x, y, width: CW, height: CH, top: y, right: x + CW, bottom: y + CH, left: x, toJSON() { return this; }, }; } getClientRects() { return [this.getBoundingClientRect()]; } // No layout engine: a stub that always returns true unblocks Playwright's // actionability polling. With a real layout we'd check display, visibility, // opacity and rect dimensions per spec. checkVisibility(opts) { return true; } // ARIA reflection properties. Without an accessibility tree we expose the // raw aria-* attributes so Playwright's getByRole / getByLabel locators can // at least find elements that author them explicitly. get role() { return this.getAttribute('role'); } set role(v) { if (v == null) this.removeAttribute('role'); else this.setAttribute('role', String(v)); } get ariaLabel() { return this.getAttribute('aria-label'); } set ariaLabel(v) { if (v == null) this.removeAttribute('aria-label'); else this.setAttribute('aria-label', String(v)); } get ariaRoleDescription() { return this.getAttribute('aria-roledescription'); } set ariaRoleDescription(v) { if (v == null) this.removeAttribute('aria-roledescription'); else this.setAttribute('aria-roledescription', String(v)); } get ariaChecked() { return this.getAttribute('aria-checked'); } set ariaChecked(v) { if (v == null) this.removeAttribute('aria-checked'); else this.setAttribute('aria-checked', String(v)); } get ariaDisabled() { return this.getAttribute('aria-disabled'); } set ariaDisabled(v) { if (v == null) this.removeAttribute('aria-disabled'); else this.setAttribute('aria-disabled', String(v)); } get ariaExpanded() { return this.getAttribute('aria-expanded'); } set ariaExpanded(v) { if (v == null) this.removeAttribute('aria-expanded'); else this.setAttribute('aria-expanded', String(v)); } get ariaHidden() { return this.getAttribute('aria-hidden'); } set ariaHidden(v) { if (v == null) this.removeAttribute('aria-hidden'); else this.setAttribute('aria-hidden', String(v)); } get ariaSelected() { return this.getAttribute('aria-selected'); } set ariaSelected(v) { if (v == null) this.removeAttribute('aria-selected'); else this.setAttribute('aria-selected', String(v)); } scrollIntoView() { globalThis.__obscura_click_target = this; } animate(keyframes, options) { const duration = typeof options === 'number' ? options : (options?.duration || 0); return { finished: Promise.resolve(), currentTime: 0, playState: 'finished', effect: { getComputedTiming() { return { duration }; } }, cancel(){}, finish(){}, play(){}, pause(){}, reverse(){}, addEventListener(){}, removeEventListener(){}, onfinish: null, oncancel: null, }; } getAnimations() { return []; } get isConnected() { var node = this; while (node) { if (node.nodeType === 9) return true; node = node.parentNode; } return false; } remove() { if (this.parentNode) this.parentNode.removeChild(this); } append(...nodes) { for (const n of nodes) { if (typeof n === "string") this.appendChild(document.createTextNode(n)); else this.appendChild(n); } } prepend(...nodes) { const ref = this.firstChild; for (const n of nodes) { const node = (typeof n === "string") ? document.createTextNode(n) : n; if (ref) this.insertBefore(node, ref); else this.appendChild(node); } } } class Document extends Node { get documentElement() { return _wrapEl(+_dom("document_element")); } get head() { return this.querySelector("head"); } get body() { return this.querySelector("body"); } get doctype() { if (this._doctype !== undefined) return this._doctype; const info = _domParse("document_doctype"); if (info && info.name) { this._doctype = new DocumentType(info.nodeId, info.name, info.publicId || "", info.systemId || ""); } else { this._doctype = null; } return this._doctype; } get title() { return _domParse("document_title") ?? ""; } set title(v) {} get URL() { return _domParse("document_url") ?? ""; } get documentURI() { return this.URL; } get location() { return globalThis.location; } set location(url) { Deno.core.ops.op_navigate(_resolveUrl(String(url)), 'GET', ''); } get defaultView() { return globalThis; } get nodeType() { return 9; } get nodeName() { return "#document"; } get ownerDocument() { return null; } // Document has no ownerDocument get compatMode() { return "CSS1Compat"; } get characterSet() { return "UTF-8"; } get contentType() { return "text/html"; } get readyState() { return globalThis.__documentReadyState__ || 'complete'; } get hidden() { return false; } get visibilityState() { return "visible"; } getElementById(id) { return _wrapEl(+_dom("get_element_by_id", id)); } querySelector(s) { return _wrapEl(+_dom("query_selector", s)); } querySelectorAll(s) { const ids = _domParse("query_selector_all", s) || []; const list = ids.map(_wrapEl).filter(Boolean); list.item = (i) => list[i] || null; list.forEach = Array.prototype.forEach.bind(list); return list; } getElementsByTagName(t) { return this.querySelectorAll(t); } getElementsByClassName(c) { return this.querySelectorAll("." + c); } getElementsByName(name) { return this.querySelectorAll('[name="' + String(name).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]'); } createElement(t) { const el = _wrapEl(+_dom("create_element", t.toLowerCase())); if (el && t.toLowerCase() === 'template') { el._templateContent = this.createDocumentFragment(); } return el; } createElementNS(ns, t) { const el = this.createElement(t); if (el) el._ns = ns; return el; } createTextNode(t) { return _wrap(+_dom("create_text_node", String(t))); } createComment(t) { const nid = +_dom("create_comment_node", String(t ?? "")); const n = new Comment(nid); _cache.set(nid, n); return n; } createDocumentFragment() { const nid = +_dom("create_document_fragment"); const frag = new DocumentFragment(nid); _cache.set(nid, frag); return frag; } // Legacy DOM Level 2 event factory. Spec returns an event of the requested // class with an empty type until init*Event() is called. We previously // returned a generic Event for every type, which broke libraries that call // createEvent('CustomEvent').initCustomEvent(...) — see issue #41. createEvent(type) { const map = { 'customevent': CustomEvent, 'customevents': CustomEvent, 'mouseevent': MouseEvent, 'mouseevents': MouseEvent, 'keyboardevent': KeyboardEvent, 'keyboardevents': KeyboardEvent, 'focusevent': FocusEvent, 'inputevent': InputEvent, 'uievent': UIEvent, 'uievents': UIEvent, 'wheelevent': WheelEvent, 'pointerevent': PointerEvent, 'errorevent': ErrorEvent, 'popstateevent': PopStateEvent, 'animationevent': AnimationEvent, 'transitionevent': TransitionEvent, }; const Cls = map[String(type || '').toLowerCase()] || Event; return new Cls(''); } createRange() { return { setStart(){}, setEnd(){}, collapse(){}, selectNodeContents(){}, cloneContents(){ return document.createDocumentFragment(); } }; } addEventListener(type, fn, opts) { if (typeof fn !== 'function') return; if (!this._listeners) this._listeners = {}; if (!this._listeners[type]) this._listeners[type] = []; if (!this._listeners[type].includes(fn)) this._listeners[type].push(fn); } removeEventListener(type, fn) { if (this._listeners?.[type]) { this._listeners[type] = this._listeners[type].filter(h => h !== fn); } } dispatchEvent(event) { if (!event) return true; const handlers = (this._listeners?.[event.type] || []).slice(); for (const h of handlers) { try { h.call(this, event); } catch(e) { console.error('document event error:', e); } } return !event.defaultPrevented; } createTreeWalker(root, whatToShow, filter) { whatToShow = whatToShow || 0xFFFFFFFF; // NodeFilter.SHOW_ALL const walker = { root: root, currentNode: root, whatToShow: whatToShow, filter: filter || null, _accept(node) { const nodeType = node.nodeType; const show = (whatToShow >> (nodeType - 1)) & 1; if (!show) return false; if (this.filter) { if (typeof this.filter === 'function') return this.filter(node) === 1; if (this.filter.acceptNode) return this.filter.acceptNode(node) === 1; } return true; }, nextNode() { let node = this.currentNode; let child = node.firstChild; while (child) { if (this._accept(child)) { this.currentNode = child; return child; } if (child.firstChild) { child = child.firstChild; continue; } if (child.nextSibling) { child = child.nextSibling; continue; } let parent = child.parentNode; while (parent && parent !== this.root) { if (parent.nextSibling) { child = parent.nextSibling; break; } parent = parent.parentNode; } if (!parent || parent === this.root) return null; } return null; }, previousNode() { let node = this.currentNode; if (node === this.root) return null; let sibling = node.previousSibling; if (sibling) { while (sibling.lastChild) sibling = sibling.lastChild; if (this._accept(sibling)) { this.currentNode = sibling; return sibling; } } let parent = node.parentNode; if (parent && parent !== this.root && this._accept(parent)) { this.currentNode = parent; return parent; } return null; }, firstChild() { let child = this.currentNode.firstChild; while (child) { if (this._accept(child)) { this.currentNode = child; return child; } child = child.nextSibling; } return null; }, lastChild() { let child = this.currentNode.lastChild; while (child) { if (this._accept(child)) { this.currentNode = child; return child; } child = child.previousSibling; } return null; }, nextSibling() { let sibling = this.currentNode.nextSibling; while (sibling) { if (this._accept(sibling)) { this.currentNode = sibling; return sibling; } sibling = sibling.nextSibling; } return null; }, previousSibling() { let sibling = this.currentNode.previousSibling; while (sibling) { if (this._accept(sibling)) { this.currentNode = sibling; return sibling; } sibling = sibling.previousSibling; } return null; }, parentNode() { let parent = this.currentNode.parentNode; if (parent && parent !== this.root && this._accept(parent)) { this.currentNode = parent; return parent; } return null; }, }; return walker; } createNodeIterator(root, whatToShow, filter) { return this.createTreeWalker(root, whatToShow, filter); } getSelection() { return globalThis.getSelection(); } get activeElement() { return globalThis.__obscura_focused || this.body; } get implementation() { return { // Spec: createHTMLDocument returns a NEW detached Document. jQuery // 3.x's selector feature-detect calls `body.innerHTML = '
'` on // the result — when we returned `globalThis.document`, the real // `` was wiped, taking every page on the open web that ships // jQuery 3.x with it. Reuse the DOMParser path to build a detached // document, then optionally set the title. createHTMLDocument(title) { const skeleton = ``; const doc = new DOMParser().parseFromString(skeleton, "text/html"); if (title != null) { const t = doc.querySelector("title"); if (t) t.textContent = String(title); } return doc; }, // Real spec: createDocument(namespaceURI, qualifiedName, doctype) → // an XML document with a root element of the given name. We don't // have a separate XML stack, so return a minimal detached document // with an element of the requested local name as documentElement. createDocument(_ns, qualifiedName, _doctype) { const name = (qualifiedName && String(qualifiedName)) || "root"; const safe = name.replace(/[^a-zA-Z0-9-]/g, ""); const html = `<${safe}>`; return new DOMParser().parseFromString(html, "application/xml"); }, hasFeature() { return true; }, }; } get styleSheets() { return []; } get forms() { return this.querySelectorAll("form"); } get images() { return this.querySelectorAll("img"); } get links() { return this.querySelectorAll("a[href], area[href]"); } get scripts() { return this.querySelectorAll("script"); } get cookie() { return Deno.core.ops.op_get_cookies(); } set cookie(v) { if (!v) return; Deno.core.ops.op_set_cookie(v); } write(...args) { var html = args.join(''); if (!html) return; var body = this.body; if (!body) return; var temp = this.createElement('div'); temp.innerHTML = html; var children = temp.childNodes; for (var i = 0; i < children.length; i++) { body.appendChild(children[i]); } } writeln(...args) { this.write(args.join('') + '\n'); } open() { var body = this.body; if (body) body.innerHTML = ''; return this; } close() { return; } hasFocus() { return true; } execCommand() { return false; } } class DocumentFragment extends Node { get nodeType() { return 11; } get nodeName() { return "#document-fragment"; } get innerHTML() { return _domParse("inner_html", this._nid) ?? ""; } set innerHTML(v) { _dom("set_inner_html", this._nid, String(v ?? "")); } querySelector(s) { return _wrapEl(+_dom("query_selector_scoped", this._nid, s)); } querySelectorAll(s) { const ids = _domParse("query_selector_all_scoped", this._nid, s) || []; const list = ids.map(_wrapEl).filter(Boolean); list.item = (i) => list[i] || null; return list; } get children() { const ids = _domParse("element_children", this._nid) || []; return ids.map(_wrapEl).filter(Boolean); } get firstElementChild() { return this.children[0] || null; } get lastElementChild() { const ch = this.children; return ch[ch.length - 1] || null; } getElementById(id) { return null; } cloneNode(deep) { const frag = document.createDocumentFragment(); if (deep) frag.innerHTML = this.innerHTML; return frag; } } class DocumentType extends Node { constructor(nid, name, publicId, systemId) { super(nid); this._name = name; this._publicId = publicId; this._systemId = systemId; } get nodeType() { return 10; } get nodeName() { return this._name; } get name() { return this._name; } get publicId() { return this._publicId; } get systemId() { return this._systemId; } get ownerDocument() { return globalThis.document; } } const _cache = new Map(); function _elementClassFor(nid) { const tag = _domParse("tag_name", nid); if (tag === "FORM" && globalThis.HTMLFormElement) return globalThis.HTMLFormElement; return Element; } function _wrap(nid) { if (nid < 0 || nid === null || nid === undefined || isNaN(nid)) return null; if (_cache.has(nid)) return _cache.get(nid); const t = +_dom("node_type", nid); let n; if (t === 1) { const C = _elementClassFor(nid); n = new C(nid); } else if (t === 3) n = new Text(nid); else if (t === 8) n = new Comment(nid); else if (t === 9) n = new Document(nid); else n = new Node(nid); _cache.set(nid, n); return n; } function _wrapEl(nid) { if (nid < 0 || nid === null || nid === undefined || isNaN(nid)) return null; if (_cache.has(nid)) return _cache.get(nid); const C = _elementClassFor(nid); const n = new C(nid); _cache.set(nid, n); return n; } globalThis._wrap = _wrap; globalThis.self = globalThis; globalThis.document = null; function _resolveUrl(url) { if (!url) return url; if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('about:')) return url; try { return new URL(url, _domParse("document_url") || "about:blank").href; } catch(e) { return url; } } // `__virtualUrl` is set by `history.pushState`/`replaceState` (and cleared by // any real navigation). When set, `location.href` and friends read it instead // of the underlying `document_url`. Without this, client-side routers // (Next.js, React Router, vue-router) call `pushState` but the URL never // changes, so their `useLocation` hooks return the wrong path and the UI // freezes on the original route. globalThis.__virtualUrl = null; function __currentUrl() { return globalThis.__virtualUrl || _domParse("document_url") || "about:blank"; } globalThis.location = { get href() { return __currentUrl(); }, set href(url) { globalThis.__virtualUrl = null; Deno.core.ops.op_navigate(_resolveUrl(url), 'GET', ''); }, get origin() { try { return new URL(this.href).origin; } catch { return ""; } }, get protocol() { try { return new URL(this.href).protocol; } catch { return ""; } }, get host() { try { return new URL(this.href).host; } catch { return ""; } }, get hostname() { try { return new URL(this.href).hostname; } catch { return ""; } }, get pathname() { try { return new URL(this.href).pathname; } catch { return "/"; } }, get search() { try { return new URL(this.href).search; } catch { return ""; } }, get hash() { try { return new URL(this.href).hash; } catch { return ""; } }, get port() { try { return new URL(this.href).port; } catch { return ""; } }, toString() { return this.href; }, assign(url) { globalThis.__virtualUrl = null; Deno.core.ops.op_navigate(_resolveUrl(url), 'GET', ''); }, reload() {}, replace(url) { globalThis.__virtualUrl = null; Deno.core.ops.op_navigate(_resolveUrl(url), 'GET', ''); }, }; const _locationObj = globalThis.location; Object.defineProperty(globalThis, 'location', { get() { return _locationObj; }, set(url) { Deno.core.ops.op_navigate(_resolveUrl(String(url)), 'GET', ''); }, configurable: false, enumerable: true, }); globalThis.window = globalThis; globalThis.self = globalThis; globalThis.top = globalThis; globalThis.parent = globalThis; globalThis.frames = globalThis; globalThis.frameElement = null; globalThis.length = 0; // HTML spec exposes on* event handler IDL attributes on Window. Libraries like // jQuery feature-detect bubbling via `("on" + ev) in window` and fall back to // a legacy IE path that crashes on missing DOM APIs when the check returns // false. Initialising them to null makes the check match real browsers. for (const _ev of [ "abort","beforeprint","beforeunload","blur","cancel","canplay","canplaythrough", "change","click","close","contextmenu","cuechange","dblclick","drag","dragend", "dragenter","dragleave","dragover","dragstart","drop","durationchange","emptied", "ended","error","focus","focusin","focusout","formdata","gotpointercapture", "hashchange","input","invalid","keydown","keypress","keyup","languagechange", "load","loadeddata","loadedmetadata","loadstart","lostpointercapture","message", "mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup", "offline","online","pagehide","pageshow","paste","pause","play","playing", "pointercancel","pointerdown","pointerenter","pointerleave","pointermove", "pointerout","pointerover","pointerup","popstate","progress","ratechange", "rejectionhandled","reset","resize","scroll","seeked","seeking","select", "stalled","storage","submit","suspend","timeupdate","toggle","unhandledrejection", "unload","volumechange","waiting","wheel", ]) { if (!(("on" + _ev) in globalThis)) globalThis["on" + _ev] = null; } globalThis.Window = globalThis.Window || function Window() {}; Object.defineProperty(globalThis.Window, Symbol.hasInstance, { value(obj) { return obj === globalThis || (obj && obj.window === obj); }, configurable: true, }); const _iframeRegistry = []; function _registerIframe(iframeEl) { const idx = _iframeRegistry.length; _iframeRegistry.push(iframeEl); globalThis.length = _iframeRegistry.length; Object.defineProperty(globalThis, idx, { get() { return iframeEl._iframeWin || null; }, configurable: true, enumerable: false, }); } globalThis.navigator = { get userAgent() { return globalThis.__obscura_ua || "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"; }, get appVersion() { return this.userAgent.replace('Mozilla/', ''); }, language: "en-US", languages: ["en-US","en"], platform: "Linux x86_64", onLine: true, cookieEnabled: true, hardwareConcurrency: 8, maxTouchPoints: 0, vendor: "Google Inc.", product: "Gecko", productSub: "20030107", doNotTrack: null, deviceMemory: 8, connection: { effectiveType: "4g", rtt: 50, downlink: 10, saveData: false }, get webdriver() { return undefined; }, pdfViewerEnabled: true, get plugins() { const p = [ { name: "PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1 }, { name: "Chrome PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1 }, { name: "Chromium PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1 }, { name: "Microsoft Edge PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1 }, { name: "WebKit built-in PDF", filename: "internal-pdf-viewer", description: "Portable Document Format", length: 1 }, ]; p.item = (i) => p[i] || null; p.namedItem = (name) => p.find(x => x.name === name) || null; p.refresh = () => {}; return p; }, get mimeTypes() { const m = [ { type: "application/pdf", description: "Portable Document Format", suffixes: "pdf", enabledPlugin: null }, { type: "text/pdf", description: "Portable Document Format", suffixes: "pdf", enabledPlugin: null }, ]; m.item = (i) => m[i] || null; m.namedItem = (name) => m.find(x => x.type === name) || null; return m; }, userAgentData: { brands: [ {brand: "Google Chrome", version: "145"}, {brand: "Chromium", version: "145"}, {brand: "Not=A?Brand", version: "24"}, ], mobile: false, platform: "Linux", getHighEntropyValues(hints) { return Promise.resolve({ architecture: "x86", bitness: "64", brands: [{brand:"Google Chrome",version:"145"},{brand:"Chromium",version:"145"},{brand:"Not=A?Brand",version:"24"}], fullVersionList: [{brand:"Google Chrome",version:"145.0.0.0"},{brand:"Chromium",version:"145.0.0.0"},{brand:"Not=A?Brand",version:"24.0.0.0"}], mobile: false, model: "", platform: "Linux", platformVersion: "6.8.0", uaFullVersion: "145.0.0.0", }); }, toJSON() { return {brands:this.brands,mobile:this.mobile,platform:this.platform}; }, }, serviceWorker: { ready: Promise.resolve(), register(){return Promise.resolve();}, getRegistrations(){return Promise.resolve([]);}, controller: null }, mediaDevices: { enumerateDevices() { return Promise.resolve([ {deviceId:"default",kind:"audioinput",label:"",groupId:"default"}, {deviceId:"comms",kind:"audioinput",label:"",groupId:"comms"}, {deviceId:"default",kind:"audiooutput",label:"",groupId:"default"}, {deviceId:"",kind:"videoinput",label:"",groupId:""}, ]); }, getUserMedia() { return Promise.reject(new DOMException("NotAllowedError")); }, getDisplayMedia() { return Promise.reject(new DOMException("NotAllowedError")); }, addEventListener(){}, removeEventListener(){}, }, clipboard: { writeText(){return Promise.resolve();}, readText(){return Promise.resolve("");} }, permissions: { query(params){ if (params?.name === 'notifications') return Promise.resolve({state:"prompt",onchange:null}); return Promise.resolve({state:"granted"}); } }, getBattery() { return Promise.resolve({ charging: _fp('batteryCharging'), chargingTime: _fp('batteryCharging') ? 0 : Infinity, dischargingTime: _fp('batteryCharging') ? Infinity : Math.floor(3600 + _fpRand(250) * 7200), level: _fp('batteryLevel'), addEventListener(){} }); }, getGamepads() { return []; }, sendBeacon() { return true; }, javaEnabled() { return false; }, }; globalThis.chrome = { app: { isInstalled: false, InstallState: { DISABLED: "disabled", INSTALLED: "installed", NOT_INSTALLED: "not_installed" }, RunningState: { CANNOT_RUN: "cannot_run", READY_TO_RUN: "ready_to_run", RUNNING: "running" } }, runtime: { OnInstalledReason: {}, OnRestartRequiredReason: {}, PlatformArch: {}, PlatformNaclArch: {}, PlatformOs: {}, RequestUpdateCheckStatus: {}, connect() { return {}; }, sendMessage() {} }, csi() { return {}; }, loadTimes() { return {}; }, }; globalThis.Notification = class Notification { static permission = "default"; static requestPermission() { return Promise.resolve("default"); } constructor() {} }; globalThis.WebGLRenderingContext = class WebGLRenderingContext {}; globalThis.WebGL2RenderingContext = class WebGL2RenderingContext {}; globalThis.screen = { width:1920, height:1080, availWidth:1920, availHeight:1040, colorDepth:24, pixelDepth:24, availTop:0, availLeft:0, orientation:{type:"landscape-primary",angle:0,addEventListener(){},removeEventListener(){},dispatchEvent(){return true;}} }; globalThis.visualViewport = { width:1920, height:1000, offsetLeft:0, offsetTop:0, scale:1, addEventListener(){}, removeEventListener(){} }; globalThis.devicePixelRatio = 1; globalThis.innerWidth = 1920; globalThis.innerHeight = 1000; globalThis.outerWidth = 1920; globalThis.outerHeight = 1080; globalThis.scrollX = 0; globalThis.scrollY = 0; globalThis.pageXOffset = 0; globalThis.pageYOffset = 0; globalThis.__fetchInterceptEnabled = false; globalThis.__fetchInterceptCallback = null; // Set by CDP to handle paused requests function _base64ToUint8Array(b64) { const clean = String(b64 || '').replace(/[\r\n\s]/g, ''); if (!clean) return new Uint8Array(); const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const padding = clean.endsWith('==') ? 2 : (clean.endsWith('=') ? 1 : 0); const bytes = new Uint8Array((clean.length * 3 >> 2) - padding); let out = 0; for (let i = 0; i < clean.length; i += 4) { const a = alphabet.indexOf(clean[i]); const b = alphabet.indexOf(clean[i + 1]); const c = clean[i + 2] === '=' ? 0 : alphabet.indexOf(clean[i + 2]); const d = clean[i + 3] === '=' ? 0 : alphabet.indexOf(clean[i + 3]); const n = (a << 18) | (b << 12) | (c << 6) | d; if (out < bytes.length) bytes[out++] = (n >> 16) & 0xff; if (out < bytes.length) bytes[out++] = (n >> 8) & 0xff; if (out < bytes.length) bytes[out++] = n & 0xff; } return bytes; } function _bodyToUint8Array(body) { if (body == null) return new Uint8Array(); if (body instanceof Uint8Array) return body; if (body instanceof ArrayBuffer) return new Uint8Array(body); if (ArrayBuffer.isView(body)) return new Uint8Array(body.buffer, body.byteOffset, body.byteLength); return new TextEncoder().encode(String(body)); } function _arrayBufferFromBytes(bytes) { return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); } function _installWasmStreamingFallback() { if (typeof WebAssembly === 'undefined') return; if (WebAssembly.instantiateStreaming && WebAssembly.instantiateStreaming.__obscuraFallback) return; const nativeInstantiateStreaming = WebAssembly.instantiateStreaming; const fallback = async function instantiateStreaming(source, imports) { const response = await source; if (response && typeof response.arrayBuffer === 'function') { return WebAssembly.instantiate(await response.arrayBuffer(), imports); } if (typeof nativeInstantiateStreaming === 'function') { return nativeInstantiateStreaming.call(WebAssembly, response, imports); } return WebAssembly.instantiate(response, imports); }; fallback.__obscuraFallback = true; WebAssembly.instantiateStreaming = fallback; } _installWasmStreamingFallback(); globalThis.fetch = async (input, init = {}) => { let url = typeof input === "string" ? input : (input instanceof Request ? input.url : ((typeof URL === 'function' && input instanceof URL) ? input.href : (input?.url || input?.href || String(input || "")))); if (url && !url.includes('://')) { try { const base = _domParse("document_url") || "about:blank"; url = new URL(url, base).href; } catch(e) { /* keep as-is if URL resolution fails */ } } const method = init.method || (input instanceof Request ? input.method : "GET"); const hdrs = JSON.stringify(init.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : init.headers || {}); const body = init.body ? String(init.body) : ""; const fetchMode = init.mode || (input instanceof Request ? input.mode : "cors"); const pageOrigin = (function() { try { const u = new URL(_domParse("document_url") || "about:blank"); return u.origin; } catch(e) { return ""; } })(); const raw = await Deno.core.ops.op_fetch_url(url, method, hdrs, body, pageOrigin, fetchMode); const parsed = JSON.parse(raw); if (parsed.blocked) { const err = new TypeError('net::ERR_FAILED'); err.name = 'AbortError'; err.__aborted = true; throw err; } if (parsed.corsBlocked) { throw new TypeError('Failed to fetch: ' + (parsed.corsError || 'CORS error')); } const respType = parsed.status === 0 ? "opaque" : (fetchMode === "no-cors" ? "opaque" : "basic"); const responseBody = parsed.bodyBase64 ? _base64ToUint8Array(parsed.bodyBase64) : (parsed.body || ""); return new Response(responseBody, { status: parsed.status, statusText: "", headers: parsed.headers || {}, type: respType, url: parsed.url || url, redirected: false, }); }; if (typeof Headers === "undefined") { globalThis.Headers = class Headers { constructor(init={}) { this._h={}; if(init) { if(init instanceof Headers) { init.forEach((v,k)=>{this._h[k]=v;}); } else if(typeof init==="object") { for(const[k,v]of Object.entries(init)) this._h[k.toLowerCase()]=String(v); } } } get(n) { return this._h[n.toLowerCase()]??null; } set(n,v) { this._h[n.toLowerCase()]=String(v); } has(n) { return n.toLowerCase() in this._h; } delete(n) { delete this._h[n.toLowerCase()]; } append(n,v) { this._h[n.toLowerCase()]=String(v); } forEach(cb) { for(const[k,v] of Object.entries(this._h)) cb(v,k,this); } entries() { return Object.entries(this._h)[Symbol.iterator](); } keys() { return Object.keys(this._h)[Symbol.iterator](); } values() { return Object.values(this._h)[Symbol.iterator](); } [Symbol.iterator]() { return this.entries(); } }; } globalThis.XMLHttpRequest = class XMLHttpRequest { static UNSENT = 0; static OPENED = 1; static HEADERS_RECEIVED = 2; static LOADING = 3; static DONE = 4; UNSENT = 0; OPENED = 1; HEADERS_RECEIVED = 2; LOADING = 3; DONE = 4; constructor() { this.readyState = 0; this.status = 0; this.statusText = ""; this.responseText = ""; this.responseXML = null; this.responseURL = ""; this.responseType = ""; this.response = null; this.timeout = 0; this.withCredentials = false; this.upload = { addEventListener(){}, removeEventListener(){} }; this._method = "GET"; this._url = ""; this._headers = {}; this._responseHeaders = {}; this._aborted = false; this._listeners = {}; this.onreadystatechange = null; this.onload = null; this.onerror = null; this.onabort = null; this.onprogress = null; this.ontimeout = null; this.onloadstart = null; this.onloadend = null; } open(method, url, async_) { this._method = method; this._url = url; this._headers = {}; this._responseHeaders = {}; this._aborted = false; this.status = 0; this.statusText = ""; this.responseText = ""; this.response = null; this._setReadyState(1); } setRequestHeader(name, value) { this._headers[name] = value; } getResponseHeader(name) { const lower = name.toLowerCase(); for (const [k, v] of Object.entries(this._responseHeaders)) { if (k.toLowerCase() === lower) return v; } return null; } getAllResponseHeaders() { return Object.entries(this._responseHeaders) .map(([k, v]) => k + ': ' + v) .join('\r\n'); } overrideMimeType(mime) { this._overrideMime = mime; } send(body) { if (this.readyState !== 1) return; if (this._aborted) return; const xhr = this; this._fireEvent('loadstart'); let url = this._url; if (url && !url.includes('://')) { try { const base = _domParse("document_url") || "about:blank"; url = new URL(url, base).href; } catch(e) {} } fetch(url, { method: this._method, headers: this._headers, body: body || undefined, mode: 'cors', }).then(async (resp) => { if (xhr._aborted) return; xhr.status = resp.status; xhr.statusText = resp.statusText || ''; xhr.responseURL = resp.url || url; if (resp.headers) { resp.headers.forEach((v, k) => { xhr._responseHeaders[k] = v; }); } xhr._setReadyState(2); // HEADERS_RECEIVED const text = await resp.text(); if (xhr._aborted) return; xhr.responseText = text; xhr._setReadyState(3); // LOADING switch (xhr.responseType) { case 'json': try { xhr.response = JSON.parse(text); } catch(e) { xhr.response = null; } break; case 'text': case '': xhr.response = text; break; case 'arraybuffer': xhr.response = new TextEncoder().encode(text).buffer; break; case 'blob': xhr.response = new Blob([text]); break; case 'document': xhr.response = text; // simplified break; default: xhr.response = text; } xhr._setReadyState(4); // DONE xhr._fireEvent('load'); xhr._fireEvent('loadend'); }).catch((err) => { if (xhr._aborted) return; xhr.status = 0; xhr.readyState = 4; xhr._fireEvent('readystatechange'); if (err && err.__aborted) { xhr._aborted = true; xhr._fireEvent('abort'); xhr._fireEvent('loadend'); if (xhr.onabort) xhr.onabort(err); } else { xhr._fireEvent('error'); xhr._fireEvent('loadend'); if (xhr.onerror) xhr.onerror(err); } }); } abort() { this._aborted = true; if (this.readyState > 0 && this.readyState < 4) { this._setReadyState(4); this._fireEvent('abort'); this._fireEvent('loadend'); } this.readyState = 0; } addEventListener(type, handler) { if (!this._listeners[type]) this._listeners[type] = []; this._listeners[type].push(handler); } removeEventListener(type, handler) { if (this._listeners[type]) { this._listeners[type] = this._listeners[type].filter(h => h !== handler); } } _setReadyState(state) { this.readyState = state; this._fireEvent('readystatechange'); if (this.onreadystatechange) { try { this.onreadystatechange(); } catch(e) {} } } _fireEvent(type) { const event = { type, target: this, currentTarget: this, bubbles: false }; const handlers = this._listeners[type] || []; for (const h of handlers) { try { h.call(this, event); } catch(e) {} } const prop = 'on' + type; if (type !== 'readystatechange' && typeof this[prop] === 'function') { try { this[prop](event); } catch(e) {} } } }; _markNative(XMLHttpRequest); _markNative(XMLHttpRequest.prototype.open); _markNative(XMLHttpRequest.prototype.send); _markNative(XMLHttpRequest.prototype.abort); _markNative(XMLHttpRequest.prototype.setRequestHeader); _markNative(XMLHttpRequest.prototype.getResponseHeader); _markNative(XMLHttpRequest.prototype.getAllResponseHeaders); if (typeof URL === 'undefined' || !URL.prototype) { globalThis.URL = class URL { constructor(url, base) { // Per WHATWG URL spec, both arguments are stringified — callers // routinely pass `window.location` (Location object) or a URL // instance as `base`. Coerce explicitly so the regex .match() calls // below don't blow up on non-strings. url = String(url); if (base !== undefined && base !== null) base = String(base); let full = url; if (base && !url.includes('://')) { var bm = base.match(/^(https?:\/\/[^\/\?#]+)(\/[^?#]*)?/); if (bm) { var bOrigin = bm[1]; var bPath = bm[2] || '/'; if (url.startsWith('/')) { full = bOrigin + url; } else if (url.startsWith('?') || url.startsWith('#')) { full = bOrigin + bPath + url; } else { var dir = bPath.substring(0, bPath.lastIndexOf('/') + 1); full = bOrigin + dir + url; } } } const m = full.match(/^(https?):\/\/([^\/\?#]+)(\/[^?#]*)?(\?[^#]*)?(#.*)?$/); if (m) { this.protocol = m[1] + ':'; this.host = m[2]; this.hostname = m[2].split(':')[0]; this.port = m[2].includes(':') ? m[2].split(':')[1] : ''; this.pathname = m[3] || '/'; this.search = m[4] || ''; this.hash = m[5] || ''; } else { this.protocol = ''; this.host = ''; this.hostname = ''; this.port = ''; this.pathname = full; this.search = ''; this.hash = ''; } this.href = full; this.origin = this.protocol + '//' + this.host; this.searchParams = new URLSearchParams(this.search); } toString() { return this.href; } toJSON() { return this.href; } static createObjectURL() { return 'blob:null/fake-' + Math.random().toString(36).slice(2); } static revokeObjectURL() {} }; } globalThis.requestIdleCallback = globalThis.requestIdleCallback || function requestIdleCallback(cb, opts) { const start = Date.now(); return setTimeout(() => { cb({ didTimeout: false, timeRemaining() { return Math.max(0, 50 - (Date.now() - start)); }, }); }, 1); }; globalThis.cancelIdleCallback = globalThis.cancelIdleCallback || function cancelIdleCallback(id) { clearTimeout(id); }; _markNative(globalThis.requestIdleCallback); _markNative(globalThis.cancelIdleCallback); if (typeof Request === 'undefined') { globalThis.Request = class Request { constructor(input, init = {}) { if (typeof input === 'string') { this.url = input; } else if (input instanceof Request) { this.url = input.url; init = { ...input, ...init }; } else if (typeof URL === 'function' && input instanceof URL) { this.url = input.href; } else { this.url = input?.url || input?.href || String(input); } this.method = (init.method || 'GET').toUpperCase(); this.headers = new Headers(init.headers); this.body = init.body || null; this.mode = init.mode || 'cors'; this.credentials = init.credentials || 'same-origin'; this.redirect = init.redirect || 'follow'; this.referrer = init.referrer || ''; this.signal = init.signal || { aborted: false, addEventListener(){}, removeEventListener(){} }; this.cache = init.cache || 'default'; } clone() { return new Request(this.url, { method: this.method, headers: this.headers, body: this.body }); } async text() { return this.body ? String(this.body) : ''; } async json() { return JSON.parse(await this.text()); } async arrayBuffer() { return new TextEncoder().encode(await this.text()).buffer; } }; } if (typeof Response === 'undefined') { globalThis.Response = class Response { constructor(body, init = {}) { this._bodyBytes = _bodyToUint8Array(body); this.status = init.status || 200; this.statusText = init.statusText || ''; this.ok = this.status >= 200 && this.status < 300; this.headers = new Headers(init.headers); this.type = init.type || 'basic'; this.url = init.url || ''; this.redirected = !!init.redirected; } async text() { return new TextDecoder().decode(this._bodyBytes); } async json() { return JSON.parse(await this.text()); } async arrayBuffer() { return _arrayBufferFromBytes(this._bodyBytes); } async blob() { return new Blob([this._bodyBytes]); } clone() { return new Response(this._bodyBytes, { status: this.status, statusText: this.statusText, headers: this.headers, type: this.type, url: this.url, redirected: this.redirected }); } static error() { return new Response(null, { status: 0 }); } static redirect(url, status) { return new Response(null, { status: status || 302, headers: { Location: url } }); } static json(data, init) { return new Response(JSON.stringify(data), { ...init, headers: { 'content-type': 'application/json', ...(init?.headers || {}) } }); } }; } if (!Element.prototype.replaceWith) { Element.prototype.replaceWith = function(...nodes) { const parent = this.parentNode; if (!parent) return; for (const n of nodes) { if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), this); else parent.insertBefore(n, this); } parent.removeChild(this); }; _markNative(Element.prototype.replaceWith); } if (!Element.prototype.before) { Element.prototype.before = function(...nodes) { const parent = this.parentNode; if (!parent) return; for (const n of nodes) { if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), this); else parent.insertBefore(n, this); } }; _markNative(Element.prototype.before); } if (!Element.prototype.after) { Element.prototype.after = function(...nodes) { const parent = this.parentNode; if (!parent) return; const ref = this.nextSibling; for (const n of nodes) { if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), ref); else parent.insertBefore(n, ref); } }; _markNative(Element.prototype.after); } // ChildNode mixin: also mix before/after/replaceWith/remove into // CharacterData.prototype (covers Text, Comment, ProcessingInstruction). // These are the same implementations as Element.prototype — frameworks // (Svelte 5, Vue, Lit) anchor on Comment/Text nodes and call these methods. if (!CharacterData.prototype.before) CharacterData.prototype.before = Element.prototype.before; if (!CharacterData.prototype.after) CharacterData.prototype.after = Element.prototype.after; if (!CharacterData.prototype.replaceWith) CharacterData.prototype.replaceWith = Element.prototype.replaceWith; if (!CharacterData.prototype.remove) CharacterData.prototype.remove = Element.prototype.remove; if (!('isConnected' in Node.prototype)) { Object.defineProperty(Node.prototype, 'isConnected', { get() { let node = this; while (node) { if (node.nodeType === 9) return true; // Document node node = node.parentNode; } return false; } }); } globalThis.ResizeObserver = class ResizeObserver { constructor(callback) { this._callback = callback; this._targets = new Set(); this._connected = true; this._fireCount = 0; } _fireFor(targets) { if (!this._connected || !targets.length) return; const records = targets.map(target => { const r = target.getBoundingClientRect ? target.getBoundingClientRect() : { x: 0, y: 0, width: 100, height: 20 }; return { target, contentRect: { x: r.x || 0, y: r.y || 0, width: r.width || 100, height: r.height || 20, top: r.top || 0, left: r.left || 0, bottom: r.bottom || 20, right: r.right || 100 }, borderBoxSize: [{ blockSize: r.height || 20, inlineSize: r.width || 100 }], contentBoxSize: [{ blockSize: r.height || 20, inlineSize: r.width || 100 }], devicePixelContentBoxSize: [{ blockSize: r.height || 20, inlineSize: r.width || 100 }], }; }); try { this._callback(records, this); } catch (e) { /* RO callbacks must not propagate */ } } observe(el) { if (!el || !this._connected) return; if (this._targets.has(el)) return; this._targets.add(el); Promise.resolve().then(() => this._fireFor([el])); [200, 800].forEach(delay => { setTimeout(() => { if (this._connected && this._targets.has(el) && this._fireCount < 16) { this._fireCount++; this._fireFor([el]); } }, delay); }); } unobserve(el) { this._targets.delete(el); } disconnect() { this._connected = false; this._targets.clear(); } }; if (typeof TextEncoder === 'undefined') { globalThis.TextEncoder = class TextEncoder { get encoding() { return 'utf-8'; } encode(str) { str = String(str); const buf = []; for (let i = 0; i < str.length; i++) { let c = str.charCodeAt(i); if (c < 0x80) buf.push(c); else if (c < 0x800) { buf.push(0xC0|(c>>6), 0x80|(c&0x3F)); } else if (c < 0xD800 || c >= 0xE000) { buf.push(0xE0|(c>>12), 0x80|((c>>6)&0x3F), 0x80|(c&0x3F)); } else { c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF)); buf.push(0xF0|(c>>18), 0x80|((c>>12)&0x3F), 0x80|((c>>6)&0x3F), 0x80|(c&0x3F)); } } return new Uint8Array(buf); } encodeInto(str, dest) { const enc = this.encode(str); dest.set(enc.slice(0, dest.length)); return { read: str.length, written: Math.min(enc.length, dest.length) }; } }; } if (typeof TextDecoder === 'undefined') { globalThis.TextDecoder = class TextDecoder { constructor(label) { this.encoding = label || 'utf-8'; } decode(buf) { if (!buf) return ''; const bytes = ArrayBuffer.isView(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : new Uint8Array(buf); let str = '', i = 0; while (i < bytes.length) { let c = bytes[i++]; if (c < 0x80) str += String.fromCharCode(c); else if (c < 0xE0) str += String.fromCharCode(((c&0x1F)<<6)|(bytes[i++]&0x3F)); else if (c < 0xF0) { const b1=bytes[i++], b2=bytes[i++]; str += String.fromCharCode(((c&0x0F)<<12)|((b1&0x3F)<<6)|(b2&0x3F)); } else { const b1=bytes[i++], b2=bytes[i++], b3=bytes[i++]; const cp=((c&0x07)<<18)|((b1&0x3F)<<12)|((b2&0x3F)<<6)|(b3&0x3F); if(cp>0xFFFF){const s=cp-0x10000;str+=String.fromCharCode(0xD800+(s>>10),0xDC00+(s&0x3FF));}else str+=String.fromCharCode(cp); } } return str; } }; } globalThis.matchMedia = _markNative(function matchMedia(q) { return { matches: false, media: q, addListener(){}, removeListener(){}, addEventListener(){}, removeEventListener(){}, dispatchEvent(){return true;} }; }); globalThis.getComputedStyle = (el) => { if (!el) el = document.body || {}; const style = el?.style || el?._style || new CSSStyleDeclaration(); // React virtualization libraries (react-window, tanstack-virtual, // react-virtuoso) all compute container dimensions via getComputedStyle. // The defaults table previously returned `auto` for width/height and // `'static'` for position, which made every list render 0 items. Pulling // width/height from the synthesized bounding rect makes those libraries // actually render content. const dimensionFor = (name) => { try { const r = el.getBoundingClientRect && el.getBoundingClientRect(); if (!r) return null; switch (name) { case 'width': case 'inline-size': return r.width != null ? `${r.width}px` : null; case 'height': case 'block-size': return r.height != null ? `${r.height}px` : null; case 'left': return r.left != null ? `${r.left}px` : null; case 'top': return r.top != null ? `${r.top}px` : null; case 'right': return r.right != null ? `${r.right}px` : null; case 'bottom': return r.bottom != null ? `${r.bottom}px` : null; case 'client-width': case 'offset-width': return r.width != null ? `${r.width}px` : null; case 'client-height': case 'offset-height': return r.height != null ? `${r.height}px` : null; } } catch (e) {} return null; }; const defaultsKebab = { display: 'block', visibility: 'visible', opacity: '1', position: 'static', overflow: 'visible', transform: 'none', 'transform-origin': '0px 0px', transition: 'none', animation: 'none', float: 'none', clear: 'none', margin: '0px', padding: '0px', 'margin-top': '0px', 'margin-right': '0px', 'margin-bottom': '0px', 'margin-left': '0px', 'padding-top': '0px', 'padding-right': '0px', 'padding-bottom': '0px', 'padding-left': '0px', 'font-size': '16px', 'line-height': 'normal', 'font-weight': '400', 'font-family': 'Times', color: 'rgb(0, 0, 0)', 'background-color': 'rgba(0, 0, 0, 0)', 'border-width': '0px', 'border-style': 'none', 'border-color': 'rgb(0, 0, 0)', 'border-top-width': '0px', 'border-right-width': '0px', 'border-bottom-width': '0px', 'border-left-width': '0px', 'border-radius': '0px', 'z-index': 'auto', 'pointer-events': 'auto', 'box-sizing': 'content-box', cursor: 'auto', 'white-space': 'normal', 'text-align': 'start', 'flex-direction': 'row', 'flex-wrap': 'nowrap', 'align-items': 'normal', 'justify-content': 'normal', gap: 'normal', 'grid-template-columns': 'none', 'grid-template-rows': 'none', 'will-change': 'auto', 'backface-visibility': 'visible', }; const lookup = (rawProp) => { if (typeof rawProp !== 'string') return ''; // Inline value first. const inlineVal = target.getPropertyValue ? target.getPropertyValue(rawProp) : ''; if (inlineVal) return inlineVal; const kebab = rawProp.replace(/([A-Z])/g, '-$1').toLowerCase(); const dim = dimensionFor(kebab); if (dim != null) return dim; if (defaultsKebab[rawProp]) return defaultsKebab[rawProp]; if (defaultsKebab[kebab]) return defaultsKebab[kebab]; return ''; }; const target = style; return new Proxy(style, { get(_, prop) { if (prop === Symbol.toPrimitive || prop === Symbol.toStringTag) return undefined; if (prop in target) return target[prop]; if (prop === 'getPropertyValue') return (name) => lookup(name); if (prop === 'getPropertyPriority') return () => ''; if (prop === 'item') return (i) => ''; if (prop === 'length') return 0; if (prop === 'cssText') return ''; if (prop === 'parentRule') return null; if (typeof prop === 'string') return lookup(prop); return undefined; }, }); }; globalThis.getSelection = _markNative(function getSelection() { return { rangeCount: 0, anchorNode: null, anchorOffset: 0, focusNode: null, focusOffset: 0, isCollapsed: true, type: 'None', removeAllRanges() { this.rangeCount = 0; }, addRange(range) { this.rangeCount = 1; this._range = range; }, getRangeAt(i) { return this._range || null; }, collapse(node, offset) { this.anchorNode = node; this.anchorOffset = offset || 0; this.isCollapsed = true; }, extend(node, offset) { this.focusNode = node; this.focusOffset = offset || 0; }, selectAllChildren(node) {}, deleteFromDocument() {}, containsNode(node) { return false; }, toString() { return ''; }, }; }); globalThis.CSSStyleSheet = class CSSStyleSheet { constructor(options) { this.cssRules = []; this.ownerRule = null; this.disabled = false; this._rules = []; } insertRule(rule, index) { const idx = index ?? this._rules.length; this._rules.splice(idx, 0, { cssText: rule, type: 1 }); this.cssRules = this._rules; return idx; } deleteRule(index) { this._rules.splice(index, 1); this.cssRules = this._rules; } addRule(selector, style, index) { return this.insertRule(selector + '{' + style + '}', index); } removeRule(index) { this.deleteRule(index); } replace(text) { this._rules = [{ cssText: text, type: 1 }]; this.cssRules = this._rules; return Promise.resolve(this); } replaceSync(text) { this._rules = [{ cssText: text, type: 1 }]; this.cssRules = this._rules; } }; Object.defineProperty(Document.prototype, 'adoptedStyleSheets', { get() { return this._adoptedStyleSheets || []; }, set(sheets) { this._adoptedStyleSheets = sheets; }, }); globalThis.__mutationObservers = []; globalThis.MutationObserver = class MutationObserver { constructor(callback) { this._callback = callback; this._targets = []; this._records = []; } observe(target, options) { this._targets.push({ target, options: options || {} }); globalThis.__mutationObservers.push(this); } disconnect() { this._targets = []; const idx = globalThis.__mutationObservers.indexOf(this); if (idx >= 0) globalThis.__mutationObservers.splice(idx, 1); } takeRecords() { const r = this._records.slice(); this._records = []; return r; } _notify(records) { this._records.push(...records); Promise.resolve().then(() => { if (this._records.length > 0) { const batch = this._records.splice(0); try { this._callback(batch, this); } catch(e) { /* observer errors shouldn't propagate */ } } }); } }; globalThis.__notifyMutation = function(type, target_nid, addedNodes, removedNodes, attributeName, oldValue) { if (!globalThis.__mutationObservers.length) return; // Use `_wrap` (the canonical node-id → wrapper resolver) instead of a // direct cache poke. The previous code referenced `globalThis._cache`, // but `_cache` is a module-local Map — the lookup always returned // undefined, so the function silently bailed every time. Result: no // MutationObserver fired in obscura, ever, despite the call sites being // wired up at appendChild / setAttribute. _wrap also lazily creates a // wrapper for nodes that didn't have one yet (e.g. children parsed from // `set innerHTML`), which we need for record.target/added/removed. const target = _wrap(target_nid); if (!target) return; const record = { type: type, // 'childList', 'attributes', 'characterData' target: target, addedNodes: (addedNodes || []).map(nid => _wrap(nid)).filter(Boolean), removedNodes: (removedNodes || []).map(nid => _wrap(nid)).filter(Boolean), attributeName: attributeName || null, oldValue: oldValue ?? null, previousSibling: null, nextSibling: null, }; // Walk target → ancestors so a subtree-mode observer rooted at any // ancestor matches. The previous implementation just checked that // `target.contains` and `target.closest` were defined (always true on // any Element), so subtree=true silently behaved like subtree=false and // every nested mutation missed its subscriber. for (const obs of globalThis.__mutationObservers) { let matched = false; for (const t of obs._targets) { const root = t.target; if (!root) continue; // Filter by type per the observer options. Default behaviour matches // real MutationObserver: attribute mutations need options.attributes, // characterData mutations need options.characterData, childList // needs options.childList. const wantsType = (type === 'attributes' && t.options.attributes) || (type === 'characterData' && t.options.characterData) || (type === 'childList' && t.options.childList); if (!wantsType) continue; if (root._nid === target_nid) { matched = true; break; } if (t.options.subtree) { // Walk parents until we hit the observed root or run off the tree. let cur = target.parentNode; while (cur) { if (cur._nid === root._nid) { matched = true; break; } cur = cur.parentNode; } if (matched) break; } } if (matched) obs._notify([record]); } }; globalThis.ShadowRoot = class ShadowRoot {}; globalThis.customElements = { _registry: new Map(), define(name, cls, opts) { this._registry.set(name, cls); }, get(name) { return this._registry.get(name); }, whenDefined(name) { return Promise.resolve(this._registry.get(name)); }, upgrade() {}, }; globalThis.NodeFilter = { SHOW_ELEMENT: 1, SHOW_TEXT: 4, SHOW_ALL: 0xFFFFFFFF }; // ResizeObserver is defined earlier with real per-target firing; the stub // that previously lived here was a no-op that clobbered the real class. // // IntersectionObserver: without a layout engine we can't compute real // intersection geometry, so every observed target is treated as fully // in-viewport (`isIntersecting: true`, `intersectionRatio: 1`). Real // libraries lean on this in three patterns we must support: // // 1. Lazy load: observe(img) -> first intersection -> load src -> unobserve. // One fire is enough — covered by the initial microtask fire. // 2. Infinite scroll: observe(sentinel) -> on intersection load more -> // new sentinel mounts -> fire again. Needs re-fires as DOM grows. // 3. Reveal-on-scroll animations: observe(card) -> isIntersecting flips // true once and an animation runs. One fire is enough. // // To cover (2) without spinning forever, we burst-fire at an exponential // backoff schedule and ALSO re-fire whenever the DOM mutates (a strong // signal that the page just rendered something new). Per-observer total // fire cap stops us from looping on a never-disconnected observer. globalThis.__intersectionObservers = []; globalThis.IntersectionObserver = class IntersectionObserver { constructor(callback, options) { this._callback = callback; this._options = options || {}; this._targets = new Set(); this._connected = true; this._fireCount = 0; globalThis.__intersectionObservers.push(this); } _fireFor(targets) { if (!this._connected || !targets.length || this._fireCount >= 256) return; this._fireCount++; const records = targets.map(target => ({ target, isIntersecting: true, intersectionRatio: 1, boundingClientRect: target.getBoundingClientRect ? target.getBoundingClientRect() : { x: 0, y: 0, width: 100, height: 20, top: 0, left: 0, right: 100, bottom: 20 }, intersectionRect: target.getBoundingClientRect ? target.getBoundingClientRect() : { x: 0, y: 0, width: 100, height: 20, top: 0, left: 0, right: 100, bottom: 20 }, rootBounds: { x: 0, y: 0, width: 1280, height: 720, top: 0, left: 0, right: 1280, bottom: 720 }, time: Date.now(), })); try { this._callback(records, this); } catch (e) { /* IO callbacks must not propagate */ } } observe(el) { if (!el || !this._connected) return; if (this._targets.has(el)) return; this._targets.add(el); Promise.resolve().then(() => this._fireFor([el])); // Exponential burst to cover infinite-scroll sentinels that "re-arm" // after content lands. Without a real scroll/layout signal, we fake the // re-fire schedule. Beyond ~10s the page has usually settled. [120, 500, 1500, 3500, 7000].forEach(delay => { setTimeout(() => { if (this._connected && this._targets.has(el)) this._fireFor([el]); }, delay); }); } unobserve(el) { this._targets.delete(el); } disconnect() { this._connected = false; this._targets.clear(); const idx = globalThis.__intersectionObservers.indexOf(this); if (idx >= 0) globalThis.__intersectionObservers.splice(idx, 1); } takeRecords() { return []; } get root() { return this._options.root || null; } get rootMargin() { return this._options.rootMargin || "0px 0px 0px 0px"; } get thresholds() { const t = this._options.threshold; if (t == null) return [0]; return Array.isArray(t) ? t.slice() : [t]; } }; // When the DOM mutates (e.g. infinite scroll loads a batch of items), re-fire // every active IntersectionObserver so libraries observing dynamic content // see a fresh isIntersecting=true event. Uses the same per-observer fire cap // to prevent runaway loops if the page is mutating in a tight cycle. (function() { const reFire = () => { for (const obs of globalThis.__intersectionObservers) { if (!obs._connected) continue; const ts = [...obs._targets]; if (ts.length) obs._fireFor(ts); } }; // Lazy-attach a single MutationObserver on document.body once the page is // ready, debounced via a microtask so a flurry of mutations only triggers // one IO sweep. let pending = false; const wireUp = () => { if (!globalThis.document?.body) return; const mo = new MutationObserver(() => { if (pending) return; pending = true; Promise.resolve().then(() => { pending = false; reFire(); }); }); try { mo.observe(globalThis.document.body, {childList: true, subtree: true}); } catch {} }; if (globalThis.document?.body) wireUp(); else Promise.resolve().then(wireUp); })(); globalThis.IntersectionObserverEntry = class IntersectionObserverEntry {}; globalThis.PerformanceObserver = class { constructor(){} observe(){} disconnect(){} }; globalThis.Event = class Event { constructor(t,o={}) { this.type=t;this.bubbles=!!o.bubbles;this.cancelable=!!o.cancelable;this.composed=!!o.composed;this.defaultPrevented=false;this.target=null;this.currentTarget=null;this.eventPhase=0;this.timeStamp=Date.now();this._propagationStopped=false;this._immediatePropagationStopped=false; } get isTrusted() { return true; } preventDefault() { if (this.cancelable) this.defaultPrevented=true; } stopPropagation(){ this._propagationStopped=true; } stopImmediatePropagation(){ this._propagationStopped=true; this._immediatePropagationStopped=true; } initEvent(type,bubbles,cancelable) { this.type=type;this.bubbles=!!bubbles;this.cancelable=!!cancelable;this.defaultPrevented=false;this._propagationStopped=false;this._immediatePropagationStopped=false; } }; globalThis.CustomEvent = class extends Event { constructor(t,o={}) { super(t,o);this.detail=o.detail; } // Legacy DOM Level 2 init; some libraries (Starbucks China bundle, older // analytics shims) still call createEvent('CustomEvent') + initCustomEvent // instead of new CustomEvent(...). See issue #41. initCustomEvent(type,bubbles,cancelable,detail) { this.type = type; this.bubbles = !!bubbles; this.cancelable = !!cancelable; this.detail = detail; } }; globalThis.MouseEvent = class extends Event { constructor(t,o={}) { super(t,o);this.clientX=o.clientX||0;this.clientY=o.clientY||0; } }; globalThis.KeyboardEvent = class extends Event { constructor(t,o={}) { super(t,o);this.key=o.key||"";this.code=o.code||""; } }; globalThis.FocusEvent = class extends Event {}; globalThis.InputEvent = class extends Event { constructor(t,o={}) { super(t,o);this.data=o.data||null;this.inputType=o.inputType||""; } }; globalThis.ErrorEvent = class extends Event { constructor(t,o={}) { super(t,o);this.message=o.message||"";this.error=o.error||null; } }; globalThis.PointerEvent = class extends Event { constructor(t,o={}) { super(t,o); } }; globalThis.AnimationEvent = class extends Event {}; globalThis.TransitionEvent = class extends Event {}; globalThis.UIEvent = class extends Event {}; globalThis.WheelEvent = class extends Event {}; globalThis.PopStateEvent = class extends Event { constructor(type, init) { super(type, init || {}); // Real PopStateEvent exposes `state` from the entry being navigated to. // The earlier stub inherited Event but never stored state, so // `popstate.state` was always undefined and SPA routers reading // `event.state` to restore route info would mis-render. this.state = init && 'state' in init ? init.state : null; } }; globalThis.HashChangeEvent = class extends Event {}; globalThis.MessageEvent = class extends Event { constructor(t,o={}) { super(t,o);this.data=o.data; } }; globalThis.ClipboardEvent = class extends Event {}; globalThis.SubmitEvent = class extends Event {}; globalThis.AbortController = class AbortController { constructor(){this.signal={aborted:false,addEventListener(){},removeEventListener(){},onabort:null};} abort(){this.signal.aborted=true;} }; globalThis.AbortSignal = { timeout(ms){return {aborted:false,addEventListener(){},removeEventListener(){}}; } }; if (typeof Blob === "undefined") globalThis.Blob = class Blob { constructor(parts=[],opts={}){this._data=parts.join("");this.size=this._data.length;this.type=opts.type||"";} async text(){return this._data;} }; if (typeof File === "undefined") globalThis.File = class extends Blob { constructor(parts,name,opts){super(parts,opts);this.name=name;} }; if (typeof FormData === "undefined") globalThis.FormData = class FormData { constructor(){this._d=[];} append(k,v){this._d.push([k,v]);} get(k){const e=this._d.find(([a])=>a===k);return e?e[1]:null;} getAll(k){return this._d.filter(([a])=>a===k).map(([,v])=>v);} has(k){return this._d.some(([a])=>a===k);} entries(){return this._d[Symbol.iterator]();} forEach(cb){this._d.forEach(([k,v])=>cb(v,k));} }; if (typeof URLSearchParams === "undefined") globalThis.URLSearchParams = class { constructor(init=""){ this._p=[]; if(typeof init==="string"){ init.replace(/^\?/,"").split("&").forEach(p=>{const[k,...v]=p.split("=");if(k)this.append(decodeURIComponent(k),decodeURIComponent(v.join("=")));}); } else if (init && typeof init[Symbol.iterator] === 'function') { for (const pair of init) if (pair && pair.length >= 2) this.append(pair[0], pair[1]); } else if (init && typeof init === 'object') { Object.keys(init).forEach(k => this.append(k, init[k])); } } append(k,v){this._p.push([String(k),String(v)]);} get(k){const p=this._p.find(([key])=>key===String(k)); return p?p[1]:null;} set(k,v){this.delete(k); this.append(k,v);} delete(k){k=String(k); this._p=this._p.filter(([key])=>key!==k);} has(k){k=String(k); return this._p.some(([key])=>key===k);} toString(){return this._p.map(([k,v])=>`${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");} forEach(cb){this._p.forEach(([k,v])=>cb(v,k,this));} }; // Real-enough DOMParser. The previous one-liner returned `globalThis.document`, // so anything that did `new DOMParser().parseFromString(s, 'text/html')` and // then read `.body.innerHTML` mutated the LIVE page (jQuery 3.x's selector // feature-detect writes `
` and wiped real bodies). We parse the // input into a detached `` element and wrap it so the common Document // API surface (body / head / documentElement / querySelector* / getElementById / // getElementsByTagName / getElementsByClassName / title / cloneNode) works. globalThis.DOMParser = class DOMParser { parseFromString(source, mimeType) { const html = String(source ?? ""); const isXml = typeof mimeType === "string" && /xml/i.test(mimeType); const root = document.createElement("html"); // innerHTML parses children via html5ever fragment-parsing rules. Most // HTML inputs start with `` / `` / `` etc.; the // fragment parser strips the outer `` and emits its head+body // children, which is what callers want. try { root.innerHTML = html; } catch (e) { /* leave empty on parse error */ } // Helper: depth-first walk to find an element by predicate. const walk = (node, pred) => { if (!node) return null; if (node.nodeType === 1 && pred(node)) return node; const children = node.children || []; for (let i = 0; i < children.length; i++) { const r = walk(children[i], pred); if (r) return r; } return null; }; const findByTagName = (name) => walk(root, n => n.tagName === name); const docNode = { _root: root, nodeName: "#document", nodeType: 9, contentType: isXml ? (mimeType || "application/xml") : "text/html", get documentElement() { return root; }, get body() { return findByTagName("BODY"); }, get head() { return findByTagName("HEAD"); }, get title() { const t = findByTagName("TITLE"); return t ? (t.textContent || "") : ""; }, get firstChild() { return root; }, get lastChild() { return root; }, get children() { return [root]; }, get childNodes() { return [root]; }, querySelector(s) { return root.querySelector(s); }, querySelectorAll(s) { return root.querySelectorAll(s); }, getElementById(id) { return walk(root, n => n.getAttribute && n.getAttribute("id") === id); }, getElementsByTagName(t) { return root.querySelectorAll(t); }, getElementsByClassName(c) { return root.querySelectorAll("." + c); }, getElementsByName(n) { return root.querySelectorAll(`[name="${n}"]`); }, createElement: (t) => document.createElement(t), createElementNS: (ns, t) => document.createElement(t), createTextNode: (t) => document.createTextNode(t), createComment: (t) => document.createComment(t), createDocumentFragment: () => document.createDocumentFragment(), adoptNode: (n) => n, importNode: (n) => n, cloneNode: function (deep) { return new DOMParser().parseFromString(root.outerHTML, mimeType); }, contains(n) { return root.contains ? root.contains(n) : false; }, addEventListener() {}, removeEventListener() {}, dispatchEvent() { return true; }, }; return docNode; } }; globalThis.XMLSerializer = class XMLSerializer { serializeToString(node) { if (!node) return ""; if (node.nodeType === 10) { let s = ""; return s; } if (node.outerHTML !== undefined) return node.outerHTML; if (node.nodeType === 9) { let s = ""; if (node.doctype) s += this.serializeToString(node.doctype); if (node.documentElement) s += node.documentElement.outerHTML; return s; } if (node.nodeType === 3) return node.textContent || ""; if (node.nodeType === 8) return ""; return ""; } }; globalThis.performance = globalThis.performance || { now: () => Date.now(), mark(){}, measure(){}, clearMarks(){}, clearMeasures(){}, clearResourceTimings(){}, getEntries(){return [];}, getEntriesByName(){return [];}, getEntriesByType(){return [];}, setResourceTimingBufferSize(){}, timeOrigin: 0, timing: { navigationStart: 0, domContentLoadedEventEnd: 0, loadEventEnd: 0 }, navigation: { type: 0, redirectCount: 0 }, memory: { jsHeapSizeLimit: 2172649472, totalJSHeapSize: 19321856, usedJSHeapSize: 16781520, }, }; Object.defineProperty(Document.prototype, 'fonts', { get() { return { ready: Promise.resolve(), check() { return true; }, load() { return Promise.resolve([]); }, add() {}, delete() { return false; }, clear() {}, has() { return false; }, forEach() {}, get size() { return 0; }, get status() { return 'loaded'; }, addEventListener() {}, removeEventListener() {}, dispatchEvent() { return true; }, [Symbol.iterator]() { return [][Symbol.iterator](); }, }; }, configurable: true, }); globalThis.crypto = globalThis.crypto || { getRandomValues(arr) { for(let i=0;i{const r=Math.random()*16|0;return(c==="x"?r:(r&3|8)).toString(16);}); } }; globalThis.structuredClone = globalThis.structuredClone || ((v) => JSON.parse(JSON.stringify(v))); globalThis.reportError = globalThis.reportError || ((e) => console.error(e)); globalThis.Storage = function Storage() {}; Storage.prototype.getItem = function(k) { return (this._data && this._data[k]) ?? null; }; Storage.prototype.setItem = function(k, v) { if (this._data) this._data[k] = String(v); }; Storage.prototype.removeItem = function(k) { if (this._data) delete this._data[k]; }; Storage.prototype.clear = function() { if (this._data) for (var k in this._data) delete this._data[k]; }; Object.defineProperty(Storage.prototype, 'length', { get: function() { return this._data ? Object.keys(this._data).length : 0; } }); Storage.prototype.key = function(i) { return this._data ? Object.keys(this._data)[i] ?? null : null; }; const _mkStore = () => { var s = Object.create(Storage.prototype); s._data = {}; return s; }; globalThis.localStorage = _mkStore(); globalThis.sessionStorage = _mkStore(); globalThis.btoa = globalThis.btoa || ((s) => { const b = new TextEncoder().encode(s); const c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let r=""; for(let i=0;i>2]+c[((a&3)<<4)|(bb>>4)]+(i+1>6)]:"=")+(i+2 { const c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let r=[]; for(let i=0;i>4)); if(cc>=0)r.push(((b&15)<<4)|(cc>>2)); if(d>=0)r.push(((cc&3)<<6)|d);} return String.fromCharCode(...r); }); // Functional History API. The earlier stub returned constant state and was a // no-op on push/replace, so any SPA that tried to update its URL (Next.js // client router, React Router, vue-router, hash-based routers) silently // failed: location.href stayed pinned to the initial page, useLocation hooks // never updated, and popstate-driven UI froze. // // Internally we keep a tiny in-memory stack of {state, url} entries. push/ // replace mutate the stack and set globalThis.__virtualUrl so location.href // reads the new URL. Real Chrome doesn't fire popstate on push/replace, // only on user-driven back/forward — we match that exactly. (() => { const stack = [{state: null, url: undefined}]; // initial entry; url=undefined means "use document URL" let idx = 0; const resolveOrFallback = (url) => { if (url === null || url === undefined) return undefined; try { return new URL(String(url), __currentUrl()).href; } catch (e) { return String(url); } }; const applyVirtual = () => { const entry = stack[idx]; globalThis.__virtualUrl = entry.url ?? null; }; const fireHashChangeIfNeeded = (prevUrl) => { try { const next = __currentUrl(); if (!prevUrl || !next) return; const a = new URL(prevUrl), b = new URL(next); if (a.origin === b.origin && a.pathname === b.pathname && a.search === b.search && a.hash !== b.hash) { const ev = new Event('hashchange'); ev.oldURL = prevUrl; ev.newURL = next; try { globalThis.dispatchEvent(ev); } catch {} } } catch {} }; globalThis.history = { get length() { return stack.length; }, get state() { return stack[idx].state; }, scrollRestoration: "auto", pushState(state, _title, url) { const prevUrl = __currentUrl(); const resolved = resolveOrFallback(url); // Truncate forward entries (real Chrome drops the forward stack on a // new push) then append + advance. stack.length = idx + 1; stack.push({state: state ?? null, url: resolved}); idx = stack.length - 1; applyVirtual(); fireHashChangeIfNeeded(prevUrl); }, replaceState(state, _title, url) { const prevUrl = __currentUrl(); const resolved = resolveOrFallback(url); stack[idx] = {state: state ?? null, url: resolved}; applyVirtual(); fireHashChangeIfNeeded(prevUrl); }, go(n) { n = (n | 0); if (n === 0) return; // real spec: go(0) reloads. We don't reload SPAs. const next = Math.max(0, Math.min(stack.length - 1, idx + n)); if (next === idx) return; const prevUrl = __currentUrl(); idx = next; applyVirtual(); // Real Chrome fires popstate on back/forward with the destination entry's state. try { const ev = new PopStateEvent('popstate', {state: stack[idx].state}); globalThis.dispatchEvent(ev); } catch {} fireHashChangeIfNeeded(prevUrl); }, back() { this.go(-1); }, forward() { this.go(1); }, }; })(); globalThis.screenX = 0; globalThis.screenY = 0; globalThis.screenLeft = 0; globalThis.screenTop = 0; globalThis.pageXOffset = 0; globalThis.pageYOffset = 0; globalThis.scrollX = 0; globalThis.scrollY = 0; globalThis.CSS = { supports(){return false;}, escape(s){return s;} }; globalThis.HTMLElement = Element; globalThis.HTMLDivElement = Element; globalThis.HTMLSpanElement = Element; globalThis.HTMLParagraphElement = Element; globalThis.HTMLAnchorElement = Element; globalThis.HTMLImageElement = Element; globalThis.HTMLInputElement = Element; globalThis.HTMLButtonElement = Element; globalThis.HTMLFormElement = class HTMLFormElement extends Element { get elements() { return this.querySelectorAll("input, select, textarea, button, fieldset, output, object"); } get length() { return this.elements.length; } // Inherit submit() from Element.prototype: it dispatches the cancelable // 'submit' event and (if not prevented) builds form data and navigates. reset() { for (const f of this.elements) { if ('value' in f) f.value = ''; } } }; globalThis.HTMLSelectElement = Element; globalThis.HTMLTextAreaElement = Element; globalThis.HTMLLabelElement = Element; globalThis.HTMLTableElement = Element; globalThis.HTMLIFrameElement = Element; globalThis.HTMLCanvasElement = Element; globalThis.HTMLVideoElement = Element; globalThis.HTMLAudioElement = Element; globalThis.HTMLScriptElement = Element; globalThis.HTMLStyleElement = Element; globalThis.HTMLLinkElement = Element; globalThis.HTMLMetaElement = Element; globalThis.HTMLHeadElement = Element; globalThis.HTMLBodyElement = Element; globalThis.HTMLHtmlElement = Element; globalThis.HTMLBRElement = Element; globalThis.HTMLHRElement = Element; globalThis.HTMLUListElement = Element; globalThis.HTMLOListElement = Element; globalThis.HTMLLIElement = Element; globalThis.HTMLPreElement = Element; globalThis.HTMLHeadingElement = Element; globalThis.HTMLTemplateElement = Element; globalThis.HTMLSlotElement = Element; globalThis.HTMLOptionElement = Element; globalThis.HTMLDataListElement = Element; globalThis.HTMLFieldSetElement = Element; globalThis.HTMLLegendElement = Element; globalThis.HTMLProgressElement = Element; globalThis.HTMLDetailsElement = Element; globalThis.HTMLDialogElement = Element; globalThis.SVGElement = Element; globalThis.SVGSVGElement = Element; globalThis.CharacterData = CharacterData; globalThis.Text = Text; globalThis.Comment = Comment; globalThis.DocumentFragment = DocumentFragment; globalThis.DocumentType = DocumentType; globalThis.Node = Node; globalThis.Element = Element; globalThis.Document = Document; globalThis.EventTarget = Node; globalThis.Range = class Range { setStart(){} setEnd(){} collapse(){} selectNodeContents(){} deleteContents(){} cloneContents(){ return document.createDocumentFragment(); } insertNode(){} getBoundingClientRect(){return {x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0};} }; [ navigator.getBattery, navigator.getGamepads, navigator.sendBeacon, navigator.javaEnabled, navigator.serviceWorker?.register, navigator.permissions?.query, navigator.credentials?.get, globalThis.fetch, globalThis.matchMedia, globalThis.getComputedStyle, globalThis.getSelection, globalThis.requestAnimationFrame, globalThis.cancelAnimationFrame, globalThis.setTimeout, globalThis.clearTimeout, globalThis.setInterval, globalThis.clearInterval, globalThis.queueMicrotask, globalThis.structuredClone, globalThis.reportError, globalThis.btoa, globalThis.atob, console.log, console.warn, console.error, console.info, console.debug, console.dir, console.assert, Element.prototype.getAttribute, Element.prototype.setAttribute, Element.prototype.removeAttribute, Element.prototype.hasAttribute, Element.prototype.querySelector, Element.prototype.querySelectorAll, Element.prototype.getElementsByTagName, Element.prototype.getElementsByClassName, Element.prototype.matches, Element.prototype.closest, Element.prototype.getBoundingClientRect, Element.prototype.getClientRects, Element.prototype.checkVisibility, Element.prototype.addEventListener, Element.prototype.removeEventListener, Element.prototype.dispatchEvent, Element.prototype.click, Element.prototype.focus, Element.prototype.blur, Element.prototype.cloneNode, Element.prototype.attachShadow, Element.prototype.insertAdjacentHTML, Element.prototype.scrollIntoView, Element.prototype.append, Element.prototype.prepend, Element.prototype.remove, Element.prototype.before, Element.prototype.after, Element.prototype.replaceWith, HTMLFormElement.prototype.reset, Element.prototype.getContext, Element.prototype.toDataURL, Element.prototype.toBlob, Node.prototype.appendChild, Node.prototype.removeChild, Node.prototype.replaceChild, Node.prototype.insertBefore, Node.prototype.contains, Node.prototype.hasChildNodes, Node.prototype.cloneNode, CharacterData.prototype.before, CharacterData.prototype.after, CharacterData.prototype.replaceWith, CharacterData.prototype.remove, Document.prototype.getElementById, Document.prototype.querySelector, Document.prototype.querySelectorAll, Document.prototype.getElementsByTagName, Document.prototype.createElement, Document.prototype.createElementNS, Document.prototype.createTextNode, Document.prototype.createComment, Document.prototype.createDocumentFragment, Document.prototype.createEvent, Document.prototype.hasFocus, Storage, Storage.prototype.getItem, Storage.prototype.setItem, Storage.prototype.removeItem, Storage.prototype.clear, Storage.prototype.key, Notification, Notification.requestPermission, window.chrome?.csi, window.chrome?.loadTimes, MutationObserver, ResizeObserver, IntersectionObserver, PerformanceObserver, XMLSerializer, XMLSerializer.prototype.serializeToString, ].forEach(fn => { if (typeof fn === 'function') _markNative(fn); }); class _IframeDocument { constructor(html, url, iframeEl) { this._url = url; this._iframeEl = iframeEl; this.nodeType = 9; this.nodeName = '#document'; this.readyState = 'complete'; this.characterSet = 'UTF-8'; this.contentType = 'text/html'; this.visibilityState = 'visible'; this.hidden = false; this._root = document.createElement('html'); this._head = document.createElement('head'); this._body = document.createElement('body'); this._root.appendChild(this._head); this._root.appendChild(this._body); var bodyContent = html .replace(/^]*>/i, '') .replace(/<\/?html[^>]*>/gi, '') .replace(/]*>[\s\S]*?<\/head>/gi, '') .replace(/<\/?body[^>]*>/gi, '') .replace(/^\s+/, ''); // trim leading whitespace (before content) if (bodyContent) { this._body.innerHTML = bodyContent; } this._title = ''; if (this._head) { const titleEl = this._head.querySelector('title'); if (titleEl) this._title = titleEl.textContent; } } get documentElement() { return this._root; } get head() { return this._head; } get body() { return this._body; } get title() { return this._title; } set title(v) { this._title = v; } get URL() { return this._url; } get documentURI() { return this._url; } get location() { return this._iframeEl?.contentWindow?.location; } get defaultView() { return this._iframeEl?.contentWindow; } get ownerDocument() { return null; } get compatMode() { return 'CSS1Compat'; } get activeElement() { return this._body; } getElementById(id) { return this._root.querySelector('#' + id); } querySelector(sel) { return this._root.querySelector(sel); } querySelectorAll(sel) { return this._root.querySelectorAll(sel); } getElementsByTagName(tag) { return this._root.querySelectorAll(tag); } getElementsByClassName(cls) { return this._root.querySelectorAll('.' + cls); } createElement(tag) { return document.createElement(tag); } createElementNS(ns, tag) { return document.createElementNS(ns, tag); } createTextNode(text) { return document.createTextNode(text); } createComment(text) { return document.createComment(text); } createDocumentFragment() { return document.createDocumentFragment(); } createEvent(type) { return document.createEvent(type); } hasFocus() { return false; } get cookie() { return ''; } set cookie(v) {} get implementation() { return document.implementation; } get styleSheets() { return []; } addEventListener() {} removeEventListener() {} dispatchEvent() { return true; } write(html) { if (this._body) this._body.innerHTML += html; } writeln(html) { this.write(html + '\n'); } open() { if (this._body) this._body.innerHTML = ''; } close() {} } class _IframeWindow { constructor(doc, url) { this.document = doc; this._url = url; this.self = this; this.top = globalThis; this.parent = globalThis; this.window = this; this.frames = this; this.frameElement = null; this.length = 0; this.name = ''; this.closed = false; this.navigator = globalThis.navigator; this.screen = globalThis.screen; this.innerWidth = 300; this.innerHeight = 150; this.outerWidth = 300; this.outerHeight = 150; this.devicePixelRatio = globalThis.devicePixelRatio; this.localStorage = globalThis.localStorage; this.sessionStorage = globalThis.sessionStorage; this.performance = globalThis.performance; this.crypto = globalThis.crypto; this.console = globalThis.console; this.chrome = globalThis.chrome; try { const u = new URL(url); this.location = { href: url, origin: u.origin, protocol: u.protocol, host: u.host, hostname: u.hostname, port: u.port, pathname: u.pathname, search: u.search, hash: u.hash, toString() { return url; }, assign(){}, reload(){}, replace(){}, }; } catch(e) { this.location = { href: url, origin: '', protocol: '', host: '', hostname: '', port: '', pathname: '/', search: '', hash: '', toString() { return url; }, assign(){}, reload(){}, replace(){} }; } } postMessage(data, origin) { const event = new MessageEvent('message', { data: data, origin: this.location.origin, source: this, }); Promise.resolve().then(() => { globalThis.dispatchEvent?.(event); }); } setTimeout(fn, ms) { return globalThis.setTimeout(fn, ms); } clearTimeout(id) { globalThis.clearTimeout(id); } setInterval(fn, ms) { return globalThis.setInterval(fn, ms); } clearInterval(id) { globalThis.clearInterval(id); } requestAnimationFrame(fn) { return globalThis.requestAnimationFrame(fn); } addEventListener(type, fn) { if (!this._listeners) this._listeners = {}; if (!this._listeners[type]) this._listeners[type] = []; this._listeners[type].push(fn); } removeEventListener(type, fn) { if (this._listeners?.[type]) { this._listeners[type] = this._listeners[type].filter(h => h !== fn); } } dispatchEvent(event) { const handlers = this._listeners?.[event?.type] || []; for (const h of handlers) { try { h.call(this, event); } catch(e) {} } return true; } getComputedStyle(el) { return globalThis.getComputedStyle(el); } matchMedia(q) { return globalThis.matchMedia(q); } getSelection() { return globalThis.getSelection(); } fetch(input, init) { return globalThis.fetch(input, init); } close() { this.closed = true; } focus() {} blur() {} } globalThis.__ariaQuerySelector = function(root, selector) { return null; }; globalThis.__ariaQuerySelectorAll = async function*(root, selector) { /* yields nothing */ }; class _Canvas2D { constructor(canvas) { this.canvas = canvas; this._w = canvas.width || 300; this._h = canvas.height || 150; this._buf = new Uint8ClampedArray(this._w * this._h * 4); for (let i = 0; i < this._w * this._h; i++) { this._buf[i*4+0] = 255 + Math.floor(_fpNoise(i % this._w, Math.floor(i / this._w), 0)); this._buf[i*4+1] = 255 + Math.floor(_fpNoise(i % this._w, Math.floor(i / this._w), 1)); this._buf[i*4+2] = 255 + Math.floor(_fpNoise(i % this._w, Math.floor(i / this._w), 2)); this._buf[i*4+3] = 255; } this.fillStyle = '#000000'; this.strokeStyle = '#000000'; this.lineWidth = 1; this.font = '10px sans-serif'; this.textAlign = 'start'; this.textBaseline = 'alphabetic'; this.globalAlpha = 1; this.globalCompositeOperation = 'source-over'; this._stateStack = []; } _parseColor(css) { if (!css || css === 'none') return [0,0,0,0]; if (css.startsWith('#')) { const hex = css.slice(1); if (hex.length === 3) return [parseInt(hex[0]+hex[0],16),parseInt(hex[1]+hex[1],16),parseInt(hex[2]+hex[2],16),255]; if (hex.length === 6) return [parseInt(hex.slice(0,2),16),parseInt(hex.slice(2,4),16),parseInt(hex.slice(4,6),16),255]; if (hex.length === 8) return [parseInt(hex.slice(0,2),16),parseInt(hex.slice(2,4),16),parseInt(hex.slice(4,6),16),parseInt(hex.slice(6,8),16)]; } const m = css.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); if (m) return [+m[1],+m[2],+m[3],m[4]!==undefined?Math.round(+m[4]*255):255]; const named = {red:[255,0,0,255],green:[0,128,0,255],blue:[0,0,255,255],white:[255,255,255,255],black:[0,0,0,255],yellow:[255,255,0,255],orange:[255,165,0,255],gray:[128,128,128,255],transparent:[0,0,0,0]}; return named[css] || [0,0,0,255]; } _setPixel(x, y, r, g, b, a) { x = Math.round(x); y = Math.round(y); if (x < 0 || x >= this._w || y < 0 || y >= this._h) return; const idx = (y * this._w + x) * 4; const alpha = (a / 255) * this.globalAlpha; this._buf[idx+0] = Math.round(r * alpha + this._buf[idx+0] * (1 - alpha)); this._buf[idx+1] = Math.round(g * alpha + this._buf[idx+1] * (1 - alpha)); this._buf[idx+2] = Math.round(b * alpha + this._buf[idx+2] * (1 - alpha)); this._buf[idx+3] = Math.min(255, Math.round(a * alpha + this._buf[idx+3] * (1 - alpha))); } fillRect(x, y, w, h) { const [r,g,b,a] = this._parseColor(this.fillStyle); x=Math.round(x); y=Math.round(y); w=Math.round(w); h=Math.round(h); for (let py = Math.max(0,y); py < Math.min(this._h, y+h); py++) { for (let px = Math.max(0,x); px < Math.min(this._w, x+w); px++) { this._setPixel(px, py, r, g, b, a); } } } clearRect(x, y, w, h) { x=Math.round(x); y=Math.round(y); w=Math.round(w); h=Math.round(h); for (let py = Math.max(0,y); py < Math.min(this._h, y+h); py++) { for (let px = Math.max(0,x); px < Math.min(this._w, x+w); px++) { const idx = (py * this._w + px) * 4; this._buf[idx] = this._buf[idx+1] = this._buf[idx+2] = this._buf[idx+3] = 0; } } } strokeRect(x, y, w, h) { const [r,g,b,a] = this._parseColor(this.strokeStyle); const lw = this.lineWidth; for (let px = Math.round(x); px < Math.round(x+w); px++) { for (let l = 0; l < lw; l++) { this._setPixel(px, Math.round(y)+l, r,g,b,a); this._setPixel(px, Math.round(y+h)-1-l, r,g,b,a); } } for (let py = Math.round(y); py < Math.round(y+h); py++) { for (let l = 0; l < lw; l++) { this._setPixel(Math.round(x)+l, py, r,g,b,a); this._setPixel(Math.round(x+w)-1-l, py, r,g,b,a); } } } fillText(text, x, y) { const [r,g,b,a] = this._parseColor(this.fillStyle); const fontSize = parseInt(this.font) || 10; const scale = Math.max(1, Math.round(fontSize / 10)); const str = String(text); let cx = Math.round(x); for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i); for (let row = 0; row < 7; row++) { for (let col = 0; col < 5; col++) { const on = ((_fpRand(code * 100 + row * 10 + col) > 0.45) && (row > 0 && row < 6 && col > 0 && col < 4)) || (_fpRand(code * 200 + row * 7 + col) > 0.7); if (on) { for (let sy = 0; sy < scale; sy++) { for (let sx = 0; sx < scale; sx++) { this._setPixel(cx + col*scale + sx, Math.round(y) - 7*scale + row*scale + sy, r, g, b, a); } } } } } cx += 6 * scale; } } strokeText(text, x, y) { this.fillText(text, x, y); } measureText(t) { const fontSize = parseInt(this.font) || 10; const scale = Math.max(1, Math.round(fontSize / 10)); return { width: String(t).length * 6 * scale, actualBoundingBoxAscent: 7*scale, actualBoundingBoxDescent: 2*scale }; } getImageData(x, y, w, h) { x=Math.round(x); y=Math.round(y); w=Math.round(w); h=Math.round(h); const data = new Uint8ClampedArray(w * h * 4); for (let py = 0; py < h; py++) { for (let px = 0; px < w; px++) { const srcX = x + px, srcY = y + py; const dstIdx = (py * w + px) * 4; if (srcX >= 0 && srcX < this._w && srcY >= 0 && srcY < this._h) { const srcIdx = (srcY * this._w + srcX) * 4; data[dstIdx] = this._buf[srcIdx]; data[dstIdx+1] = this._buf[srcIdx+1]; data[dstIdx+2] = this._buf[srcIdx+2]; data[dstIdx+3] = this._buf[srcIdx+3]; } } } return { data, width: w, height: h }; } putImageData(imageData, dx, dy) { dx=Math.round(dx); dy=Math.round(dy); const {data, width: w, height: h} = imageData; for (let py = 0; py < h; py++) { for (let px = 0; px < w; px++) { const srcIdx = (py * w + px) * 4; const x = dx + px, y = dy + py; if (x >= 0 && x < this._w && y >= 0 && y < this._h) { const dstIdx = (y * this._w + x) * 4; this._buf[dstIdx] = data[srcIdx]; this._buf[dstIdx+1] = data[srcIdx+1]; this._buf[dstIdx+2] = data[srcIdx+2]; this._buf[dstIdx+3] = data[srcIdx+3]; } } } } createImageData(w, h) { return { data: new Uint8ClampedArray(w*h*4), width: w, height: h }; } drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh) { if (img && img._ctx && img._ctx._buf) { const src = img._ctx; dx = dx ?? sx; dy = dy ?? sy; dw = dw ?? (sw ?? src._w); dh = dh ?? (sh ?? src._h); for (let py = 0; py < dh; py++) { for (let px = 0; px < dw; px++) { const srcX = Math.floor((sx||0) + px * (sw||src._w) / dw); const srcY = Math.floor((sy||0) + py * (sh||src._h) / dh); if (srcX >= 0 && srcX < src._w && srcY >= 0 && srcY < src._h) { const srcIdx = (srcY * src._w + srcX) * 4; this._setPixel(dx+px, dy+py, src._buf[srcIdx], src._buf[srcIdx+1], src._buf[srcIdx+2], src._buf[srcIdx+3]); } } } } } beginPath() { this._path = []; } closePath() {} moveTo(x, y) { if (this._path) this._path.push({t:'M',x,y}); } lineTo(x, y) { if (this._path) this._path.push({t:'L',x,y}); } bezierCurveTo() {} quadraticCurveTo() {} arc(x, y, r, s, e) { if (this._path) this._path.push({t:'A',x,y,r}); } arcTo() {} rect(x, y, w, h) { this.fillRect(x, y, w, h); } fill() {} stroke() {} clip() {} save() { this._stateStack.push({fillStyle: this.fillStyle, strokeStyle: this.strokeStyle, globalAlpha: this.globalAlpha, font: this.font, lineWidth: this.lineWidth}); } restore() { const s = this._stateStack.pop(); if (s) Object.assign(this, s); } translate() {} rotate() {} scale() {} setTransform() {} resetTransform() {} transform() {} createLinearGradient(x0,y0,x1,y1) { return { addColorStop(){}, _x0:x0,_y0:y0,_x1:x1,_y1:y1 }; } createRadialGradient() { return { addColorStop(){} }; } createPattern() { return {}; } isPointInPath() { return false; } isPointInStroke() { return false; } } Element.prototype.getContext = function getContext(type) { if (type === '2d') { if (!this._ctx) { this._ctx = new _Canvas2D(this); } return this._ctx; } if (type === 'webgl' || type === 'experimental-webgl' || type === 'webgl2') { return { canvas: this, getExtension(name) { if (name === 'WEBGL_debug_renderer_info') return { UNMASKED_VENDOR_WEBGL: 0x9245, UNMASKED_RENDERER_WEBGL: 0x9246 }; return null; }, getParameter(pname) { if (pname === 0x9245) return _fp('gpuVendor'); if (pname === 0x9246) return _fp('gpu'); if (pname === 0x1F01) return 'WebKit WebGL'; // GL_RENDERER if (pname === 0x1F00) return 'WebKit'; // GL_VENDOR if (pname === 0x1F02) return 'OpenGL ES 3.0 (ANGLE)'; // GL_VERSION if (pname === 0x8B8C) return 'WebGL GLSL ES 3.00 (ANGLE)'; // GL_SHADING_LANGUAGE_VERSION return 0; }, getSupportedExtensions() { return ['WEBGL_debug_renderer_info','EXT_texture_filter_anisotropic','WEBGL_compressed_texture_s3tc','WEBGL_lose_context']; }, getShaderPrecisionFormat() { return { rangeMin: 127, rangeMax: 127, precision: 23 }; }, createBuffer() { return {}; }, createShader() { return {}; }, createProgram() { return {}; }, shaderSource() {}, compileShader() {}, attachShader() {}, linkProgram() {}, getProgramParameter() { return true; }, useProgram() {}, deleteShader() {}, bindBuffer() {}, bufferData() {}, enableVertexAttribArray() {}, vertexAttribPointer() {}, drawArrays() {}, drawElements() {}, viewport() {}, clear() {}, clearColor() {}, enable() {}, disable() {}, blendFunc() {}, depthFunc() {}, getUniformLocation() { return {}; }, getAttribLocation() { return 0; }, uniform1f() {}, uniform1i() {}, uniformMatrix4fv() {}, createTexture() { return {}; }, bindTexture() {}, texImage2D() {}, texParameteri() {}, activeTexture() {}, pixelStorei() {}, generateMipmap() {}, createFramebuffer() { return {}; }, bindFramebuffer() {}, framebufferTexture2D() {}, readPixels(x,y,w,h,f,t,d) { if(d) for(let i=0;i>> 0) % 64]; } return b64 + '=='; } return _fp('canvasFingerprint'); }; Element.prototype.toBlob = function(cb, type, q) { cb(new Blob([''])); }; _markNative(Element.prototype.getContext); _markNative(Element.prototype.toDataURL); _markNative(Element.prototype.toBlob); Element.prototype.attachShadow = function attachShadow(opts) { const host = this; const children = []; const shadow = { mode: opts?.mode || 'open', host: host, get innerHTML() { return children.map(c => c.outerHTML || c.textContent || '').join(''); }, set innerHTML(v) { children.length = 0; if (v) { const tmp = document.createElement('div'); tmp.innerHTML = v; for (let i = 0; i < tmp.childNodes.length; i++) children.push(tmp.childNodes[i]); } }, get childNodes() { return children; }, get firstChild() { return children[0] || null; }, get lastChild() { return children[children.length - 1] || null; }, get firstElementChild() { return children.find(c => c.nodeType === 1) || null; }, get children() { return children.filter(c => c.nodeType === 1); }, appendChild(c) { if (c) { children.push(c); try { c.parentNode = shadow; } catch (_) { /* parentNode is getter-only on Node, ignore */ } } return c; }, insertBefore(n, ref) { if (!n) return n; if (!ref) { shadow.appendChild(n); return n; } const idx = children.indexOf(ref); if (idx >= 0) { children.splice(idx, 0, n); try { n.parentNode = shadow; } catch (_) {} } else shadow.appendChild(n); return n; }, removeChild(c) { const idx = children.indexOf(c); if (idx >= 0) children.splice(idx, 1); return c; }, replaceChild(n, o) { const idx = children.indexOf(o); if (idx >= 0) { children[idx] = n; try { n.parentNode = shadow; } catch (_) {} } return o; }, querySelector(s) { for (const c of children) { if (c.matches && c.matches(s)) return c; if (c.querySelector) { const r = c.querySelector(s); if (r) return r; } } return null; }, querySelectorAll(s) { const results = []; for (const c of children) { if (c.matches && c.matches(s)) results.push(c); if (c.querySelectorAll) results.push(...c.querySelectorAll(s)); } return results; }, getElementById(id) { return shadow.querySelector('#' + id); }, contains(n) { return children.includes(n); }, getRootNode() { return shadow; }, get ownerDocument() { return document; }, get nodeType() { return 11; }, // DOCUMENT_FRAGMENT_NODE get nodeName() { return '#document-fragment'; }, addEventListener() {}, removeEventListener() {}, dispatchEvent() { return true; }, cloneNode() { return shadow; }, }; this.shadowRoot = shadow; return shadow; }; _markNative(Element.prototype.attachShadow); globalThis.AudioContext = class AudioContext { constructor() { this.sampleRate=_fp('audioSampleRate'); this.state='running'; this.currentTime=0; this.baseLatency=_fp('audioBaseLatency'); this.destination={maxChannelCount:2,numberOfInputs:1,numberOfOutputs:0,channelCount:2}; } createOscillator() { return {type:'sine',frequency:{value:440,setValueAtTime(){}},connect(){},start(){},stop(){},disconnect(){},addEventListener(){}}; } createDynamicsCompressor() { return {threshold:{value:_fp('compThreshold')},knee:{value:_fp('compKnee')},ratio:{value:_fp('compRatio')},attack:{value:0.003},release:{value:0.25},reduction:0,connect(){},disconnect(){}}; } createAnalyser() { return {fftSize:2048,frequencyBinCount:1024,connect(){},disconnect(){}, getByteFrequencyData(a){for(let i=0;i { try { req.result = produceResult(); req.readyState = 'done'; if (typeof req.onsuccess === 'function') { try { req.onsuccess({ target: req, type: 'success' }); } catch (e) {} } } catch (e) { req.error = e; req.readyState = 'done'; if (typeof req.onerror === 'function') { try { req.onerror({ target: req, type: 'error' }); } catch (e2) {} } } }); return req; } function _idbObjectStore(name) { const data = new Map(); return { name, keyPath: null, autoIncrement: false, indexNames: { contains() { return false; }, length: 0, item() { return null; } }, transaction: null, add(value, key) { const k = key ?? Date.now(); data.set(k, value); return _idbRequest(() => k); }, put(value, key) { const k = key ?? Date.now(); data.set(k, value); return _idbRequest(() => k); }, get(key) { return _idbRequest(() => data.get(key) ?? undefined); }, getAll() { return _idbRequest(() => Array.from(data.values())); }, getAllKeys() { return _idbRequest(() => Array.from(data.keys())); }, getKey(key) { return _idbRequest(() => (data.has(key) ? key : undefined)); }, delete(key) { return _idbRequest(() => { data.delete(key); return undefined; }); }, clear() { return _idbRequest(() => { data.clear(); return undefined; }); }, count() { return _idbRequest(() => data.size); }, openCursor() { return _idbRequest(() => null); }, openKeyCursor() { return _idbRequest(() => null); }, createIndex() { return { name: '', keyPath: '', unique: false, multiEntry: false, get() { return _idbRequest(() => undefined); } }; }, index() { return { get() { return _idbRequest(() => undefined); }, getAll() { return _idbRequest(() => []); }, count() { return _idbRequest(() => 0); }, openCursor() { return _idbRequest(() => null); } }; }, deleteIndex() {}, }; } function _idbTransaction(storeNames) { const stores = new Map(); const names = Array.isArray(storeNames) ? storeNames : [storeNames]; for (const n of names) stores.set(String(n), _idbObjectStore(String(n))); const tx = { db: null, mode: 'readonly', objectStoreNames: { contains: (n) => stores.has(String(n)), length: stores.size }, onabort: null, oncomplete: null, onerror: null, error: null, objectStore(name) { let s = stores.get(name); if (!s) { s = _idbObjectStore(name); stores.set(name, s); } s.transaction = tx; return s; }, abort() {}, commit() {}, addEventListener(type, fn) { tx['on' + type] = fn; }, removeEventListener(type, fn) { if (tx['on' + type] === fn) tx['on' + type] = null; }, }; Promise.resolve().then(() => { if (typeof tx.oncomplete === 'function') { try { tx.oncomplete({ target: tx, type: 'complete' }); } catch (e) {} } }); return tx; } function _idbDatabase(name, version) { return { name, version, objectStoreNames: { contains() { return false; }, length: 0, item() { return null; } }, createObjectStore(n) { return _idbObjectStore(n); }, deleteObjectStore() {}, transaction(storeNames, mode) { const tx = _idbTransaction(storeNames); tx.mode = mode || 'readonly'; return tx; }, close() {}, onversionchange: null, onabort: null, onerror: null, onclose: null, addEventListener() {}, removeEventListener() {}, }; } globalThis.indexedDB = { open(name, version) { return _idbRequest(() => _idbDatabase(name, version || 1)); }, deleteDatabase(_name) { return _idbRequest(() => undefined); }, databases() { return Promise.resolve([]); }, cmp(a, b) { return a < b ? -1 : a > b ? 1 : 0; }, }; globalThis.IDBKeyRange = { only(v) { return { lower: v, upper: v, lowerOpen: false, upperOpen: false, includes(x) { return x === v; } }; }, lowerBound(v, open) { return { lower: v, upper: null, lowerOpen: !!open, upperOpen: false, includes(x) { return open ? x > v : x >= v; } }; }, upperBound(v, open) { return { lower: null, upper: v, lowerOpen: false, upperOpen: !!open, includes(x) { return open ? x < v : x <= v; } }; }, bound(l, u, lo, uo) { return { lower: l, upper: u, lowerOpen: !!lo, upperOpen: !!uo, includes(x) { return (lo ? x > l : x >= l) && (uo ? x < u : x <= u); } }; }, }; globalThis.caches = { open() { return Promise.resolve({ match(){return Promise.resolve(undefined);}, put(){return Promise.resolve();}, delete(){return Promise.resolve(false);}, keys(){return Promise.resolve([]);} }); }, match() { return Promise.resolve(undefined); }, has() { return Promise.resolve(false); }, delete() { return Promise.resolve(false); }, keys() { return Promise.resolve([]); }, }; _markNative(AudioContext); _markNative(OfflineAudioContext); _markNative(SpeechSynthesisUtterance); _markNative(MediaStream); _markNative(MediaStreamTrack); _markNative(RTCPeerConnection); _markNative(RTCSessionDescription); _markNative(RTCIceCandidate); const _OrigDateTimeFormat = Intl.DateTimeFormat; const _defaultTZ = 'America/New_York'; Intl.DateTimeFormat = function(locales, options) { if (!options) options = {}; if (!options.timeZone) options.timeZone = _defaultTZ; return new _OrigDateTimeFormat(locales, options); }; Intl.DateTimeFormat.prototype = _OrigDateTimeFormat.prototype; Intl.DateTimeFormat.supportedLocalesOf = _OrigDateTimeFormat.supportedLocalesOf; const _origResolved = _OrigDateTimeFormat.prototype.resolvedOptions; _OrigDateTimeFormat.prototype.resolvedOptions = function() { const r = _origResolved.call(this); if (r.timeZone === 'UTC') r.timeZone = _defaultTZ; return r; }; if (typeof PointerEvent === 'undefined') { globalThis.PointerEvent = class PointerEvent extends MouseEvent { constructor(type, opts={}) { super(type, opts); this.pointerId = opts.pointerId || 0; this.width = opts.width || 1; this.height = opts.height || 1; this.pressure = opts.pressure || 0; this.pointerType = opts.pointerType || 'mouse'; } }; } if (typeof navigator.credentials === 'undefined') { navigator.credentials = { get(){return Promise.resolve(null);}, create(){return Promise.resolve(null);}, store(){return Promise.resolve();}, preventSilentAccess(){return Promise.resolve();} }; } globalThis.opener = null; globalThis.Worker = class Worker { constructor(url) { this.onmessage = null; this.onerror = null; this._terminated = false; this._listeners = {}; const worker = this; if (typeof url === 'string' && (url.startsWith('blob:') || url.startsWith('http'))) { const blobContent = globalThis.__blobStore?.[url]; if (blobContent) { this._code = blobContent; } else { (async () => { try { const resp = await fetch(url); worker._code = await resp.text(); } catch(e) { if (worker.onerror) worker.onerror(e); } })(); } } } postMessage(data) { if (this._terminated) return; const worker = this; setTimeout(() => { if (worker._terminated || !worker._code) return; try { const workerSelf = { onmessage: null, postMessage: (msg) => { const evt = { data: msg }; if (worker.onmessage) worker.onmessage(evt); const handlers = worker._listeners['message'] || []; for (const h of handlers) h(evt); }, addEventListener: (type, fn) => { workerSelf['on' + type] = fn; }, close: () => { worker._terminated = true; }, crypto: globalThis.crypto, TextEncoder: globalThis.TextEncoder, TextDecoder: globalThis.TextDecoder, atob: globalThis.atob, btoa: globalThis.btoa, setTimeout: globalThis.setTimeout, setInterval: globalThis.setInterval, clearTimeout: globalThis.clearTimeout, clearInterval: globalThis.clearInterval, fetch: globalThis.fetch, console: globalThis.console, }; const fn = new Function('self', 'postMessage', 'addEventListener', 'close', worker._code); fn(workerSelf, workerSelf.postMessage, workerSelf.addEventListener, workerSelf.close); if (workerSelf.onmessage) workerSelf.onmessage({ data }); } catch(e) { console.error('Worker error:', e.message); if (worker.onerror) worker.onerror(e); } }, 0); } terminate() { this._terminated = true; } addEventListener(type, fn) { if (!this._listeners[type]) this._listeners[type] = []; this._listeners[type].push(fn); } removeEventListener(type, fn) { if (this._listeners[type]) this._listeners[type] = this._listeners[type].filter(h => h !== fn); } }; globalThis.__blobStore = globalThis.__blobStore || {}; const _origCreateObjectURL = URL.createObjectURL; URL.createObjectURL = function(blob) { if (blob && typeof blob.text === 'function') { const id = 'blob:obscura/' + Math.random().toString(36).substring(2); blob.text().then(text => { globalThis.__blobStore[id] = text; }); return id; } return 'blob:obscura/fallback'; }; URL.revokeObjectURL = function(url) { delete globalThis.__blobStore[url]; }; globalThis.scrollTo = function(x, y) {}; globalThis.scrollBy = function(x, y) {}; globalThis.scroll = function(x, y) {}; globalThis.focus = function() {}; globalThis.blur = function() {}; globalThis.print = function() {}; globalThis.alert = function() {}; globalThis.confirm = function() { return true; }; globalThis.prompt = function() { return null; }; globalThis.open = function() { return null; }; globalThis.close = function() {}; globalThis.stop = function() {}; globalThis.postMessage = function() {}; globalThis.requestIdleCallback = globalThis.requestIdleCallback || function(cb) { return setTimeout(cb, 0); }; globalThis.cancelIdleCallback = globalThis.cancelIdleCallback || function(id) { clearTimeout(id); }; if (typeof ReadableStream === 'undefined') { globalThis.ReadableStream = class ReadableStream { constructor(source = {}, strategy = {}) { this._source = source; this._queue = []; this._closed = false; this.locked = false; if (source.start) source.start({ enqueue: (chunk) => this._queue.push(chunk), close: () => { this._closed = true; }, error: () => {} }); } getReader() { this.locked = true; const stream = this; return { read() { if (stream._queue.length > 0) return Promise.resolve({ value: stream._queue.shift(), done: false }); if (stream._closed) return Promise.resolve({ value: undefined, done: true }); return Promise.resolve({ value: undefined, done: true }); }, releaseLock() { stream.locked = false; }, cancel() { stream._closed = true; return Promise.resolve(); }, get closed() { return stream._closed ? Promise.resolve() : new Promise(() => {}); }, }; } cancel() { this._closed = true; return Promise.resolve(); } pipeTo(dest) { return Promise.resolve(); } pipeThrough(transform) { return transform.readable || new ReadableStream(); } tee() { return [new ReadableStream(), new ReadableStream()]; } [Symbol.asyncIterator]() { const reader = this.getReader(); return { next: () => reader.read(), return: () => { reader.releaseLock(); return Promise.resolve({done:true}); } }; } }; } if (typeof WritableStream === 'undefined') { globalThis.WritableStream = class WritableStream { constructor(sink = {}) { this._sink = sink; this.locked = false; } getWriter() { this.locked = true; const stream = this; return { write(chunk) { if (stream._sink.write) stream._sink.write(chunk); return Promise.resolve(); }, close() { if (stream._sink.close) stream._sink.close(); return Promise.resolve(); }, abort() { return Promise.resolve(); }, releaseLock() { stream.locked = false; }, get ready() { return Promise.resolve(); }, get closed() { return Promise.resolve(); }, get desiredSize() { return 1; }, }; } close() { return Promise.resolve(); } abort() { return Promise.resolve(); } }; } if (typeof TransformStream === 'undefined') { globalThis.TransformStream = class TransformStream { constructor(transformer = {}) { this.readable = new ReadableStream(); this.writable = new WritableStream(); } }; } if (!globalThis.crypto) globalThis.crypto = {}; if (!globalThis.crypto.subtle) { globalThis.crypto.subtle = { async digest(algorithm, data) { // Real WebCrypto digest. Delegates to `op_subtle_digest` which runs // the actual SHA-1/256/384/512 via Rust's `sha1` and `sha2` crates. // The previous JS implementation was a custom FNV variant that // produced bytes shaped like the hash but with wrong contents, so // SRI checks, JWS signature verification, and OAuth PKCE silently // accepted invalid input. const name = (typeof algorithm === 'string' ? algorithm : algorithm?.name) || 'SHA-256'; let bytes; if (data instanceof ArrayBuffer) bytes = new Uint8Array(data); else if (ArrayBuffer.isView(data)) bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); else bytes = new Uint8Array(data || []); const out = Deno.core.ops.op_subtle_digest(name, bytes); return new Uint8Array(out).buffer; }, async encrypt() { throw new DOMException('NotSupportedError'); }, async decrypt() { throw new DOMException('NotSupportedError'); }, async sign() { return new ArrayBuffer(32); }, async verify() { return true; }, async generateKey() { return { type: 'secret', algorithm: {}, extractable: false, usages: [] }; }, async importKey() { return { type: 'secret', algorithm: {}, extractable: false, usages: [] }; }, async exportKey() { return new ArrayBuffer(32); }, async deriveBits() { return new ArrayBuffer(32); }, async deriveKey() { return { type: 'secret', algorithm: {}, extractable: false, usages: [] }; }, async wrapKey() { return new ArrayBuffer(32); }, async unwrapKey() { return { type: 'secret', algorithm: {}, extractable: false, usages: [] }; }, }; } if (typeof DOMRect === 'undefined') { globalThis.DOMRect = class DOMRect { constructor(x=0,y=0,w=0,h=0) { this.x=x;this.y=y;this.width=w;this.height=h;this.top=y;this.right=x+w;this.bottom=y+h;this.left=x; } toJSON() { return {x:this.x,y:this.y,width:this.width,height:this.height,top:this.top,right:this.right,bottom:this.bottom,left:this.left}; } static fromRect(r={}) { return new DOMRect(r.x,r.y,r.width,r.height); } }; } if (typeof DOMPoint === 'undefined') { globalThis.DOMPoint = class DOMPoint { constructor(x=0,y=0,z=0,w=1) { this.x=x;this.y=y;this.z=z;this.w=w; } static fromPoint(p={}) { return new DOMPoint(p.x,p.y,p.z,p.w); } }; } if (typeof DOMMatrix === 'undefined') { globalThis.DOMMatrix = class DOMMatrix { constructor() { this.a=1;this.b=0;this.c=0;this.d=1;this.e=0;this.f=0;this.is2D=true;this.isIdentity=true; } static fromMatrix() { return new DOMMatrix(); } static fromFloat32Array() { return new DOMMatrix(); } static fromFloat64Array() { return new DOMMatrix(); } multiply() { return new DOMMatrix(); } inverse() { return new DOMMatrix(); } translate() { return new DOMMatrix(); } scale() { return new DOMMatrix(); } rotate() { return new DOMMatrix(); } transformPoint(p) { return new DOMPoint(p?.x||0,p?.y||0); } }; } if (typeof Image === 'undefined') { globalThis.Image = class Image { constructor(w, h) { this.width = w || 0; this.height = h || 0; this.src = ''; this.onload = null; this.onerror = null; this.complete = false; this.naturalWidth = 0; this.naturalHeight = 0; } addEventListener() {} removeEventListener() {} setAttribute(k, v) { this[k] = v; if (k === 'src' && this.onload) setTimeout(() => { this.complete = true; this.onload(); }, 0); } getAttribute(k) { return this[k]; } }; } if (typeof Audio === 'undefined') { globalThis.Audio = class Audio { constructor(src) { this.src = src || ''; this.paused = true; this.volume = 1; this.currentTime = 0; this.duration = 0; } play() { return Promise.resolve(); } pause() { this.paused = true; } load() {} addEventListener() {} removeEventListener() {} }; } if (typeof FileReader === 'undefined') { globalThis.FileReader = class FileReader { constructor() { this.result = null; this.readyState = 0; this.onload = null; this.onerror = null; } readAsText(blob) { if (blob?.text) blob.text().then(t => { this.result = t; this.readyState = 2; if (this.onload) this.onload({target:this}); }); } readAsDataURL(blob) { this.result = 'data:;base64,'; this.readyState = 2; if (this.onload) setTimeout(() => this.onload({target:this}), 0); } readAsArrayBuffer(blob) { this.result = new ArrayBuffer(0); this.readyState = 2; if (this.onload) setTimeout(() => this.onload({target:this}), 0); } abort() { this.readyState = 0; } addEventListener(t, fn) { if (t === 'load') this.onload = fn; } removeEventListener() {} }; } // Real network sockets aren't implemented; we don't have a runtime WS / SSE // client in V8. But pages that wait for an `open` event (Vite HMR clients // embedded on docs sites, live-dashboards, anything calling // `await new Promise(r => ws.addEventListener('open', r))`) silently hang // forever otherwise. Fire `open` after a microtask so the consumer at least // proceeds; subsequent messages never arrive, which is no worse than the // current "no signal whatsoever" behaviour. // Minimal EventTarget shared by socket-like classes. Real `EventTarget` is // currently aliased to `Node`, which would drag DOM-tree assumptions into a // `WebSocket`. Defining a private shim avoids that. function _makeListenerBox(self) { const map = new Map(); self.addEventListener = function (type, fn) { if (typeof fn !== 'function') return; let bucket = map.get(type); if (!bucket) { bucket = []; map.set(type, bucket); } bucket.push(fn); }; self.removeEventListener = function (type, fn) { const bucket = map.get(type); if (!bucket) return; const i = bucket.indexOf(fn); if (i >= 0) bucket.splice(i, 1); }; self.dispatchEvent = function (event) { const bucket = map.get(event.type); if (!bucket) return true; for (const fn of bucket.slice()) { try { fn.call(self, event); } catch (e) { /* swallow */ } } return true; }; } if (typeof EventSource === 'undefined') { globalThis.EventSource = class EventSource { constructor(url, init) { this.url = url; this.readyState = 0; // CONNECTING this.withCredentials = !!(init && init.withCredentials); this.onopen = null; this.onmessage = null; this.onerror = null; _makeListenerBox(this); Promise.resolve().then(() => { if (this.readyState !== 0) return; this.readyState = 1; // OPEN const ev = new Event('open'); if (typeof this.onopen === 'function') { try { this.onopen(ev); } catch (e) {} } try { this.dispatchEvent(ev); } catch (e) {} }); } close() { this.readyState = 2; } static CONNECTING = 0; static OPEN = 1; static CLOSED = 2; }; } if (typeof WebSocket === 'undefined') { globalThis.WebSocket = class WebSocket { constructor(url, protocols) { this.url = url; this.readyState = 0; // CONNECTING this.bufferedAmount = 0; this.binaryType = 'blob'; this.extensions = ''; this.protocol = Array.isArray(protocols) ? (protocols[0] || '') : (protocols || ''); this.onopen = null; this.onmessage = null; this.onerror = null; this.onclose = null; _makeListenerBox(this); Promise.resolve().then(() => { if (this.readyState !== 0) return; this.readyState = 1; // OPEN const ev = new Event('open'); if (typeof this.onopen === 'function') { try { this.onopen(ev); } catch (e) {} } try { this.dispatchEvent(ev); } catch (e) {} }); } send(data) { /* drop; no real socket */ } close(code, reason) { if (this.readyState >= 2) return; this.readyState = 3; // CLOSED const ev = new Event('close'); ev.code = code || 1000; ev.reason = reason || ''; ev.wasClean = true; if (typeof this.onclose === 'function') { try { this.onclose(ev); } catch (e) {} } try { this.dispatchEvent(ev); } catch (e) {} } static CONNECTING = 0; static OPEN = 1; static CLOSING = 2; static CLOSED = 3; }; } if (typeof BroadcastChannel === 'undefined') { globalThis.BroadcastChannel = class BroadcastChannel { constructor(name) { this.name = name; this.onmessage = null; this.onmessageerror = null; _makeListenerBox(this); } postMessage(msg) {} close() {} }; } if (typeof MediaQueryList === 'undefined') { globalThis.MediaQueryList = class MediaQueryList { constructor(q) { this.media = q || ''; this.matches = false; } addListener() {} removeListener() {} addEventListener() {} removeEventListener() {} }; } if (typeof ImageData === 'undefined') { globalThis.ImageData = class ImageData { constructor(w, h) { if (w instanceof Uint8ClampedArray) { this.data = w; this.width = h; this.height = w.length / (4 * h); } else { this.width = w; this.height = h; this.data = new Uint8ClampedArray(w * h * 4); } } }; } if (typeof CanvasRenderingContext2D === 'undefined') { globalThis.CanvasRenderingContext2D = class CanvasRenderingContext2D {}; } if (typeof OffscreenCanvas === 'undefined') { globalThis.OffscreenCanvas = class OffscreenCanvas { constructor(w, h) { this.width = w; this.height = h; } getContext(type) { return globalThis.document?.createElement('canvas')?.getContext(type) || null; } convertToBlob() { return Promise.resolve(new Blob([''])); } transferToImageBitmap() { return {}; } }; } if (typeof Path2D === 'undefined') { globalThis.Path2D = class Path2D { constructor(){} moveTo(){} lineTo(){} arc(){} rect(){} closePath(){} addPath(){} }; } if (typeof ImageBitmap === 'undefined') { globalThis.ImageBitmap = class ImageBitmap { constructor(){this.width=0;this.height=0;} close(){} }; globalThis.createImageBitmap = function() { return Promise.resolve(new ImageBitmap()); }; } if (typeof Selection === 'undefined') { globalThis.Selection = class Selection { constructor(){this.anchorNode=null;this.focusNode=null;this.rangeCount=0;this.isCollapsed=true;this.type='None';} getRangeAt(){return null;} collapse(){} extend(){} selectAllChildren(){} deleteFromDocument(){} addRange(){} removeRange(){} removeAllRanges(){} toString(){return '';} }; } if (typeof NodeFilter === 'undefined') { globalThis.NodeFilter = { SHOW_ALL:0xFFFFFFFF, SHOW_ELEMENT:1, SHOW_TEXT:4, SHOW_COMMENT:128, FILTER_ACCEPT:1, FILTER_REJECT:2, FILTER_SKIP:3 }; } if (typeof TreeWalker === 'undefined') { globalThis.TreeWalker = class TreeWalker { constructor(root){this.root=root;this.currentNode=root;this.whatToShow=0xFFFFFFFF;this.filter=null;} parentNode(){return this.currentNode?.parentNode||null;} firstChild(){return this.currentNode?.firstChild||null;} lastChild(){return this.currentNode?.lastChild||null;} previousSibling(){return this.currentNode?.previousSibling||null;} nextSibling(){return this.currentNode?.nextSibling||null;} nextNode(){return null;} previousNode(){return null;} }; } if (typeof Range === 'undefined') { globalThis.Range = class Range { constructor(){this.startContainer=null;this.startOffset=0;this.endContainer=null;this.endOffset=0;this.collapsed=true;this.commonAncestorContainer=null;} setStart(n,o){this.startContainer=n;this.startOffset=o;} setEnd(n,o){this.endContainer=n;this.endOffset=o;} collapse(){} selectNode(){} selectNodeContents(){} cloneContents(){return document?.createDocumentFragment();} deleteContents(){} insertNode(){} getBoundingClientRect(){return new DOMRect();} getClientRects(){return [];} cloneRange(){return new Range();} toString(){return '';} }; } if (typeof SharedWorker === 'undefined') { globalThis.SharedWorker = class SharedWorker { constructor() { this.port = { postMessage(){}, onmessage:null, start(){}, close(){}, addEventListener(){}, removeEventListener(){} }; this.onerror = null; } }; } if (typeof ServiceWorkerContainer === 'undefined') { globalThis.ServiceWorkerContainer = class { register(){return Promise.resolve();} getRegistrations(){return Promise.resolve([]);} }; } if (typeof URLPattern === 'undefined') { globalThis.URLPattern = class URLPattern { constructor(pattern){this._pattern=pattern||{};} test(){return false;} exec(){return null;} }; } if (typeof Document !== 'undefined' && !Document.prototype.importNode) { Document.prototype.importNode = function(node, deep) { return node?.cloneNode(!!deep) || null; }; } // Document.elementFromPoint / elementsFromPoint — no layout engine, so this is a stub: // in-viewport coords return (or as fallback), out-of-viewport returns null. // Wrong-but-non-throwing beats "undefined", which traps ad/analytics bootstraps in retry loops // (see issue #63). if (typeof Document !== 'undefined' && !Document.prototype.elementFromPoint) { Document.prototype.elementFromPoint = function(x, y) { if (typeof x !== 'number' || typeof y !== 'number' || !isFinite(x) || !isFinite(y)) { return null; } var w = (typeof window !== 'undefined' && window.innerWidth) || 0; var h = (typeof window !== 'undefined' && window.innerHeight) || 0; if (x < 0 || y < 0 || x > w || y > h) { return null; } return this.body || this.documentElement || null; }; Document.prototype.elementsFromPoint = function(x, y) { var el = this.elementFromPoint(x, y); return el ? [el] : []; }; } if (typeof ShadowRoot !== 'undefined' && !ShadowRoot.prototype.elementFromPoint) { ShadowRoot.prototype.elementFromPoint = function(x, y) { return Document.prototype.elementFromPoint.call(globalThis.document || this, x, y); }; ShadowRoot.prototype.elementsFromPoint = function(x, y) { return Document.prototype.elementsFromPoint.call(globalThis.document || this, x, y); }; } globalThis.__obscura_init = function() { _fpSeed = Date.now() ^ (Math.random() * 0xFFFFFFFF >>> 0); _fpCache = null; _installWasmStreamingFallback(); globalThis.document = new Document(+_dom("document_node_id")); const scr = _fp('screen'); const sw = scr[0], sh = scr[1]; globalThis.screen = { width:sw, height:sh, availWidth:sw, availHeight:sh-40, colorDepth:24, pixelDepth:24, availTop:0, availLeft:0, orientation:{type:"landscape-primary",angle:0,addEventListener(){},removeEventListener(){},dispatchEvent(){return true;}} }; globalThis.visualViewport = { width:sw, height:sh-80, offsetLeft:0, offsetTop:0, scale:1, addEventListener(){}, removeEventListener(){} }; globalThis.devicePixelRatio = sw >= 2560 ? 2 : 1; globalThis.innerWidth = sw; globalThis.innerHeight = sh - 80; globalThis.outerWidth = sw; globalThis.outerHeight = sh; const t0 = Date.now(); globalThis.performance.timeOrigin = t0; globalThis.performance.timing = { navigationStart: t0, domContentLoadedEventEnd: t0, loadEventEnd: t0 }; const hide = (obj, props) => { for (const p of props) { if (p in obj) { try { Object.defineProperty(obj, p, { enumerable: false, configurable: true }); } catch(e) {} } } }; const toHide = Object.keys(globalThis).filter(k => k.startsWith('_') || k.includes('obscura') || k.includes('Obscura') ); for (const p of toHide) { try { Object.defineProperty(globalThis, p, { enumerable: false }); } catch(e) { } } delete globalThis.__obscura_init; };