mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
120 lines
3.9 KiB
JavaScript
120 lines
3.9 KiB
JavaScript
import { $x, $$ } from "basic-devtools";
|
|
|
|
import xworker from "./worker/class.js";
|
|
import { handle, runtimes } from "./script-handler.js";
|
|
import { all, assign, create, defineProperty } from "./utils.js";
|
|
import { registry, selectors, prefixes } from "./runtimes.js";
|
|
import { PLUGINS_SELECTORS, handlePlugin } from "./plugins.js";
|
|
|
|
export { registerPlugin } from "./plugins.js";
|
|
export const XWorker = xworker();
|
|
|
|
const RUNTIME_SELECTOR = selectors.join(",");
|
|
|
|
// ensure both runtime and its queue are awaited then returns the runtime
|
|
const awaitRuntime = async (key) => {
|
|
if (runtimes.has(key)) {
|
|
const { runtime, queue } = runtimes.get(key);
|
|
return (await all([runtime, queue]))[0];
|
|
}
|
|
|
|
const available = runtimes.size
|
|
? `Available runtimes are: ${[...runtimes.keys()]
|
|
.map((r) => `"${r}"`)
|
|
.join(", ")}.`
|
|
: `There are no runtimes in this page.`;
|
|
|
|
throw new Error(`The runtime "${key}" was not found. ${available}`);
|
|
};
|
|
|
|
defineProperty(globalThis, "pyscript", {
|
|
value: {
|
|
env: new Proxy(create(null), { get: (_, name) => awaitRuntime(name) }),
|
|
},
|
|
});
|
|
|
|
let index = 0;
|
|
globalThis.__events = new Map();
|
|
|
|
// attributes are tested via integration / e2e
|
|
/* c8 ignore next 17 */
|
|
const listener = async (event) => {
|
|
const { type, currentTarget } = event;
|
|
for (let { name, value, ownerElement: el } of $x(
|
|
`./@*[${prefixes.map((p) => `name()="${p}${type}"`).join(" or ")}]`,
|
|
currentTarget,
|
|
)) {
|
|
name = name.slice(0, -(type.length + 1));
|
|
const runtime = await awaitRuntime(
|
|
el.getAttribute(`${name}-env`) || name,
|
|
);
|
|
const i = index++;
|
|
try {
|
|
globalThis.__events.set(i, event);
|
|
registry.get(name).runEvent(runtime, value, i);
|
|
} finally {
|
|
globalThis.__events.delete(i);
|
|
}
|
|
}
|
|
};
|
|
|
|
// attributes are tested via integration / e2e
|
|
/* c8 ignore next 8 */
|
|
for (let { name, ownerElement: el } of $x(
|
|
`.//@*[${prefixes.map((p) => `starts-with(name(),"${p}")`).join(" or ")}]`,
|
|
)) {
|
|
name = name.slice(name.indexOf("-") + 1);
|
|
if (name !== "env") el.addEventListener(name, listener);
|
|
}
|
|
|
|
const mo = new MutationObserver((records) => {
|
|
for (const { type, target, attributeName, addedNodes } of records) {
|
|
// attributes are tested via integration / e2e
|
|
/* c8 ignore next 17 */
|
|
if (type === "attributes") {
|
|
const i = attributeName.indexOf("-") + 1;
|
|
if (i) {
|
|
const prefix = attributeName.slice(0, i);
|
|
for (const p of prefixes) {
|
|
if (prefix === p) {
|
|
const type = attributeName.slice(i);
|
|
if (type !== "env") {
|
|
const method = target.hasAttribute(attributeName)
|
|
? "add"
|
|
: "remove";
|
|
target[`${method}EventListener`](type, listener);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
for (const node of addedNodes) {
|
|
if (node.nodeType === 1) {
|
|
if (node.matches(RUNTIME_SELECTOR)) handle(node);
|
|
else {
|
|
$$(RUNTIME_SELECTOR, node).forEach(handle);
|
|
if (!PLUGINS_SELECTORS.length) continue;
|
|
handlePlugin(node);
|
|
$$(PLUGINS_SELECTORS.join(","), node).forEach(handlePlugin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const observe = (root) => {
|
|
mo.observe(root, { childList: true, subtree: true, attributes: true });
|
|
return root;
|
|
};
|
|
|
|
const { attachShadow } = Element.prototype;
|
|
assign(Element.prototype, {
|
|
attachShadow(init) {
|
|
return observe(attachShadow.call(this, init));
|
|
},
|
|
});
|
|
|
|
$$(RUNTIME_SELECTOR, observe(document)).forEach(handle);
|