window.__canonRuntime = {
_init_count: 0,
_replay_count: 0,
_observer_events: 0,
_trackInit(uid) {
this._init_count++;
if (this._inited_uids && this._inited_uids.has(uid)) {
this._replay_count++;
}
if (!this._inited_uids) this._inited_uids = new Set();
this._inited_uids.add(uid);
},
_trackObserver() {
this._observer_events++;
},
get init_count() { return this._init_count; },
get replay_count() { return this._replay_count; },
get observer_events() { return this._observer_events; },
get active_listeners() {
try { return window.__canonLoader?.mod?.runtime_active_listeners?.() ?? -1; }
catch(e) { return -1; }
},
get orphan_listeners() {
try { return window.__canonLoader?.mod?.runtime_orphan_listeners?.() ?? -1; }
catch(e) { return -1; }
},
get initialized_count() {
try { return window.__canonLoader?.mod?.runtime_initialized_count?.() ?? -1; }
catch(e) { return -1; }
},
get namespaces() {
try { return Array.from(window.__canonLoader?.mod?.runtime_namespaces?.() ?? []); }
catch(e) { return []; }
},
snapshot() {
return {
init_count: this.init_count,
replay_count: this.replay_count,
observer_events: this.observer_events,
active_listeners: this.active_listeners,
orphan_listeners: this.orphan_listeners,
initialized_count: this.initialized_count,
namespaces: this.namespaces,
};
}
};
window.__canonLoader = {
initializedUids: new Set(),
mod: null,
async load() {
if (this.mod) return this.mod;
const mod = await import('/wasm/canonrs_interactions.js');
await mod.default();
this.mod = mod;
return mod;
},
async initElement(el) {
if (!(el instanceof Element)) return;
if (this._isInitialized(el)) return;
const group = el.getAttribute('data-rs-interaction');
if (!group) return;
const mod = await this.load();
if (typeof mod.init_subtree !== 'function') return;
this._markInitialized(el);
window.__canonRuntime._trackInit(el.getAttribute('data-rs-uid') || group);
try { mod.init_subtree(el); } catch(e) { console.warn('[canon] init failed:', group, el, e); }
},
_isInitialized(el) {
const uid = el.getAttribute('data-rs-uid');
if (uid) return this.initializedUids.has(uid);
return el.hasAttribute('data-rs-initialized');
},
_markInitialized(el) {
const uid = el.getAttribute('data-rs-uid');
if (uid) { this.initializedUids.add(uid); }
else { el.setAttribute('data-rs-initialized', 'true'); }
}
};
const bootstrap = {
ran: false,
stabilize() {
return new Promise(resolve => {
let frames = 0;
const tick = () => {
if (++frames > 3) { resolve(); return; }
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
});
},
async run() {
if (this.ran) return;
this.ran = true;
await this.stabilize();
await this.loadAll();
},
async loadAll() {
try {
const mod = await window.__canonLoader.load();
if (typeof mod.init_all === 'function') mod.init_all();
document.querySelectorAll('[data-rs-interaction]').forEach(el => {
window.__canonLoader.initElement(el);
});
} catch(e) {
console.warn('[canon] bootstrap failed:', e);
setTimeout(() => bootstrap.run(), 200);
}
}
};
const startObserver = () => {
const observer = new MutationObserver((mutations) => {
window.__canonRuntime._trackObserver();
for (const m of mutations) {
for (const node of m.addedNodes) {
if (!(node instanceof Element)) continue;
if (node.hasAttribute('data-rs-interaction')) {
window.__canonLoader.initElement(node);
}
node.querySelectorAll('[data-rs-interaction]').forEach(el => {
window.__canonLoader.initElement(el);
});
}
if (m.type === 'attributes' && m.attributeName === 'hidden') {
const el = m.target;
if (el instanceof Element && !el.hasAttribute('hidden')) {
el.querySelectorAll('[data-rs-interaction]').forEach(child => {
window.__canonLoader.initElement(child);
});
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributeFilter: ['hidden']
});
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => bootstrap.run());
} else {
bootstrap.run();
}
if (document.body) {
startObserver();
} else {
document.addEventListener('DOMContentLoaded', startObserver);
}