diff --git a/lib/rdoc/generator/template/rails/_head.rhtml b/lib/rdoc/generator/template/rails/_head.rhtml
index cc3be53f..e7ed0efd 100644
--- a/lib/rdoc/generator/template/rails/_head.rhtml
+++ b/lib/rdoc/generator/template/rails/_head.rhtml
@@ -6,7 +6,7 @@
-
+
diff --git a/lib/rdoc/generator/template/rails/resources/js/@hotwired--turbo.js b/lib/rdoc/generator/template/rails/resources/js/@hotwired--turbo.js
index 303ecf2e..f1ebc032 100644
--- a/lib/rdoc/generator/template/rails/resources/js/@hotwired--turbo.js
+++ b/lib/rdoc/generator/template/rails/resources/js/@hotwired--turbo.js
@@ -2,3959 +2,3953 @@
Turbo 7.3.0
Copyright © 2023 37signals LLC
*/
-(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Turbo = {}));
-})(this, (function (exports) { 'use strict';
+(function () {
+ if (window.Reflect === undefined ||
+ window.customElements === undefined ||
+ window.customElements.polyfillWrapFlushCallback) {
+ return;
+ }
+ const BuiltInHTMLElement = HTMLElement;
+ const wrapperForTheName = {
+ HTMLElement: function HTMLElement() {
+ return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
+ },
+ };
+ window.HTMLElement = wrapperForTheName["HTMLElement"];
+ HTMLElement.prototype = BuiltInHTMLElement.prototype;
+ HTMLElement.prototype.constructor = HTMLElement;
+ Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
+})();
- (function () {
- if (window.Reflect === undefined ||
- window.customElements === undefined ||
- window.customElements.polyfillWrapFlushCallback) {
- return;
- }
- const BuiltInHTMLElement = HTMLElement;
- const wrapperForTheName = {
- HTMLElement: function HTMLElement() {
- return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
- },
- };
- window.HTMLElement = wrapperForTheName["HTMLElement"];
- HTMLElement.prototype = BuiltInHTMLElement.prototype;
- HTMLElement.prototype.constructor = HTMLElement;
- Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
- })();
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Javan Makhmali
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
- /**
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Javan Makhmali
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
+(function(prototype) {
+ if (typeof prototype.requestSubmit == "function") return
- (function(prototype) {
- if (typeof prototype.requestSubmit == "function") return
+ prototype.requestSubmit = function(submitter) {
+ if (submitter) {
+ validateSubmitter(submitter, this);
+ submitter.click();
+ } else {
+ submitter = document.createElement("input");
+ submitter.type = "submit";
+ submitter.hidden = true;
+ this.appendChild(submitter);
+ submitter.click();
+ this.removeChild(submitter);
+ }
+ };
- prototype.requestSubmit = function(submitter) {
- if (submitter) {
- validateSubmitter(submitter, this);
- submitter.click();
- } else {
- submitter = document.createElement("input");
- submitter.type = "submit";
- submitter.hidden = true;
- this.appendChild(submitter);
- submitter.click();
- this.removeChild(submitter);
- }
- };
+ function validateSubmitter(submitter, form) {
+ submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
+ submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
+ submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
+ }
- function validateSubmitter(submitter, form) {
- submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
- submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
- submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
- }
+ function raise(errorConstructor, message, name) {
+ throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
+ }
+})(HTMLFormElement.prototype);
- function raise(errorConstructor, message, name) {
- throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
- }
- })(HTMLFormElement.prototype);
+const submittersByForm = new WeakMap();
+function findSubmitterFromClickTarget(target) {
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
+ const candidate = element ? element.closest("input, button") : null;
+ return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
+}
+function clickCaptured(event) {
+ const submitter = findSubmitterFromClickTarget(event.target);
+ if (submitter && submitter.form) {
+ submittersByForm.set(submitter.form, submitter);
+ }
+}
+(function () {
+ if ("submitter" in Event.prototype)
+ return;
+ let prototype = window.Event.prototype;
+ if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
+ prototype = window.SubmitEvent.prototype;
+ }
+ else if ("SubmitEvent" in window) {
+ return;
+ }
+ addEventListener("click", clickCaptured, true);
+ Object.defineProperty(prototype, "submitter", {
+ get() {
+ if (this.type == "submit" && this.target instanceof HTMLFormElement) {
+ return submittersByForm.get(this.target);
+ }
+ },
+ });
+})();
- const submittersByForm = new WeakMap();
- function findSubmitterFromClickTarget(target) {
- const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
- const candidate = element ? element.closest("input, button") : null;
- return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
+var FrameLoadingStyle;
+(function (FrameLoadingStyle) {
+ FrameLoadingStyle["eager"] = "eager";
+ FrameLoadingStyle["lazy"] = "lazy";
+})(FrameLoadingStyle || (FrameLoadingStyle = {}));
+class FrameElement extends HTMLElement {
+ static get observedAttributes() {
+ return ["disabled", "complete", "loading", "src"];
}
- function clickCaptured(event) {
- const submitter = findSubmitterFromClickTarget(event.target);
- if (submitter && submitter.form) {
- submittersByForm.set(submitter.form, submitter);
- }
+ constructor() {
+ super();
+ this.loaded = Promise.resolve();
+ this.delegate = new FrameElement.delegateConstructor(this);
}
- (function () {
- if ("submitter" in Event.prototype)
- return;
- let prototype = window.Event.prototype;
- if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
- prototype = window.SubmitEvent.prototype;
- }
- else if ("SubmitEvent" in window) {
- return;
- }
- addEventListener("click", clickCaptured, true);
- Object.defineProperty(prototype, "submitter", {
- get() {
- if (this.type == "submit" && this.target instanceof HTMLFormElement) {
- return submittersByForm.get(this.target);
- }
- },
- });
- })();
-
- exports.FrameLoadingStyle = void 0;
- (function (FrameLoadingStyle) {
- FrameLoadingStyle["eager"] = "eager";
- FrameLoadingStyle["lazy"] = "lazy";
- })(exports.FrameLoadingStyle || (exports.FrameLoadingStyle = {}));
- class FrameElement extends HTMLElement {
- static get observedAttributes() {
- return ["disabled", "complete", "loading", "src"];
- }
- constructor() {
- super();
- this.loaded = Promise.resolve();
- this.delegate = new FrameElement.delegateConstructor(this);
- }
- connectedCallback() {
- this.delegate.connect();
- }
- disconnectedCallback() {
- this.delegate.disconnect();
- }
- reload() {
- return this.delegate.sourceURLReloaded();
- }
- attributeChangedCallback(name) {
- if (name == "loading") {
- this.delegate.loadingStyleChanged();
- }
- else if (name == "complete") {
- this.delegate.completeChanged();
- }
- else if (name == "src") {
- this.delegate.sourceURLChanged();
- }
- else {
- this.delegate.disabledChanged();
- }
+ connectedCallback() {
+ this.delegate.connect();
+ }
+ disconnectedCallback() {
+ this.delegate.disconnect();
+ }
+ reload() {
+ return this.delegate.sourceURLReloaded();
+ }
+ attributeChangedCallback(name) {
+ if (name == "loading") {
+ this.delegate.loadingStyleChanged();
}
- get src() {
- return this.getAttribute("src");
+ else if (name == "complete") {
+ this.delegate.completeChanged();
}
- set src(value) {
- if (value) {
- this.setAttribute("src", value);
- }
- else {
- this.removeAttribute("src");
- }
+ else if (name == "src") {
+ this.delegate.sourceURLChanged();
}
- get loading() {
- return frameLoadingStyleFromString(this.getAttribute("loading") || "");
+ else {
+ this.delegate.disabledChanged();
}
- set loading(value) {
- if (value) {
- this.setAttribute("loading", value);
- }
- else {
- this.removeAttribute("loading");
- }
+ }
+ get src() {
+ return this.getAttribute("src");
+ }
+ set src(value) {
+ if (value) {
+ this.setAttribute("src", value);
}
- get disabled() {
- return this.hasAttribute("disabled");
+ else {
+ this.removeAttribute("src");
}
- set disabled(value) {
- if (value) {
- this.setAttribute("disabled", "");
- }
- else {
- this.removeAttribute("disabled");
- }
+ }
+ get loading() {
+ return frameLoadingStyleFromString(this.getAttribute("loading") || "");
+ }
+ set loading(value) {
+ if (value) {
+ this.setAttribute("loading", value);
}
- get autoscroll() {
- return this.hasAttribute("autoscroll");
+ else {
+ this.removeAttribute("loading");
}
- set autoscroll(value) {
- if (value) {
- this.setAttribute("autoscroll", "");
- }
- else {
- this.removeAttribute("autoscroll");
- }
+ }
+ get disabled() {
+ return this.hasAttribute("disabled");
+ }
+ set disabled(value) {
+ if (value) {
+ this.setAttribute("disabled", "");
}
- get complete() {
- return !this.delegate.isLoading;
+ else {
+ this.removeAttribute("disabled");
}
- get isActive() {
- return this.ownerDocument === document && !this.isPreview;
+ }
+ get autoscroll() {
+ return this.hasAttribute("autoscroll");
+ }
+ set autoscroll(value) {
+ if (value) {
+ this.setAttribute("autoscroll", "");
}
- get isPreview() {
- var _a, _b;
- return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
+ else {
+ this.removeAttribute("autoscroll");
}
}
- function frameLoadingStyleFromString(style) {
- switch (style.toLowerCase()) {
- case "lazy":
- return exports.FrameLoadingStyle.lazy;
- default:
- return exports.FrameLoadingStyle.eager;
- }
+ get complete() {
+ return !this.delegate.isLoading;
+ }
+ get isActive() {
+ return this.ownerDocument === document && !this.isPreview;
+ }
+ get isPreview() {
+ var _a, _b;
+ return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
}
+}
+function frameLoadingStyleFromString(style) {
+ switch (style.toLowerCase()) {
+ case "lazy":
+ return FrameLoadingStyle.lazy;
+ default:
+ return FrameLoadingStyle.eager;
+ }
+}
+
+function expandURL(locatable) {
+ return new URL(locatable.toString(), document.baseURI);
+}
+function getAnchor(url) {
+ let anchorMatch;
+ if (url.hash) {
+ return url.hash.slice(1);
+ }
+ else if ((anchorMatch = url.href.match(/#(.*)$/))) {
+ return anchorMatch[1];
+ }
+}
+function getAction(form, submitter) {
+ const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
+ return expandURL(action);
+}
+function getExtension(url) {
+ return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
+}
+function isHTML(url) {
+ return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
+}
+function isPrefixedBy(baseURL, url) {
+ const prefix = getPrefix(url);
+ return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
+}
+function locationIsVisitable(location, rootLocation) {
+ return isPrefixedBy(location, rootLocation) && isHTML(location);
+}
+function getRequestURL(url) {
+ const anchor = getAnchor(url);
+ return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
+}
+function toCacheKey(url) {
+ return getRequestURL(url);
+}
+function urlsAreEqual(left, right) {
+ return expandURL(left).href == expandURL(right).href;
+}
+function getPathComponents(url) {
+ return url.pathname.split("/").slice(1);
+}
+function getLastPathComponent(url) {
+ return getPathComponents(url).slice(-1)[0];
+}
+function getPrefix(url) {
+ return addTrailingSlash(url.origin + url.pathname);
+}
+function addTrailingSlash(value) {
+ return value.endsWith("/") ? value : value + "/";
+}
- function expandURL(locatable) {
- return new URL(locatable.toString(), document.baseURI);
+class FetchResponse {
+ constructor(response) {
+ this.response = response;
}
- function getAnchor(url) {
- let anchorMatch;
- if (url.hash) {
- return url.hash.slice(1);
- }
- else if ((anchorMatch = url.href.match(/#(.*)$/))) {
- return anchorMatch[1];
- }
+ get succeeded() {
+ return this.response.ok;
}
- function getAction(form, submitter) {
- const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
- return expandURL(action);
+ get failed() {
+ return !this.succeeded;
}
- function getExtension(url) {
- return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
+ get clientError() {
+ return this.statusCode >= 400 && this.statusCode <= 499;
}
- function isHTML(url) {
- return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
+ get serverError() {
+ return this.statusCode >= 500 && this.statusCode <= 599;
}
- function isPrefixedBy(baseURL, url) {
- const prefix = getPrefix(url);
- return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
+ get redirected() {
+ return this.response.redirected;
}
- function locationIsVisitable(location, rootLocation) {
- return isPrefixedBy(location, rootLocation) && isHTML(location);
+ get location() {
+ return expandURL(this.response.url);
}
- function getRequestURL(url) {
- const anchor = getAnchor(url);
- return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
+ get isHTML() {
+ return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
}
- function toCacheKey(url) {
- return getRequestURL(url);
+ get statusCode() {
+ return this.response.status;
}
- function urlsAreEqual(left, right) {
- return expandURL(left).href == expandURL(right).href;
+ get contentType() {
+ return this.header("Content-Type");
}
- function getPathComponents(url) {
- return url.pathname.split("/").slice(1);
+ get responseText() {
+ return this.response.clone().text();
}
- function getLastPathComponent(url) {
- return getPathComponents(url).slice(-1)[0];
+ get responseHTML() {
+ if (this.isHTML) {
+ return this.response.clone().text();
+ }
+ else {
+ return Promise.resolve(undefined);
+ }
}
- function getPrefix(url) {
- return addTrailingSlash(url.origin + url.pathname);
+ header(name) {
+ return this.response.headers.get(name);
}
- function addTrailingSlash(value) {
- return value.endsWith("/") ? value : value + "/";
+}
+
+function activateScriptElement(element) {
+ if (element.getAttribute("data-turbo-eval") == "false") {
+ return element;
+ }
+ else {
+ const createdScriptElement = document.createElement("script");
+ const cspNonce = getMetaContent("csp-nonce");
+ if (cspNonce) {
+ createdScriptElement.nonce = cspNonce;
+ }
+ createdScriptElement.textContent = element.textContent;
+ createdScriptElement.async = false;
+ copyElementAttributes(createdScriptElement, element);
+ return createdScriptElement;
+ }
+}
+function copyElementAttributes(destinationElement, sourceElement) {
+ for (const { name, value } of sourceElement.attributes) {
+ destinationElement.setAttribute(name, value);
+ }
+}
+function createDocumentFragment(html) {
+ const template = document.createElement("template");
+ template.innerHTML = html;
+ return template.content;
+}
+function dispatch(eventName, { target, cancelable, detail } = {}) {
+ const event = new CustomEvent(eventName, {
+ cancelable,
+ bubbles: true,
+ composed: true,
+ detail,
+ });
+ if (target && target.isConnected) {
+ target.dispatchEvent(event);
+ }
+ else {
+ document.documentElement.dispatchEvent(event);
+ }
+ return event;
+}
+function nextAnimationFrame() {
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
+}
+function nextEventLoopTick() {
+ return new Promise((resolve) => setTimeout(() => resolve(), 0));
+}
+function nextMicrotask() {
+ return Promise.resolve();
+}
+function parseHTMLDocument(html = "") {
+ return new DOMParser().parseFromString(html, "text/html");
+}
+function unindent(strings, ...values) {
+ const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
+ const match = lines[0].match(/^\s+/);
+ const indent = match ? match[0].length : 0;
+ return lines.map((line) => line.slice(indent)).join("\n");
+}
+function interpolate(strings, values) {
+ return strings.reduce((result, string, i) => {
+ const value = values[i] == undefined ? "" : values[i];
+ return result + string + value;
+ }, "");
+}
+function uuid() {
+ return Array.from({ length: 36 })
+ .map((_, i) => {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ return "-";
+ }
+ else if (i == 14) {
+ return "4";
+ }
+ else if (i == 19) {
+ return (Math.floor(Math.random() * 4) + 8).toString(16);
+ }
+ else {
+ return Math.floor(Math.random() * 15).toString(16);
+ }
+ })
+ .join("");
+}
+function getAttribute(attributeName, ...elements) {
+ for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
+ if (typeof value == "string")
+ return value;
}
+ return null;
+}
+function hasAttribute(attributeName, ...elements) {
+ return elements.some((element) => element && element.hasAttribute(attributeName));
+}
+function markAsBusy(...elements) {
+ for (const element of elements) {
+ if (element.localName == "turbo-frame") {
+ element.setAttribute("busy", "");
+ }
+ element.setAttribute("aria-busy", "true");
+ }
+}
+function clearBusyState(...elements) {
+ for (const element of elements) {
+ if (element.localName == "turbo-frame") {
+ element.removeAttribute("busy");
+ }
+ element.removeAttribute("aria-busy");
+ }
+}
+function waitForLoad(element, timeoutInMilliseconds = 2000) {
+ return new Promise((resolve) => {
+ const onComplete = () => {
+ element.removeEventListener("error", onComplete);
+ element.removeEventListener("load", onComplete);
+ resolve();
+ };
+ element.addEventListener("load", onComplete, { once: true });
+ element.addEventListener("error", onComplete, { once: true });
+ setTimeout(resolve, timeoutInMilliseconds);
+ });
+}
+function getHistoryMethodForAction(action) {
+ switch (action) {
+ case "replace":
+ return history.replaceState;
+ case "advance":
+ case "restore":
+ return history.pushState;
+ }
+}
+function isAction(action) {
+ return action == "advance" || action == "replace" || action == "restore";
+}
+function getVisitAction(...elements) {
+ const action = getAttribute("data-turbo-action", ...elements);
+ return isAction(action) ? action : null;
+}
+function getMetaElement(name) {
+ return document.querySelector(`meta[name="${name}"]`);
+}
+function getMetaContent(name) {
+ const element = getMetaElement(name);
+ return element && element.content;
+}
+function setMetaContent(name, content) {
+ let element = getMetaElement(name);
+ if (!element) {
+ element = document.createElement("meta");
+ element.setAttribute("name", name);
+ document.head.appendChild(element);
+ }
+ element.setAttribute("content", content);
+ return element;
+}
+function findClosestRecursively(element, selector) {
+ var _a;
+ if (element instanceof Element) {
+ return (element.closest(selector) ||
+ findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
+ }
+}
- class FetchResponse {
- constructor(response) {
- this.response = response;
+var FetchMethod;
+(function (FetchMethod) {
+ FetchMethod[FetchMethod["get"] = 0] = "get";
+ FetchMethod[FetchMethod["post"] = 1] = "post";
+ FetchMethod[FetchMethod["put"] = 2] = "put";
+ FetchMethod[FetchMethod["patch"] = 3] = "patch";
+ FetchMethod[FetchMethod["delete"] = 4] = "delete";
+})(FetchMethod || (FetchMethod = {}));
+function fetchMethodFromString(method) {
+ switch (method.toLowerCase()) {
+ case "get":
+ return FetchMethod.get;
+ case "post":
+ return FetchMethod.post;
+ case "put":
+ return FetchMethod.put;
+ case "patch":
+ return FetchMethod.patch;
+ case "delete":
+ return FetchMethod.delete;
+ }
+}
+class FetchRequest {
+ constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
+ this.abortController = new AbortController();
+ this.resolveRequestPromise = (_value) => { };
+ this.delegate = delegate;
+ this.method = method;
+ this.headers = this.defaultHeaders;
+ this.body = body;
+ this.url = location;
+ this.target = target;
+ }
+ get location() {
+ return this.url;
+ }
+ get params() {
+ return this.url.searchParams;
+ }
+ get entries() {
+ return this.body ? Array.from(this.body.entries()) : [];
+ }
+ cancel() {
+ this.abortController.abort();
+ }
+ async perform() {
+ const { fetchOptions } = this;
+ this.delegate.prepareRequest(this);
+ await this.allowRequestToBeIntercepted(fetchOptions);
+ try {
+ this.delegate.requestStarted(this);
+ const response = await fetch(this.url.href, fetchOptions);
+ return await this.receive(response);
+ }
+ catch (error) {
+ if (error.name !== "AbortError") {
+ if (this.willDelegateErrorHandling(error)) {
+ this.delegate.requestErrored(this, error);
+ }
+ throw error;
+ }
+ }
+ finally {
+ this.delegate.requestFinished(this);
+ }
+ }
+ async receive(response) {
+ const fetchResponse = new FetchResponse(response);
+ const event = dispatch("turbo:before-fetch-response", {
+ cancelable: true,
+ detail: { fetchResponse },
+ target: this.target,
+ });
+ if (event.defaultPrevented) {
+ this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
}
- get succeeded() {
- return this.response.ok;
+ else if (fetchResponse.succeeded) {
+ this.delegate.requestSucceededWithResponse(this, fetchResponse);
+ }
+ else {
+ this.delegate.requestFailedWithResponse(this, fetchResponse);
}
- get failed() {
- return !this.succeeded;
+ return fetchResponse;
+ }
+ get fetchOptions() {
+ var _a;
+ return {
+ method: FetchMethod[this.method].toUpperCase(),
+ credentials: "same-origin",
+ headers: this.headers,
+ redirect: "follow",
+ body: this.isSafe ? null : this.body,
+ signal: this.abortSignal,
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
+ };
+ }
+ get defaultHeaders() {
+ return {
+ Accept: "text/html, application/xhtml+xml",
+ };
+ }
+ get isSafe() {
+ return this.method === FetchMethod.get;
+ }
+ get abortSignal() {
+ return this.abortController.signal;
+ }
+ acceptResponseType(mimeType) {
+ this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
+ }
+ async allowRequestToBeIntercepted(fetchOptions) {
+ const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
+ const event = dispatch("turbo:before-fetch-request", {
+ cancelable: true,
+ detail: {
+ fetchOptions,
+ url: this.url,
+ resume: this.resolveRequestPromise,
+ },
+ target: this.target,
+ });
+ if (event.defaultPrevented)
+ await requestInterception;
+ }
+ willDelegateErrorHandling(error) {
+ const event = dispatch("turbo:fetch-request-error", {
+ target: this.target,
+ cancelable: true,
+ detail: { request: this, error: error },
+ });
+ return !event.defaultPrevented;
+ }
+}
+
+class AppearanceObserver {
+ constructor(delegate, element) {
+ this.started = false;
+ this.intersect = (entries) => {
+ const lastEntry = entries.slice(-1)[0];
+ if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
+ this.delegate.elementAppearedInViewport(this.element);
+ }
+ };
+ this.delegate = delegate;
+ this.element = element;
+ this.intersectionObserver = new IntersectionObserver(this.intersect);
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ this.intersectionObserver.observe(this.element);
}
- get clientError() {
- return this.statusCode >= 400 && this.statusCode <= 499;
+ }
+ stop() {
+ if (this.started) {
+ this.started = false;
+ this.intersectionObserver.unobserve(this.element);
}
- get serverError() {
- return this.statusCode >= 500 && this.statusCode <= 599;
+ }
+}
+
+class StreamMessage {
+ static wrap(message) {
+ if (typeof message == "string") {
+ return new this(createDocumentFragment(message));
}
- get redirected() {
- return this.response.redirected;
+ else {
+ return message;
}
- get location() {
- return expandURL(this.response.url);
+ }
+ constructor(fragment) {
+ this.fragment = importStreamElements(fragment);
+ }
+}
+StreamMessage.contentType = "text/vnd.turbo-stream.html";
+function importStreamElements(fragment) {
+ for (const element of fragment.querySelectorAll("turbo-stream")) {
+ const streamElement = document.importNode(element, true);
+ for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
+ inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
}
- get isHTML() {
- return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
+ element.replaceWith(streamElement);
+ }
+ return fragment;
+}
+
+var FormSubmissionState;
+(function (FormSubmissionState) {
+ FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
+ FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
+ FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
+ FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
+ FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
+ FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
+})(FormSubmissionState || (FormSubmissionState = {}));
+var FormEnctype;
+(function (FormEnctype) {
+ FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
+ FormEnctype["multipart"] = "multipart/form-data";
+ FormEnctype["plain"] = "text/plain";
+})(FormEnctype || (FormEnctype = {}));
+function formEnctypeFromString(encoding) {
+ switch (encoding.toLowerCase()) {
+ case FormEnctype.multipart:
+ return FormEnctype.multipart;
+ case FormEnctype.plain:
+ return FormEnctype.plain;
+ default:
+ return FormEnctype.urlEncoded;
+ }
+}
+class FormSubmission {
+ static confirmMethod(message, _element, _submitter) {
+ return Promise.resolve(confirm(message));
+ }
+ constructor(delegate, formElement, submitter, mustRedirect = false) {
+ this.state = FormSubmissionState.initialized;
+ this.delegate = delegate;
+ this.formElement = formElement;
+ this.submitter = submitter;
+ this.formData = buildFormData(formElement, submitter);
+ this.location = expandURL(this.action);
+ if (this.method == FetchMethod.get) {
+ mergeFormDataEntries(this.location, [...this.body.entries()]);
+ }
+ this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
+ this.mustRedirect = mustRedirect;
+ }
+ get method() {
+ var _a;
+ const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
+ return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
+ }
+ get action() {
+ var _a;
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
+ if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
+ return this.submitter.getAttribute("formaction") || "";
}
- get statusCode() {
- return this.response.status;
+ else {
+ return this.formElement.getAttribute("action") || formElementAction || "";
}
- get contentType() {
- return this.header("Content-Type");
+ }
+ get body() {
+ if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
+ return new URLSearchParams(this.stringFormData);
}
- get responseText() {
- return this.response.clone().text();
+ else {
+ return this.formData;
}
- get responseHTML() {
- if (this.isHTML) {
- return this.response.clone().text();
- }
- else {
- return Promise.resolve(undefined);
+ }
+ get enctype() {
+ var _a;
+ return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
+ }
+ get isSafe() {
+ return this.fetchRequest.isSafe;
+ }
+ get stringFormData() {
+ return [...this.formData].reduce((entries, [name, value]) => {
+ return entries.concat(typeof value == "string" ? [[name, value]] : []);
+ }, []);
+ }
+ async start() {
+ const { initialized, requesting } = FormSubmissionState;
+ const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
+ if (typeof confirmationMessage === "string") {
+ const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
+ if (!answer) {
+ return;
}
}
- header(name) {
- return this.response.headers.get(name);
+ if (this.state == initialized) {
+ this.state = requesting;
+ return this.fetchRequest.perform();
}
}
-
- function activateScriptElement(element) {
- if (element.getAttribute("data-turbo-eval") == "false") {
- return element;
+ stop() {
+ const { stopping, stopped } = FormSubmissionState;
+ if (this.state != stopping && this.state != stopped) {
+ this.state = stopping;
+ this.fetchRequest.cancel();
+ return true;
}
- else {
- const createdScriptElement = document.createElement("script");
- const cspNonce = getMetaContent("csp-nonce");
- if (cspNonce) {
- createdScriptElement.nonce = cspNonce;
+ }
+ prepareRequest(request) {
+ if (!request.isSafe) {
+ const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
+ if (token) {
+ request.headers["X-CSRF-Token"] = token;
}
- createdScriptElement.textContent = element.textContent;
- createdScriptElement.async = false;
- copyElementAttributes(createdScriptElement, element);
- return createdScriptElement;
}
+ if (this.requestAcceptsTurboStreamResponse(request)) {
+ request.acceptResponseType(StreamMessage.contentType);
+ }
+ }
+ requestStarted(_request) {
+ var _a;
+ this.state = FormSubmissionState.waiting;
+ (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
+ this.setSubmitsWith();
+ dispatch("turbo:submit-start", {
+ target: this.formElement,
+ detail: { formSubmission: this },
+ });
+ this.delegate.formSubmissionStarted(this);
+ }
+ requestPreventedHandlingResponse(request, response) {
+ this.result = { success: response.succeeded, fetchResponse: response };
}
- function copyElementAttributes(destinationElement, sourceElement) {
- for (const { name, value } of sourceElement.attributes) {
- destinationElement.setAttribute(name, value);
+ requestSucceededWithResponse(request, response) {
+ if (response.clientError || response.serverError) {
+ this.delegate.formSubmissionFailedWithResponse(this, response);
+ }
+ else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
+ const error = new Error("Form responses must redirect to another location");
+ this.delegate.formSubmissionErrored(this, error);
+ }
+ else {
+ this.state = FormSubmissionState.receiving;
+ this.result = { success: true, fetchResponse: response };
+ this.delegate.formSubmissionSucceededWithResponse(this, response);
}
}
- function createDocumentFragment(html) {
- const template = document.createElement("template");
- template.innerHTML = html;
- return template.content;
+ requestFailedWithResponse(request, response) {
+ this.result = { success: false, fetchResponse: response };
+ this.delegate.formSubmissionFailedWithResponse(this, response);
}
- function dispatch(eventName, { target, cancelable, detail } = {}) {
- const event = new CustomEvent(eventName, {
- cancelable,
- bubbles: true,
- composed: true,
- detail,
+ requestErrored(request, error) {
+ this.result = { success: false, error };
+ this.delegate.formSubmissionErrored(this, error);
+ }
+ requestFinished(_request) {
+ var _a;
+ this.state = FormSubmissionState.stopped;
+ (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
+ this.resetSubmitterText();
+ dispatch("turbo:submit-end", {
+ target: this.formElement,
+ detail: Object.assign({ formSubmission: this }, this.result),
});
- if (target && target.isConnected) {
- target.dispatchEvent(event);
+ this.delegate.formSubmissionFinished(this);
+ }
+ setSubmitsWith() {
+ if (!this.submitter || !this.submitsWith)
+ return;
+ if (this.submitter.matches("button")) {
+ this.originalSubmitText = this.submitter.innerHTML;
+ this.submitter.innerHTML = this.submitsWith;
}
- else {
- document.documentElement.dispatchEvent(event);
+ else if (this.submitter.matches("input")) {
+ const input = this.submitter;
+ this.originalSubmitText = input.value;
+ input.value = this.submitsWith;
+ }
+ }
+ resetSubmitterText() {
+ if (!this.submitter || !this.originalSubmitText)
+ return;
+ if (this.submitter.matches("button")) {
+ this.submitter.innerHTML = this.originalSubmitText;
}
- return event;
+ else if (this.submitter.matches("input")) {
+ const input = this.submitter;
+ input.value = this.originalSubmitText;
+ }
+ }
+ requestMustRedirect(request) {
+ return !request.isSafe && this.mustRedirect;
}
- function nextAnimationFrame() {
- return new Promise((resolve) => requestAnimationFrame(() => resolve()));
+ requestAcceptsTurboStreamResponse(request) {
+ return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
}
- function nextEventLoopTick() {
- return new Promise((resolve) => setTimeout(() => resolve(), 0));
+ get submitsWith() {
+ var _a;
+ return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
+ }
+}
+function buildFormData(formElement, submitter) {
+ const formData = new FormData(formElement);
+ const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
+ const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
+ if (name) {
+ formData.append(name, value || "");
+ }
+ return formData;
+}
+function getCookieValue(cookieName) {
+ if (cookieName != null) {
+ const cookies = document.cookie ? document.cookie.split("; ") : [];
+ const cookie = cookies.find((cookie) => cookie.startsWith(cookieName));
+ if (cookie) {
+ const value = cookie.split("=").slice(1).join("=");
+ return value ? decodeURIComponent(value) : undefined;
+ }
+ }
+}
+function responseSucceededWithoutRedirect(response) {
+ return response.statusCode == 200 && !response.redirected;
+}
+function mergeFormDataEntries(url, entries) {
+ const searchParams = new URLSearchParams();
+ for (const [name, value] of entries) {
+ if (value instanceof File)
+ continue;
+ searchParams.append(name, value);
+ }
+ url.search = searchParams.toString();
+ return url;
+}
+
+class Snapshot {
+ constructor(element) {
+ this.element = element;
}
- function nextMicrotask() {
- return Promise.resolve();
+ get activeElement() {
+ return this.element.ownerDocument.activeElement;
}
- function parseHTMLDocument(html = "") {
- return new DOMParser().parseFromString(html, "text/html");
+ get children() {
+ return [...this.element.children];
}
- function unindent(strings, ...values) {
- const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
- const match = lines[0].match(/^\s+/);
- const indent = match ? match[0].length : 0;
- return lines.map((line) => line.slice(indent)).join("\n");
+ hasAnchor(anchor) {
+ return this.getElementForAnchor(anchor) != null;
}
- function interpolate(strings, values) {
- return strings.reduce((result, string, i) => {
- const value = values[i] == undefined ? "" : values[i];
- return result + string + value;
- }, "");
+ getElementForAnchor(anchor) {
+ return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
}
- function uuid() {
- return Array.from({ length: 36 })
- .map((_, i) => {
- if (i == 8 || i == 13 || i == 18 || i == 23) {
- return "-";
- }
- else if (i == 14) {
- return "4";
- }
- else if (i == 19) {
- return (Math.floor(Math.random() * 4) + 8).toString(16);
- }
- else {
- return Math.floor(Math.random() * 15).toString(16);
- }
- })
- .join("");
+ get isConnected() {
+ return this.element.isConnected;
}
- function getAttribute(attributeName, ...elements) {
- for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
- if (typeof value == "string")
- return value;
+ get firstAutofocusableElement() {
+ const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
+ for (const element of this.element.querySelectorAll("[autofocus]")) {
+ if (element.closest(inertDisabledOrHidden) == null)
+ return element;
+ else
+ continue;
}
return null;
}
- function hasAttribute(attributeName, ...elements) {
- return elements.some((element) => element && element.hasAttribute(attributeName));
+ get permanentElements() {
+ return queryPermanentElementsAll(this.element);
}
- function markAsBusy(...elements) {
- for (const element of elements) {
- if (element.localName == "turbo-frame") {
- element.setAttribute("busy", "");
+ getPermanentElementById(id) {
+ return getPermanentElementById(this.element, id);
+ }
+ getPermanentElementMapForSnapshot(snapshot) {
+ const permanentElementMap = {};
+ for (const currentPermanentElement of this.permanentElements) {
+ const { id } = currentPermanentElement;
+ const newPermanentElement = snapshot.getPermanentElementById(id);
+ if (newPermanentElement) {
+ permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
}
- element.setAttribute("aria-busy", "true");
}
+ return permanentElementMap;
}
- function clearBusyState(...elements) {
- for (const element of elements) {
- if (element.localName == "turbo-frame") {
- element.removeAttribute("busy");
+}
+function getPermanentElementById(node, id) {
+ return node.querySelector(`#${id}[data-turbo-permanent]`);
+}
+function queryPermanentElementsAll(node) {
+ return node.querySelectorAll("[id][data-turbo-permanent]");
+}
+
+class FormSubmitObserver {
+ constructor(delegate, eventTarget) {
+ this.started = false;
+ this.submitCaptured = () => {
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
+ };
+ this.submitBubbled = ((event) => {
+ if (!event.defaultPrevented) {
+ const form = event.target instanceof HTMLFormElement ? event.target : undefined;
+ const submitter = event.submitter || undefined;
+ if (form &&
+ submissionDoesNotDismissDialog(form, submitter) &&
+ submissionDoesNotTargetIFrame(form, submitter) &&
+ this.delegate.willSubmitForm(form, submitter)) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ this.delegate.formSubmitted(form, submitter);
+ }
}
- element.removeAttribute("aria-busy");
+ });
+ this.delegate = delegate;
+ this.eventTarget = eventTarget;
+ }
+ start() {
+ if (!this.started) {
+ this.eventTarget.addEventListener("submit", this.submitCaptured, true);
+ this.started = true;
}
}
- function waitForLoad(element, timeoutInMilliseconds = 2000) {
- return new Promise((resolve) => {
- const onComplete = () => {
- element.removeEventListener("error", onComplete);
- element.removeEventListener("load", onComplete);
- resolve();
- };
- element.addEventListener("load", onComplete, { once: true });
- element.addEventListener("error", onComplete, { once: true });
- setTimeout(resolve, timeoutInMilliseconds);
- });
+ stop() {
+ if (this.started) {
+ this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
+ this.started = false;
+ }
}
- function getHistoryMethodForAction(action) {
- switch (action) {
- case "replace":
- return history.replaceState;
- case "advance":
- case "restore":
- return history.pushState;
+}
+function submissionDoesNotDismissDialog(form, submitter) {
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
+ return method != "dialog";
+}
+function submissionDoesNotTargetIFrame(form, submitter) {
+ if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
+ const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
+ for (const element of document.getElementsByName(target)) {
+ if (element instanceof HTMLIFrameElement)
+ return false;
}
+ return true;
}
- function isAction(action) {
- return action == "advance" || action == "replace" || action == "restore";
+ else {
+ return true;
}
- function getVisitAction(...elements) {
- const action = getAttribute("data-turbo-action", ...elements);
- return isAction(action) ? action : null;
+}
+
+class View {
+ constructor(delegate, element) {
+ this.resolveRenderPromise = (_value) => { };
+ this.resolveInterceptionPromise = (_value) => { };
+ this.delegate = delegate;
+ this.element = element;
+ }
+ scrollToAnchor(anchor) {
+ const element = this.snapshot.getElementForAnchor(anchor);
+ if (element) {
+ this.scrollToElement(element);
+ this.focusElement(element);
+ }
+ else {
+ this.scrollToPosition({ x: 0, y: 0 });
+ }
}
- function getMetaElement(name) {
- return document.querySelector(`meta[name="${name}"]`);
+ scrollToAnchorFromLocation(location) {
+ this.scrollToAnchor(getAnchor(location));
}
- function getMetaContent(name) {
- const element = getMetaElement(name);
- return element && element.content;
+ scrollToElement(element) {
+ element.scrollIntoView();
}
- function setMetaContent(name, content) {
- let element = getMetaElement(name);
- if (!element) {
- element = document.createElement("meta");
- element.setAttribute("name", name);
- document.head.appendChild(element);
+ focusElement(element) {
+ if (element instanceof HTMLElement) {
+ if (element.hasAttribute("tabindex")) {
+ element.focus();
+ }
+ else {
+ element.setAttribute("tabindex", "-1");
+ element.focus();
+ element.removeAttribute("tabindex");
+ }
}
- element.setAttribute("content", content);
- return element;
}
- function findClosestRecursively(element, selector) {
- var _a;
- if (element instanceof Element) {
- return (element.closest(selector) ||
- findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
- }
+ scrollToPosition({ x, y }) {
+ this.scrollRoot.scrollTo(x, y);
}
-
- var FetchMethod;
- (function (FetchMethod) {
- FetchMethod[FetchMethod["get"] = 0] = "get";
- FetchMethod[FetchMethod["post"] = 1] = "post";
- FetchMethod[FetchMethod["put"] = 2] = "put";
- FetchMethod[FetchMethod["patch"] = 3] = "patch";
- FetchMethod[FetchMethod["delete"] = 4] = "delete";
- })(FetchMethod || (FetchMethod = {}));
- function fetchMethodFromString(method) {
- switch (method.toLowerCase()) {
- case "get":
- return FetchMethod.get;
- case "post":
- return FetchMethod.post;
- case "put":
- return FetchMethod.put;
- case "patch":
- return FetchMethod.patch;
- case "delete":
- return FetchMethod.delete;
- }
- }
- class FetchRequest {
- constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
- this.abortController = new AbortController();
- this.resolveRequestPromise = (_value) => { };
- this.delegate = delegate;
- this.method = method;
- this.headers = this.defaultHeaders;
- this.body = body;
- this.url = location;
- this.target = target;
- }
- get location() {
- return this.url;
- }
- get params() {
- return this.url.searchParams;
- }
- get entries() {
- return this.body ? Array.from(this.body.entries()) : [];
- }
- cancel() {
- this.abortController.abort();
- }
- async perform() {
- const { fetchOptions } = this;
- this.delegate.prepareRequest(this);
- await this.allowRequestToBeIntercepted(fetchOptions);
+ scrollToTop() {
+ this.scrollToPosition({ x: 0, y: 0 });
+ }
+ get scrollRoot() {
+ return window;
+ }
+ async render(renderer) {
+ const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
+ if (shouldRender) {
try {
- this.delegate.requestStarted(this);
- const response = await fetch(this.url.href, fetchOptions);
- return await this.receive(response);
- }
- catch (error) {
- if (error.name !== "AbortError") {
- if (this.willDelegateErrorHandling(error)) {
- this.delegate.requestErrored(this, error);
- }
- throw error;
- }
+ this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
+ this.renderer = renderer;
+ await this.prepareToRenderSnapshot(renderer);
+ const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
+ const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
+ const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
+ if (!immediateRender)
+ await renderInterception;
+ await this.renderSnapshot(renderer);
+ this.delegate.viewRenderedSnapshot(snapshot, isPreview);
+ this.delegate.preloadOnLoadLinksForView(this.element);
+ this.finishRenderingSnapshot(renderer);
}
finally {
- this.delegate.requestFinished(this);
- }
- }
- async receive(response) {
- const fetchResponse = new FetchResponse(response);
- const event = dispatch("turbo:before-fetch-response", {
- cancelable: true,
- detail: { fetchResponse },
- target: this.target,
- });
- if (event.defaultPrevented) {
- this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
- }
- else if (fetchResponse.succeeded) {
- this.delegate.requestSucceededWithResponse(this, fetchResponse);
+ delete this.renderer;
+ this.resolveRenderPromise(undefined);
+ delete this.renderPromise;
}
- else {
- this.delegate.requestFailedWithResponse(this, fetchResponse);
- }
- return fetchResponse;
}
- get fetchOptions() {
- var _a;
- return {
- method: FetchMethod[this.method].toUpperCase(),
- credentials: "same-origin",
- headers: this.headers,
- redirect: "follow",
- body: this.isSafe ? null : this.body,
- signal: this.abortSignal,
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
- };
+ else {
+ this.invalidate(renderer.reloadReason);
}
- get defaultHeaders() {
- return {
- Accept: "text/html, application/xhtml+xml",
- };
+ }
+ invalidate(reason) {
+ this.delegate.viewInvalidated(reason);
+ }
+ async prepareToRenderSnapshot(renderer) {
+ this.markAsPreview(renderer.isPreview);
+ await renderer.prepareToRender();
+ }
+ markAsPreview(isPreview) {
+ if (isPreview) {
+ this.element.setAttribute("data-turbo-preview", "");
}
- get isSafe() {
- return this.method === FetchMethod.get;
- }
- get abortSignal() {
- return this.abortController.signal;
- }
- acceptResponseType(mimeType) {
- this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
- }
- async allowRequestToBeIntercepted(fetchOptions) {
- const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
- const event = dispatch("turbo:before-fetch-request", {
- cancelable: true,
- detail: {
- fetchOptions,
- url: this.url,
- resume: this.resolveRequestPromise,
- },
- target: this.target,
- });
- if (event.defaultPrevented)
- await requestInterception;
- }
- willDelegateErrorHandling(error) {
- const event = dispatch("turbo:fetch-request-error", {
- target: this.target,
- cancelable: true,
- detail: { request: this, error: error },
- });
- return !event.defaultPrevented;
+ else {
+ this.element.removeAttribute("data-turbo-preview");
}
}
+ async renderSnapshot(renderer) {
+ await renderer.render();
+ }
+ finishRenderingSnapshot(renderer) {
+ renderer.finishRendering();
+ }
+}
- class AppearanceObserver {
- constructor(delegate, element) {
- this.started = false;
- this.intersect = (entries) => {
- const lastEntry = entries.slice(-1)[0];
- if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
- this.delegate.elementAppearedInViewport(this.element);
- }
- };
- this.delegate = delegate;
- this.element = element;
- this.intersectionObserver = new IntersectionObserver(this.intersect);
- }
- start() {
- if (!this.started) {
- this.started = true;
- this.intersectionObserver.observe(this.element);
- }
- }
- stop() {
- if (this.started) {
- this.started = false;
- this.intersectionObserver.unobserve(this.element);
- }
- }
+class FrameView extends View {
+ missing() {
+ this.element.innerHTML = `Content missing`;
+ }
+ get snapshot() {
+ return new Snapshot(this.element);
}
+}
- class StreamMessage {
- static wrap(message) {
- if (typeof message == "string") {
- return new this(createDocumentFragment(message));
+class LinkInterceptor {
+ constructor(delegate, element) {
+ this.clickBubbled = (event) => {
+ if (this.respondsToEventTarget(event.target)) {
+ this.clickEvent = event;
}
else {
- return message;
+ delete this.clickEvent;
}
- }
- constructor(fragment) {
- this.fragment = importStreamElements(fragment);
- }
- }
- StreamMessage.contentType = "text/vnd.turbo-stream.html";
- function importStreamElements(fragment) {
- for (const element of fragment.querySelectorAll("turbo-stream")) {
- const streamElement = document.importNode(element, true);
- for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
- inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
+ };
+ this.linkClicked = ((event) => {
+ if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
+ if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
+ this.clickEvent.preventDefault();
+ event.preventDefault();
+ this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
+ }
}
- element.replaceWith(streamElement);
- }
- return fragment;
+ delete this.clickEvent;
+ });
+ this.willVisit = ((_event) => {
+ delete this.clickEvent;
+ });
+ this.delegate = delegate;
+ this.element = element;
+ }
+ start() {
+ this.element.addEventListener("click", this.clickBubbled);
+ document.addEventListener("turbo:click", this.linkClicked);
+ document.addEventListener("turbo:before-visit", this.willVisit);
+ }
+ stop() {
+ this.element.removeEventListener("click", this.clickBubbled);
+ document.removeEventListener("turbo:click", this.linkClicked);
+ document.removeEventListener("turbo:before-visit", this.willVisit);
}
+ respondsToEventTarget(target) {
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
+ return element && element.closest("turbo-frame, html") == this.element;
+ }
+}
- var FormSubmissionState;
- (function (FormSubmissionState) {
- FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
- FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
- FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
- FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
- FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
- FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
- })(FormSubmissionState || (FormSubmissionState = {}));
- var FormEnctype;
- (function (FormEnctype) {
- FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
- FormEnctype["multipart"] = "multipart/form-data";
- FormEnctype["plain"] = "text/plain";
- })(FormEnctype || (FormEnctype = {}));
- function formEnctypeFromString(encoding) {
- switch (encoding.toLowerCase()) {
- case FormEnctype.multipart:
- return FormEnctype.multipart;
- case FormEnctype.plain:
- return FormEnctype.plain;
- default:
- return FormEnctype.urlEncoded;
+class LinkClickObserver {
+ constructor(delegate, eventTarget) {
+ this.started = false;
+ this.clickCaptured = () => {
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
+ };
+ this.clickBubbled = (event) => {
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
+ const target = (event.composedPath && event.composedPath()[0]) || event.target;
+ const link = this.findLinkFromClickTarget(target);
+ if (link && doesNotTargetIFrame(link)) {
+ const location = this.getLocationForLink(link);
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
+ event.preventDefault();
+ this.delegate.followedLinkToLocation(link, location);
+ }
+ }
+ }
+ };
+ this.delegate = delegate;
+ this.eventTarget = eventTarget;
+ }
+ start() {
+ if (!this.started) {
+ this.eventTarget.addEventListener("click", this.clickCaptured, true);
+ this.started = true;
}
}
- class FormSubmission {
- static confirmMethod(message, _element, _submitter) {
- return Promise.resolve(confirm(message));
+ stop() {
+ if (this.started) {
+ this.eventTarget.removeEventListener("click", this.clickCaptured, true);
+ this.started = false;
}
- constructor(delegate, formElement, submitter, mustRedirect = false) {
- this.state = FormSubmissionState.initialized;
- this.delegate = delegate;
- this.formElement = formElement;
- this.submitter = submitter;
- this.formData = buildFormData(formElement, submitter);
- this.location = expandURL(this.action);
- if (this.method == FetchMethod.get) {
- mergeFormDataEntries(this.location, [...this.body.entries()]);
- }
- this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
- this.mustRedirect = mustRedirect;
- }
- get method() {
- var _a;
- const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
- return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
- }
- get action() {
- var _a;
- const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
- if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
- return this.submitter.getAttribute("formaction") || "";
- }
- else {
- return this.formElement.getAttribute("action") || formElementAction || "";
- }
- }
- get body() {
- if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
- return new URLSearchParams(this.stringFormData);
- }
- else {
- return this.formData;
- }
+ }
+ clickEventIsSignificant(event) {
+ return !((event.target && event.target.isContentEditable) ||
+ event.defaultPrevented ||
+ event.which > 1 ||
+ event.altKey ||
+ event.ctrlKey ||
+ event.metaKey ||
+ event.shiftKey);
+ }
+ findLinkFromClickTarget(target) {
+ return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
+ }
+ getLocationForLink(link) {
+ return expandURL(link.getAttribute("href") || "");
+ }
+}
+function doesNotTargetIFrame(anchor) {
+ if (anchor.hasAttribute("target")) {
+ for (const element of document.getElementsByName(anchor.target)) {
+ if (element instanceof HTMLIFrameElement)
+ return false;
}
- get enctype() {
- var _a;
- return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
+ return true;
+ }
+ else {
+ return true;
+ }
+}
+
+class FormLinkClickObserver {
+ constructor(delegate, element) {
+ this.delegate = delegate;
+ this.linkInterceptor = new LinkClickObserver(this, element);
+ }
+ start() {
+ this.linkInterceptor.start();
+ }
+ stop() {
+ this.linkInterceptor.stop();
+ }
+ willFollowLinkToLocation(link, location, originalEvent) {
+ return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
+ link.hasAttribute("data-turbo-method"));
+ }
+ followedLinkToLocation(link, location) {
+ const form = document.createElement("form");
+ const type = "hidden";
+ for (const [name, value] of location.searchParams) {
+ form.append(Object.assign(document.createElement("input"), { type, name, value }));
+ }
+ const action = Object.assign(location, { search: "" });
+ form.setAttribute("data-turbo", "true");
+ form.setAttribute("action", action.href);
+ form.setAttribute("hidden", "");
+ const method = link.getAttribute("data-turbo-method");
+ if (method)
+ form.setAttribute("method", method);
+ const turboFrame = link.getAttribute("data-turbo-frame");
+ if (turboFrame)
+ form.setAttribute("data-turbo-frame", turboFrame);
+ const turboAction = getVisitAction(link);
+ if (turboAction)
+ form.setAttribute("data-turbo-action", turboAction);
+ const turboConfirm = link.getAttribute("data-turbo-confirm");
+ if (turboConfirm)
+ form.setAttribute("data-turbo-confirm", turboConfirm);
+ const turboStream = link.hasAttribute("data-turbo-stream");
+ if (turboStream)
+ form.setAttribute("data-turbo-stream", "");
+ this.delegate.submittedFormLinkToLocation(link, location, form);
+ document.body.appendChild(form);
+ form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
+ requestAnimationFrame(() => form.requestSubmit());
+ }
+}
+
+class Bardo {
+ static async preservingPermanentElements(delegate, permanentElementMap, callback) {
+ const bardo = new this(delegate, permanentElementMap);
+ bardo.enter();
+ await callback();
+ bardo.leave();
+ }
+ constructor(delegate, permanentElementMap) {
+ this.delegate = delegate;
+ this.permanentElementMap = permanentElementMap;
+ }
+ enter() {
+ for (const id in this.permanentElementMap) {
+ const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
+ this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
+ this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
+ }
+ }
+ leave() {
+ for (const id in this.permanentElementMap) {
+ const [currentPermanentElement] = this.permanentElementMap[id];
+ this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
+ this.replacePlaceholderWithPermanentElement(currentPermanentElement);
+ this.delegate.leavingBardo(currentPermanentElement);
+ }
+ }
+ replaceNewPermanentElementWithPlaceholder(permanentElement) {
+ const placeholder = createPlaceholderForPermanentElement(permanentElement);
+ permanentElement.replaceWith(placeholder);
+ }
+ replaceCurrentPermanentElementWithClone(permanentElement) {
+ const clone = permanentElement.cloneNode(true);
+ permanentElement.replaceWith(clone);
+ }
+ replacePlaceholderWithPermanentElement(permanentElement) {
+ const placeholder = this.getPlaceholderById(permanentElement.id);
+ placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
+ }
+ getPlaceholderById(id) {
+ return this.placeholders.find((element) => element.content == id);
+ }
+ get placeholders() {
+ return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
+ }
+}
+function createPlaceholderForPermanentElement(permanentElement) {
+ const element = document.createElement("meta");
+ element.setAttribute("name", "turbo-permanent-placeholder");
+ element.setAttribute("content", permanentElement.id);
+ return element;
+}
+
+class Renderer {
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
+ this.activeElement = null;
+ this.currentSnapshot = currentSnapshot;
+ this.newSnapshot = newSnapshot;
+ this.isPreview = isPreview;
+ this.willRender = willRender;
+ this.renderElement = renderElement;
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
+ }
+ get shouldRender() {
+ return true;
+ }
+ get reloadReason() {
+ return;
+ }
+ prepareToRender() {
+ return;
+ }
+ finishRendering() {
+ if (this.resolvingFunctions) {
+ this.resolvingFunctions.resolve();
+ delete this.resolvingFunctions;
}
- get isSafe() {
- return this.fetchRequest.isSafe;
+ }
+ async preservingPermanentElements(callback) {
+ await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
+ }
+ focusFirstAutofocusableElement() {
+ const element = this.connectedSnapshot.firstAutofocusableElement;
+ if (elementIsFocusable(element)) {
+ element.focus();
}
- get stringFormData() {
- return [...this.formData].reduce((entries, [name, value]) => {
- return entries.concat(typeof value == "string" ? [[name, value]] : []);
- }, []);
+ }
+ enteringBardo(currentPermanentElement) {
+ if (this.activeElement)
+ return;
+ if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
+ this.activeElement = this.currentSnapshot.activeElement;
}
- async start() {
- const { initialized, requesting } = FormSubmissionState;
- const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
- if (typeof confirmationMessage === "string") {
- const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
- if (!answer) {
- return;
- }
- }
- if (this.state == initialized) {
- this.state = requesting;
- return this.fetchRequest.perform();
- }
+ }
+ leavingBardo(currentPermanentElement) {
+ if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
+ this.activeElement.focus();
+ this.activeElement = null;
}
- stop() {
- const { stopping, stopped } = FormSubmissionState;
- if (this.state != stopping && this.state != stopped) {
- this.state = stopping;
- this.fetchRequest.cancel();
+ }
+ get connectedSnapshot() {
+ return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
+ }
+ get currentElement() {
+ return this.currentSnapshot.element;
+ }
+ get newElement() {
+ return this.newSnapshot.element;
+ }
+ get permanentElementMap() {
+ return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
+ }
+}
+function elementIsFocusable(element) {
+ return element && typeof element.focus == "function";
+}
+
+class FrameRenderer extends Renderer {
+ static renderElement(currentElement, newElement) {
+ var _a;
+ const destinationRange = document.createRange();
+ destinationRange.selectNodeContents(currentElement);
+ destinationRange.deleteContents();
+ const frameElement = newElement;
+ const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
+ if (sourceRange) {
+ sourceRange.selectNodeContents(frameElement);
+ currentElement.appendChild(sourceRange.extractContents());
+ }
+ }
+ constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
+ super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
+ this.delegate = delegate;
+ }
+ get shouldRender() {
+ return true;
+ }
+ async render() {
+ await nextAnimationFrame();
+ this.preservingPermanentElements(() => {
+ this.loadFrameElement();
+ });
+ this.scrollFrameIntoView();
+ await nextAnimationFrame();
+ this.focusFirstAutofocusableElement();
+ await nextAnimationFrame();
+ this.activateScriptElements();
+ }
+ loadFrameElement() {
+ this.delegate.willRenderFrame(this.currentElement, this.newElement);
+ this.renderElement(this.currentElement, this.newElement);
+ }
+ scrollFrameIntoView() {
+ if (this.currentElement.autoscroll || this.newElement.autoscroll) {
+ const element = this.currentElement.firstElementChild;
+ const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
+ const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
+ if (element) {
+ element.scrollIntoView({ block, behavior });
return true;
}
}
- prepareRequest(request) {
- if (!request.isSafe) {
- const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
- if (token) {
- request.headers["X-CSRF-Token"] = token;
- }
- }
- if (this.requestAcceptsTurboStreamResponse(request)) {
- request.acceptResponseType(StreamMessage.contentType);
- }
- }
- requestStarted(_request) {
- var _a;
- this.state = FormSubmissionState.waiting;
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
- this.setSubmitsWith();
- dispatch("turbo:submit-start", {
- target: this.formElement,
- detail: { formSubmission: this },
- });
- this.delegate.formSubmissionStarted(this);
- }
- requestPreventedHandlingResponse(request, response) {
- this.result = { success: response.succeeded, fetchResponse: response };
- }
- requestSucceededWithResponse(request, response) {
- if (response.clientError || response.serverError) {
- this.delegate.formSubmissionFailedWithResponse(this, response);
- }
- else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
- const error = new Error("Form responses must redirect to another location");
- this.delegate.formSubmissionErrored(this, error);
- }
- else {
- this.state = FormSubmissionState.receiving;
- this.result = { success: true, fetchResponse: response };
- this.delegate.formSubmissionSucceededWithResponse(this, response);
- }
- }
- requestFailedWithResponse(request, response) {
- this.result = { success: false, fetchResponse: response };
- this.delegate.formSubmissionFailedWithResponse(this, response);
- }
- requestErrored(request, error) {
- this.result = { success: false, error };
- this.delegate.formSubmissionErrored(this, error);
- }
- requestFinished(_request) {
- var _a;
- this.state = FormSubmissionState.stopped;
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
- this.resetSubmitterText();
- dispatch("turbo:submit-end", {
- target: this.formElement,
- detail: Object.assign({ formSubmission: this }, this.result),
- });
- this.delegate.formSubmissionFinished(this);
- }
- setSubmitsWith() {
- if (!this.submitter || !this.submitsWith)
- return;
- if (this.submitter.matches("button")) {
- this.originalSubmitText = this.submitter.innerHTML;
- this.submitter.innerHTML = this.submitsWith;
- }
- else if (this.submitter.matches("input")) {
- const input = this.submitter;
- this.originalSubmitText = input.value;
- input.value = this.submitsWith;
- }
- }
- resetSubmitterText() {
- if (!this.submitter || !this.originalSubmitText)
- return;
- if (this.submitter.matches("button")) {
- this.submitter.innerHTML = this.originalSubmitText;
- }
- else if (this.submitter.matches("input")) {
- const input = this.submitter;
- input.value = this.originalSubmitText;
- }
- }
- requestMustRedirect(request) {
- return !request.isSafe && this.mustRedirect;
- }
- requestAcceptsTurboStreamResponse(request) {
- return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
- }
- get submitsWith() {
- var _a;
- return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
- }
+ return false;
}
- function buildFormData(formElement, submitter) {
- const formData = new FormData(formElement);
- const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
- const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
- if (name) {
- formData.append(name, value || "");
+ activateScriptElements() {
+ for (const inertScriptElement of this.newScriptElements) {
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
+ inertScriptElement.replaceWith(activatedScriptElement);
}
- return formData;
}
- function getCookieValue(cookieName) {
- if (cookieName != null) {
- const cookies = document.cookie ? document.cookie.split("; ") : [];
- const cookie = cookies.find((cookie) => cookie.startsWith(cookieName));
- if (cookie) {
- const value = cookie.split("=").slice(1).join("=");
- return value ? decodeURIComponent(value) : undefined;
- }
- }
+ get newScriptElements() {
+ return this.currentElement.querySelectorAll("script");
}
- function responseSucceededWithoutRedirect(response) {
- return response.statusCode == 200 && !response.redirected;
+}
+function readScrollLogicalPosition(value, defaultValue) {
+ if (value == "end" || value == "start" || value == "center" || value == "nearest") {
+ return value;
}
- function mergeFormDataEntries(url, entries) {
- const searchParams = new URLSearchParams();
- for (const [name, value] of entries) {
- if (value instanceof File)
- continue;
- searchParams.append(name, value);
- }
- url.search = searchParams.toString();
- return url;
+ else {
+ return defaultValue;
+ }
+}
+function readScrollBehavior(value, defaultValue) {
+ if (value == "auto" || value == "smooth") {
+ return value;
+ }
+ else {
+ return defaultValue;
}
+}
- class Snapshot {
- constructor(element) {
- this.element = element;
- }
- get activeElement() {
- return this.element.ownerDocument.activeElement;
- }
- get children() {
- return [...this.element.children];
- }
- hasAnchor(anchor) {
- return this.getElementForAnchor(anchor) != null;
- }
- getElementForAnchor(anchor) {
- return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
- }
- get isConnected() {
- return this.element.isConnected;
- }
- get firstAutofocusableElement() {
- const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
- for (const element of this.element.querySelectorAll("[autofocus]")) {
- if (element.closest(inertDisabledOrHidden) == null)
- return element;
- else
- continue;
- }
- return null;
+class ProgressBar {
+ static get defaultCSS() {
+ return unindent `
+ .turbo-progress-bar {
+ position: fixed;
+ display: block;
+ top: 0;
+ left: 0;
+ height: 3px;
+ background: #0076ff;
+ z-index: 2147483647;
+ transition:
+ width ${ProgressBar.animationDuration}ms ease-out,
+ opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
+ transform: translate3d(0, 0, 0);
+ }
+ `;
+ }
+ constructor() {
+ this.hiding = false;
+ this.value = 0;
+ this.visible = false;
+ this.trickle = () => {
+ this.setValue(this.value + Math.random() / 100);
+ };
+ this.stylesheetElement = this.createStylesheetElement();
+ this.progressElement = this.createProgressElement();
+ this.installStylesheetElement();
+ this.setValue(0);
+ }
+ show() {
+ if (!this.visible) {
+ this.visible = true;
+ this.installProgressElement();
+ this.startTrickling();
+ }
+ }
+ hide() {
+ if (this.visible && !this.hiding) {
+ this.hiding = true;
+ this.fadeProgressElement(() => {
+ this.uninstallProgressElement();
+ this.stopTrickling();
+ this.visible = false;
+ this.hiding = false;
+ });
}
- get permanentElements() {
- return queryPermanentElementsAll(this.element);
+ }
+ setValue(value) {
+ this.value = value;
+ this.refresh();
+ }
+ installStylesheetElement() {
+ document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
+ }
+ installProgressElement() {
+ this.progressElement.style.width = "0";
+ this.progressElement.style.opacity = "1";
+ document.documentElement.insertBefore(this.progressElement, document.body);
+ this.refresh();
+ }
+ fadeProgressElement(callback) {
+ this.progressElement.style.opacity = "0";
+ setTimeout(callback, ProgressBar.animationDuration * 1.5);
+ }
+ uninstallProgressElement() {
+ if (this.progressElement.parentNode) {
+ document.documentElement.removeChild(this.progressElement);
}
- getPermanentElementById(id) {
- return getPermanentElementById(this.element, id);
+ }
+ startTrickling() {
+ if (!this.trickleInterval) {
+ this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
}
- getPermanentElementMapForSnapshot(snapshot) {
- const permanentElementMap = {};
- for (const currentPermanentElement of this.permanentElements) {
- const { id } = currentPermanentElement;
- const newPermanentElement = snapshot.getPermanentElementById(id);
- if (newPermanentElement) {
- permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
- }
- }
- return permanentElementMap;
+ }
+ stopTrickling() {
+ window.clearInterval(this.trickleInterval);
+ delete this.trickleInterval;
+ }
+ refresh() {
+ requestAnimationFrame(() => {
+ this.progressElement.style.width = `${10 + this.value * 90}%`;
+ });
+ }
+ createStylesheetElement() {
+ const element = document.createElement("style");
+ element.type = "text/css";
+ element.textContent = ProgressBar.defaultCSS;
+ if (this.cspNonce) {
+ element.nonce = this.cspNonce;
}
+ return element;
}
- function getPermanentElementById(node, id) {
- return node.querySelector(`#${id}[data-turbo-permanent]`);
+ createProgressElement() {
+ const element = document.createElement("div");
+ element.className = "turbo-progress-bar";
+ return element;
}
- function queryPermanentElementsAll(node) {
- return node.querySelectorAll("[id][data-turbo-permanent]");
+ get cspNonce() {
+ return getMetaContent("csp-nonce");
}
+}
+ProgressBar.animationDuration = 300;
- class FormSubmitObserver {
- constructor(delegate, eventTarget) {
- this.started = false;
- this.submitCaptured = () => {
- this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
- this.eventTarget.addEventListener("submit", this.submitBubbled, false);
- };
- this.submitBubbled = ((event) => {
- if (!event.defaultPrevented) {
- const form = event.target instanceof HTMLFormElement ? event.target : undefined;
- const submitter = event.submitter || undefined;
- if (form &&
- submissionDoesNotDismissDialog(form, submitter) &&
- submissionDoesNotTargetIFrame(form, submitter) &&
- this.delegate.willSubmitForm(form, submitter)) {
- event.preventDefault();
- event.stopImmediatePropagation();
- this.delegate.formSubmitted(form, submitter);
- }
- }
- });
- this.delegate = delegate;
- this.eventTarget = eventTarget;
- }
- start() {
- if (!this.started) {
- this.eventTarget.addEventListener("submit", this.submitCaptured, true);
- this.started = true;
+class HeadSnapshot extends Snapshot {
+ constructor() {
+ super(...arguments);
+ this.detailsByOuterHTML = this.children
+ .filter((element) => !elementIsNoscript(element))
+ .map((element) => elementWithoutNonce(element))
+ .reduce((result, element) => {
+ const { outerHTML } = element;
+ const details = outerHTML in result
+ ? result[outerHTML]
+ : {
+ type: elementType(element),
+ tracked: elementIsTracked(element),
+ elements: [],
+ };
+ return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
+ }, {});
+ }
+ get trackedElementSignature() {
+ return Object.keys(this.detailsByOuterHTML)
+ .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
+ .join("");
+ }
+ getScriptElementsNotInSnapshot(snapshot) {
+ return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
+ }
+ getStylesheetElementsNotInSnapshot(snapshot) {
+ return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
+ }
+ getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
+ return Object.keys(this.detailsByOuterHTML)
+ .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
+ .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
+ .filter(({ type }) => type == matchedType)
+ .map(({ elements: [element] }) => element);
+ }
+ get provisionalElements() {
+ return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
+ const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
+ if (type == null && !tracked) {
+ return [...result, ...elements];
}
- }
- stop() {
- if (this.started) {
- this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
- this.started = false;
+ else if (elements.length > 1) {
+ return [...result, ...elements.slice(1)];
}
- }
+ else {
+ return result;
+ }
+ }, []);
+ }
+ getMetaValue(name) {
+ const element = this.findMetaElementByName(name);
+ return element ? element.getAttribute("content") : null;
+ }
+ findMetaElementByName(name) {
+ return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
+ const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
+ return elementIsMetaElementWithName(element, name) ? element : result;
+ }, undefined);
+ }
+}
+function elementType(element) {
+ if (elementIsScript(element)) {
+ return "script";
+ }
+ else if (elementIsStylesheet(element)) {
+ return "stylesheet";
+ }
+}
+function elementIsTracked(element) {
+ return element.getAttribute("data-turbo-track") == "reload";
+}
+function elementIsScript(element) {
+ const tagName = element.localName;
+ return tagName == "script";
+}
+function elementIsNoscript(element) {
+ const tagName = element.localName;
+ return tagName == "noscript";
+}
+function elementIsStylesheet(element) {
+ const tagName = element.localName;
+ return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
+}
+function elementIsMetaElementWithName(element, name) {
+ const tagName = element.localName;
+ return tagName == "meta" && element.getAttribute("name") == name;
+}
+function elementWithoutNonce(element) {
+ if (element.hasAttribute("nonce")) {
+ element.setAttribute("nonce", "");
+ }
+ return element;
+}
+
+class PageSnapshot extends Snapshot {
+ static fromHTMLString(html = "") {
+ return this.fromDocument(parseHTMLDocument(html));
}
- function submissionDoesNotDismissDialog(form, submitter) {
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
- return method != "dialog";
+ static fromElement(element) {
+ return this.fromDocument(element.ownerDocument);
}
- function submissionDoesNotTargetIFrame(form, submitter) {
- if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
- const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
- for (const element of document.getElementsByName(target)) {
- if (element instanceof HTMLIFrameElement)
- return false;
- }
- return true;
+ static fromDocument({ head, body }) {
+ return new this(body, new HeadSnapshot(head));
+ }
+ constructor(element, headSnapshot) {
+ super(element);
+ this.headSnapshot = headSnapshot;
+ }
+ clone() {
+ const clonedElement = this.element.cloneNode(true);
+ const selectElements = this.element.querySelectorAll("select");
+ const clonedSelectElements = clonedElement.querySelectorAll("select");
+ for (const [index, source] of selectElements.entries()) {
+ const clone = clonedSelectElements[index];
+ for (const option of clone.selectedOptions)
+ option.selected = false;
+ for (const option of source.selectedOptions)
+ clone.options[option.index].selected = true;
}
- else {
- return true;
+ for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
+ clonedPasswordInput.value = "";
}
+ return new PageSnapshot(clonedElement, this.headSnapshot);
+ }
+ get headElement() {
+ return this.headSnapshot.element;
+ }
+ get rootLocation() {
+ var _a;
+ const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
+ return expandURL(root);
+ }
+ get cacheControlValue() {
+ return this.getSetting("cache-control");
+ }
+ get isPreviewable() {
+ return this.cacheControlValue != "no-preview";
}
+ get isCacheable() {
+ return this.cacheControlValue != "no-cache";
+ }
+ get isVisitable() {
+ return this.getSetting("visit-control") != "reload";
+ }
+ getSetting(name) {
+ return this.headSnapshot.getMetaValue(`turbo-${name}`);
+ }
+}
- class View {
- constructor(delegate, element) {
- this.resolveRenderPromise = (_value) => { };
- this.resolveInterceptionPromise = (_value) => { };
- this.delegate = delegate;
- this.element = element;
- }
- scrollToAnchor(anchor) {
- const element = this.snapshot.getElementForAnchor(anchor);
- if (element) {
- this.scrollToElement(element);
- this.focusElement(element);
+var TimingMetric;
+(function (TimingMetric) {
+ TimingMetric["visitStart"] = "visitStart";
+ TimingMetric["requestStart"] = "requestStart";
+ TimingMetric["requestEnd"] = "requestEnd";
+ TimingMetric["visitEnd"] = "visitEnd";
+})(TimingMetric || (TimingMetric = {}));
+var VisitState;
+(function (VisitState) {
+ VisitState["initialized"] = "initialized";
+ VisitState["started"] = "started";
+ VisitState["canceled"] = "canceled";
+ VisitState["failed"] = "failed";
+ VisitState["completed"] = "completed";
+})(VisitState || (VisitState = {}));
+const defaultOptions = {
+ action: "advance",
+ historyChanged: false,
+ visitCachedSnapshot: () => { },
+ willRender: true,
+ updateHistory: true,
+ shouldCacheSnapshot: true,
+ acceptsStreamResponse: false,
+};
+var SystemStatusCode;
+(function (SystemStatusCode) {
+ SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
+ SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
+ SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
+})(SystemStatusCode || (SystemStatusCode = {}));
+class Visit {
+ constructor(delegate, location, restorationIdentifier, options = {}) {
+ this.identifier = uuid();
+ this.timingMetrics = {};
+ this.followedRedirect = false;
+ this.historyChanged = false;
+ this.scrolled = false;
+ this.shouldCacheSnapshot = true;
+ this.acceptsStreamResponse = false;
+ this.snapshotCached = false;
+ this.state = VisitState.initialized;
+ this.delegate = delegate;
+ this.location = location;
+ this.restorationIdentifier = restorationIdentifier || uuid();
+ const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
+ this.action = action;
+ this.historyChanged = historyChanged;
+ this.referrer = referrer;
+ this.snapshot = snapshot;
+ this.snapshotHTML = snapshotHTML;
+ this.response = response;
+ this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
+ this.visitCachedSnapshot = visitCachedSnapshot;
+ this.willRender = willRender;
+ this.updateHistory = updateHistory;
+ this.scrolled = !willRender;
+ this.shouldCacheSnapshot = shouldCacheSnapshot;
+ this.acceptsStreamResponse = acceptsStreamResponse;
+ }
+ get adapter() {
+ return this.delegate.adapter;
+ }
+ get view() {
+ return this.delegate.view;
+ }
+ get history() {
+ return this.delegate.history;
+ }
+ get restorationData() {
+ return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
+ }
+ get silent() {
+ return this.isSamePage;
+ }
+ start() {
+ if (this.state == VisitState.initialized) {
+ this.recordTimingMetric(TimingMetric.visitStart);
+ this.state = VisitState.started;
+ this.adapter.visitStarted(this);
+ this.delegate.visitStarted(this);
+ }
+ }
+ cancel() {
+ if (this.state == VisitState.started) {
+ if (this.request) {
+ this.request.cancel();
}
- else {
- this.scrollToPosition({ x: 0, y: 0 });
- }
- }
- scrollToAnchorFromLocation(location) {
- this.scrollToAnchor(getAnchor(location));
- }
- scrollToElement(element) {
- element.scrollIntoView();
+ this.cancelRender();
+ this.state = VisitState.canceled;
}
- focusElement(element) {
- if (element instanceof HTMLElement) {
- if (element.hasAttribute("tabindex")) {
- element.focus();
- }
- else {
- element.setAttribute("tabindex", "-1");
- element.focus();
- element.removeAttribute("tabindex");
- }
+ }
+ complete() {
+ if (this.state == VisitState.started) {
+ this.recordTimingMetric(TimingMetric.visitEnd);
+ this.state = VisitState.completed;
+ this.followRedirect();
+ if (!this.followedRedirect) {
+ this.adapter.visitCompleted(this);
+ this.delegate.visitCompleted(this);
}
}
- scrollToPosition({ x, y }) {
- this.scrollRoot.scrollTo(x, y);
+ }
+ fail() {
+ if (this.state == VisitState.started) {
+ this.state = VisitState.failed;
+ this.adapter.visitFailed(this);
}
- scrollToTop() {
- this.scrollToPosition({ x: 0, y: 0 });
+ }
+ changeHistory() {
+ var _a;
+ if (!this.historyChanged && this.updateHistory) {
+ const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
+ const method = getHistoryMethodForAction(actionForHistory);
+ this.history.update(method, this.location, this.restorationIdentifier);
+ this.historyChanged = true;
}
- get scrollRoot() {
- return window;
- }
- async render(renderer) {
- const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
- if (shouldRender) {
- try {
- this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
- this.renderer = renderer;
- await this.prepareToRenderSnapshot(renderer);
- const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
- const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
- const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
- if (!immediateRender)
- await renderInterception;
- await this.renderSnapshot(renderer);
- this.delegate.viewRenderedSnapshot(snapshot, isPreview);
- this.delegate.preloadOnLoadLinksForView(this.element);
- this.finishRenderingSnapshot(renderer);
- }
- finally {
- delete this.renderer;
- this.resolveRenderPromise(undefined);
- delete this.renderPromise;
- }
- }
- else {
- this.invalidate(renderer.reloadReason);
- }
+ }
+ issueRequest() {
+ if (this.hasPreloadedResponse()) {
+ this.simulateRequest();
}
- invalidate(reason) {
- this.delegate.viewInvalidated(reason);
+ else if (this.shouldIssueRequest() && !this.request) {
+ this.request = new FetchRequest(this, FetchMethod.get, this.location);
+ this.request.perform();
}
- async prepareToRenderSnapshot(renderer) {
- this.markAsPreview(renderer.isPreview);
- await renderer.prepareToRender();
+ }
+ simulateRequest() {
+ if (this.response) {
+ this.startRequest();
+ this.recordResponse();
+ this.finishRequest();
}
- markAsPreview(isPreview) {
- if (isPreview) {
- this.element.setAttribute("data-turbo-preview", "");
+ }
+ startRequest() {
+ this.recordTimingMetric(TimingMetric.requestStart);
+ this.adapter.visitRequestStarted(this);
+ }
+ recordResponse(response = this.response) {
+ this.response = response;
+ if (response) {
+ const { statusCode } = response;
+ if (isSuccessful(statusCode)) {
+ this.adapter.visitRequestCompleted(this);
}
else {
- this.element.removeAttribute("data-turbo-preview");
+ this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
}
}
- async renderSnapshot(renderer) {
- await renderer.render();
- }
- finishRenderingSnapshot(renderer) {
- renderer.finishRendering();
+ }
+ finishRequest() {
+ this.recordTimingMetric(TimingMetric.requestEnd);
+ this.adapter.visitRequestFinished(this);
+ }
+ loadResponse() {
+ if (this.response) {
+ const { statusCode, responseHTML } = this.response;
+ this.render(async () => {
+ if (this.shouldCacheSnapshot)
+ this.cacheSnapshot();
+ if (this.view.renderPromise)
+ await this.view.renderPromise;
+ if (isSuccessful(statusCode) && responseHTML != null) {
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
+ this.performScroll();
+ this.adapter.visitRendered(this);
+ this.complete();
+ }
+ else {
+ await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
+ this.adapter.visitRendered(this);
+ this.fail();
+ }
+ });
}
}
-
- class FrameView extends View {
- missing() {
- this.element.innerHTML = `Content missing`;
+ getCachedSnapshot() {
+ const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
+ if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
+ if (this.action == "restore" || snapshot.isPreviewable) {
+ return snapshot;
+ }
}
- get snapshot() {
- return new Snapshot(this.element);
+ }
+ getPreloadedSnapshot() {
+ if (this.snapshotHTML) {
+ return PageSnapshot.fromHTMLString(this.snapshotHTML);
}
}
-
- class LinkInterceptor {
- constructor(delegate, element) {
- this.clickBubbled = (event) => {
- if (this.respondsToEventTarget(event.target)) {
- this.clickEvent = event;
+ hasCachedSnapshot() {
+ return this.getCachedSnapshot() != null;
+ }
+ loadCachedSnapshot() {
+ const snapshot = this.getCachedSnapshot();
+ if (snapshot) {
+ const isPreview = this.shouldIssueRequest();
+ this.render(async () => {
+ this.cacheSnapshot();
+ if (this.isSamePage) {
+ this.adapter.visitRendered(this);
}
else {
- delete this.clickEvent;
- }
- };
- this.linkClicked = ((event) => {
- if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
- if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
- this.clickEvent.preventDefault();
- event.preventDefault();
- this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
+ if (this.view.renderPromise)
+ await this.view.renderPromise;
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
+ this.performScroll();
+ this.adapter.visitRendered(this);
+ if (!isPreview) {
+ this.complete();
}
}
- delete this.clickEvent;
- });
- this.willVisit = ((_event) => {
- delete this.clickEvent;
});
- this.delegate = delegate;
- this.element = element;
}
- start() {
- this.element.addEventListener("click", this.clickBubbled);
- document.addEventListener("turbo:click", this.linkClicked);
- document.addEventListener("turbo:before-visit", this.willVisit);
- }
- stop() {
- this.element.removeEventListener("click", this.clickBubbled);
- document.removeEventListener("turbo:click", this.linkClicked);
- document.removeEventListener("turbo:before-visit", this.willVisit);
- }
- respondsToEventTarget(target) {
- const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
- return element && element.closest("turbo-frame, html") == this.element;
+ }
+ followRedirect() {
+ var _a;
+ if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
+ this.adapter.visitProposedToLocation(this.redirectedToLocation, {
+ action: "replace",
+ response: this.response,
+ shouldCacheSnapshot: false,
+ willRender: false,
+ });
+ this.followedRedirect = true;
}
}
-
- class LinkClickObserver {
- constructor(delegate, eventTarget) {
- this.started = false;
- this.clickCaptured = () => {
- this.eventTarget.removeEventListener("click", this.clickBubbled, false);
- this.eventTarget.addEventListener("click", this.clickBubbled, false);
- };
- this.clickBubbled = (event) => {
- if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
- const target = (event.composedPath && event.composedPath()[0]) || event.target;
- const link = this.findLinkFromClickTarget(target);
- if (link && doesNotTargetIFrame(link)) {
- const location = this.getLocationForLink(link);
- if (this.delegate.willFollowLinkToLocation(link, location, event)) {
- event.preventDefault();
- this.delegate.followedLinkToLocation(link, location);
- }
- }
- }
- };
- this.delegate = delegate;
- this.eventTarget = eventTarget;
+ goToSamePageAnchor() {
+ if (this.isSamePage) {
+ this.render(async () => {
+ this.cacheSnapshot();
+ this.performScroll();
+ this.changeHistory();
+ this.adapter.visitRendered(this);
+ });
}
- start() {
- if (!this.started) {
- this.eventTarget.addEventListener("click", this.clickCaptured, true);
- this.started = true;
- }
+ }
+ prepareRequest(request) {
+ if (this.acceptsStreamResponse) {
+ request.acceptResponseType(StreamMessage.contentType);
}
- stop() {
- if (this.started) {
- this.eventTarget.removeEventListener("click", this.clickCaptured, true);
- this.started = false;
- }
+ }
+ requestStarted() {
+ this.startRequest();
+ }
+ requestPreventedHandlingResponse(_request, _response) { }
+ async requestSucceededWithResponse(request, response) {
+ const responseHTML = await response.responseHTML;
+ const { redirected, statusCode } = response;
+ if (responseHTML == undefined) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.contentTypeMismatch,
+ redirected,
+ });
}
- clickEventIsSignificant(event) {
- return !((event.target && event.target.isContentEditable) ||
- event.defaultPrevented ||
- event.which > 1 ||
- event.altKey ||
- event.ctrlKey ||
- event.metaKey ||
- event.shiftKey);
+ else {
+ this.redirectedToLocation = response.redirected ? response.location : undefined;
+ this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
}
- findLinkFromClickTarget(target) {
- return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
+ }
+ async requestFailedWithResponse(request, response) {
+ const responseHTML = await response.responseHTML;
+ const { redirected, statusCode } = response;
+ if (responseHTML == undefined) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.contentTypeMismatch,
+ redirected,
+ });
}
- getLocationForLink(link) {
- return expandURL(link.getAttribute("href") || "");
+ else {
+ this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
}
}
- function doesNotTargetIFrame(anchor) {
- if (anchor.hasAttribute("target")) {
- for (const element of document.getElementsByName(anchor.target)) {
- if (element instanceof HTMLIFrameElement)
- return false;
+ requestErrored(_request, _error) {
+ this.recordResponse({
+ statusCode: SystemStatusCode.networkFailure,
+ redirected: false,
+ });
+ }
+ requestFinished() {
+ this.finishRequest();
+ }
+ performScroll() {
+ if (!this.scrolled && !this.view.forceReloaded) {
+ if (this.action == "restore") {
+ this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
}
+ else {
+ this.scrollToAnchor() || this.view.scrollToTop();
+ }
+ if (this.isSamePage) {
+ this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
+ }
+ this.scrolled = true;
+ }
+ }
+ scrollToRestoredPosition() {
+ const { scrollPosition } = this.restorationData;
+ if (scrollPosition) {
+ this.view.scrollToPosition(scrollPosition);
return true;
}
- else {
+ }
+ scrollToAnchor() {
+ const anchor = getAnchor(this.location);
+ if (anchor != null) {
+ this.view.scrollToAnchor(anchor);
return true;
}
}
-
- class FormLinkClickObserver {
- constructor(delegate, element) {
- this.delegate = delegate;
- this.linkInterceptor = new LinkClickObserver(this, element);
+ recordTimingMetric(metric) {
+ this.timingMetrics[metric] = new Date().getTime();
+ }
+ getTimingMetrics() {
+ return Object.assign({}, this.timingMetrics);
+ }
+ getHistoryMethodForAction(action) {
+ switch (action) {
+ case "replace":
+ return history.replaceState;
+ case "advance":
+ case "restore":
+ return history.pushState;
}
- start() {
- this.linkInterceptor.start();
+ }
+ hasPreloadedResponse() {
+ return typeof this.response == "object";
+ }
+ shouldIssueRequest() {
+ if (this.isSamePage) {
+ return false;
}
- stop() {
- this.linkInterceptor.stop();
+ else if (this.action == "restore") {
+ return !this.hasCachedSnapshot();
}
- willFollowLinkToLocation(link, location, originalEvent) {
- return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
- link.hasAttribute("data-turbo-method"));
- }
- followedLinkToLocation(link, location) {
- const form = document.createElement("form");
- const type = "hidden";
- for (const [name, value] of location.searchParams) {
- form.append(Object.assign(document.createElement("input"), { type, name, value }));
- }
- const action = Object.assign(location, { search: "" });
- form.setAttribute("data-turbo", "true");
- form.setAttribute("action", action.href);
- form.setAttribute("hidden", "");
- const method = link.getAttribute("data-turbo-method");
- if (method)
- form.setAttribute("method", method);
- const turboFrame = link.getAttribute("data-turbo-frame");
- if (turboFrame)
- form.setAttribute("data-turbo-frame", turboFrame);
- const turboAction = getVisitAction(link);
- if (turboAction)
- form.setAttribute("data-turbo-action", turboAction);
- const turboConfirm = link.getAttribute("data-turbo-confirm");
- if (turboConfirm)
- form.setAttribute("data-turbo-confirm", turboConfirm);
- const turboStream = link.hasAttribute("data-turbo-stream");
- if (turboStream)
- form.setAttribute("data-turbo-stream", "");
- this.delegate.submittedFormLinkToLocation(link, location, form);
- document.body.appendChild(form);
- form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
- requestAnimationFrame(() => form.requestSubmit());
+ else {
+ return this.willRender;
}
}
-
- class Bardo {
- static async preservingPermanentElements(delegate, permanentElementMap, callback) {
- const bardo = new this(delegate, permanentElementMap);
- bardo.enter();
- await callback();
- bardo.leave();
- }
- constructor(delegate, permanentElementMap) {
- this.delegate = delegate;
- this.permanentElementMap = permanentElementMap;
- }
- enter() {
- for (const id in this.permanentElementMap) {
- const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
- this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
- this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
- }
- }
- leave() {
- for (const id in this.permanentElementMap) {
- const [currentPermanentElement] = this.permanentElementMap[id];
- this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
- this.replacePlaceholderWithPermanentElement(currentPermanentElement);
- this.delegate.leavingBardo(currentPermanentElement);
- }
- }
- replaceNewPermanentElementWithPlaceholder(permanentElement) {
- const placeholder = createPlaceholderForPermanentElement(permanentElement);
- permanentElement.replaceWith(placeholder);
- }
- replaceCurrentPermanentElementWithClone(permanentElement) {
- const clone = permanentElement.cloneNode(true);
- permanentElement.replaceWith(clone);
- }
- replacePlaceholderWithPermanentElement(permanentElement) {
- const placeholder = this.getPlaceholderById(permanentElement.id);
- placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
- }
- getPlaceholderById(id) {
- return this.placeholders.find((element) => element.content == id);
- }
- get placeholders() {
- return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
+ cacheSnapshot() {
+ if (!this.snapshotCached) {
+ this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
+ this.snapshotCached = true;
}
}
- function createPlaceholderForPermanentElement(permanentElement) {
- const element = document.createElement("meta");
- element.setAttribute("name", "turbo-permanent-placeholder");
- element.setAttribute("content", permanentElement.id);
- return element;
+ async render(callback) {
+ this.cancelRender();
+ await new Promise((resolve) => {
+ this.frame = requestAnimationFrame(() => resolve());
+ });
+ await callback();
+ delete this.frame;
}
-
- class Renderer {
- constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
- this.activeElement = null;
- this.currentSnapshot = currentSnapshot;
- this.newSnapshot = newSnapshot;
- this.isPreview = isPreview;
- this.willRender = willRender;
- this.renderElement = renderElement;
- this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
- }
- get shouldRender() {
- return true;
- }
- get reloadReason() {
- return;
- }
- prepareToRender() {
- return;
- }
- finishRendering() {
- if (this.resolvingFunctions) {
- this.resolvingFunctions.resolve();
- delete this.resolvingFunctions;
- }
- }
- async preservingPermanentElements(callback) {
- await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
- }
- focusFirstAutofocusableElement() {
- const element = this.connectedSnapshot.firstAutofocusableElement;
- if (elementIsFocusable(element)) {
- element.focus();
- }
- }
- enteringBardo(currentPermanentElement) {
- if (this.activeElement)
- return;
- if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
- this.activeElement = this.currentSnapshot.activeElement;
- }
- }
- leavingBardo(currentPermanentElement) {
- if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
- this.activeElement.focus();
- this.activeElement = null;
- }
- }
- get connectedSnapshot() {
- return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
- }
- get currentElement() {
- return this.currentSnapshot.element;
- }
- get newElement() {
- return this.newSnapshot.element;
- }
- get permanentElementMap() {
- return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
+ cancelRender() {
+ if (this.frame) {
+ cancelAnimationFrame(this.frame);
+ delete this.frame;
}
}
- function elementIsFocusable(element) {
- return element && typeof element.focus == "function";
- }
+}
+function isSuccessful(statusCode) {
+ return statusCode >= 200 && statusCode < 300;
+}
- class FrameRenderer extends Renderer {
- static renderElement(currentElement, newElement) {
- var _a;
- const destinationRange = document.createRange();
- destinationRange.selectNodeContents(currentElement);
- destinationRange.deleteContents();
- const frameElement = newElement;
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
- if (sourceRange) {
- sourceRange.selectNodeContents(frameElement);
- currentElement.appendChild(sourceRange.extractContents());
- }
- }
- constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
- super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
- this.delegate = delegate;
- }
- get shouldRender() {
- return true;
- }
- async render() {
- await nextAnimationFrame();
- this.preservingPermanentElements(() => {
- this.loadFrameElement();
- });
- this.scrollFrameIntoView();
- await nextAnimationFrame();
- this.focusFirstAutofocusableElement();
- await nextAnimationFrame();
- this.activateScriptElements();
- }
- loadFrameElement() {
- this.delegate.willRenderFrame(this.currentElement, this.newElement);
- this.renderElement(this.currentElement, this.newElement);
- }
- scrollFrameIntoView() {
- if (this.currentElement.autoscroll || this.newElement.autoscroll) {
- const element = this.currentElement.firstElementChild;
- const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
- const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
- if (element) {
- element.scrollIntoView({ block, behavior });
- return true;
- }
- }
- return false;
- }
- activateScriptElements() {
- for (const inertScriptElement of this.newScriptElements) {
- const activatedScriptElement = activateScriptElement(inertScriptElement);
- inertScriptElement.replaceWith(activatedScriptElement);
- }
- }
- get newScriptElements() {
- return this.currentElement.querySelectorAll("script");
- }
+class BrowserAdapter {
+ constructor(session) {
+ this.progressBar = new ProgressBar();
+ this.showProgressBar = () => {
+ this.progressBar.show();
+ };
+ this.session = session;
}
- function readScrollLogicalPosition(value, defaultValue) {
- if (value == "end" || value == "start" || value == "center" || value == "nearest") {
- return value;
- }
- else {
- return defaultValue;
- }
+ visitProposedToLocation(location, options) {
+ this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
}
- function readScrollBehavior(value, defaultValue) {
- if (value == "auto" || value == "smooth") {
- return value;
- }
- else {
- return defaultValue;
- }
+ visitStarted(visit) {
+ this.location = visit.location;
+ visit.loadCachedSnapshot();
+ visit.issueRequest();
+ visit.goToSamePageAnchor();
}
-
- class ProgressBar {
- static get defaultCSS() {
- return unindent `
- .turbo-progress-bar {
- position: fixed;
- display: block;
- top: 0;
- left: 0;
- height: 3px;
- background: #0076ff;
- z-index: 2147483647;
- transition:
- width ${ProgressBar.animationDuration}ms ease-out,
- opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
- transform: translate3d(0, 0, 0);
- }
- `;
+ visitRequestStarted(visit) {
+ this.progressBar.setValue(0);
+ if (visit.hasCachedSnapshot() || visit.action != "restore") {
+ this.showVisitProgressBarAfterDelay();
}
- constructor() {
- this.hiding = false;
- this.value = 0;
- this.visible = false;
- this.trickle = () => {
- this.setValue(this.value + Math.random() / 100);
- };
- this.stylesheetElement = this.createStylesheetElement();
- this.progressElement = this.createProgressElement();
- this.installStylesheetElement();
- this.setValue(0);
- }
- show() {
- if (!this.visible) {
- this.visible = true;
- this.installProgressElement();
- this.startTrickling();
- }
- }
- hide() {
- if (this.visible && !this.hiding) {
- this.hiding = true;
- this.fadeProgressElement(() => {
- this.uninstallProgressElement();
- this.stopTrickling();
- this.visible = false;
- this.hiding = false;
+ else {
+ this.showProgressBar();
+ }
+ }
+ visitRequestCompleted(visit) {
+ visit.loadResponse();
+ }
+ visitRequestFailedWithStatusCode(visit, statusCode) {
+ switch (statusCode) {
+ case SystemStatusCode.networkFailure:
+ case SystemStatusCode.timeoutFailure:
+ case SystemStatusCode.contentTypeMismatch:
+ return this.reload({
+ reason: "request_failed",
+ context: {
+ statusCode,
+ },
});
- }
- }
- setValue(value) {
- this.value = value;
- this.refresh();
- }
- installStylesheetElement() {
- document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
- }
- installProgressElement() {
- this.progressElement.style.width = "0";
- this.progressElement.style.opacity = "1";
- document.documentElement.insertBefore(this.progressElement, document.body);
- this.refresh();
- }
- fadeProgressElement(callback) {
- this.progressElement.style.opacity = "0";
- setTimeout(callback, ProgressBar.animationDuration * 1.5);
- }
- uninstallProgressElement() {
- if (this.progressElement.parentNode) {
- document.documentElement.removeChild(this.progressElement);
- }
- }
- startTrickling() {
- if (!this.trickleInterval) {
- this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
- }
- }
- stopTrickling() {
- window.clearInterval(this.trickleInterval);
- delete this.trickleInterval;
- }
- refresh() {
- requestAnimationFrame(() => {
- this.progressElement.style.width = `${10 + this.value * 90}%`;
- });
- }
- createStylesheetElement() {
- const element = document.createElement("style");
- element.type = "text/css";
- element.textContent = ProgressBar.defaultCSS;
- if (this.cspNonce) {
- element.nonce = this.cspNonce;
- }
- return element;
- }
- createProgressElement() {
- const element = document.createElement("div");
- element.className = "turbo-progress-bar";
- return element;
- }
- get cspNonce() {
- return getMetaContent("csp-nonce");
+ default:
+ return visit.loadResponse();
}
}
- ProgressBar.animationDuration = 300;
-
- class HeadSnapshot extends Snapshot {
- constructor() {
- super(...arguments);
- this.detailsByOuterHTML = this.children
- .filter((element) => !elementIsNoscript(element))
- .map((element) => elementWithoutNonce(element))
- .reduce((result, element) => {
- const { outerHTML } = element;
- const details = outerHTML in result
- ? result[outerHTML]
- : {
- type: elementType(element),
- tracked: elementIsTracked(element),
- elements: [],
- };
- return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
- }, {});
- }
- get trackedElementSignature() {
- return Object.keys(this.detailsByOuterHTML)
- .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
- .join("");
- }
- getScriptElementsNotInSnapshot(snapshot) {
- return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
- }
- getStylesheetElementsNotInSnapshot(snapshot) {
- return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
- }
- getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
- return Object.keys(this.detailsByOuterHTML)
- .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
- .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
- .filter(({ type }) => type == matchedType)
- .map(({ elements: [element] }) => element);
- }
- get provisionalElements() {
- return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
- const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
- if (type == null && !tracked) {
- return [...result, ...elements];
- }
- else if (elements.length > 1) {
- return [...result, ...elements.slice(1)];
- }
- else {
- return result;
- }
- }, []);
- }
- getMetaValue(name) {
- const element = this.findMetaElementByName(name);
- return element ? element.getAttribute("content") : null;
- }
- findMetaElementByName(name) {
- return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
- const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
- return elementIsMetaElementWithName(element, name) ? element : result;
- }, undefined);
- }
+ visitRequestFinished(_visit) {
+ this.progressBar.setValue(1);
+ this.hideVisitProgressBar();
}
- function elementType(element) {
- if (elementIsScript(element)) {
- return "script";
- }
- else if (elementIsStylesheet(element)) {
- return "stylesheet";
- }
+ visitCompleted(_visit) { }
+ pageInvalidated(reason) {
+ this.reload(reason);
}
- function elementIsTracked(element) {
- return element.getAttribute("data-turbo-track") == "reload";
+ visitFailed(_visit) { }
+ visitRendered(_visit) { }
+ formSubmissionStarted(_formSubmission) {
+ this.progressBar.setValue(0);
+ this.showFormProgressBarAfterDelay();
}
- function elementIsScript(element) {
- const tagName = element.localName;
- return tagName == "script";
+ formSubmissionFinished(_formSubmission) {
+ this.progressBar.setValue(1);
+ this.hideFormProgressBar();
}
- function elementIsNoscript(element) {
- const tagName = element.localName;
- return tagName == "noscript";
+ showVisitProgressBarAfterDelay() {
+ this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
}
- function elementIsStylesheet(element) {
- const tagName = element.localName;
- return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
+ hideVisitProgressBar() {
+ this.progressBar.hide();
+ if (this.visitProgressBarTimeout != null) {
+ window.clearTimeout(this.visitProgressBarTimeout);
+ delete this.visitProgressBarTimeout;
+ }
}
- function elementIsMetaElementWithName(element, name) {
- const tagName = element.localName;
- return tagName == "meta" && element.getAttribute("name") == name;
+ showFormProgressBarAfterDelay() {
+ if (this.formProgressBarTimeout == null) {
+ this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
+ }
}
- function elementWithoutNonce(element) {
- if (element.hasAttribute("nonce")) {
- element.setAttribute("nonce", "");
+ hideFormProgressBar() {
+ this.progressBar.hide();
+ if (this.formProgressBarTimeout != null) {
+ window.clearTimeout(this.formProgressBarTimeout);
+ delete this.formProgressBarTimeout;
}
- return element;
}
+ reload(reason) {
+ var _a;
+ dispatch("turbo:reload", { detail: reason });
+ window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
+ }
+ get navigator() {
+ return this.session.navigator;
+ }
+}
- class PageSnapshot extends Snapshot {
- static fromHTMLString(html = "") {
- return this.fromDocument(parseHTMLDocument(html));
- }
- static fromElement(element) {
- return this.fromDocument(element.ownerDocument);
- }
- static fromDocument({ head, body }) {
- return new this(body, new HeadSnapshot(head));
- }
- constructor(element, headSnapshot) {
- super(element);
- this.headSnapshot = headSnapshot;
- }
- clone() {
- const clonedElement = this.element.cloneNode(true);
- const selectElements = this.element.querySelectorAll("select");
- const clonedSelectElements = clonedElement.querySelectorAll("select");
- for (const [index, source] of selectElements.entries()) {
- const clone = clonedSelectElements[index];
- for (const option of clone.selectedOptions)
- option.selected = false;
- for (const option of source.selectedOptions)
- clone.options[option.index].selected = true;
+class CacheObserver {
+ constructor() {
+ this.selector = "[data-turbo-temporary]";
+ this.deprecatedSelector = "[data-turbo-cache=false]";
+ this.started = false;
+ this.removeTemporaryElements = ((_event) => {
+ for (const element of this.temporaryElements) {
+ element.remove();
}
- for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
- clonedPasswordInput.value = "";
- }
- return new PageSnapshot(clonedElement, this.headSnapshot);
- }
- get headElement() {
- return this.headSnapshot.element;
- }
- get rootLocation() {
- var _a;
- const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
- return expandURL(root);
- }
- get cacheControlValue() {
- return this.getSetting("cache-control");
- }
- get isPreviewable() {
- return this.cacheControlValue != "no-preview";
- }
- get isCacheable() {
- return this.cacheControlValue != "no-cache";
+ });
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
}
- get isVisitable() {
- return this.getSetting("visit-control") != "reload";
+ }
+ stop() {
+ if (this.started) {
+ this.started = false;
+ removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
}
- getSetting(name) {
- return this.headSnapshot.getMetaValue(`turbo-${name}`);
+ }
+ get temporaryElements() {
+ return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
+ }
+ get temporaryElementsWithDeprecation() {
+ const elements = document.querySelectorAll(this.deprecatedSelector);
+ if (elements.length) {
+ console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
}
+ return [...elements];
}
+}
- var TimingMetric;
- (function (TimingMetric) {
- TimingMetric["visitStart"] = "visitStart";
- TimingMetric["requestStart"] = "requestStart";
- TimingMetric["requestEnd"] = "requestEnd";
- TimingMetric["visitEnd"] = "visitEnd";
- })(TimingMetric || (TimingMetric = {}));
- var VisitState;
- (function (VisitState) {
- VisitState["initialized"] = "initialized";
- VisitState["started"] = "started";
- VisitState["canceled"] = "canceled";
- VisitState["failed"] = "failed";
- VisitState["completed"] = "completed";
- })(VisitState || (VisitState = {}));
- const defaultOptions = {
- action: "advance",
- historyChanged: false,
- visitCachedSnapshot: () => { },
- willRender: true,
- updateHistory: true,
- shouldCacheSnapshot: true,
- acceptsStreamResponse: false,
- };
- var SystemStatusCode;
- (function (SystemStatusCode) {
- SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
- SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
- SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
- })(SystemStatusCode || (SystemStatusCode = {}));
- class Visit {
- constructor(delegate, location, restorationIdentifier, options = {}) {
- this.identifier = uuid();
- this.timingMetrics = {};
- this.followedRedirect = false;
- this.historyChanged = false;
- this.scrolled = false;
- this.shouldCacheSnapshot = true;
- this.acceptsStreamResponse = false;
- this.snapshotCached = false;
- this.state = VisitState.initialized;
- this.delegate = delegate;
- this.location = location;
- this.restorationIdentifier = restorationIdentifier || uuid();
- const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
- this.action = action;
- this.historyChanged = historyChanged;
- this.referrer = referrer;
- this.snapshot = snapshot;
- this.snapshotHTML = snapshotHTML;
- this.response = response;
- this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
- this.visitCachedSnapshot = visitCachedSnapshot;
- this.willRender = willRender;
- this.updateHistory = updateHistory;
- this.scrolled = !willRender;
- this.shouldCacheSnapshot = shouldCacheSnapshot;
- this.acceptsStreamResponse = acceptsStreamResponse;
- }
- get adapter() {
- return this.delegate.adapter;
- }
- get view() {
- return this.delegate.view;
- }
- get history() {
- return this.delegate.history;
- }
- get restorationData() {
- return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
- }
- get silent() {
- return this.isSamePage;
- }
- start() {
- if (this.state == VisitState.initialized) {
- this.recordTimingMetric(TimingMetric.visitStart);
- this.state = VisitState.started;
- this.adapter.visitStarted(this);
- this.delegate.visitStarted(this);
- }
- }
- cancel() {
- if (this.state == VisitState.started) {
- if (this.request) {
- this.request.cancel();
- }
- this.cancelRender();
- this.state = VisitState.canceled;
- }
- }
- complete() {
- if (this.state == VisitState.started) {
- this.recordTimingMetric(TimingMetric.visitEnd);
- this.state = VisitState.completed;
- this.followRedirect();
- if (!this.followedRedirect) {
- this.adapter.visitCompleted(this);
- this.delegate.visitCompleted(this);
- }
- }
- }
- fail() {
- if (this.state == VisitState.started) {
- this.state = VisitState.failed;
- this.adapter.visitFailed(this);
- }
- }
- changeHistory() {
- var _a;
- if (!this.historyChanged && this.updateHistory) {
- const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
- const method = getHistoryMethodForAction(actionForHistory);
- this.history.update(method, this.location, this.restorationIdentifier);
- this.historyChanged = true;
- }
- }
- issueRequest() {
- if (this.hasPreloadedResponse()) {
- this.simulateRequest();
- }
- else if (this.shouldIssueRequest() && !this.request) {
- this.request = new FetchRequest(this, FetchMethod.get, this.location);
- this.request.perform();
- }
- }
- simulateRequest() {
- if (this.response) {
- this.startRequest();
- this.recordResponse();
- this.finishRequest();
- }
- }
- startRequest() {
- this.recordTimingMetric(TimingMetric.requestStart);
- this.adapter.visitRequestStarted(this);
- }
- recordResponse(response = this.response) {
- this.response = response;
- if (response) {
- const { statusCode } = response;
- if (isSuccessful(statusCode)) {
- this.adapter.visitRequestCompleted(this);
- }
- else {
- this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
- }
- }
- }
- finishRequest() {
- this.recordTimingMetric(TimingMetric.requestEnd);
- this.adapter.visitRequestFinished(this);
- }
- loadResponse() {
- if (this.response) {
- const { statusCode, responseHTML } = this.response;
- this.render(async () => {
- if (this.shouldCacheSnapshot)
- this.cacheSnapshot();
- if (this.view.renderPromise)
- await this.view.renderPromise;
- if (isSuccessful(statusCode) && responseHTML != null) {
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
- this.performScroll();
- this.adapter.visitRendered(this);
- this.complete();
- }
- else {
- await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
- this.adapter.visitRendered(this);
- this.fail();
- }
- });
- }
- }
- getCachedSnapshot() {
- const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
- if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
- if (this.action == "restore" || snapshot.isPreviewable) {
- return snapshot;
- }
- }
- }
- getPreloadedSnapshot() {
- if (this.snapshotHTML) {
- return PageSnapshot.fromHTMLString(this.snapshotHTML);
- }
- }
- hasCachedSnapshot() {
- return this.getCachedSnapshot() != null;
- }
- loadCachedSnapshot() {
- const snapshot = this.getCachedSnapshot();
- if (snapshot) {
- const isPreview = this.shouldIssueRequest();
- this.render(async () => {
- this.cacheSnapshot();
- if (this.isSamePage) {
- this.adapter.visitRendered(this);
- }
- else {
- if (this.view.renderPromise)
- await this.view.renderPromise;
- await this.view.renderPage(snapshot, isPreview, this.willRender, this);
- this.performScroll();
- this.adapter.visitRendered(this);
- if (!isPreview) {
- this.complete();
- }
- }
- });
- }
- }
- followRedirect() {
- var _a;
- if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
- this.adapter.visitProposedToLocation(this.redirectedToLocation, {
- action: "replace",
- response: this.response,
- shouldCacheSnapshot: false,
- willRender: false,
- });
- this.followedRedirect = true;
- }
- }
- goToSamePageAnchor() {
- if (this.isSamePage) {
- this.render(async () => {
- this.cacheSnapshot();
- this.performScroll();
- this.changeHistory();
- this.adapter.visitRendered(this);
- });
- }
- }
- prepareRequest(request) {
- if (this.acceptsStreamResponse) {
- request.acceptResponseType(StreamMessage.contentType);
- }
- }
- requestStarted() {
- this.startRequest();
- }
- requestPreventedHandlingResponse(_request, _response) { }
- async requestSucceededWithResponse(request, response) {
- const responseHTML = await response.responseHTML;
- const { redirected, statusCode } = response;
- if (responseHTML == undefined) {
- this.recordResponse({
- statusCode: SystemStatusCode.contentTypeMismatch,
- redirected,
- });
- }
- else {
- this.redirectedToLocation = response.redirected ? response.location : undefined;
- this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
- }
- }
- async requestFailedWithResponse(request, response) {
- const responseHTML = await response.responseHTML;
- const { redirected, statusCode } = response;
- if (responseHTML == undefined) {
- this.recordResponse({
- statusCode: SystemStatusCode.contentTypeMismatch,
- redirected,
- });
- }
- else {
- this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
- }
- }
- requestErrored(_request, _error) {
- this.recordResponse({
- statusCode: SystemStatusCode.networkFailure,
- redirected: false,
- });
- }
- requestFinished() {
- this.finishRequest();
- }
- performScroll() {
- if (!this.scrolled && !this.view.forceReloaded) {
- if (this.action == "restore") {
- this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
- }
- else {
- this.scrollToAnchor() || this.view.scrollToTop();
- }
- if (this.isSamePage) {
- this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
- }
- this.scrolled = true;
- }
- }
- scrollToRestoredPosition() {
- const { scrollPosition } = this.restorationData;
- if (scrollPosition) {
- this.view.scrollToPosition(scrollPosition);
- return true;
- }
- }
- scrollToAnchor() {
- const anchor = getAnchor(this.location);
- if (anchor != null) {
- this.view.scrollToAnchor(anchor);
- return true;
- }
- }
- recordTimingMetric(metric) {
- this.timingMetrics[metric] = new Date().getTime();
- }
- getTimingMetrics() {
- return Object.assign({}, this.timingMetrics);
- }
- getHistoryMethodForAction(action) {
- switch (action) {
- case "replace":
- return history.replaceState;
- case "advance":
- case "restore":
- return history.pushState;
- }
- }
- hasPreloadedResponse() {
- return typeof this.response == "object";
- }
- shouldIssueRequest() {
- if (this.isSamePage) {
- return false;
- }
- else if (this.action == "restore") {
- return !this.hasCachedSnapshot();
- }
- else {
- return this.willRender;
- }
- }
- cacheSnapshot() {
- if (!this.snapshotCached) {
- this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
- this.snapshotCached = true;
- }
- }
- async render(callback) {
- this.cancelRender();
- await new Promise((resolve) => {
- this.frame = requestAnimationFrame(() => resolve());
- });
- await callback();
- delete this.frame;
- }
- cancelRender() {
- if (this.frame) {
- cancelAnimationFrame(this.frame);
- delete this.frame;
- }
- }
+class FrameRedirector {
+ constructor(session, element) {
+ this.session = session;
+ this.element = element;
+ this.linkInterceptor = new LinkInterceptor(this, element);
+ this.formSubmitObserver = new FormSubmitObserver(this, element);
}
- function isSuccessful(statusCode) {
- return statusCode >= 200 && statusCode < 300;
+ start() {
+ this.linkInterceptor.start();
+ this.formSubmitObserver.start();
}
-
- class BrowserAdapter {
- constructor(session) {
- this.progressBar = new ProgressBar();
- this.showProgressBar = () => {
- this.progressBar.show();
- };
- this.session = session;
- }
- visitProposedToLocation(location, options) {
- this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
- }
- visitStarted(visit) {
- this.location = visit.location;
- visit.loadCachedSnapshot();
- visit.issueRequest();
- visit.goToSamePageAnchor();
- }
- visitRequestStarted(visit) {
- this.progressBar.setValue(0);
- if (visit.hasCachedSnapshot() || visit.action != "restore") {
- this.showVisitProgressBarAfterDelay();
- }
- else {
- this.showProgressBar();
- }
- }
- visitRequestCompleted(visit) {
- visit.loadResponse();
- }
- visitRequestFailedWithStatusCode(visit, statusCode) {
- switch (statusCode) {
- case SystemStatusCode.networkFailure:
- case SystemStatusCode.timeoutFailure:
- case SystemStatusCode.contentTypeMismatch:
- return this.reload({
- reason: "request_failed",
- context: {
- statusCode,
- },
- });
- default:
- return visit.loadResponse();
- }
- }
- visitRequestFinished(_visit) {
- this.progressBar.setValue(1);
- this.hideVisitProgressBar();
- }
- visitCompleted(_visit) { }
- pageInvalidated(reason) {
- this.reload(reason);
- }
- visitFailed(_visit) { }
- visitRendered(_visit) { }
- formSubmissionStarted(_formSubmission) {
- this.progressBar.setValue(0);
- this.showFormProgressBarAfterDelay();
- }
- formSubmissionFinished(_formSubmission) {
- this.progressBar.setValue(1);
- this.hideFormProgressBar();
+ stop() {
+ this.linkInterceptor.stop();
+ this.formSubmitObserver.stop();
+ }
+ shouldInterceptLinkClick(element, _location, _event) {
+ return this.shouldRedirect(element);
+ }
+ linkClickIntercepted(element, url, event) {
+ const frame = this.findFrameElement(element);
+ if (frame) {
+ frame.delegate.linkClickIntercepted(element, url, event);
}
- showVisitProgressBarAfterDelay() {
- this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
+ }
+ willSubmitForm(element, submitter) {
+ return (element.closest("turbo-frame") == null &&
+ this.shouldSubmit(element, submitter) &&
+ this.shouldRedirect(element, submitter));
+ }
+ formSubmitted(element, submitter) {
+ const frame = this.findFrameElement(element, submitter);
+ if (frame) {
+ frame.delegate.formSubmitted(element, submitter);
}
- hideVisitProgressBar() {
- this.progressBar.hide();
- if (this.visitProgressBarTimeout != null) {
- window.clearTimeout(this.visitProgressBarTimeout);
- delete this.visitProgressBarTimeout;
- }
+ }
+ shouldSubmit(form, submitter) {
+ var _a;
+ const action = getAction(form, submitter);
+ const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+ const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
+ return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
+ }
+ shouldRedirect(element, submitter) {
+ const isNavigatable = element instanceof HTMLFormElement
+ ? this.session.submissionIsNavigatable(element, submitter)
+ : this.session.elementIsNavigatable(element);
+ if (isNavigatable) {
+ const frame = this.findFrameElement(element, submitter);
+ return frame ? frame != element.closest("turbo-frame") : false;
}
- showFormProgressBarAfterDelay() {
- if (this.formProgressBarTimeout == null) {
- this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
- }
+ else {
+ return false;
}
- hideFormProgressBar() {
- this.progressBar.hide();
- if (this.formProgressBarTimeout != null) {
- window.clearTimeout(this.formProgressBarTimeout);
- delete this.formProgressBarTimeout;
- }
- }
- reload(reason) {
- var _a;
- dispatch("turbo:reload", { detail: reason });
- window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
- }
- get navigator() {
- return this.session.navigator;
- }
- }
-
- class CacheObserver {
- constructor() {
- this.selector = "[data-turbo-temporary]";
- this.deprecatedSelector = "[data-turbo-cache=false]";
- this.started = false;
- this.removeTemporaryElements = ((_event) => {
- for (const element of this.temporaryElements) {
- element.remove();
- }
- });
- }
- start() {
- if (!this.started) {
- this.started = true;
- addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
- }
- }
- stop() {
- if (this.started) {
- this.started = false;
- removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
- }
- }
- get temporaryElements() {
- return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
- }
- get temporaryElementsWithDeprecation() {
- const elements = document.querySelectorAll(this.deprecatedSelector);
- if (elements.length) {
- console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
- }
- return [...elements];
- }
- }
-
- class FrameRedirector {
- constructor(session, element) {
- this.session = session;
- this.element = element;
- this.linkInterceptor = new LinkInterceptor(this, element);
- this.formSubmitObserver = new FormSubmitObserver(this, element);
- }
- start() {
- this.linkInterceptor.start();
- this.formSubmitObserver.start();
- }
- stop() {
- this.linkInterceptor.stop();
- this.formSubmitObserver.stop();
- }
- shouldInterceptLinkClick(element, _location, _event) {
- return this.shouldRedirect(element);
- }
- linkClickIntercepted(element, url, event) {
- const frame = this.findFrameElement(element);
- if (frame) {
- frame.delegate.linkClickIntercepted(element, url, event);
- }
- }
- willSubmitForm(element, submitter) {
- return (element.closest("turbo-frame") == null &&
- this.shouldSubmit(element, submitter) &&
- this.shouldRedirect(element, submitter));
- }
- formSubmitted(element, submitter) {
- const frame = this.findFrameElement(element, submitter);
- if (frame) {
- frame.delegate.formSubmitted(element, submitter);
- }
- }
- shouldSubmit(form, submitter) {
- var _a;
- const action = getAction(form, submitter);
- const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
- const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
- return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
- }
- shouldRedirect(element, submitter) {
- const isNavigatable = element instanceof HTMLFormElement
- ? this.session.submissionIsNavigatable(element, submitter)
- : this.session.elementIsNavigatable(element);
- if (isNavigatable) {
- const frame = this.findFrameElement(element, submitter);
- return frame ? frame != element.closest("turbo-frame") : false;
- }
- else {
- return false;
- }
- }
- findFrameElement(element, submitter) {
- const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
- if (id && id != "_top") {
- const frame = this.element.querySelector(`#${id}:not([disabled])`);
- if (frame instanceof FrameElement) {
- return frame;
- }
- }
- }
- }
-
- class History {
- constructor(delegate) {
- this.restorationIdentifier = uuid();
- this.restorationData = {};
- this.started = false;
- this.pageLoaded = false;
- this.onPopState = (event) => {
- if (this.shouldHandlePopState()) {
- const { turbo } = event.state || {};
- if (turbo) {
- this.location = new URL(window.location.href);
- const { restorationIdentifier } = turbo;
- this.restorationIdentifier = restorationIdentifier;
- this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
- }
- }
- };
- this.onPageLoad = async (_event) => {
- await nextMicrotask();
- this.pageLoaded = true;
- };
- this.delegate = delegate;
- }
- start() {
- if (!this.started) {
- addEventListener("popstate", this.onPopState, false);
- addEventListener("load", this.onPageLoad, false);
- this.started = true;
- this.replace(new URL(window.location.href));
- }
- }
- stop() {
- if (this.started) {
- removeEventListener("popstate", this.onPopState, false);
- removeEventListener("load", this.onPageLoad, false);
- this.started = false;
- }
- }
- push(location, restorationIdentifier) {
- this.update(history.pushState, location, restorationIdentifier);
- }
- replace(location, restorationIdentifier) {
- this.update(history.replaceState, location, restorationIdentifier);
- }
- update(method, location, restorationIdentifier = uuid()) {
- const state = { turbo: { restorationIdentifier } };
- method.call(history, state, "", location.href);
- this.location = location;
- this.restorationIdentifier = restorationIdentifier;
- }
- getRestorationDataForIdentifier(restorationIdentifier) {
- return this.restorationData[restorationIdentifier] || {};
- }
- updateRestorationData(additionalData) {
- const { restorationIdentifier } = this;
- const restorationData = this.restorationData[restorationIdentifier];
- this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
- }
- assumeControlOfScrollRestoration() {
- var _a;
- if (!this.previousScrollRestoration) {
- this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
- history.scrollRestoration = "manual";
- }
- }
- relinquishControlOfScrollRestoration() {
- if (this.previousScrollRestoration) {
- history.scrollRestoration = this.previousScrollRestoration;
- delete this.previousScrollRestoration;
- }
- }
- shouldHandlePopState() {
- return this.pageIsLoaded();
- }
- pageIsLoaded() {
- return this.pageLoaded || document.readyState == "complete";
- }
- }
-
- class Navigator {
- constructor(delegate) {
- this.delegate = delegate;
- }
- proposeVisit(location, options = {}) {
- if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
- if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
- this.delegate.visitProposedToLocation(location, options);
- }
- else {
- window.location.href = location.toString();
- }
- }
- }
- startVisit(locatable, restorationIdentifier, options = {}) {
- this.stop();
- this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
- this.currentVisit.start();
- }
- submitForm(form, submitter) {
- this.stop();
- this.formSubmission = new FormSubmission(this, form, submitter, true);
- this.formSubmission.start();
- }
- stop() {
- if (this.formSubmission) {
- this.formSubmission.stop();
- delete this.formSubmission;
- }
- if (this.currentVisit) {
- this.currentVisit.cancel();
- delete this.currentVisit;
- }
- }
- get adapter() {
- return this.delegate.adapter;
- }
- get view() {
- return this.delegate.view;
- }
- get history() {
- return this.delegate.history;
- }
- formSubmissionStarted(formSubmission) {
- if (typeof this.adapter.formSubmissionStarted === "function") {
- this.adapter.formSubmissionStarted(formSubmission);
- }
- }
- async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
- if (formSubmission == this.formSubmission) {
- const responseHTML = await fetchResponse.responseHTML;
- if (responseHTML) {
- const shouldCacheSnapshot = formSubmission.isSafe;
- if (!shouldCacheSnapshot) {
- this.view.clearSnapshotCache();
- }
- const { statusCode, redirected } = fetchResponse;
- const action = this.getActionForFormSubmission(formSubmission);
- const visitOptions = {
- action,
- shouldCacheSnapshot,
- response: { statusCode, responseHTML, redirected },
- };
- this.proposeVisit(fetchResponse.location, visitOptions);
- }
- }
- }
- async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
- const responseHTML = await fetchResponse.responseHTML;
- if (responseHTML) {
- const snapshot = PageSnapshot.fromHTMLString(responseHTML);
- if (fetchResponse.serverError) {
- await this.view.renderError(snapshot, this.currentVisit);
- }
- else {
- await this.view.renderPage(snapshot, false, true, this.currentVisit);
- }
- this.view.scrollToTop();
- this.view.clearSnapshotCache();
- }
- }
- formSubmissionErrored(formSubmission, error) {
- console.error(error);
- }
- formSubmissionFinished(formSubmission) {
- if (typeof this.adapter.formSubmissionFinished === "function") {
- this.adapter.formSubmissionFinished(formSubmission);
- }
- }
- visitStarted(visit) {
- this.delegate.visitStarted(visit);
- }
- visitCompleted(visit) {
- this.delegate.visitCompleted(visit);
- }
- locationWithActionIsSamePage(location, action) {
- const anchor = getAnchor(location);
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
- const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
- return (action !== "replace" &&
- getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
- (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
- }
- visitScrolledToSamePageLocation(oldURL, newURL) {
- this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
- }
- get location() {
- return this.history.location;
- }
- get restorationIdentifier() {
- return this.history.restorationIdentifier;
- }
- getActionForFormSubmission({ submitter, formElement }) {
- return getVisitAction(submitter, formElement) || "advance";
- }
- }
-
- var PageStage;
- (function (PageStage) {
- PageStage[PageStage["initial"] = 0] = "initial";
- PageStage[PageStage["loading"] = 1] = "loading";
- PageStage[PageStage["interactive"] = 2] = "interactive";
- PageStage[PageStage["complete"] = 3] = "complete";
- })(PageStage || (PageStage = {}));
- class PageObserver {
- constructor(delegate) {
- this.stage = PageStage.initial;
- this.started = false;
- this.interpretReadyState = () => {
- const { readyState } = this;
- if (readyState == "interactive") {
- this.pageIsInteractive();
- }
- else if (readyState == "complete") {
- this.pageIsComplete();
- }
- };
- this.pageWillUnload = () => {
- this.delegate.pageWillUnload();
- };
- this.delegate = delegate;
- }
- start() {
- if (!this.started) {
- if (this.stage == PageStage.initial) {
- this.stage = PageStage.loading;
- }
- document.addEventListener("readystatechange", this.interpretReadyState, false);
- addEventListener("pagehide", this.pageWillUnload, false);
- this.started = true;
- }
- }
- stop() {
- if (this.started) {
- document.removeEventListener("readystatechange", this.interpretReadyState, false);
- removeEventListener("pagehide", this.pageWillUnload, false);
- this.started = false;
- }
- }
- pageIsInteractive() {
- if (this.stage == PageStage.loading) {
- this.stage = PageStage.interactive;
- this.delegate.pageBecameInteractive();
- }
- }
- pageIsComplete() {
- this.pageIsInteractive();
- if (this.stage == PageStage.interactive) {
- this.stage = PageStage.complete;
- this.delegate.pageLoaded();
- }
- }
- get readyState() {
- return document.readyState;
- }
- }
-
- class ScrollObserver {
- constructor(delegate) {
- this.started = false;
- this.onScroll = () => {
- this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
- };
- this.delegate = delegate;
- }
- start() {
- if (!this.started) {
- addEventListener("scroll", this.onScroll, false);
- this.onScroll();
- this.started = true;
- }
- }
- stop() {
- if (this.started) {
- removeEventListener("scroll", this.onScroll, false);
- this.started = false;
- }
- }
- updatePosition(position) {
- this.delegate.scrollPositionChanged(position);
- }
- }
-
- class StreamMessageRenderer {
- render({ fragment }) {
- Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
- }
- enteringBardo(currentPermanentElement, newPermanentElement) {
- newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
- }
- leavingBardo() { }
- }
- function getPermanentElementMapForFragment(fragment) {
- const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
- const permanentElementMap = {};
- for (const permanentElementInDocument of permanentElementsInDocument) {
- const { id } = permanentElementInDocument;
- for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
- const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
- if (elementInStream) {
- permanentElementMap[id] = [permanentElementInDocument, elementInStream];
- }
- }
- }
- return permanentElementMap;
- }
-
- class StreamObserver {
- constructor(delegate) {
- this.sources = new Set();
- this.started = false;
- this.inspectFetchResponse = ((event) => {
- const response = fetchResponseFromEvent(event);
- if (response && fetchResponseIsStream(response)) {
- event.preventDefault();
- this.receiveMessageResponse(response);
- }
- });
- this.receiveMessageEvent = (event) => {
- if (this.started && typeof event.data == "string") {
- this.receiveMessageHTML(event.data);
- }
- };
- this.delegate = delegate;
- }
- start() {
- if (!this.started) {
- this.started = true;
- addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
- }
- }
- stop() {
- if (this.started) {
- this.started = false;
- removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
- }
- }
- connectStreamSource(source) {
- if (!this.streamSourceIsConnected(source)) {
- this.sources.add(source);
- source.addEventListener("message", this.receiveMessageEvent, false);
- }
- }
- disconnectStreamSource(source) {
- if (this.streamSourceIsConnected(source)) {
- this.sources.delete(source);
- source.removeEventListener("message", this.receiveMessageEvent, false);
- }
- }
- streamSourceIsConnected(source) {
- return this.sources.has(source);
- }
- async receiveMessageResponse(response) {
- const html = await response.responseHTML;
- if (html) {
- this.receiveMessageHTML(html);
- }
- }
- receiveMessageHTML(html) {
- this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
- }
- }
- function fetchResponseFromEvent(event) {
- var _a;
- const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
- if (fetchResponse instanceof FetchResponse) {
- return fetchResponse;
- }
- }
- function fetchResponseIsStream(response) {
- var _a;
- const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
- return contentType.startsWith(StreamMessage.contentType);
- }
-
- class ErrorRenderer extends Renderer {
- static renderElement(currentElement, newElement) {
- const { documentElement, body } = document;
- documentElement.replaceChild(newElement, body);
- }
- async render() {
- this.replaceHeadAndBody();
- this.activateScriptElements();
- }
- replaceHeadAndBody() {
- const { documentElement, head } = document;
- documentElement.replaceChild(this.newHead, head);
- this.renderElement(this.currentElement, this.newElement);
- }
- activateScriptElements() {
- for (const replaceableElement of this.scriptElements) {
- const parentNode = replaceableElement.parentNode;
- if (parentNode) {
- const element = activateScriptElement(replaceableElement);
- parentNode.replaceChild(element, replaceableElement);
- }
- }
- }
- get newHead() {
- return this.newSnapshot.headSnapshot.element;
- }
- get scriptElements() {
- return document.documentElement.querySelectorAll("script");
- }
- }
-
- class PageRenderer extends Renderer {
- static renderElement(currentElement, newElement) {
- if (document.body && newElement instanceof HTMLBodyElement) {
- document.body.replaceWith(newElement);
- }
- else {
- document.documentElement.appendChild(newElement);
- }
- }
- get shouldRender() {
- return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
- }
- get reloadReason() {
- if (!this.newSnapshot.isVisitable) {
- return {
- reason: "turbo_visit_control_is_reload",
- };
- }
- if (!this.trackedElementsAreIdentical) {
- return {
- reason: "tracked_element_mismatch",
- };
- }
- }
- async prepareToRender() {
- await this.mergeHead();
- }
- async render() {
- if (this.willRender) {
- await this.replaceBody();
- }
- }
- finishRendering() {
- super.finishRendering();
- if (!this.isPreview) {
- this.focusFirstAutofocusableElement();
- }
- }
- get currentHeadSnapshot() {
- return this.currentSnapshot.headSnapshot;
- }
- get newHeadSnapshot() {
- return this.newSnapshot.headSnapshot;
- }
- get newElement() {
- return this.newSnapshot.element;
- }
- async mergeHead() {
- const mergedHeadElements = this.mergeProvisionalElements();
- const newStylesheetElements = this.copyNewHeadStylesheetElements();
- this.copyNewHeadScriptElements();
- await mergedHeadElements;
- await newStylesheetElements;
- }
- async replaceBody() {
- await this.preservingPermanentElements(async () => {
- this.activateNewBody();
- await this.assignNewBody();
- });
- }
- get trackedElementsAreIdentical() {
- return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
- }
- async copyNewHeadStylesheetElements() {
- const loadingElements = [];
- for (const element of this.newHeadStylesheetElements) {
- loadingElements.push(waitForLoad(element));
- document.head.appendChild(element);
- }
- await Promise.all(loadingElements);
- }
- copyNewHeadScriptElements() {
- for (const element of this.newHeadScriptElements) {
- document.head.appendChild(activateScriptElement(element));
- }
- }
- async mergeProvisionalElements() {
- const newHeadElements = [...this.newHeadProvisionalElements];
- for (const element of this.currentHeadProvisionalElements) {
- if (!this.isCurrentElementInElementList(element, newHeadElements)) {
- document.head.removeChild(element);
- }
- }
- for (const element of newHeadElements) {
- document.head.appendChild(element);
- }
- }
- isCurrentElementInElementList(element, elementList) {
- for (const [index, newElement] of elementList.entries()) {
- if (element.tagName == "TITLE") {
- if (newElement.tagName != "TITLE") {
- continue;
- }
- if (element.innerHTML == newElement.innerHTML) {
- elementList.splice(index, 1);
- return true;
- }
- }
- if (newElement.isEqualNode(element)) {
- elementList.splice(index, 1);
- return true;
- }
- }
- return false;
- }
- removeCurrentHeadProvisionalElements() {
- for (const element of this.currentHeadProvisionalElements) {
- document.head.removeChild(element);
- }
- }
- copyNewHeadProvisionalElements() {
- for (const element of this.newHeadProvisionalElements) {
- document.head.appendChild(element);
- }
- }
- activateNewBody() {
- document.adoptNode(this.newElement);
- this.activateNewBodyScriptElements();
- }
- activateNewBodyScriptElements() {
- for (const inertScriptElement of this.newBodyScriptElements) {
- const activatedScriptElement = activateScriptElement(inertScriptElement);
- inertScriptElement.replaceWith(activatedScriptElement);
- }
- }
- async assignNewBody() {
- await this.renderElement(this.currentElement, this.newElement);
- }
- get newHeadStylesheetElements() {
- return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
- }
- get newHeadScriptElements() {
- return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
- }
- get currentHeadProvisionalElements() {
- return this.currentHeadSnapshot.provisionalElements;
- }
- get newHeadProvisionalElements() {
- return this.newHeadSnapshot.provisionalElements;
- }
- get newBodyScriptElements() {
- return this.newElement.querySelectorAll("script");
- }
- }
-
- class SnapshotCache {
- constructor(size) {
- this.keys = [];
- this.snapshots = {};
- this.size = size;
- }
- has(location) {
- return toCacheKey(location) in this.snapshots;
- }
- get(location) {
- if (this.has(location)) {
- const snapshot = this.read(location);
- this.touch(location);
- return snapshot;
- }
- }
- put(location, snapshot) {
- this.write(location, snapshot);
- this.touch(location);
- return snapshot;
- }
- clear() {
- this.snapshots = {};
- }
- read(location) {
- return this.snapshots[toCacheKey(location)];
- }
- write(location, snapshot) {
- this.snapshots[toCacheKey(location)] = snapshot;
- }
- touch(location) {
- const key = toCacheKey(location);
- const index = this.keys.indexOf(key);
- if (index > -1)
- this.keys.splice(index, 1);
- this.keys.unshift(key);
- this.trim();
- }
- trim() {
- for (const key of this.keys.splice(this.size)) {
- delete this.snapshots[key];
+ }
+ findFrameElement(element, submitter) {
+ const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
+ if (id && id != "_top") {
+ const frame = this.element.querySelector(`#${id}:not([disabled])`);
+ if (frame instanceof FrameElement) {
+ return frame;
}
}
}
+}
- class PageView extends View {
- constructor() {
- super(...arguments);
- this.snapshotCache = new SnapshotCache(10);
- this.lastRenderedLocation = new URL(location.href);
- this.forceReloaded = false;
- }
- renderPage(snapshot, isPreview = false, willRender = true, visit) {
- const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
- if (!renderer.shouldRender) {
- this.forceReloaded = true;
- }
- else {
- visit === null || visit === void 0 ? void 0 : visit.changeHistory();
- }
- return this.render(renderer);
- }
- renderError(snapshot, visit) {
- visit === null || visit === void 0 ? void 0 : visit.changeHistory();
- const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
- return this.render(renderer);
- }
- clearSnapshotCache() {
- this.snapshotCache.clear();
- }
- async cacheSnapshot(snapshot = this.snapshot) {
- if (snapshot.isCacheable) {
- this.delegate.viewWillCacheSnapshot();
- const { lastRenderedLocation: location } = this;
- await nextEventLoopTick();
- const cachedSnapshot = snapshot.clone();
- this.snapshotCache.put(location, cachedSnapshot);
- return cachedSnapshot;
- }
- }
- getCachedSnapshotForLocation(location) {
- return this.snapshotCache.get(location);
- }
- get snapshot() {
- return PageSnapshot.fromElement(this.element);
- }
- }
-
- class Preloader {
- constructor(delegate) {
- this.selector = "a[data-turbo-preload]";
- this.delegate = delegate;
- }
- get snapshotCache() {
- return this.delegate.navigator.view.snapshotCache;
- }
- start() {
- if (document.readyState === "loading") {
- return document.addEventListener("DOMContentLoaded", () => {
- this.preloadOnLoadLinksForView(document.body);
- });
- }
- else {
- this.preloadOnLoadLinksForView(document.body);
- }
- }
- preloadOnLoadLinksForView(element) {
- for (const link of element.querySelectorAll(this.selector)) {
- this.preloadURL(link);
- }
- }
- async preloadURL(link) {
- const location = new URL(link.href);
- if (this.snapshotCache.has(location)) {
- return;
- }
- try {
- const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
- const responseText = await response.text();
- const snapshot = PageSnapshot.fromHTMLString(responseText);
- this.snapshotCache.put(location, snapshot);
- }
- catch (_) {
- }
- }
- }
-
- class Session {
- constructor() {
- this.navigator = new Navigator(this);
- this.history = new History(this);
- this.preloader = new Preloader(this);
- this.view = new PageView(this, document.documentElement);
- this.adapter = new BrowserAdapter(this);
- this.pageObserver = new PageObserver(this);
- this.cacheObserver = new CacheObserver();
- this.linkClickObserver = new LinkClickObserver(this, window);
- this.formSubmitObserver = new FormSubmitObserver(this, document);
- this.scrollObserver = new ScrollObserver(this);
- this.streamObserver = new StreamObserver(this);
- this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
- this.frameRedirector = new FrameRedirector(this, document.documentElement);
- this.streamMessageRenderer = new StreamMessageRenderer();
- this.drive = true;
- this.enabled = true;
- this.progressBarDelay = 500;
- this.started = false;
- this.formMode = "on";
- }
- start() {
- if (!this.started) {
- this.pageObserver.start();
- this.cacheObserver.start();
- this.formLinkClickObserver.start();
- this.linkClickObserver.start();
- this.formSubmitObserver.start();
- this.scrollObserver.start();
- this.streamObserver.start();
- this.frameRedirector.start();
- this.history.start();
- this.preloader.start();
- this.started = true;
- this.enabled = true;
- }
- }
- disable() {
- this.enabled = false;
- }
- stop() {
- if (this.started) {
- this.pageObserver.stop();
- this.cacheObserver.stop();
- this.formLinkClickObserver.stop();
- this.linkClickObserver.stop();
- this.formSubmitObserver.stop();
- this.scrollObserver.stop();
- this.streamObserver.stop();
- this.frameRedirector.stop();
- this.history.stop();
- this.started = false;
- }
- }
- registerAdapter(adapter) {
- this.adapter = adapter;
- }
- visit(location, options = {}) {
- const frameElement = options.frame ? document.getElementById(options.frame) : null;
- if (frameElement instanceof FrameElement) {
- frameElement.src = location.toString();
- frameElement.loaded;
- }
- else {
- this.navigator.proposeVisit(expandURL(location), options);
- }
- }
- connectStreamSource(source) {
- this.streamObserver.connectStreamSource(source);
- }
- disconnectStreamSource(source) {
- this.streamObserver.disconnectStreamSource(source);
- }
- renderStreamMessage(message) {
- this.streamMessageRenderer.render(StreamMessage.wrap(message));
- }
- clearCache() {
- this.view.clearSnapshotCache();
- }
- setProgressBarDelay(delay) {
- this.progressBarDelay = delay;
- }
- setFormMode(mode) {
- this.formMode = mode;
- }
- get location() {
- return this.history.location;
- }
- get restorationIdentifier() {
- return this.history.restorationIdentifier;
- }
- historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
- if (this.enabled) {
- this.navigator.startVisit(location, restorationIdentifier, {
- action: "restore",
- historyChanged: true,
- });
- }
- else {
- this.adapter.pageInvalidated({
- reason: "turbo_disabled",
- });
- }
- }
- scrollPositionChanged(position) {
- this.history.updateRestorationData({ scrollPosition: position });
- }
- willSubmitFormLinkToLocation(link, location) {
- return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
- }
- submittedFormLinkToLocation() { }
- willFollowLinkToLocation(link, location, event) {
- return (this.elementIsNavigatable(link) &&
- locationIsVisitable(location, this.snapshot.rootLocation) &&
- this.applicationAllowsFollowingLinkToLocation(link, location, event));
- }
- followedLinkToLocation(link, location) {
- const action = this.getActionForLink(link);
- const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
- this.visit(location.href, { action, acceptsStreamResponse });
- }
- allowsVisitingLocationWithAction(location, action) {
- return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
- }
- visitProposedToLocation(location, options) {
- extendURLWithDeprecatedProperties(location);
- this.adapter.visitProposedToLocation(location, options);
- }
- visitStarted(visit) {
- if (!visit.acceptsStreamResponse) {
- markAsBusy(document.documentElement);
- }
- extendURLWithDeprecatedProperties(visit.location);
- if (!visit.silent) {
- this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
- }
- }
- visitCompleted(visit) {
- clearBusyState(document.documentElement);
- this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
- }
- locationWithActionIsSamePage(location, action) {
- return this.navigator.locationWithActionIsSamePage(location, action);
- }
- visitScrolledToSamePageLocation(oldURL, newURL) {
- this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
- }
- willSubmitForm(form, submitter) {
- const action = getAction(form, submitter);
- return (this.submissionIsNavigatable(form, submitter) &&
- locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
- }
- formSubmitted(form, submitter) {
- this.navigator.submitForm(form, submitter);
- }
- pageBecameInteractive() {
- this.view.lastRenderedLocation = this.location;
- this.notifyApplicationAfterPageLoad();
- }
- pageLoaded() {
- this.history.assumeControlOfScrollRestoration();
- }
- pageWillUnload() {
- this.history.relinquishControlOfScrollRestoration();
- }
- receivedMessageFromStream(message) {
- this.renderStreamMessage(message);
- }
- viewWillCacheSnapshot() {
- var _a;
- if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
- this.notifyApplicationBeforeCachingSnapshot();
- }
- }
- allowsImmediateRender({ element }, options) {
- const event = this.notifyApplicationBeforeRender(element, options);
- const { defaultPrevented, detail: { render }, } = event;
- if (this.view.renderer && render) {
- this.view.renderer.renderElement = render;
- }
- return !defaultPrevented;
- }
- viewRenderedSnapshot(_snapshot, _isPreview) {
- this.view.lastRenderedLocation = this.history.location;
- this.notifyApplicationAfterRender();
- }
- preloadOnLoadLinksForView(element) {
- this.preloader.preloadOnLoadLinksForView(element);
- }
- viewInvalidated(reason) {
- this.adapter.pageInvalidated(reason);
- }
- frameLoaded(frame) {
- this.notifyApplicationAfterFrameLoad(frame);
- }
- frameRendered(fetchResponse, frame) {
- this.notifyApplicationAfterFrameRender(fetchResponse, frame);
- }
- applicationAllowsFollowingLinkToLocation(link, location, ev) {
- const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
- return !event.defaultPrevented;
- }
- applicationAllowsVisitingLocation(location) {
- const event = this.notifyApplicationBeforeVisitingLocation(location);
- return !event.defaultPrevented;
- }
- notifyApplicationAfterClickingLinkToLocation(link, location, event) {
- return dispatch("turbo:click", {
- target: link,
- detail: { url: location.href, originalEvent: event },
- cancelable: true,
- });
- }
- notifyApplicationBeforeVisitingLocation(location) {
- return dispatch("turbo:before-visit", {
- detail: { url: location.href },
- cancelable: true,
- });
+class History {
+ constructor(delegate) {
+ this.restorationIdentifier = uuid();
+ this.restorationData = {};
+ this.started = false;
+ this.pageLoaded = false;
+ this.onPopState = (event) => {
+ if (this.shouldHandlePopState()) {
+ const { turbo } = event.state || {};
+ if (turbo) {
+ this.location = new URL(window.location.href);
+ const { restorationIdentifier } = turbo;
+ this.restorationIdentifier = restorationIdentifier;
+ this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
+ }
+ }
+ };
+ this.onPageLoad = async (_event) => {
+ await nextMicrotask();
+ this.pageLoaded = true;
+ };
+ this.delegate = delegate;
+ }
+ start() {
+ if (!this.started) {
+ addEventListener("popstate", this.onPopState, false);
+ addEventListener("load", this.onPageLoad, false);
+ this.started = true;
+ this.replace(new URL(window.location.href));
}
- notifyApplicationAfterVisitingLocation(location, action) {
- return dispatch("turbo:visit", { detail: { url: location.href, action } });
+ }
+ stop() {
+ if (this.started) {
+ removeEventListener("popstate", this.onPopState, false);
+ removeEventListener("load", this.onPageLoad, false);
+ this.started = false;
}
- notifyApplicationBeforeCachingSnapshot() {
- return dispatch("turbo:before-cache");
+ }
+ push(location, restorationIdentifier) {
+ this.update(history.pushState, location, restorationIdentifier);
+ }
+ replace(location, restorationIdentifier) {
+ this.update(history.replaceState, location, restorationIdentifier);
+ }
+ update(method, location, restorationIdentifier = uuid()) {
+ const state = { turbo: { restorationIdentifier } };
+ method.call(history, state, "", location.href);
+ this.location = location;
+ this.restorationIdentifier = restorationIdentifier;
+ }
+ getRestorationDataForIdentifier(restorationIdentifier) {
+ return this.restorationData[restorationIdentifier] || {};
+ }
+ updateRestorationData(additionalData) {
+ const { restorationIdentifier } = this;
+ const restorationData = this.restorationData[restorationIdentifier];
+ this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
+ }
+ assumeControlOfScrollRestoration() {
+ var _a;
+ if (!this.previousScrollRestoration) {
+ this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
+ history.scrollRestoration = "manual";
}
- notifyApplicationBeforeRender(newBody, options) {
- return dispatch("turbo:before-render", {
- detail: Object.assign({ newBody }, options),
- cancelable: true,
- });
+ }
+ relinquishControlOfScrollRestoration() {
+ if (this.previousScrollRestoration) {
+ history.scrollRestoration = this.previousScrollRestoration;
+ delete this.previousScrollRestoration;
}
- notifyApplicationAfterRender() {
- return dispatch("turbo:render");
+ }
+ shouldHandlePopState() {
+ return this.pageIsLoaded();
+ }
+ pageIsLoaded() {
+ return this.pageLoaded || document.readyState == "complete";
+ }
+}
+
+class Navigator {
+ constructor(delegate) {
+ this.delegate = delegate;
+ }
+ proposeVisit(location, options = {}) {
+ if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
+ if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
+ this.delegate.visitProposedToLocation(location, options);
+ }
+ else {
+ window.location.href = location.toString();
+ }
}
- notifyApplicationAfterPageLoad(timing = {}) {
- return dispatch("turbo:load", {
- detail: { url: this.location.href, timing },
- });
+ }
+ startVisit(locatable, restorationIdentifier, options = {}) {
+ this.stop();
+ this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
+ this.currentVisit.start();
+ }
+ submitForm(form, submitter) {
+ this.stop();
+ this.formSubmission = new FormSubmission(this, form, submitter, true);
+ this.formSubmission.start();
+ }
+ stop() {
+ if (this.formSubmission) {
+ this.formSubmission.stop();
+ delete this.formSubmission;
}
- notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
- dispatchEvent(new HashChangeEvent("hashchange", {
- oldURL: oldURL.toString(),
- newURL: newURL.toString(),
- }));
+ if (this.currentVisit) {
+ this.currentVisit.cancel();
+ delete this.currentVisit;
}
- notifyApplicationAfterFrameLoad(frame) {
- return dispatch("turbo:frame-load", { target: frame });
+ }
+ get adapter() {
+ return this.delegate.adapter;
+ }
+ get view() {
+ return this.delegate.view;
+ }
+ get history() {
+ return this.delegate.history;
+ }
+ formSubmissionStarted(formSubmission) {
+ if (typeof this.adapter.formSubmissionStarted === "function") {
+ this.adapter.formSubmissionStarted(formSubmission);
}
- notifyApplicationAfterFrameRender(fetchResponse, frame) {
- return dispatch("turbo:frame-render", {
- detail: { fetchResponse },
- target: frame,
- cancelable: true,
- });
+ }
+ async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
+ if (formSubmission == this.formSubmission) {
+ const responseHTML = await fetchResponse.responseHTML;
+ if (responseHTML) {
+ const shouldCacheSnapshot = formSubmission.isSafe;
+ if (!shouldCacheSnapshot) {
+ this.view.clearSnapshotCache();
+ }
+ const { statusCode, redirected } = fetchResponse;
+ const action = this.getActionForFormSubmission(formSubmission);
+ const visitOptions = {
+ action,
+ shouldCacheSnapshot,
+ response: { statusCode, responseHTML, redirected },
+ };
+ this.proposeVisit(fetchResponse.location, visitOptions);
+ }
}
- submissionIsNavigatable(form, submitter) {
- if (this.formMode == "off") {
- return false;
+ }
+ async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
+ const responseHTML = await fetchResponse.responseHTML;
+ if (responseHTML) {
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
+ if (fetchResponse.serverError) {
+ await this.view.renderError(snapshot, this.currentVisit);
}
else {
- const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
- if (this.formMode == "optin") {
- return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
- }
- else {
- return submitterIsNavigatable && this.elementIsNavigatable(form);
- }
+ await this.view.renderPage(snapshot, false, true, this.currentVisit);
}
+ this.view.scrollToTop();
+ this.view.clearSnapshotCache();
}
- elementIsNavigatable(element) {
- const container = findClosestRecursively(element, "[data-turbo]");
- const withinFrame = findClosestRecursively(element, "turbo-frame");
- if (this.drive || withinFrame) {
- if (container) {
- return container.getAttribute("data-turbo") != "false";
- }
- else {
- return true;
- }
+ }
+ formSubmissionErrored(formSubmission, error) {
+ console.error(error);
+ }
+ formSubmissionFinished(formSubmission) {
+ if (typeof this.adapter.formSubmissionFinished === "function") {
+ this.adapter.formSubmissionFinished(formSubmission);
+ }
+ }
+ visitStarted(visit) {
+ this.delegate.visitStarted(visit);
+ }
+ visitCompleted(visit) {
+ this.delegate.visitCompleted(visit);
+ }
+ locationWithActionIsSamePage(location, action) {
+ const anchor = getAnchor(location);
+ const currentAnchor = getAnchor(this.view.lastRenderedLocation);
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
+ return (action !== "replace" &&
+ getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
+ (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
+ }
+ visitScrolledToSamePageLocation(oldURL, newURL) {
+ this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
+ }
+ get location() {
+ return this.history.location;
+ }
+ get restorationIdentifier() {
+ return this.history.restorationIdentifier;
+ }
+ getActionForFormSubmission({ submitter, formElement }) {
+ return getVisitAction(submitter, formElement) || "advance";
+ }
+}
+
+var PageStage;
+(function (PageStage) {
+ PageStage[PageStage["initial"] = 0] = "initial";
+ PageStage[PageStage["loading"] = 1] = "loading";
+ PageStage[PageStage["interactive"] = 2] = "interactive";
+ PageStage[PageStage["complete"] = 3] = "complete";
+})(PageStage || (PageStage = {}));
+class PageObserver {
+ constructor(delegate) {
+ this.stage = PageStage.initial;
+ this.started = false;
+ this.interpretReadyState = () => {
+ const { readyState } = this;
+ if (readyState == "interactive") {
+ this.pageIsInteractive();
+ }
+ else if (readyState == "complete") {
+ this.pageIsComplete();
}
- else {
- if (container) {
- return container.getAttribute("data-turbo") == "true";
- }
- else {
- return false;
- }
+ };
+ this.pageWillUnload = () => {
+ this.delegate.pageWillUnload();
+ };
+ this.delegate = delegate;
+ }
+ start() {
+ if (!this.started) {
+ if (this.stage == PageStage.initial) {
+ this.stage = PageStage.loading;
}
+ document.addEventListener("readystatechange", this.interpretReadyState, false);
+ addEventListener("pagehide", this.pageWillUnload, false);
+ this.started = true;
}
- getActionForLink(link) {
- return getVisitAction(link) || "advance";
+ }
+ stop() {
+ if (this.started) {
+ document.removeEventListener("readystatechange", this.interpretReadyState, false);
+ removeEventListener("pagehide", this.pageWillUnload, false);
+ this.started = false;
}
- get snapshot() {
- return this.view.snapshot;
+ }
+ pageIsInteractive() {
+ if (this.stage == PageStage.loading) {
+ this.stage = PageStage.interactive;
+ this.delegate.pageBecameInteractive();
}
}
- function extendURLWithDeprecatedProperties(url) {
- Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
+ pageIsComplete() {
+ this.pageIsInteractive();
+ if (this.stage == PageStage.interactive) {
+ this.stage = PageStage.complete;
+ this.delegate.pageLoaded();
+ }
}
- const deprecatedLocationPropertyDescriptors = {
- absoluteURL: {
- get() {
- return this.toString();
- },
- },
- };
+ get readyState() {
+ return document.readyState;
+ }
+}
- class Cache {
- constructor(session) {
- this.session = session;
+class ScrollObserver {
+ constructor(delegate) {
+ this.started = false;
+ this.onScroll = () => {
+ this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
+ };
+ this.delegate = delegate;
+ }
+ start() {
+ if (!this.started) {
+ addEventListener("scroll", this.onScroll, false);
+ this.onScroll();
+ this.started = true;
}
- clear() {
- this.session.clearCache();
+ }
+ stop() {
+ if (this.started) {
+ removeEventListener("scroll", this.onScroll, false);
+ this.started = false;
}
- resetCacheControl() {
- this.setCacheControl("");
+ }
+ updatePosition(position) {
+ this.delegate.scrollPositionChanged(position);
+ }
+}
+
+class StreamMessageRenderer {
+ render({ fragment }) {
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
+ }
+ enteringBardo(currentPermanentElement, newPermanentElement) {
+ newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
+ }
+ leavingBardo() { }
+}
+function getPermanentElementMapForFragment(fragment) {
+ const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
+ const permanentElementMap = {};
+ for (const permanentElementInDocument of permanentElementsInDocument) {
+ const { id } = permanentElementInDocument;
+ for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
+ const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
+ if (elementInStream) {
+ permanentElementMap[id] = [permanentElementInDocument, elementInStream];
+ }
}
- exemptPageFromCache() {
- this.setCacheControl("no-cache");
+ }
+ return permanentElementMap;
+}
+
+class StreamObserver {
+ constructor(delegate) {
+ this.sources = new Set();
+ this.started = false;
+ this.inspectFetchResponse = ((event) => {
+ const response = fetchResponseFromEvent(event);
+ if (response && fetchResponseIsStream(response)) {
+ event.preventDefault();
+ this.receiveMessageResponse(response);
+ }
+ });
+ this.receiveMessageEvent = (event) => {
+ if (this.started && typeof event.data == "string") {
+ this.receiveMessageHTML(event.data);
+ }
+ };
+ this.delegate = delegate;
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
}
- exemptPageFromPreview() {
- this.setCacheControl("no-preview");
+ }
+ stop() {
+ if (this.started) {
+ this.started = false;
+ removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
}
- setCacheControl(value) {
- setMetaContent("turbo-cache-control", value);
+ }
+ connectStreamSource(source) {
+ if (!this.streamSourceIsConnected(source)) {
+ this.sources.add(source);
+ source.addEventListener("message", this.receiveMessageEvent, false);
}
}
-
- const StreamActions = {
- after() {
- this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
- },
- append() {
- this.removeDuplicateTargetChildren();
- this.targetElements.forEach((e) => e.append(this.templateContent));
- },
- before() {
- this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
- },
- prepend() {
- this.removeDuplicateTargetChildren();
- this.targetElements.forEach((e) => e.prepend(this.templateContent));
- },
- remove() {
- this.targetElements.forEach((e) => e.remove());
- },
- replace() {
- this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
- },
- update() {
- this.targetElements.forEach((targetElement) => {
- targetElement.innerHTML = "";
- targetElement.append(this.templateContent);
- });
- },
- };
-
- const session = new Session();
- const cache = new Cache(session);
- const { navigator: navigator$1 } = session;
- function start() {
- session.start();
+ disconnectStreamSource(source) {
+ if (this.streamSourceIsConnected(source)) {
+ this.sources.delete(source);
+ source.removeEventListener("message", this.receiveMessageEvent, false);
+ }
+ }
+ streamSourceIsConnected(source) {
+ return this.sources.has(source);
}
- function registerAdapter(adapter) {
- session.registerAdapter(adapter);
+ async receiveMessageResponse(response) {
+ const html = await response.responseHTML;
+ if (html) {
+ this.receiveMessageHTML(html);
+ }
}
- function visit(location, options) {
- session.visit(location, options);
+ receiveMessageHTML(html) {
+ this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
}
- function connectStreamSource(source) {
- session.connectStreamSource(source);
+}
+function fetchResponseFromEvent(event) {
+ var _a;
+ const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
+ if (fetchResponse instanceof FetchResponse) {
+ return fetchResponse;
}
- function disconnectStreamSource(source) {
- session.disconnectStreamSource(source);
+}
+function fetchResponseIsStream(response) {
+ var _a;
+ const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
+ return contentType.startsWith(StreamMessage.contentType);
+}
+
+class ErrorRenderer extends Renderer {
+ static renderElement(currentElement, newElement) {
+ const { documentElement, body } = document;
+ documentElement.replaceChild(newElement, body);
}
- function renderStreamMessage(message) {
- session.renderStreamMessage(message);
+ async render() {
+ this.replaceHeadAndBody();
+ this.activateScriptElements();
}
- function clearCache() {
- console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
- session.clearCache();
+ replaceHeadAndBody() {
+ const { documentElement, head } = document;
+ documentElement.replaceChild(this.newHead, head);
+ this.renderElement(this.currentElement, this.newElement);
}
- function setProgressBarDelay(delay) {
- session.setProgressBarDelay(delay);
+ activateScriptElements() {
+ for (const replaceableElement of this.scriptElements) {
+ const parentNode = replaceableElement.parentNode;
+ if (parentNode) {
+ const element = activateScriptElement(replaceableElement);
+ parentNode.replaceChild(element, replaceableElement);
+ }
+ }
}
- function setConfirmMethod(confirmMethod) {
- FormSubmission.confirmMethod = confirmMethod;
+ get newHead() {
+ return this.newSnapshot.headSnapshot.element;
}
- function setFormMode(mode) {
- session.setFormMode(mode);
+ get scriptElements() {
+ return document.documentElement.querySelectorAll("script");
}
+}
- var Turbo = /*#__PURE__*/Object.freeze({
- __proto__: null,
- navigator: navigator$1,
- session: session,
- cache: cache,
- PageRenderer: PageRenderer,
- PageSnapshot: PageSnapshot,
- FrameRenderer: FrameRenderer,
- start: start,
- registerAdapter: registerAdapter,
- visit: visit,
- connectStreamSource: connectStreamSource,
- disconnectStreamSource: disconnectStreamSource,
- renderStreamMessage: renderStreamMessage,
- clearCache: clearCache,
- setProgressBarDelay: setProgressBarDelay,
- setConfirmMethod: setConfirmMethod,
- setFormMode: setFormMode,
- StreamActions: StreamActions
- });
-
- class TurboFrameMissingError extends Error {
+class PageRenderer extends Renderer {
+ static renderElement(currentElement, newElement) {
+ if (document.body && newElement instanceof HTMLBodyElement) {
+ document.body.replaceWith(newElement);
+ }
+ else {
+ document.documentElement.appendChild(newElement);
+ }
}
-
- class FrameController {
- constructor(element) {
- this.fetchResponseLoaded = (_fetchResponse) => { };
- this.currentFetchRequest = null;
- this.resolveVisitPromise = () => { };
- this.connected = false;
- this.hasBeenLoaded = false;
- this.ignoredAttributes = new Set();
- this.action = null;
- this.visitCachedSnapshot = ({ element }) => {
- const frame = element.querySelector("#" + this.element.id);
- if (frame && this.previousFrameElement) {
- frame.replaceChildren(...this.previousFrameElement.children);
- }
- delete this.previousFrameElement;
+ get shouldRender() {
+ return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
+ }
+ get reloadReason() {
+ if (!this.newSnapshot.isVisitable) {
+ return {
+ reason: "turbo_visit_control_is_reload",
};
- this.element = element;
- this.view = new FrameView(this, this.element);
- this.appearanceObserver = new AppearanceObserver(this, this.element);
- this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
- this.linkInterceptor = new LinkInterceptor(this, this.element);
- this.restorationIdentifier = uuid();
- this.formSubmitObserver = new FormSubmitObserver(this, this.element);
- }
- connect() {
- if (!this.connected) {
- this.connected = true;
- if (this.loadingStyle == exports.FrameLoadingStyle.lazy) {
- this.appearanceObserver.start();
- }
- else {
- this.loadSourceURL();
- }
- this.formLinkClickObserver.start();
- this.linkInterceptor.start();
- this.formSubmitObserver.start();
- }
}
- disconnect() {
- if (this.connected) {
- this.connected = false;
- this.appearanceObserver.stop();
- this.formLinkClickObserver.stop();
- this.linkInterceptor.stop();
- this.formSubmitObserver.stop();
- }
+ if (!this.trackedElementsAreIdentical) {
+ return {
+ reason: "tracked_element_mismatch",
+ };
}
- disabledChanged() {
- if (this.loadingStyle == exports.FrameLoadingStyle.eager) {
- this.loadSourceURL();
- }
+ }
+ async prepareToRender() {
+ await this.mergeHead();
+ }
+ async render() {
+ if (this.willRender) {
+ await this.replaceBody();
}
- sourceURLChanged() {
- if (this.isIgnoringChangesTo("src"))
- return;
- if (this.element.isConnected) {
- this.complete = false;
- }
- if (this.loadingStyle == exports.FrameLoadingStyle.eager || this.hasBeenLoaded) {
- this.loadSourceURL();
- }
+ }
+ finishRendering() {
+ super.finishRendering();
+ if (!this.isPreview) {
+ this.focusFirstAutofocusableElement();
}
- sourceURLReloaded() {
- const { src } = this.element;
- this.ignoringChangesToAttribute("complete", () => {
- this.element.removeAttribute("complete");
- });
- this.element.src = null;
- this.element.src = src;
- return this.element.loaded;
+ }
+ get currentHeadSnapshot() {
+ return this.currentSnapshot.headSnapshot;
+ }
+ get newHeadSnapshot() {
+ return this.newSnapshot.headSnapshot;
+ }
+ get newElement() {
+ return this.newSnapshot.element;
+ }
+ async mergeHead() {
+ const mergedHeadElements = this.mergeProvisionalElements();
+ const newStylesheetElements = this.copyNewHeadStylesheetElements();
+ this.copyNewHeadScriptElements();
+ await mergedHeadElements;
+ await newStylesheetElements;
+ }
+ async replaceBody() {
+ await this.preservingPermanentElements(async () => {
+ this.activateNewBody();
+ await this.assignNewBody();
+ });
+ }
+ get trackedElementsAreIdentical() {
+ return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
+ }
+ async copyNewHeadStylesheetElements() {
+ const loadingElements = [];
+ for (const element of this.newHeadStylesheetElements) {
+ loadingElements.push(waitForLoad(element));
+ document.head.appendChild(element);
}
- completeChanged() {
- if (this.isIgnoringChangesTo("complete"))
- return;
- this.loadSourceURL();
+ await Promise.all(loadingElements);
+ }
+ copyNewHeadScriptElements() {
+ for (const element of this.newHeadScriptElements) {
+ document.head.appendChild(activateScriptElement(element));
}
- loadingStyleChanged() {
- if (this.loadingStyle == exports.FrameLoadingStyle.lazy) {
- this.appearanceObserver.start();
- }
- else {
- this.appearanceObserver.stop();
- this.loadSourceURL();
+ }
+ async mergeProvisionalElements() {
+ const newHeadElements = [...this.newHeadProvisionalElements];
+ for (const element of this.currentHeadProvisionalElements) {
+ if (!this.isCurrentElementInElementList(element, newHeadElements)) {
+ document.head.removeChild(element);
}
}
- async loadSourceURL() {
- if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
- this.element.loaded = this.visit(expandURL(this.sourceURL));
- this.appearanceObserver.stop();
- await this.element.loaded;
- this.hasBeenLoaded = true;
- }
+ for (const element of newHeadElements) {
+ document.head.appendChild(element);
}
- async loadResponse(fetchResponse) {
- if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
- this.sourceURL = fetchResponse.response.url;
- }
- try {
- const html = await fetchResponse.responseHTML;
- if (html) {
- const document = parseHTMLDocument(html);
- const pageSnapshot = PageSnapshot.fromDocument(document);
- if (pageSnapshot.isVisitable) {
- await this.loadFrameResponse(fetchResponse, document);
- }
- else {
- await this.handleUnvisitableFrameResponse(fetchResponse);
- }
+ }
+ isCurrentElementInElementList(element, elementList) {
+ for (const [index, newElement] of elementList.entries()) {
+ if (element.tagName == "TITLE") {
+ if (newElement.tagName != "TITLE") {
+ continue;
+ }
+ if (element.innerHTML == newElement.innerHTML) {
+ elementList.splice(index, 1);
+ return true;
}
}
- finally {
- this.fetchResponseLoaded = () => { };
+ if (newElement.isEqualNode(element)) {
+ elementList.splice(index, 1);
+ return true;
}
}
- elementAppearedInViewport(element) {
- this.proposeVisitIfNavigatedWithAction(element, element);
- this.loadSourceURL();
+ return false;
+ }
+ removeCurrentHeadProvisionalElements() {
+ for (const element of this.currentHeadProvisionalElements) {
+ document.head.removeChild(element);
}
- willSubmitFormLinkToLocation(link) {
- return this.shouldInterceptNavigation(link);
+ }
+ copyNewHeadProvisionalElements() {
+ for (const element of this.newHeadProvisionalElements) {
+ document.head.appendChild(element);
}
- submittedFormLinkToLocation(link, _location, form) {
- const frame = this.findFrameElement(link);
- if (frame)
- form.setAttribute("data-turbo-frame", frame.id);
+ }
+ activateNewBody() {
+ document.adoptNode(this.newElement);
+ this.activateNewBodyScriptElements();
+ }
+ activateNewBodyScriptElements() {
+ for (const inertScriptElement of this.newBodyScriptElements) {
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
+ inertScriptElement.replaceWith(activatedScriptElement);
}
- shouldInterceptLinkClick(element, _location, _event) {
- return this.shouldInterceptNavigation(element);
+ }
+ async assignNewBody() {
+ await this.renderElement(this.currentElement, this.newElement);
+ }
+ get newHeadStylesheetElements() {
+ return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
+ }
+ get newHeadScriptElements() {
+ return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
+ }
+ get currentHeadProvisionalElements() {
+ return this.currentHeadSnapshot.provisionalElements;
+ }
+ get newHeadProvisionalElements() {
+ return this.newHeadSnapshot.provisionalElements;
+ }
+ get newBodyScriptElements() {
+ return this.newElement.querySelectorAll("script");
+ }
+}
+
+class SnapshotCache {
+ constructor(size) {
+ this.keys = [];
+ this.snapshots = {};
+ this.size = size;
+ }
+ has(location) {
+ return toCacheKey(location) in this.snapshots;
+ }
+ get(location) {
+ if (this.has(location)) {
+ const snapshot = this.read(location);
+ this.touch(location);
+ return snapshot;
}
- linkClickIntercepted(element, location) {
- this.navigateFrame(element, location);
+ }
+ put(location, snapshot) {
+ this.write(location, snapshot);
+ this.touch(location);
+ return snapshot;
+ }
+ clear() {
+ this.snapshots = {};
+ }
+ read(location) {
+ return this.snapshots[toCacheKey(location)];
+ }
+ write(location, snapshot) {
+ this.snapshots[toCacheKey(location)] = snapshot;
+ }
+ touch(location) {
+ const key = toCacheKey(location);
+ const index = this.keys.indexOf(key);
+ if (index > -1)
+ this.keys.splice(index, 1);
+ this.keys.unshift(key);
+ this.trim();
+ }
+ trim() {
+ for (const key of this.keys.splice(this.size)) {
+ delete this.snapshots[key];
}
- willSubmitForm(element, submitter) {
- return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
+ }
+}
+
+class PageView extends View {
+ constructor() {
+ super(...arguments);
+ this.snapshotCache = new SnapshotCache(10);
+ this.lastRenderedLocation = new URL(location.href);
+ this.forceReloaded = false;
+ }
+ renderPage(snapshot, isPreview = false, willRender = true, visit) {
+ const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
+ if (!renderer.shouldRender) {
+ this.forceReloaded = true;
}
- formSubmitted(element, submitter) {
- if (this.formSubmission) {
- this.formSubmission.stop();
- }
- this.formSubmission = new FormSubmission(this, element, submitter);
- const { fetchRequest } = this.formSubmission;
- this.prepareRequest(fetchRequest);
- this.formSubmission.start();
+ else {
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
}
- prepareRequest(request) {
- var _a;
- request.headers["Turbo-Frame"] = this.id;
- if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
- request.acceptResponseType(StreamMessage.contentType);
- }
+ return this.render(renderer);
+ }
+ renderError(snapshot, visit) {
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
+ const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
+ return this.render(renderer);
+ }
+ clearSnapshotCache() {
+ this.snapshotCache.clear();
+ }
+ async cacheSnapshot(snapshot = this.snapshot) {
+ if (snapshot.isCacheable) {
+ this.delegate.viewWillCacheSnapshot();
+ const { lastRenderedLocation: location } = this;
+ await nextEventLoopTick();
+ const cachedSnapshot = snapshot.clone();
+ this.snapshotCache.put(location, cachedSnapshot);
+ return cachedSnapshot;
}
- requestStarted(_request) {
- markAsBusy(this.element);
+ }
+ getCachedSnapshotForLocation(location) {
+ return this.snapshotCache.get(location);
+ }
+ get snapshot() {
+ return PageSnapshot.fromElement(this.element);
+ }
+}
+
+class Preloader {
+ constructor(delegate) {
+ this.selector = "a[data-turbo-preload]";
+ this.delegate = delegate;
+ }
+ get snapshotCache() {
+ return this.delegate.navigator.view.snapshotCache;
+ }
+ start() {
+ if (document.readyState === "loading") {
+ return document.addEventListener("DOMContentLoaded", () => {
+ this.preloadOnLoadLinksForView(document.body);
+ });
}
- requestPreventedHandlingResponse(_request, _response) {
- this.resolveVisitPromise();
+ else {
+ this.preloadOnLoadLinksForView(document.body);
}
- async requestSucceededWithResponse(request, response) {
- await this.loadResponse(response);
- this.resolveVisitPromise();
+ }
+ preloadOnLoadLinksForView(element) {
+ for (const link of element.querySelectorAll(this.selector)) {
+ this.preloadURL(link);
}
- async requestFailedWithResponse(request, response) {
- await this.loadResponse(response);
- this.resolveVisitPromise();
+ }
+ async preloadURL(link) {
+ const location = new URL(link.href);
+ if (this.snapshotCache.has(location)) {
+ return;
}
- requestErrored(request, error) {
- console.error(error);
- this.resolveVisitPromise();
+ try {
+ const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
+ const responseText = await response.text();
+ const snapshot = PageSnapshot.fromHTMLString(responseText);
+ this.snapshotCache.put(location, snapshot);
}
- requestFinished(_request) {
- clearBusyState(this.element);
+ catch (_) {
}
- formSubmissionStarted({ formElement }) {
- markAsBusy(formElement, this.findFrameElement(formElement));
+ }
+}
+
+class Session {
+ constructor() {
+ this.navigator = new Navigator(this);
+ this.history = new History(this);
+ this.preloader = new Preloader(this);
+ this.view = new PageView(this, document.documentElement);
+ this.adapter = new BrowserAdapter(this);
+ this.pageObserver = new PageObserver(this);
+ this.cacheObserver = new CacheObserver();
+ this.linkClickObserver = new LinkClickObserver(this, window);
+ this.formSubmitObserver = new FormSubmitObserver(this, document);
+ this.scrollObserver = new ScrollObserver(this);
+ this.streamObserver = new StreamObserver(this);
+ this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
+ this.frameRedirector = new FrameRedirector(this, document.documentElement);
+ this.streamMessageRenderer = new StreamMessageRenderer();
+ this.drive = true;
+ this.enabled = true;
+ this.progressBarDelay = 500;
+ this.started = false;
+ this.formMode = "on";
+ }
+ start() {
+ if (!this.started) {
+ this.pageObserver.start();
+ this.cacheObserver.start();
+ this.formLinkClickObserver.start();
+ this.linkClickObserver.start();
+ this.formSubmitObserver.start();
+ this.scrollObserver.start();
+ this.streamObserver.start();
+ this.frameRedirector.start();
+ this.history.start();
+ this.preloader.start();
+ this.started = true;
+ this.enabled = true;
}
- formSubmissionSucceededWithResponse(formSubmission, response) {
- const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
- frame.delegate.loadResponse(response);
- if (!formSubmission.isSafe) {
- session.clearCache();
- }
+ }
+ disable() {
+ this.enabled = false;
+ }
+ stop() {
+ if (this.started) {
+ this.pageObserver.stop();
+ this.cacheObserver.stop();
+ this.formLinkClickObserver.stop();
+ this.linkClickObserver.stop();
+ this.formSubmitObserver.stop();
+ this.scrollObserver.stop();
+ this.streamObserver.stop();
+ this.frameRedirector.stop();
+ this.history.stop();
+ this.started = false;
}
- formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
- this.element.delegate.loadResponse(fetchResponse);
- session.clearCache();
+ }
+ registerAdapter(adapter) {
+ this.adapter = adapter;
+ }
+ visit(location, options = {}) {
+ const frameElement = options.frame ? document.getElementById(options.frame) : null;
+ if (frameElement instanceof FrameElement) {
+ frameElement.src = location.toString();
+ frameElement.loaded;
}
- formSubmissionErrored(formSubmission, error) {
- console.error(error);
+ else {
+ this.navigator.proposeVisit(expandURL(location), options);
}
- formSubmissionFinished({ formElement }) {
- clearBusyState(formElement, this.findFrameElement(formElement));
+ }
+ connectStreamSource(source) {
+ this.streamObserver.connectStreamSource(source);
+ }
+ disconnectStreamSource(source) {
+ this.streamObserver.disconnectStreamSource(source);
+ }
+ renderStreamMessage(message) {
+ this.streamMessageRenderer.render(StreamMessage.wrap(message));
+ }
+ clearCache() {
+ this.view.clearSnapshotCache();
+ }
+ setProgressBarDelay(delay) {
+ this.progressBarDelay = delay;
+ }
+ setFormMode(mode) {
+ this.formMode = mode;
+ }
+ get location() {
+ return this.history.location;
+ }
+ get restorationIdentifier() {
+ return this.history.restorationIdentifier;
+ }
+ historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
+ if (this.enabled) {
+ this.navigator.startVisit(location, restorationIdentifier, {
+ action: "restore",
+ historyChanged: true,
+ });
}
- allowsImmediateRender({ element: newFrame }, options) {
- const event = dispatch("turbo:before-frame-render", {
- target: this.element,
- detail: Object.assign({ newFrame }, options),
- cancelable: true,
+ else {
+ this.adapter.pageInvalidated({
+ reason: "turbo_disabled",
});
- const { defaultPrevented, detail: { render }, } = event;
- if (this.view.renderer && render) {
- this.view.renderer.renderElement = render;
- }
- return !defaultPrevented;
}
- viewRenderedSnapshot(_snapshot, _isPreview) { }
- preloadOnLoadLinksForView(element) {
- session.preloadOnLoadLinksForView(element);
+ }
+ scrollPositionChanged(position) {
+ this.history.updateRestorationData({ scrollPosition: position });
+ }
+ willSubmitFormLinkToLocation(link, location) {
+ return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
+ }
+ submittedFormLinkToLocation() { }
+ willFollowLinkToLocation(link, location, event) {
+ return (this.elementIsNavigatable(link) &&
+ locationIsVisitable(location, this.snapshot.rootLocation) &&
+ this.applicationAllowsFollowingLinkToLocation(link, location, event));
+ }
+ followedLinkToLocation(link, location) {
+ const action = this.getActionForLink(link);
+ const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
+ this.visit(location.href, { action, acceptsStreamResponse });
+ }
+ allowsVisitingLocationWithAction(location, action) {
+ return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
+ }
+ visitProposedToLocation(location, options) {
+ extendURLWithDeprecatedProperties(location);
+ this.adapter.visitProposedToLocation(location, options);
+ }
+ visitStarted(visit) {
+ if (!visit.acceptsStreamResponse) {
+ markAsBusy(document.documentElement);
+ }
+ extendURLWithDeprecatedProperties(visit.location);
+ if (!visit.silent) {
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
}
- viewInvalidated() { }
- willRenderFrame(currentElement, _newElement) {
- this.previousFrameElement = currentElement.cloneNode(true);
+ }
+ visitCompleted(visit) {
+ clearBusyState(document.documentElement);
+ this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
+ }
+ locationWithActionIsSamePage(location, action) {
+ return this.navigator.locationWithActionIsSamePage(location, action);
+ }
+ visitScrolledToSamePageLocation(oldURL, newURL) {
+ this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
+ }
+ willSubmitForm(form, submitter) {
+ const action = getAction(form, submitter);
+ return (this.submissionIsNavigatable(form, submitter) &&
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
+ }
+ formSubmitted(form, submitter) {
+ this.navigator.submitForm(form, submitter);
+ }
+ pageBecameInteractive() {
+ this.view.lastRenderedLocation = this.location;
+ this.notifyApplicationAfterPageLoad();
+ }
+ pageLoaded() {
+ this.history.assumeControlOfScrollRestoration();
+ }
+ pageWillUnload() {
+ this.history.relinquishControlOfScrollRestoration();
+ }
+ receivedMessageFromStream(message) {
+ this.renderStreamMessage(message);
+ }
+ viewWillCacheSnapshot() {
+ var _a;
+ if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
+ this.notifyApplicationBeforeCachingSnapshot();
}
- async loadFrameResponse(fetchResponse, document) {
- const newFrameElement = await this.extractForeignFrameElement(document.body);
- if (newFrameElement) {
- const snapshot = new Snapshot(newFrameElement);
- const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
- if (this.view.renderPromise)
- await this.view.renderPromise;
- this.changeHistory();
- await this.view.render(renderer);
- this.complete = true;
- session.frameRendered(fetchResponse, this.element);
- session.frameLoaded(this.element);
- this.fetchResponseLoaded(fetchResponse);
- }
- else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
- this.handleFrameMissingFromResponse(fetchResponse);
- }
- }
- async visit(url) {
- var _a;
- const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
- (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
- this.currentFetchRequest = request;
- return new Promise((resolve) => {
- this.resolveVisitPromise = () => {
- this.resolveVisitPromise = () => { };
- this.currentFetchRequest = null;
- resolve();
- };
- request.perform();
- });
+ }
+ allowsImmediateRender({ element }, options) {
+ const event = this.notifyApplicationBeforeRender(element, options);
+ const { defaultPrevented, detail: { render }, } = event;
+ if (this.view.renderer && render) {
+ this.view.renderer.renderElement = render;
}
- navigateFrame(element, url, submitter) {
- const frame = this.findFrameElement(element, submitter);
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
- this.withCurrentNavigationElement(element, () => {
- frame.src = url;
- });
+ return !defaultPrevented;
+ }
+ viewRenderedSnapshot(_snapshot, _isPreview) {
+ this.view.lastRenderedLocation = this.history.location;
+ this.notifyApplicationAfterRender();
+ }
+ preloadOnLoadLinksForView(element) {
+ this.preloader.preloadOnLoadLinksForView(element);
+ }
+ viewInvalidated(reason) {
+ this.adapter.pageInvalidated(reason);
+ }
+ frameLoaded(frame) {
+ this.notifyApplicationAfterFrameLoad(frame);
+ }
+ frameRendered(fetchResponse, frame) {
+ this.notifyApplicationAfterFrameRender(fetchResponse, frame);
+ }
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
+ return !event.defaultPrevented;
+ }
+ applicationAllowsVisitingLocation(location) {
+ const event = this.notifyApplicationBeforeVisitingLocation(location);
+ return !event.defaultPrevented;
+ }
+ notifyApplicationAfterClickingLinkToLocation(link, location, event) {
+ return dispatch("turbo:click", {
+ target: link,
+ detail: { url: location.href, originalEvent: event },
+ cancelable: true,
+ });
+ }
+ notifyApplicationBeforeVisitingLocation(location) {
+ return dispatch("turbo:before-visit", {
+ detail: { url: location.href },
+ cancelable: true,
+ });
+ }
+ notifyApplicationAfterVisitingLocation(location, action) {
+ return dispatch("turbo:visit", { detail: { url: location.href, action } });
+ }
+ notifyApplicationBeforeCachingSnapshot() {
+ return dispatch("turbo:before-cache");
+ }
+ notifyApplicationBeforeRender(newBody, options) {
+ return dispatch("turbo:before-render", {
+ detail: Object.assign({ newBody }, options),
+ cancelable: true,
+ });
+ }
+ notifyApplicationAfterRender() {
+ return dispatch("turbo:render");
+ }
+ notifyApplicationAfterPageLoad(timing = {}) {
+ return dispatch("turbo:load", {
+ detail: { url: this.location.href, timing },
+ });
+ }
+ notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
+ dispatchEvent(new HashChangeEvent("hashchange", {
+ oldURL: oldURL.toString(),
+ newURL: newURL.toString(),
+ }));
+ }
+ notifyApplicationAfterFrameLoad(frame) {
+ return dispatch("turbo:frame-load", { target: frame });
+ }
+ notifyApplicationAfterFrameRender(fetchResponse, frame) {
+ return dispatch("turbo:frame-render", {
+ detail: { fetchResponse },
+ target: frame,
+ cancelable: true,
+ });
+ }
+ submissionIsNavigatable(form, submitter) {
+ if (this.formMode == "off") {
+ return false;
}
- proposeVisitIfNavigatedWithAction(frame, element, submitter) {
- this.action = getVisitAction(submitter, element, frame);
- if (this.action) {
- const pageSnapshot = PageSnapshot.fromElement(frame).clone();
- const { visitCachedSnapshot } = frame.delegate;
- frame.delegate.fetchResponseLoaded = (fetchResponse) => {
- if (frame.src) {
- const { statusCode, redirected } = fetchResponse;
- const responseHTML = frame.ownerDocument.documentElement.outerHTML;
- const response = { statusCode, redirected, responseHTML };
- const options = {
- response,
- visitCachedSnapshot,
- willRender: false,
- updateHistory: false,
- restorationIdentifier: this.restorationIdentifier,
- snapshot: pageSnapshot,
- };
- if (this.action)
- options.action = this.action;
- session.visit(frame.src, options);
- }
- };
+ else {
+ const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
+ if (this.formMode == "optin") {
+ return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
}
- }
- changeHistory() {
- if (this.action) {
- const method = getHistoryMethodForAction(this.action);
- session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
+ else {
+ return submitterIsNavigatable && this.elementIsNavigatable(form);
}
}
- async handleUnvisitableFrameResponse(fetchResponse) {
- console.warn(`The response (${fetchResponse.statusCode}) from is performing a full page visit due to turbo-visit-control.`);
- await this.visitResponse(fetchResponse.response);
- }
- willHandleFrameMissingFromResponse(fetchResponse) {
- this.element.setAttribute("complete", "");
- const response = fetchResponse.response;
- const visit = async (url, options = {}) => {
- if (url instanceof Response) {
- this.visitResponse(url);
- }
- else {
- session.visit(url, options);
- }
- };
- const event = dispatch("turbo:frame-missing", {
- target: this.element,
- detail: { response, visit },
- cancelable: true,
- });
- return !event.defaultPrevented;
- }
- handleFrameMissingFromResponse(fetchResponse) {
- this.view.missing();
- this.throwFrameMissingError(fetchResponse);
- }
- throwFrameMissingError(fetchResponse) {
- const message = `The response (${fetchResponse.statusCode}) did not contain the expected and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
- throw new TurboFrameMissingError(message);
- }
- async visitResponse(response) {
- const wrapped = new FetchResponse(response);
- const responseHTML = await wrapped.responseHTML;
- const { location, redirected, statusCode } = wrapped;
- return session.visit(location, { response: { redirected, statusCode, responseHTML } });
- }
- findFrameElement(element, submitter) {
- var _a;
- const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
- return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
- }
- async extractForeignFrameElement(container) {
- let element;
- const id = CSS.escape(this.id);
- try {
- element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
- if (element) {
- return element;
- }
- element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
- if (element) {
- await element.loaded;
- return await this.extractForeignFrameElement(element);
- }
+ }
+ elementIsNavigatable(element) {
+ const container = findClosestRecursively(element, "[data-turbo]");
+ const withinFrame = findClosestRecursively(element, "turbo-frame");
+ if (this.drive || withinFrame) {
+ if (container) {
+ return container.getAttribute("data-turbo") != "false";
}
- catch (error) {
- console.error(error);
- return new FrameElement();
+ else {
+ return true;
}
- return null;
- }
- formActionIsVisitable(form, submitter) {
- const action = getAction(form, submitter);
- return locationIsVisitable(expandURL(action), this.rootLocation);
}
- shouldInterceptNavigation(element, submitter) {
- const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
- if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
- return false;
+ else {
+ if (container) {
+ return container.getAttribute("data-turbo") == "true";
}
- if (!this.enabled || id == "_top") {
+ else {
return false;
}
- if (id) {
- const frameElement = getFrameElementById(id);
- if (frameElement) {
- return !frameElement.disabled;
- }
- }
- if (!session.elementIsNavigatable(element)) {
- return false;
+ }
+ }
+ getActionForLink(link) {
+ return getVisitAction(link) || "advance";
+ }
+ get snapshot() {
+ return this.view.snapshot;
+ }
+}
+function extendURLWithDeprecatedProperties(url) {
+ Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
+}
+const deprecatedLocationPropertyDescriptors = {
+ absoluteURL: {
+ get() {
+ return this.toString();
+ },
+ },
+};
+
+class Cache {
+ constructor(session) {
+ this.session = session;
+ }
+ clear() {
+ this.session.clearCache();
+ }
+ resetCacheControl() {
+ this.setCacheControl("");
+ }
+ exemptPageFromCache() {
+ this.setCacheControl("no-cache");
+ }
+ exemptPageFromPreview() {
+ this.setCacheControl("no-preview");
+ }
+ setCacheControl(value) {
+ setMetaContent("turbo-cache-control", value);
+ }
+}
+
+const StreamActions = {
+ after() {
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
+ },
+ append() {
+ this.removeDuplicateTargetChildren();
+ this.targetElements.forEach((e) => e.append(this.templateContent));
+ },
+ before() {
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
+ },
+ prepend() {
+ this.removeDuplicateTargetChildren();
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
+ },
+ remove() {
+ this.targetElements.forEach((e) => e.remove());
+ },
+ replace() {
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
+ },
+ update() {
+ this.targetElements.forEach((targetElement) => {
+ targetElement.innerHTML = "";
+ targetElement.append(this.templateContent);
+ });
+ },
+};
+
+const session = new Session();
+const cache = new Cache(session);
+const { navigator: navigator$1 } = session;
+function start() {
+ session.start();
+}
+function registerAdapter(adapter) {
+ session.registerAdapter(adapter);
+}
+function visit(location, options) {
+ session.visit(location, options);
+}
+function connectStreamSource(source) {
+ session.connectStreamSource(source);
+}
+function disconnectStreamSource(source) {
+ session.disconnectStreamSource(source);
+}
+function renderStreamMessage(message) {
+ session.renderStreamMessage(message);
+}
+function clearCache() {
+ console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
+ session.clearCache();
+}
+function setProgressBarDelay(delay) {
+ session.setProgressBarDelay(delay);
+}
+function setConfirmMethod(confirmMethod) {
+ FormSubmission.confirmMethod = confirmMethod;
+}
+function setFormMode(mode) {
+ session.setFormMode(mode);
+}
+
+var Turbo = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ navigator: navigator$1,
+ session: session,
+ cache: cache,
+ PageRenderer: PageRenderer,
+ PageSnapshot: PageSnapshot,
+ FrameRenderer: FrameRenderer,
+ start: start,
+ registerAdapter: registerAdapter,
+ visit: visit,
+ connectStreamSource: connectStreamSource,
+ disconnectStreamSource: disconnectStreamSource,
+ renderStreamMessage: renderStreamMessage,
+ clearCache: clearCache,
+ setProgressBarDelay: setProgressBarDelay,
+ setConfirmMethod: setConfirmMethod,
+ setFormMode: setFormMode,
+ StreamActions: StreamActions
+});
+
+class TurboFrameMissingError extends Error {
+}
+
+class FrameController {
+ constructor(element) {
+ this.fetchResponseLoaded = (_fetchResponse) => { };
+ this.currentFetchRequest = null;
+ this.resolveVisitPromise = () => { };
+ this.connected = false;
+ this.hasBeenLoaded = false;
+ this.ignoredAttributes = new Set();
+ this.action = null;
+ this.visitCachedSnapshot = ({ element }) => {
+ const frame = element.querySelector("#" + this.element.id);
+ if (frame && this.previousFrameElement) {
+ frame.replaceChildren(...this.previousFrameElement.children);
+ }
+ delete this.previousFrameElement;
+ };
+ this.element = element;
+ this.view = new FrameView(this, this.element);
+ this.appearanceObserver = new AppearanceObserver(this, this.element);
+ this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
+ this.linkInterceptor = new LinkInterceptor(this, this.element);
+ this.restorationIdentifier = uuid();
+ this.formSubmitObserver = new FormSubmitObserver(this, this.element);
+ }
+ connect() {
+ if (!this.connected) {
+ this.connected = true;
+ if (this.loadingStyle == FrameLoadingStyle.lazy) {
+ this.appearanceObserver.start();
}
- if (submitter && !session.elementIsNavigatable(submitter)) {
- return false;
+ else {
+ this.loadSourceURL();
}
- return true;
+ this.formLinkClickObserver.start();
+ this.linkInterceptor.start();
+ this.formSubmitObserver.start();
}
- get id() {
- return this.element.id;
+ }
+ disconnect() {
+ if (this.connected) {
+ this.connected = false;
+ this.appearanceObserver.stop();
+ this.formLinkClickObserver.stop();
+ this.linkInterceptor.stop();
+ this.formSubmitObserver.stop();
}
- get enabled() {
- return !this.element.disabled;
+ }
+ disabledChanged() {
+ if (this.loadingStyle == FrameLoadingStyle.eager) {
+ this.loadSourceURL();
}
- get sourceURL() {
- if (this.element.src) {
- return this.element.src;
- }
+ }
+ sourceURLChanged() {
+ if (this.isIgnoringChangesTo("src"))
+ return;
+ if (this.element.isConnected) {
+ this.complete = false;
}
- set sourceURL(sourceURL) {
- this.ignoringChangesToAttribute("src", () => {
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
- });
+ if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
+ this.loadSourceURL();
+ }
+ }
+ sourceURLReloaded() {
+ const { src } = this.element;
+ this.ignoringChangesToAttribute("complete", () => {
+ this.element.removeAttribute("complete");
+ });
+ this.element.src = null;
+ this.element.src = src;
+ return this.element.loaded;
+ }
+ completeChanged() {
+ if (this.isIgnoringChangesTo("complete"))
+ return;
+ this.loadSourceURL();
+ }
+ loadingStyleChanged() {
+ if (this.loadingStyle == FrameLoadingStyle.lazy) {
+ this.appearanceObserver.start();
}
- get loadingStyle() {
- return this.element.loading;
+ else {
+ this.appearanceObserver.stop();
+ this.loadSourceURL();
}
- get isLoading() {
- return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
+ }
+ async loadSourceURL() {
+ if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
+ this.element.loaded = this.visit(expandURL(this.sourceURL));
+ this.appearanceObserver.stop();
+ await this.element.loaded;
+ this.hasBeenLoaded = true;
}
- get complete() {
- return this.element.hasAttribute("complete");
+ }
+ async loadResponse(fetchResponse) {
+ if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
+ this.sourceURL = fetchResponse.response.url;
}
- set complete(value) {
- this.ignoringChangesToAttribute("complete", () => {
- if (value) {
- this.element.setAttribute("complete", "");
+ try {
+ const html = await fetchResponse.responseHTML;
+ if (html) {
+ const document = parseHTMLDocument(html);
+ const pageSnapshot = PageSnapshot.fromDocument(document);
+ if (pageSnapshot.isVisitable) {
+ await this.loadFrameResponse(fetchResponse, document);
}
else {
- this.element.removeAttribute("complete");
+ await this.handleUnvisitableFrameResponse(fetchResponse);
}
- });
+ }
}
- get isActive() {
- return this.element.isActive && this.connected;
+ finally {
+ this.fetchResponseLoaded = () => { };
}
- get rootLocation() {
- var _a;
- const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
- const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
- return expandURL(root);
+ }
+ elementAppearedInViewport(element) {
+ this.proposeVisitIfNavigatedWithAction(element, element);
+ this.loadSourceURL();
+ }
+ willSubmitFormLinkToLocation(link) {
+ return this.shouldInterceptNavigation(link);
+ }
+ submittedFormLinkToLocation(link, _location, form) {
+ const frame = this.findFrameElement(link);
+ if (frame)
+ form.setAttribute("data-turbo-frame", frame.id);
+ }
+ shouldInterceptLinkClick(element, _location, _event) {
+ return this.shouldInterceptNavigation(element);
+ }
+ linkClickIntercepted(element, location) {
+ this.navigateFrame(element, location);
+ }
+ willSubmitForm(element, submitter) {
+ return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
+ }
+ formSubmitted(element, submitter) {
+ if (this.formSubmission) {
+ this.formSubmission.stop();
}
- isIgnoringChangesTo(attributeName) {
- return this.ignoredAttributes.has(attributeName);
+ this.formSubmission = new FormSubmission(this, element, submitter);
+ const { fetchRequest } = this.formSubmission;
+ this.prepareRequest(fetchRequest);
+ this.formSubmission.start();
+ }
+ prepareRequest(request) {
+ var _a;
+ request.headers["Turbo-Frame"] = this.id;
+ if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
+ request.acceptResponseType(StreamMessage.contentType);
}
- ignoringChangesToAttribute(attributeName, callback) {
- this.ignoredAttributes.add(attributeName);
- callback();
- this.ignoredAttributes.delete(attributeName);
+ }
+ requestStarted(_request) {
+ markAsBusy(this.element);
+ }
+ requestPreventedHandlingResponse(_request, _response) {
+ this.resolveVisitPromise();
+ }
+ async requestSucceededWithResponse(request, response) {
+ await this.loadResponse(response);
+ this.resolveVisitPromise();
+ }
+ async requestFailedWithResponse(request, response) {
+ await this.loadResponse(response);
+ this.resolveVisitPromise();
+ }
+ requestErrored(request, error) {
+ console.error(error);
+ this.resolveVisitPromise();
+ }
+ requestFinished(_request) {
+ clearBusyState(this.element);
+ }
+ formSubmissionStarted({ formElement }) {
+ markAsBusy(formElement, this.findFrameElement(formElement));
+ }
+ formSubmissionSucceededWithResponse(formSubmission, response) {
+ const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
+ frame.delegate.loadResponse(response);
+ if (!formSubmission.isSafe) {
+ session.clearCache();
}
- withCurrentNavigationElement(element, callback) {
- this.currentNavigationElement = element;
- callback();
- delete this.currentNavigationElement;
+ }
+ formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
+ this.element.delegate.loadResponse(fetchResponse);
+ session.clearCache();
+ }
+ formSubmissionErrored(formSubmission, error) {
+ console.error(error);
+ }
+ formSubmissionFinished({ formElement }) {
+ clearBusyState(formElement, this.findFrameElement(formElement));
+ }
+ allowsImmediateRender({ element: newFrame }, options) {
+ const event = dispatch("turbo:before-frame-render", {
+ target: this.element,
+ detail: Object.assign({ newFrame }, options),
+ cancelable: true,
+ });
+ const { defaultPrevented, detail: { render }, } = event;
+ if (this.view.renderer && render) {
+ this.view.renderer.renderElement = render;
+ }
+ return !defaultPrevented;
+ }
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
+ preloadOnLoadLinksForView(element) {
+ session.preloadOnLoadLinksForView(element);
+ }
+ viewInvalidated() { }
+ willRenderFrame(currentElement, _newElement) {
+ this.previousFrameElement = currentElement.cloneNode(true);
+ }
+ async loadFrameResponse(fetchResponse, document) {
+ const newFrameElement = await this.extractForeignFrameElement(document.body);
+ if (newFrameElement) {
+ const snapshot = new Snapshot(newFrameElement);
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
+ if (this.view.renderPromise)
+ await this.view.renderPromise;
+ this.changeHistory();
+ await this.view.render(renderer);
+ this.complete = true;
+ session.frameRendered(fetchResponse, this.element);
+ session.frameLoaded(this.element);
+ this.fetchResponseLoaded(fetchResponse);
+ }
+ else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
+ this.handleFrameMissingFromResponse(fetchResponse);
+ }
+ }
+ async visit(url) {
+ var _a;
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
+ (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
+ this.currentFetchRequest = request;
+ return new Promise((resolve) => {
+ this.resolveVisitPromise = () => {
+ this.resolveVisitPromise = () => { };
+ this.currentFetchRequest = null;
+ resolve();
+ };
+ request.perform();
+ });
+ }
+ navigateFrame(element, url, submitter) {
+ const frame = this.findFrameElement(element, submitter);
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
+ this.withCurrentNavigationElement(element, () => {
+ frame.src = url;
+ });
+ }
+ proposeVisitIfNavigatedWithAction(frame, element, submitter) {
+ this.action = getVisitAction(submitter, element, frame);
+ if (this.action) {
+ const pageSnapshot = PageSnapshot.fromElement(frame).clone();
+ const { visitCachedSnapshot } = frame.delegate;
+ frame.delegate.fetchResponseLoaded = (fetchResponse) => {
+ if (frame.src) {
+ const { statusCode, redirected } = fetchResponse;
+ const responseHTML = frame.ownerDocument.documentElement.outerHTML;
+ const response = { statusCode, redirected, responseHTML };
+ const options = {
+ response,
+ visitCachedSnapshot,
+ willRender: false,
+ updateHistory: false,
+ restorationIdentifier: this.restorationIdentifier,
+ snapshot: pageSnapshot,
+ };
+ if (this.action)
+ options.action = this.action;
+ session.visit(frame.src, options);
+ }
+ };
}
}
- function getFrameElementById(id) {
- if (id != null) {
- const element = document.getElementById(id);
- if (element instanceof FrameElement) {
- return element;
- }
+ changeHistory() {
+ if (this.action) {
+ const method = getHistoryMethodForAction(this.action);
+ session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
}
}
- function activateElement(element, currentURL) {
- if (element) {
- const src = element.getAttribute("src");
- if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
- throw new Error(`Matching element has a source URL which references itself`);
+ async handleUnvisitableFrameResponse(fetchResponse) {
+ console.warn(`The response (${fetchResponse.statusCode}) from is performing a full page visit due to turbo-visit-control.`);
+ await this.visitResponse(fetchResponse.response);
+ }
+ willHandleFrameMissingFromResponse(fetchResponse) {
+ this.element.setAttribute("complete", "");
+ const response = fetchResponse.response;
+ const visit = async (url, options = {}) => {
+ if (url instanceof Response) {
+ this.visitResponse(url);
}
- if (element.ownerDocument !== document) {
- element = document.importNode(element, true);
+ else {
+ session.visit(url, options);
}
- if (element instanceof FrameElement) {
- element.connectedCallback();
- element.disconnectedCallback();
+ };
+ const event = dispatch("turbo:frame-missing", {
+ target: this.element,
+ detail: { response, visit },
+ cancelable: true,
+ });
+ return !event.defaultPrevented;
+ }
+ handleFrameMissingFromResponse(fetchResponse) {
+ this.view.missing();
+ this.throwFrameMissingError(fetchResponse);
+ }
+ throwFrameMissingError(fetchResponse) {
+ const message = `The response (${fetchResponse.statusCode}) did not contain the expected and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
+ throw new TurboFrameMissingError(message);
+ }
+ async visitResponse(response) {
+ const wrapped = new FetchResponse(response);
+ const responseHTML = await wrapped.responseHTML;
+ const { location, redirected, statusCode } = wrapped;
+ return session.visit(location, { response: { redirected, statusCode, responseHTML } });
+ }
+ findFrameElement(element, submitter) {
+ var _a;
+ const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+ return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
+ }
+ async extractForeignFrameElement(container) {
+ let element;
+ const id = CSS.escape(this.id);
+ try {
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
+ if (element) {
return element;
}
+ element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
+ if (element) {
+ await element.loaded;
+ return await this.extractForeignFrameElement(element);
+ }
}
- }
-
- class StreamElement extends HTMLElement {
- static async renderElement(newElement) {
- await newElement.performAction();
+ catch (error) {
+ console.error(error);
+ return new FrameElement();
}
- async connectedCallback() {
- try {
- await this.render();
- }
- catch (error) {
- console.error(error);
- }
- finally {
- this.disconnect();
- }
+ return null;
+ }
+ formActionIsVisitable(form, submitter) {
+ const action = getAction(form, submitter);
+ return locationIsVisitable(expandURL(action), this.rootLocation);
+ }
+ shouldInterceptNavigation(element, submitter) {
+ const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
+ if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
+ return false;
}
- async render() {
- var _a;
- return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
- const event = this.beforeRenderEvent;
- if (this.dispatchEvent(event)) {
- await nextAnimationFrame();
- await event.detail.render(this);
- }
- })()));
+ if (!this.enabled || id == "_top") {
+ return false;
}
- disconnect() {
- try {
- this.remove();
+ if (id) {
+ const frameElement = getFrameElementById(id);
+ if (frameElement) {
+ return !frameElement.disabled;
}
- catch (_a) { }
}
- removeDuplicateTargetChildren() {
- this.duplicateChildren.forEach((c) => c.remove());
+ if (!session.elementIsNavigatable(element)) {
+ return false;
}
- get duplicateChildren() {
- var _a;
- const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
- const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
- return existingChildren.filter((c) => newChildrenIds.includes(c.id));
+ if (submitter && !session.elementIsNavigatable(submitter)) {
+ return false;
}
- get performAction() {
- if (this.action) {
- const actionFunction = StreamActions[this.action];
- if (actionFunction) {
- return actionFunction;
- }
- this.raise("unknown action");
- }
- this.raise("action attribute is missing");
+ return true;
+ }
+ get id() {
+ return this.element.id;
+ }
+ get enabled() {
+ return !this.element.disabled;
+ }
+ get sourceURL() {
+ if (this.element.src) {
+ return this.element.src;
}
- get targetElements() {
- if (this.target) {
- return this.targetElementsById;
- }
- else if (this.targets) {
- return this.targetElementsByQuery;
+ }
+ set sourceURL(sourceURL) {
+ this.ignoringChangesToAttribute("src", () => {
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
+ });
+ }
+ get loadingStyle() {
+ return this.element.loading;
+ }
+ get isLoading() {
+ return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
+ }
+ get complete() {
+ return this.element.hasAttribute("complete");
+ }
+ set complete(value) {
+ this.ignoringChangesToAttribute("complete", () => {
+ if (value) {
+ this.element.setAttribute("complete", "");
}
else {
- this.raise("target or targets attribute is missing");
- }
- }
- get templateContent() {
- return this.templateElement.content.cloneNode(true);
- }
- get templateElement() {
- if (this.firstElementChild === null) {
- const template = this.ownerDocument.createElement("template");
- this.appendChild(template);
- return template;
- }
- else if (this.firstElementChild instanceof HTMLTemplateElement) {
- return this.firstElementChild;
+ this.element.removeAttribute("complete");
}
- this.raise("first child element must be a element");
+ });
+ }
+ get isActive() {
+ return this.element.isActive && this.connected;
+ }
+ get rootLocation() {
+ var _a;
+ const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
+ const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
+ return expandURL(root);
+ }
+ isIgnoringChangesTo(attributeName) {
+ return this.ignoredAttributes.has(attributeName);
+ }
+ ignoringChangesToAttribute(attributeName, callback) {
+ this.ignoredAttributes.add(attributeName);
+ callback();
+ this.ignoredAttributes.delete(attributeName);
+ }
+ withCurrentNavigationElement(element, callback) {
+ this.currentNavigationElement = element;
+ callback();
+ delete this.currentNavigationElement;
+ }
+}
+function getFrameElementById(id) {
+ if (id != null) {
+ const element = document.getElementById(id);
+ if (element instanceof FrameElement) {
+ return element;
}
- get action() {
- return this.getAttribute("action");
+ }
+}
+function activateElement(element, currentURL) {
+ if (element) {
+ const src = element.getAttribute("src");
+ if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
+ throw new Error(`Matching element has a source URL which references itself`);
}
- get target() {
- return this.getAttribute("target");
+ if (element.ownerDocument !== document) {
+ element = document.importNode(element, true);
}
- get targets() {
- return this.getAttribute("targets");
+ if (element instanceof FrameElement) {
+ element.connectedCallback();
+ element.disconnectedCallback();
+ return element;
}
- raise(message) {
- throw new Error(`${this.description}: ${message}`);
+ }
+}
+
+class StreamElement extends HTMLElement {
+ static async renderElement(newElement) {
+ await newElement.performAction();
+ }
+ async connectedCallback() {
+ try {
+ await this.render();
}
- get description() {
- var _a, _b;
- return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "";
+ catch (error) {
+ console.error(error);
}
- get beforeRenderEvent() {
- return new CustomEvent("turbo:before-stream-render", {
- bubbles: true,
- cancelable: true,
- detail: { newStream: this, render: StreamElement.renderElement },
- });
+ finally {
+ this.disconnect();
}
- get targetElementsById() {
- var _a;
- const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
- if (element !== null) {
- return [element];
- }
- else {
- return [];
+ }
+ async render() {
+ var _a;
+ return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
+ const event = this.beforeRenderEvent;
+ if (this.dispatchEvent(event)) {
+ await nextAnimationFrame();
+ await event.detail.render(this);
}
+ })()));
+ }
+ disconnect() {
+ try {
+ this.remove();
}
- get targetElementsByQuery() {
- var _a;
- const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
- if (elements.length !== 0) {
- return Array.prototype.slice.call(elements);
- }
- else {
- return [];
+ catch (_a) { }
+ }
+ removeDuplicateTargetChildren() {
+ this.duplicateChildren.forEach((c) => c.remove());
+ }
+ get duplicateChildren() {
+ var _a;
+ const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
+ const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
+ return existingChildren.filter((c) => newChildrenIds.includes(c.id));
+ }
+ get performAction() {
+ if (this.action) {
+ const actionFunction = StreamActions[this.action];
+ if (actionFunction) {
+ return actionFunction;
}
+ this.raise("unknown action");
}
+ this.raise("action attribute is missing");
}
-
- class StreamSourceElement extends HTMLElement {
- constructor() {
- super(...arguments);
- this.streamSource = null;
+ get targetElements() {
+ if (this.target) {
+ return this.targetElementsById;
}
- connectedCallback() {
- this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
- connectStreamSource(this.streamSource);
+ else if (this.targets) {
+ return this.targetElementsByQuery;
}
- disconnectedCallback() {
- if (this.streamSource) {
- disconnectStreamSource(this.streamSource);
- }
+ else {
+ this.raise("target or targets attribute is missing");
}
- get src() {
- return this.getAttribute("src") || "";
+ }
+ get templateContent() {
+ return this.templateElement.content.cloneNode(true);
+ }
+ get templateElement() {
+ if (this.firstElementChild === null) {
+ const template = this.ownerDocument.createElement("template");
+ this.appendChild(template);
+ return template;
+ }
+ else if (this.firstElementChild instanceof HTMLTemplateElement) {
+ return this.firstElementChild;
+ }
+ this.raise("first child element must be a element");
+ }
+ get action() {
+ return this.getAttribute("action");
+ }
+ get target() {
+ return this.getAttribute("target");
+ }
+ get targets() {
+ return this.getAttribute("targets");
+ }
+ raise(message) {
+ throw new Error(`${this.description}: ${message}`);
+ }
+ get description() {
+ var _a, _b;
+ return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "";
+ }
+ get beforeRenderEvent() {
+ return new CustomEvent("turbo:before-stream-render", {
+ bubbles: true,
+ cancelable: true,
+ detail: { newStream: this, render: StreamElement.renderElement },
+ });
+ }
+ get targetElementsById() {
+ var _a;
+ const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
+ if (element !== null) {
+ return [element];
+ }
+ else {
+ return [];
}
}
+ get targetElementsByQuery() {
+ var _a;
+ const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
+ if (elements.length !== 0) {
+ return Array.prototype.slice.call(elements);
+ }
+ else {
+ return [];
+ }
+ }
+}
- FrameElement.delegateConstructor = FrameController;
- if (customElements.get("turbo-frame") === undefined) {
- customElements.define("turbo-frame", FrameElement);
+class StreamSourceElement extends HTMLElement {
+ constructor() {
+ super(...arguments);
+ this.streamSource = null;
+ }
+ connectedCallback() {
+ this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
+ connectStreamSource(this.streamSource);
}
- if (customElements.get("turbo-stream") === undefined) {
- customElements.define("turbo-stream", StreamElement);
+ disconnectedCallback() {
+ if (this.streamSource) {
+ disconnectStreamSource(this.streamSource);
+ }
}
- if (customElements.get("turbo-stream-source") === undefined) {
- customElements.define("turbo-stream-source", StreamSourceElement);
+ get src() {
+ return this.getAttribute("src") || "";
}
+}
- (() => {
- let element = document.currentScript;
- if (!element)
- return;
- if (element.hasAttribute("data-turbo-suppress-warning"))
- return;
- element = element.parentElement;
- while (element) {
- if (element == document.body) {
- return console.warn(unindent `
+FrameElement.delegateConstructor = FrameController;
+if (customElements.get("turbo-frame") === undefined) {
+ customElements.define("turbo-frame", FrameElement);
+}
+if (customElements.get("turbo-stream") === undefined) {
+ customElements.define("turbo-stream", StreamElement);
+}
+if (customElements.get("turbo-stream-source") === undefined) {
+ customElements.define("turbo-stream-source", StreamSourceElement);
+}
+
+(() => {
+ let element = document.currentScript;
+ if (!element)
+ return;
+ if (element.hasAttribute("data-turbo-suppress-warning"))
+ return;
+ element = element.parentElement;
+ while (element) {
+ if (element == document.body) {
+ return console.warn(unindent `
You are loading Turbo from a