import * as Turbo from "https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/+esm"; Turbo.session.drive = false; const turboFrameEventsForFrameLoad = []; function getHeaderIfExists(event, header) { return ( event.detail && event.detail.fetchResponse && event.detail.fetchResponse.response && event.detail.fetchResponse.response.headers.get(header) ); } addEventListener("turbo:before-fetch-response", (event) => { const eventStreams = getHeaderIfExists(event, "X-Turbo-Frame-Dispatch-Js-Event"); if (eventStreams && eventStreams !== "") { const eventNames = eventStreams.split(",").map((e) => e.trimStart()); // headers.get returns a list of comma separated strings when the same header exists multiple times. What kind of an API design is that 🥷? for (const eventName of eventNames) { dispatchEvent(new Event(eventName)); } } const eventStreamsFrameLoad = getHeaderIfExists(event, "X-Turbo-Frame-Dispatch-Js-Event-Frame-Load"); if (eventStreamsFrameLoad && eventStreamsFrameLoad !== "") { const eventNames = eventStreamsFrameLoad.split(",").map((e) => e.trimStart()); // headers.get returns a list of comma separated strings when the same header exists multiple times. What kind of an API design is that 🥷? for (const eventName of eventNames) { turboFrameEventsForFrameLoad.push(eventName); } } const escUrl = getHeaderIfExists(event, "X-Turbo-Frame-Esc"); if (escUrl && escUrl !== "") { location.assign(escUrl); if (location.hash !== "") { location.reload(); } } const visitUrl = getHeaderIfExists(event, "X-Turbo-Frame-Esc-Visit"); if (visitUrl && visitUrl !== "") { Turbo.visit(visitUrl); } const otherFrameId = getHeaderIfExists(event, "X-Turbo-Frame-Target-Id"); if (otherFrameId && otherFrameId !== "") { const src = getHeaderIfExists(event, "X-Turbo-Frame-Target-Src"); if (!src || src === "") { console.warn(`missing src to load turbo-frame ${otherFrameId}`); return; } const otherFrame = document.getElementById(otherFrameId); if (!otherFrame) { console.warn(`cannot find turbo-frame ${otherFrameId}`); return; } otherFrame.src = src; } }); addEventListener("turbo:before-frame-render", (eventBeforeFrameRender) => { const newFrame = eventBeforeFrameRender.detail.newFrame; const orgTurboElements = newFrame.querySelectorAll("turbo-element"); if (orgTurboElements.length === 0) { return; } const targetTurboFrameId1 = newFrame.getAttribute("id"); const turboElements = []; for (const orgTurboElement of orgTurboElements) { const action = orgTurboElement.getAttribute("action"); const tpl = orgTurboElement.querySelector("template"); if (!tpl && action !== "remove") { console.warn("the first and only child of turbo-element must be template"); return; } const turboElement = { action: action, target: orgTurboElement.getAttribute("target"), tplContent: tpl.content.cloneNode(true), }; turboElements.push(turboElement); } addEventListener("turbo:frame-load", (eventFrameLoad) => { const targetTurboFrameId2 = eventFrameLoad.target.getAttribute("id"); if (targetTurboFrameId1 !== targetTurboFrameId2) { return; } for (const turboElement of turboElements) { const targetElement = document.getElementById(turboElement.target); if (!targetElement) { console.warn("turbo-element target element doesn't exists"); return; } switch (turboElement.action) { case "append": targetElement.appendChild(turboElement.tplContent); return; case "prepend": targetElement.prepend(turboElement.tplContent); return; case "replace": targetElement.replaceWith(turboElement.tplContent); return; case "before": targetElement.before(turboElement.tplContent); return; case "after": targetElement.after(turboElement.tplContent); return; } } }); }); addEventListener("turbo:frame-load", () => { for (; turboFrameEventsForFrameLoad.length > 0; ) { const eventName = turboFrameEventsForFrameLoad.shift(); dispatchEvent(new Event(eventName)); } }); function dispatchFetchEventsWithTargetId(eventPrefix, event) { if (event.target.tagName === "FORM") { const formId = event.target.id; if (formId !== "") { const fetchEvent = new Event(`${eventPrefix}:${formId}`); dispatchEvent(fetchEvent); } else { const turboFrame = event.target.closest("turbo-frame"); if (turboFrame) { const fetchEvent = new Event(`${eventPrefix}:${turboFrame.id}`); dispatchEvent(fetchEvent); } } } else { const fetchEvent = new Event(`${eventPrefix}:${event.target.id}`); dispatchEvent(fetchEvent); } } addEventListener("turbo:before-fetch-request", (event) => { dispatchFetchEventsWithTargetId("turbo-before-request", event); }); addEventListener("turbo:before-fetch-response", (event) => { dispatchFetchEventsWithTargetId("turbo-before-fetch-response", event); }); addEventListener("turbo:fetch-request-error", (event) => { dispatchFetchEventsWithTargetId("turbo-fetch-request-error", event); });