Project_Demo / classroom_static /pygame-support.js
steveyehtw's picture
Harden demo runtime and add browser Pygame runner
7547388
Raw History Blame Contribute Delete
2.64 kB
(() => {
let frame = null;
let ready = false;
let bootPromise = null;
let resolveBoot = null;
let rejectBoot = null;
let timeout = null;
let runId = 0;
let activeRunId = 0;
let onMessage = () => {};
function receive(event) {
if (!frame || event.source !== frame.contentWindow) return;
const message = event.data || {};
if (message.type === "ready") {
ready = true;
clearTimeout(timeout);
timeout = null;
resolveBoot?.();
resolveBoot = null;
rejectBoot = null;
bootPromise = null;
return;
}
if (message.type === "load_error") {
const error = new Error(message.text || "無法載入瀏覽器版 Pygame");
clearTimeout(timeout);
timeout = null;
rejectBoot?.(error);
resolveBoot = null;
rejectBoot = null;
bootPromise = null;
onMessage({ ...message, runId: activeRunId });
return;
}
if (message.runId && message.runId !== activeRunId) return;
onMessage(message);
}
window.addEventListener("message", receive);
window.ClassroomPygame = {
attach(target, handler) {
frame = target;
onMessage = handler;
},
isPygame(files) {
return Object.entries(files || {}).some(([path, code]) =>
path.toLowerCase().endsWith(".py") &&
/^\s*(?:import\s+pygame(?:\s|,|$)|from\s+pygame(?:\.|\s+import\s))/m.test(code)
);
},
async run(files) {
const thisRun = ++runId;
activeRunId = thisRun;
frame.hidden = false;
if (!ready) {
if (!bootPromise) {
bootPromise = new Promise((resolve, reject) => {
resolveBoot = resolve;
rejectBoot = reject;
timeout = setTimeout(() => {
const error = new Error("Pygame 載入逾時,請確認網路連線後重試。");
bootPromise = null;
resolveBoot = null;
rejectBoot = null;
reject(error);
}, 60000);
frame.src = "/classroom-assets/pygame-runner.html?v=2026fall-1";
});
}
await bootPromise;
}
if (thisRun !== runId) return;
frame.contentWindow.postMessage({ type: "run", runId: thisRun, files }, "*");
},
stop() {
++runId;
activeRunId = runId;
ready = false;
clearTimeout(timeout);
timeout = null;
if (rejectBoot) rejectBoot(new Error("Pygame 已停止"));
resolveBoot = null;
rejectBoot = null;
bootPromise = null;
if (frame) {
frame.hidden = true;
frame.src = "about:blank";
}
},
};
})();