"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 = '` 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;
};