const output = document.querySelector("#run-output"); if (output) { let refreshTimer; const refresh = async () => { const response = await fetch(output.dataset.outputUrl, { cache: "no-store" }); if (response.ok) { const nextOutput = (await response.json()).output; if (nextOutput !== output.textContent) { output.textContent = nextOutput; } } const status = document.querySelector("#run-status"); if (status) { const stateResponse = await fetch(status.dataset.stateUrl, { cache: "no-store" }); if (stateResponse.ok) { const state = await stateResponse.json(); status.textContent = state.status; if (state.status !== "queued" && state.status !== "running") { window.clearInterval(refreshTimer); } } } }; refresh(); refreshTimer = window.setInterval(refresh, 2000); } const refreshArtifacts = async (container) => { const response = await fetch(container.dataset.artifactsUrl, { cache: "no-store" }); if (!response.ok) { return []; } const artifacts = (await response.json()).artifacts; const list = container.querySelector("ul"); list.replaceChildren(); for (const artifact of artifacts) { const item = document.createElement("li"); const link = document.createElement("a"); if (artifact.view_url) { link.href = artifact.view_url; link.target = "_blank"; link.rel = "noopener"; link.textContent = `Open ${artifact.name}`; } else { link.href = artifact.url; link.textContent = `Download ${artifact.name}`; } item.append(link); list.append(item); } container.hidden = artifacts.length === 0; return artifacts; }; for (const container of document.querySelectorAll("#run-artifacts")) { refreshArtifacts(container); window.setInterval(() => refreshArtifacts(container), 2000); } for (const choice of document.querySelectorAll("button[data-execution-choice]")) { choice.addEventListener("click", () => { const control = choice.closest(".split-button"); const value = control?.querySelector("input[data-execution-value]"); const label = control?.querySelector(".button-label"); if (!control || !value || !label) { return; } value.value = choice.dataset.value || ""; label.textContent = choice.dataset.label || "Run"; control.querySelectorAll("button[data-execution-choice]").forEach((option) => { option.setAttribute("aria-pressed", String(option === choice)); }); const menu = control.querySelector("details"); if (menu) { menu.open = false; } }); } for (const form of document.querySelectorAll("form[data-wait-for-result]")) { const result = form.querySelector(".task-result"); const controls = [...form.querySelectorAll("input, select, button")]; let waiting = false; const warnBeforeLeaving = (event) => { event.preventDefault(); event.returnValue = ""; }; const setWaiting = (value) => { waiting = value; form.classList.toggle("is-waiting", value); controls.forEach((control) => { control.disabled = value; }); form.querySelectorAll("details.split-menu").forEach((menu) => { menu.open = false; }); if (value) { window.addEventListener("beforeunload", warnBeforeLeaving); } else { window.removeEventListener("beforeunload", warnBeforeLeaving); } }; const showResult = (message, kind, detailUrl) => { result.replaceChildren(); result.className = `task-result ${kind}`; result.append(message); if (detailUrl) { const link = document.createElement("a"); link.href = detailUrl; link.textContent = "View details"; result.append(" ", link); } }; const downloadArtifacts = async (run) => { const response = await fetch(run.artifacts_url, { cache: "no-store" }); if (!response.ok) { return; } const artifacts = (await response.json()).artifacts; const artifact = artifacts.find((item) => !item.view_url); if (!artifact) { return; } const link = document.createElement("a"); link.href = artifact.url; link.download = ""; document.body.append(link); link.click(); link.remove(); }; const waitForRun = async (run) => { while (true) { const response = await fetch(run.state_url, { cache: "no-store" }); if (!response.ok) { throw new Error("Could not read task status."); } const state = await response.json(); if (state.status !== "queued" && state.status !== "running") { if (state.status === "succeeded") { showResult("Completed successfully.", "succeeded", run.detail_url); if (form.dataset.downloadArtifacts !== undefined) { await downloadArtifacts(run); } } else { showResult(`Finished with status: ${state.status}.`, "failed", run.detail_url); } return; } await new Promise((resolve) => window.setTimeout(resolve, 500)); } }; form.addEventListener("submit", async (event) => { if (waiting) { event.preventDefault(); return; } if (!form.reportValidity()) { return; } event.preventDefault(); // Disabled fields are omitted from FormData, so collect values first. const formData = new FormData(form); setWaiting(true); showResult("Running...", "running"); try { const response = await fetch(form.action, { method: "POST", body: formData, headers: { Accept: "application/json" }, }); const data = await response.json(); if (!response.ok) { const messages = data.errors ? Object.values(data.errors).join(" ") : "Could not start task."; throw new Error(messages); } await waitForRun(data); } catch (error) { showResult(error instanceof Error ? error.message : "Could not run task.", "failed"); } finally { setWaiting(false); } }); }