UNPKG

136 kBJavaScriptView Raw
1/*
2Turbo 7.2.4
3Copyright © 2022 37signals LLC
4 */
5(function () {
6 if (window.Reflect === undefined ||
7 window.customElements === undefined ||
8 window.customElements.polyfillWrapFlushCallback) {
9 return;
10 }
11 const BuiltInHTMLElement = HTMLElement;
12 const wrapperForTheName = {
13 HTMLElement: function HTMLElement() {
14 return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
15 },
16 };
17 window.HTMLElement = wrapperForTheName["HTMLElement"];
18 HTMLElement.prototype = BuiltInHTMLElement.prototype;
19 HTMLElement.prototype.constructor = HTMLElement;
20 Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
21})();
22
23/**
24 * The MIT License (MIT)
25 *
26 * Copyright (c) 2019 Javan Makhmali
27 *
28 * Permission is hereby granted, free of charge, to any person obtaining a copy
29 * of this software and associated documentation files (the "Software"), to deal
30 * in the Software without restriction, including without limitation the rights
31 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32 * copies of the Software, and to permit persons to whom the Software is
33 * furnished to do so, subject to the following conditions:
34 *
35 * The above copyright notice and this permission notice shall be included in
36 * all copies or substantial portions of the Software.
37 *
38 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
44 * THE SOFTWARE.
45 */
46
47(function(prototype) {
48 if (typeof prototype.requestSubmit == "function") return
49
50 prototype.requestSubmit = function(submitter) {
51 if (submitter) {
52 validateSubmitter(submitter, this);
53 submitter.click();
54 } else {
55 submitter = document.createElement("input");
56 submitter.type = "submit";
57 submitter.hidden = true;
58 this.appendChild(submitter);
59 submitter.click();
60 this.removeChild(submitter);
61 }
62 };
63
64 function validateSubmitter(submitter, form) {
65 submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
66 submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
67 submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
68 }
69
70 function raise(errorConstructor, message, name) {
71 throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
72 }
73})(HTMLFormElement.prototype);
74
75const submittersByForm = new WeakMap();
76function findSubmitterFromClickTarget(target) {
77 const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
78 const candidate = element ? element.closest("input, button") : null;
79 return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
80}
81function clickCaptured(event) {
82 const submitter = findSubmitterFromClickTarget(event.target);
83 if (submitter && submitter.form) {
84 submittersByForm.set(submitter.form, submitter);
85 }
86}
87(function () {
88 if ("submitter" in Event.prototype)
89 return;
90 let prototype;
91 if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
92 prototype = window.SubmitEvent.prototype;
93 }
94 else if ("SubmitEvent" in window) {
95 return;
96 }
97 else {
98 prototype = window.Event.prototype;
99 }
100 addEventListener("click", clickCaptured, true);
101 Object.defineProperty(prototype, "submitter", {
102 get() {
103 if (this.type == "submit" && this.target instanceof HTMLFormElement) {
104 return submittersByForm.get(this.target);
105 }
106 },
107 });
108})();
109
110var FrameLoadingStyle;
111(function (FrameLoadingStyle) {
112 FrameLoadingStyle["eager"] = "eager";
113 FrameLoadingStyle["lazy"] = "lazy";
114})(FrameLoadingStyle || (FrameLoadingStyle = {}));
115class FrameElement extends HTMLElement {
116 constructor() {
117 super();
118 this.loaded = Promise.resolve();
119 this.delegate = new FrameElement.delegateConstructor(this);
120 }
121 static get observedAttributes() {
122 return ["disabled", "complete", "loading", "src"];
123 }
124 connectedCallback() {
125 this.delegate.connect();
126 }
127 disconnectedCallback() {
128 this.delegate.disconnect();
129 }
130 reload() {
131 return this.delegate.sourceURLReloaded();
132 }
133 attributeChangedCallback(name) {
134 if (name == "loading") {
135 this.delegate.loadingStyleChanged();
136 }
137 else if (name == "complete") {
138 this.delegate.completeChanged();
139 }
140 else if (name == "src") {
141 this.delegate.sourceURLChanged();
142 }
143 else {
144 this.delegate.disabledChanged();
145 }
146 }
147 get src() {
148 return this.getAttribute("src");
149 }
150 set src(value) {
151 if (value) {
152 this.setAttribute("src", value);
153 }
154 else {
155 this.removeAttribute("src");
156 }
157 }
158 get loading() {
159 return frameLoadingStyleFromString(this.getAttribute("loading") || "");
160 }
161 set loading(value) {
162 if (value) {
163 this.setAttribute("loading", value);
164 }
165 else {
166 this.removeAttribute("loading");
167 }
168 }
169 get disabled() {
170 return this.hasAttribute("disabled");
171 }
172 set disabled(value) {
173 if (value) {
174 this.setAttribute("disabled", "");
175 }
176 else {
177 this.removeAttribute("disabled");
178 }
179 }
180 get autoscroll() {
181 return this.hasAttribute("autoscroll");
182 }
183 set autoscroll(value) {
184 if (value) {
185 this.setAttribute("autoscroll", "");
186 }
187 else {
188 this.removeAttribute("autoscroll");
189 }
190 }
191 get complete() {
192 return !this.delegate.isLoading;
193 }
194 get isActive() {
195 return this.ownerDocument === document && !this.isPreview;
196 }
197 get isPreview() {
198 var _a, _b;
199 return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
200 }
201}
202function frameLoadingStyleFromString(style) {
203 switch (style.toLowerCase()) {
204 case "lazy":
205 return FrameLoadingStyle.lazy;
206 default:
207 return FrameLoadingStyle.eager;
208 }
209}
210
211function expandURL(locatable) {
212 return new URL(locatable.toString(), document.baseURI);
213}
214function getAnchor(url) {
215 let anchorMatch;
216 if (url.hash) {
217 return url.hash.slice(1);
218 }
219 else if ((anchorMatch = url.href.match(/#(.*)$/))) {
220 return anchorMatch[1];
221 }
222}
223function getAction(form, submitter) {
224 const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
225 return expandURL(action);
226}
227function getExtension(url) {
228 return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
229}
230function isHTML(url) {
231 return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
232}
233function isPrefixedBy(baseURL, url) {
234 const prefix = getPrefix(url);
235 return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
236}
237function locationIsVisitable(location, rootLocation) {
238 return isPrefixedBy(location, rootLocation) && isHTML(location);
239}
240function getRequestURL(url) {
241 const anchor = getAnchor(url);
242 return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
243}
244function toCacheKey(url) {
245 return getRequestURL(url);
246}
247function urlsAreEqual(left, right) {
248 return expandURL(left).href == expandURL(right).href;
249}
250function getPathComponents(url) {
251 return url.pathname.split("/").slice(1);
252}
253function getLastPathComponent(url) {
254 return getPathComponents(url).slice(-1)[0];
255}
256function getPrefix(url) {
257 return addTrailingSlash(url.origin + url.pathname);
258}
259function addTrailingSlash(value) {
260 return value.endsWith("/") ? value : value + "/";
261}
262
263class FetchResponse {
264 constructor(response) {
265 this.response = response;
266 }
267 get succeeded() {
268 return this.response.ok;
269 }
270 get failed() {
271 return !this.succeeded;
272 }
273 get clientError() {
274 return this.statusCode >= 400 && this.statusCode <= 499;
275 }
276 get serverError() {
277 return this.statusCode >= 500 && this.statusCode <= 599;
278 }
279 get redirected() {
280 return this.response.redirected;
281 }
282 get location() {
283 return expandURL(this.response.url);
284 }
285 get isHTML() {
286 return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
287 }
288 get statusCode() {
289 return this.response.status;
290 }
291 get contentType() {
292 return this.header("Content-Type");
293 }
294 get responseText() {
295 return this.response.clone().text();
296 }
297 get responseHTML() {
298 if (this.isHTML) {
299 return this.response.clone().text();
300 }
301 else {
302 return Promise.resolve(undefined);
303 }
304 }
305 header(name) {
306 return this.response.headers.get(name);
307 }
308}
309
310function isAction(action) {
311 return action == "advance" || action == "replace" || action == "restore";
312}
313
314function activateScriptElement(element) {
315 if (element.getAttribute("data-turbo-eval") == "false") {
316 return element;
317 }
318 else {
319 const createdScriptElement = document.createElement("script");
320 const cspNonce = getMetaContent("csp-nonce");
321 if (cspNonce) {
322 createdScriptElement.nonce = cspNonce;
323 }
324 createdScriptElement.textContent = element.textContent;
325 createdScriptElement.async = false;
326 copyElementAttributes(createdScriptElement, element);
327 return createdScriptElement;
328 }
329}
330function copyElementAttributes(destinationElement, sourceElement) {
331 for (const { name, value } of sourceElement.attributes) {
332 destinationElement.setAttribute(name, value);
333 }
334}
335function createDocumentFragment(html) {
336 const template = document.createElement("template");
337 template.innerHTML = html;
338 return template.content;
339}
340function dispatch(eventName, { target, cancelable, detail } = {}) {
341 const event = new CustomEvent(eventName, {
342 cancelable,
343 bubbles: true,
344 detail,
345 });
346 if (target && target.isConnected) {
347 target.dispatchEvent(event);
348 }
349 else {
350 document.documentElement.dispatchEvent(event);
351 }
352 return event;
353}
354function nextAnimationFrame() {
355 return new Promise((resolve) => requestAnimationFrame(() => resolve()));
356}
357function nextEventLoopTick() {
358 return new Promise((resolve) => setTimeout(() => resolve(), 0));
359}
360function nextMicrotask() {
361 return Promise.resolve();
362}
363function parseHTMLDocument(html = "") {
364 return new DOMParser().parseFromString(html, "text/html");
365}
366function unindent(strings, ...values) {
367 const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
368 const match = lines[0].match(/^\s+/);
369 const indent = match ? match[0].length : 0;
370 return lines.map((line) => line.slice(indent)).join("\n");
371}
372function interpolate(strings, values) {
373 return strings.reduce((result, string, i) => {
374 const value = values[i] == undefined ? "" : values[i];
375 return result + string + value;
376 }, "");
377}
378function uuid() {
379 return Array.from({ length: 36 })
380 .map((_, i) => {
381 if (i == 8 || i == 13 || i == 18 || i == 23) {
382 return "-";
383 }
384 else if (i == 14) {
385 return "4";
386 }
387 else if (i == 19) {
388 return (Math.floor(Math.random() * 4) + 8).toString(16);
389 }
390 else {
391 return Math.floor(Math.random() * 15).toString(16);
392 }
393 })
394 .join("");
395}
396function getAttribute(attributeName, ...elements) {
397 for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
398 if (typeof value == "string")
399 return value;
400 }
401 return null;
402}
403function hasAttribute(attributeName, ...elements) {
404 return elements.some((element) => element && element.hasAttribute(attributeName));
405}
406function markAsBusy(...elements) {
407 for (const element of elements) {
408 if (element.localName == "turbo-frame") {
409 element.setAttribute("busy", "");
410 }
411 element.setAttribute("aria-busy", "true");
412 }
413}
414function clearBusyState(...elements) {
415 for (const element of elements) {
416 if (element.localName == "turbo-frame") {
417 element.removeAttribute("busy");
418 }
419 element.removeAttribute("aria-busy");
420 }
421}
422function waitForLoad(element, timeoutInMilliseconds = 2000) {
423 return new Promise((resolve) => {
424 const onComplete = () => {
425 element.removeEventListener("error", onComplete);
426 element.removeEventListener("load", onComplete);
427 resolve();
428 };
429 element.addEventListener("load", onComplete, { once: true });
430 element.addEventListener("error", onComplete, { once: true });
431 setTimeout(resolve, timeoutInMilliseconds);
432 });
433}
434function getHistoryMethodForAction(action) {
435 switch (action) {
436 case "replace":
437 return history.replaceState;
438 case "advance":
439 case "restore":
440 return history.pushState;
441 }
442}
443function getVisitAction(...elements) {
444 const action = getAttribute("data-turbo-action", ...elements);
445 return isAction(action) ? action : null;
446}
447function getMetaElement(name) {
448 return document.querySelector(`meta[name="${name}"]`);
449}
450function getMetaContent(name) {
451 const element = getMetaElement(name);
452 return element && element.content;
453}
454function setMetaContent(name, content) {
455 let element = getMetaElement(name);
456 if (!element) {
457 element = document.createElement("meta");
458 element.setAttribute("name", name);
459 document.head.appendChild(element);
460 }
461 element.setAttribute("content", content);
462 return element;
463}
464
465var FetchMethod;
466(function (FetchMethod) {
467 FetchMethod[FetchMethod["get"] = 0] = "get";
468 FetchMethod[FetchMethod["post"] = 1] = "post";
469 FetchMethod[FetchMethod["put"] = 2] = "put";
470 FetchMethod[FetchMethod["patch"] = 3] = "patch";
471 FetchMethod[FetchMethod["delete"] = 4] = "delete";
472})(FetchMethod || (FetchMethod = {}));
473function fetchMethodFromString(method) {
474 switch (method.toLowerCase()) {
475 case "get":
476 return FetchMethod.get;
477 case "post":
478 return FetchMethod.post;
479 case "put":
480 return FetchMethod.put;
481 case "patch":
482 return FetchMethod.patch;
483 case "delete":
484 return FetchMethod.delete;
485 }
486}
487class FetchRequest {
488 constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
489 this.abortController = new AbortController();
490 this.resolveRequestPromise = (_value) => { };
491 this.delegate = delegate;
492 this.method = method;
493 this.headers = this.defaultHeaders;
494 this.body = body;
495 this.url = location;
496 this.target = target;
497 }
498 get location() {
499 return this.url;
500 }
501 get params() {
502 return this.url.searchParams;
503 }
504 get entries() {
505 return this.body ? Array.from(this.body.entries()) : [];
506 }
507 cancel() {
508 this.abortController.abort();
509 }
510 async perform() {
511 var _a, _b;
512 const { fetchOptions } = this;
513 (_b = (_a = this.delegate).prepareHeadersForRequest) === null || _b === void 0 ? void 0 : _b.call(_a, this.headers, this);
514 await this.allowRequestToBeIntercepted(fetchOptions);
515 try {
516 this.delegate.requestStarted(this);
517 const response = await fetch(this.url.href, fetchOptions);
518 return await this.receive(response);
519 }
520 catch (error) {
521 if (error.name !== "AbortError") {
522 if (this.willDelegateErrorHandling(error)) {
523 this.delegate.requestErrored(this, error);
524 }
525 throw error;
526 }
527 }
528 finally {
529 this.delegate.requestFinished(this);
530 }
531 }
532 async receive(response) {
533 const fetchResponse = new FetchResponse(response);
534 const event = dispatch("turbo:before-fetch-response", {
535 cancelable: true,
536 detail: { fetchResponse },
537 target: this.target,
538 });
539 if (event.defaultPrevented) {
540 this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
541 }
542 else if (fetchResponse.succeeded) {
543 this.delegate.requestSucceededWithResponse(this, fetchResponse);
544 }
545 else {
546 this.delegate.requestFailedWithResponse(this, fetchResponse);
547 }
548 return fetchResponse;
549 }
550 get fetchOptions() {
551 var _a;
552 return {
553 method: FetchMethod[this.method].toUpperCase(),
554 credentials: "same-origin",
555 headers: this.headers,
556 redirect: "follow",
557 body: this.isIdempotent ? null : this.body,
558 signal: this.abortSignal,
559 referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
560 };
561 }
562 get defaultHeaders() {
563 return {
564 Accept: "text/html, application/xhtml+xml",
565 };
566 }
567 get isIdempotent() {
568 return this.method == FetchMethod.get;
569 }
570 get abortSignal() {
571 return this.abortController.signal;
572 }
573 acceptResponseType(mimeType) {
574 this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
575 }
576 async allowRequestToBeIntercepted(fetchOptions) {
577 const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
578 const event = dispatch("turbo:before-fetch-request", {
579 cancelable: true,
580 detail: {
581 fetchOptions,
582 url: this.url,
583 resume: this.resolveRequestPromise,
584 },
585 target: this.target,
586 });
587 if (event.defaultPrevented)
588 await requestInterception;
589 }
590 willDelegateErrorHandling(error) {
591 const event = dispatch("turbo:fetch-request-error", {
592 target: this.target,
593 cancelable: true,
594 detail: { request: this, error: error },
595 });
596 return !event.defaultPrevented;
597 }
598}
599
600class AppearanceObserver {
601 constructor(delegate, element) {
602 this.started = false;
603 this.intersect = (entries) => {
604 const lastEntry = entries.slice(-1)[0];
605 if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
606 this.delegate.elementAppearedInViewport(this.element);
607 }
608 };
609 this.delegate = delegate;
610 this.element = element;
611 this.intersectionObserver = new IntersectionObserver(this.intersect);
612 }
613 start() {
614 if (!this.started) {
615 this.started = true;
616 this.intersectionObserver.observe(this.element);
617 }
618 }
619 stop() {
620 if (this.started) {
621 this.started = false;
622 this.intersectionObserver.unobserve(this.element);
623 }
624 }
625}
626
627class StreamMessage {
628 constructor(fragment) {
629 this.fragment = importStreamElements(fragment);
630 }
631 static wrap(message) {
632 if (typeof message == "string") {
633 return new this(createDocumentFragment(message));
634 }
635 else {
636 return message;
637 }
638 }
639}
640StreamMessage.contentType = "text/vnd.turbo-stream.html";
641function importStreamElements(fragment) {
642 for (const element of fragment.querySelectorAll("turbo-stream")) {
643 const streamElement = document.importNode(element, true);
644 for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
645 inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
646 }
647 element.replaceWith(streamElement);
648 }
649 return fragment;
650}
651
652var FormSubmissionState;
653(function (FormSubmissionState) {
654 FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
655 FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
656 FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
657 FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
658 FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
659 FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
660})(FormSubmissionState || (FormSubmissionState = {}));
661var FormEnctype;
662(function (FormEnctype) {
663 FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
664 FormEnctype["multipart"] = "multipart/form-data";
665 FormEnctype["plain"] = "text/plain";
666})(FormEnctype || (FormEnctype = {}));
667function formEnctypeFromString(encoding) {
668 switch (encoding.toLowerCase()) {
669 case FormEnctype.multipart:
670 return FormEnctype.multipart;
671 case FormEnctype.plain:
672 return FormEnctype.plain;
673 default:
674 return FormEnctype.urlEncoded;
675 }
676}
677class FormSubmission {
678 constructor(delegate, formElement, submitter, mustRedirect = false) {
679 this.state = FormSubmissionState.initialized;
680 this.delegate = delegate;
681 this.formElement = formElement;
682 this.submitter = submitter;
683 this.formData = buildFormData(formElement, submitter);
684 this.location = expandURL(this.action);
685 if (this.method == FetchMethod.get) {
686 mergeFormDataEntries(this.location, [...this.body.entries()]);
687 }
688 this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
689 this.mustRedirect = mustRedirect;
690 }
691 static confirmMethod(message, _element, _submitter) {
692 return Promise.resolve(confirm(message));
693 }
694 get method() {
695 var _a;
696 const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
697 return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
698 }
699 get action() {
700 var _a;
701 const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
702 if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
703 return this.submitter.getAttribute("formaction") || "";
704 }
705 else {
706 return this.formElement.getAttribute("action") || formElementAction || "";
707 }
708 }
709 get body() {
710 if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
711 return new URLSearchParams(this.stringFormData);
712 }
713 else {
714 return this.formData;
715 }
716 }
717 get enctype() {
718 var _a;
719 return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
720 }
721 get isIdempotent() {
722 return this.fetchRequest.isIdempotent;
723 }
724 get stringFormData() {
725 return [...this.formData].reduce((entries, [name, value]) => {
726 return entries.concat(typeof value == "string" ? [[name, value]] : []);
727 }, []);
728 }
729 async start() {
730 const { initialized, requesting } = FormSubmissionState;
731 const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
732 if (typeof confirmationMessage === "string") {
733 const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
734 if (!answer) {
735 return;
736 }
737 }
738 if (this.state == initialized) {
739 this.state = requesting;
740 return this.fetchRequest.perform();
741 }
742 }
743 stop() {
744 const { stopping, stopped } = FormSubmissionState;
745 if (this.state != stopping && this.state != stopped) {
746 this.state = stopping;
747 this.fetchRequest.cancel();
748 return true;
749 }
750 }
751 prepareHeadersForRequest(headers, request) {
752 if (!request.isIdempotent) {
753 const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
754 if (token) {
755 headers["X-CSRF-Token"] = token;
756 }
757 }
758 if (this.requestAcceptsTurboStreamResponse(request)) {
759 request.acceptResponseType(StreamMessage.contentType);
760 }
761 }
762 requestStarted(_request) {
763 var _a;
764 this.state = FormSubmissionState.waiting;
765 (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
766 dispatch("turbo:submit-start", {
767 target: this.formElement,
768 detail: { formSubmission: this },
769 });
770 this.delegate.formSubmissionStarted(this);
771 }
772 requestPreventedHandlingResponse(request, response) {
773 this.result = { success: response.succeeded, fetchResponse: response };
774 }
775 requestSucceededWithResponse(request, response) {
776 if (response.clientError || response.serverError) {
777 this.delegate.formSubmissionFailedWithResponse(this, response);
778 }
779 else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
780 const error = new Error("Form responses must redirect to another location");
781 this.delegate.formSubmissionErrored(this, error);
782 }
783 else {
784 this.state = FormSubmissionState.receiving;
785 this.result = { success: true, fetchResponse: response };
786 this.delegate.formSubmissionSucceededWithResponse(this, response);
787 }
788 }
789 requestFailedWithResponse(request, response) {
790 this.result = { success: false, fetchResponse: response };
791 this.delegate.formSubmissionFailedWithResponse(this, response);
792 }
793 requestErrored(request, error) {
794 this.result = { success: false, error };
795 this.delegate.formSubmissionErrored(this, error);
796 }
797 requestFinished(_request) {
798 var _a;
799 this.state = FormSubmissionState.stopped;
800 (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
801 dispatch("turbo:submit-end", {
802 target: this.formElement,
803 detail: Object.assign({ formSubmission: this }, this.result),
804 });
805 this.delegate.formSubmissionFinished(this);
806 }
807 requestMustRedirect(request) {
808 return !request.isIdempotent && this.mustRedirect;
809 }
810 requestAcceptsTurboStreamResponse(request) {
811 return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
812 }
813}
814function buildFormData(formElement, submitter) {
815 const formData = new FormData(formElement);
816 const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
817 const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
818 if (name) {
819 formData.append(name, value || "");
820 }
821 return formData;
822}
823function getCookieValue(cookieName) {
824 if (cookieName != null) {
825 const cookies = document.cookie ? document.cookie.split("; ") : [];
826 const cookie = cookies.find((cookie) => cookie.startsWith(cookieName));
827 if (cookie) {
828 const value = cookie.split("=").slice(1).join("=");
829 return value ? decodeURIComponent(value) : undefined;
830 }
831 }
832}
833function responseSucceededWithoutRedirect(response) {
834 return response.statusCode == 200 && !response.redirected;
835}
836function mergeFormDataEntries(url, entries) {
837 const searchParams = new URLSearchParams();
838 for (const [name, value] of entries) {
839 if (value instanceof File)
840 continue;
841 searchParams.append(name, value);
842 }
843 url.search = searchParams.toString();
844 return url;
845}
846
847class Snapshot {
848 constructor(element) {
849 this.element = element;
850 }
851 get activeElement() {
852 return this.element.ownerDocument.activeElement;
853 }
854 get children() {
855 return [...this.element.children];
856 }
857 hasAnchor(anchor) {
858 return this.getElementForAnchor(anchor) != null;
859 }
860 getElementForAnchor(anchor) {
861 return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
862 }
863 get isConnected() {
864 return this.element.isConnected;
865 }
866 get firstAutofocusableElement() {
867 const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
868 for (const element of this.element.querySelectorAll("[autofocus]")) {
869 if (element.closest(inertDisabledOrHidden) == null)
870 return element;
871 else
872 continue;
873 }
874 return null;
875 }
876 get permanentElements() {
877 return queryPermanentElementsAll(this.element);
878 }
879 getPermanentElementById(id) {
880 return getPermanentElementById(this.element, id);
881 }
882 getPermanentElementMapForSnapshot(snapshot) {
883 const permanentElementMap = {};
884 for (const currentPermanentElement of this.permanentElements) {
885 const { id } = currentPermanentElement;
886 const newPermanentElement = snapshot.getPermanentElementById(id);
887 if (newPermanentElement) {
888 permanentElementMap[id] = [currentPermanentElement, newPermanentElement];
889 }
890 }
891 return permanentElementMap;
892 }
893}
894function getPermanentElementById(node, id) {
895 return node.querySelector(`#${id}[data-turbo-permanent]`);
896}
897function queryPermanentElementsAll(node) {
898 return node.querySelectorAll("[id][data-turbo-permanent]");
899}
900
901class FormSubmitObserver {
902 constructor(delegate, eventTarget) {
903 this.started = false;
904 this.submitCaptured = () => {
905 this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
906 this.eventTarget.addEventListener("submit", this.submitBubbled, false);
907 };
908 this.submitBubbled = ((event) => {
909 if (!event.defaultPrevented) {
910 const form = event.target instanceof HTMLFormElement ? event.target : undefined;
911 const submitter = event.submitter || undefined;
912 if (form &&
913 submissionDoesNotDismissDialog(form, submitter) &&
914 submissionDoesNotTargetIFrame(form, submitter) &&
915 this.delegate.willSubmitForm(form, submitter)) {
916 event.preventDefault();
917 event.stopImmediatePropagation();
918 this.delegate.formSubmitted(form, submitter);
919 }
920 }
921 });
922 this.delegate = delegate;
923 this.eventTarget = eventTarget;
924 }
925 start() {
926 if (!this.started) {
927 this.eventTarget.addEventListener("submit", this.submitCaptured, true);
928 this.started = true;
929 }
930 }
931 stop() {
932 if (this.started) {
933 this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
934 this.started = false;
935 }
936 }
937}
938function submissionDoesNotDismissDialog(form, submitter) {
939 const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
940 return method != "dialog";
941}
942function submissionDoesNotTargetIFrame(form, submitter) {
943 const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
944 for (const element of document.getElementsByName(target)) {
945 if (element instanceof HTMLIFrameElement)
946 return false;
947 }
948 return true;
949}
950
951class View {
952 constructor(delegate, element) {
953 this.resolveRenderPromise = (_value) => { };
954 this.resolveInterceptionPromise = (_value) => { };
955 this.delegate = delegate;
956 this.element = element;
957 }
958 scrollToAnchor(anchor) {
959 const element = this.snapshot.getElementForAnchor(anchor);
960 if (element) {
961 this.scrollToElement(element);
962 this.focusElement(element);
963 }
964 else {
965 this.scrollToPosition({ x: 0, y: 0 });
966 }
967 }
968 scrollToAnchorFromLocation(location) {
969 this.scrollToAnchor(getAnchor(location));
970 }
971 scrollToElement(element) {
972 element.scrollIntoView();
973 }
974 focusElement(element) {
975 if (element instanceof HTMLElement) {
976 if (element.hasAttribute("tabindex")) {
977 element.focus();
978 }
979 else {
980 element.setAttribute("tabindex", "-1");
981 element.focus();
982 element.removeAttribute("tabindex");
983 }
984 }
985 }
986 scrollToPosition({ x, y }) {
987 this.scrollRoot.scrollTo(x, y);
988 }
989 scrollToTop() {
990 this.scrollToPosition({ x: 0, y: 0 });
991 }
992 get scrollRoot() {
993 return window;
994 }
995 async render(renderer) {
996 const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
997 if (shouldRender) {
998 try {
999 this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
1000 this.renderer = renderer;
1001 await this.prepareToRenderSnapshot(renderer);
1002 const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
1003 const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
1004 const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
1005 if (!immediateRender)
1006 await renderInterception;
1007 await this.renderSnapshot(renderer);
1008 this.delegate.viewRenderedSnapshot(snapshot, isPreview);
1009 this.delegate.preloadOnLoadLinksForView(this.element);
1010 this.finishRenderingSnapshot(renderer);
1011 }
1012 finally {
1013 delete this.renderer;
1014 this.resolveRenderPromise(undefined);
1015 delete this.renderPromise;
1016 }
1017 }
1018 else {
1019 this.invalidate(renderer.reloadReason);
1020 }
1021 }
1022 invalidate(reason) {
1023 this.delegate.viewInvalidated(reason);
1024 }
1025 async prepareToRenderSnapshot(renderer) {
1026 this.markAsPreview(renderer.isPreview);
1027 await renderer.prepareToRender();
1028 }
1029 markAsPreview(isPreview) {
1030 if (isPreview) {
1031 this.element.setAttribute("data-turbo-preview", "");
1032 }
1033 else {
1034 this.element.removeAttribute("data-turbo-preview");
1035 }
1036 }
1037 async renderSnapshot(renderer) {
1038 await renderer.render();
1039 }
1040 finishRenderingSnapshot(renderer) {
1041 renderer.finishRendering();
1042 }
1043}
1044
1045class FrameView extends View {
1046 invalidate() {
1047 this.element.innerHTML = "";
1048 }
1049 get snapshot() {
1050 return new Snapshot(this.element);
1051 }
1052}
1053
1054class LinkInterceptor {
1055 constructor(delegate, element) {
1056 this.clickBubbled = (event) => {
1057 if (this.respondsToEventTarget(event.target)) {
1058 this.clickEvent = event;
1059 }
1060 else {
1061 delete this.clickEvent;
1062 }
1063 };
1064 this.linkClicked = ((event) => {
1065 if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
1066 if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1067 this.clickEvent.preventDefault();
1068 event.preventDefault();
1069 this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1070 }
1071 }
1072 delete this.clickEvent;
1073 });
1074 this.willVisit = ((_event) => {
1075 delete this.clickEvent;
1076 });
1077 this.delegate = delegate;
1078 this.element = element;
1079 }
1080 start() {
1081 this.element.addEventListener("click", this.clickBubbled);
1082 document.addEventListener("turbo:click", this.linkClicked);
1083 document.addEventListener("turbo:before-visit", this.willVisit);
1084 }
1085 stop() {
1086 this.element.removeEventListener("click", this.clickBubbled);
1087 document.removeEventListener("turbo:click", this.linkClicked);
1088 document.removeEventListener("turbo:before-visit", this.willVisit);
1089 }
1090 respondsToEventTarget(target) {
1091 const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
1092 return element && element.closest("turbo-frame, html") == this.element;
1093 }
1094}
1095
1096class LinkClickObserver {
1097 constructor(delegate, eventTarget) {
1098 this.started = false;
1099 this.clickCaptured = () => {
1100 this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1101 this.eventTarget.addEventListener("click", this.clickBubbled, false);
1102 };
1103 this.clickBubbled = (event) => {
1104 if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1105 const target = (event.composedPath && event.composedPath()[0]) || event.target;
1106 const link = this.findLinkFromClickTarget(target);
1107 if (link && doesNotTargetIFrame(link)) {
1108 const location = this.getLocationForLink(link);
1109 if (this.delegate.willFollowLinkToLocation(link, location, event)) {
1110 event.preventDefault();
1111 this.delegate.followedLinkToLocation(link, location);
1112 }
1113 }
1114 }
1115 };
1116 this.delegate = delegate;
1117 this.eventTarget = eventTarget;
1118 }
1119 start() {
1120 if (!this.started) {
1121 this.eventTarget.addEventListener("click", this.clickCaptured, true);
1122 this.started = true;
1123 }
1124 }
1125 stop() {
1126 if (this.started) {
1127 this.eventTarget.removeEventListener("click", this.clickCaptured, true);
1128 this.started = false;
1129 }
1130 }
1131 clickEventIsSignificant(event) {
1132 return !((event.target && event.target.isContentEditable) ||
1133 event.defaultPrevented ||
1134 event.which > 1 ||
1135 event.altKey ||
1136 event.ctrlKey ||
1137 event.metaKey ||
1138 event.shiftKey);
1139 }
1140 findLinkFromClickTarget(target) {
1141 if (target instanceof Element) {
1142 return target.closest("a[href]:not([target^=_]):not([download])");
1143 }
1144 }
1145 getLocationForLink(link) {
1146 return expandURL(link.getAttribute("href") || "");
1147 }
1148}
1149function doesNotTargetIFrame(anchor) {
1150 for (const element of document.getElementsByName(anchor.target)) {
1151 if (element instanceof HTMLIFrameElement)
1152 return false;
1153 }
1154 return true;
1155}
1156
1157class FormLinkClickObserver {
1158 constructor(delegate, element) {
1159 this.delegate = delegate;
1160 this.linkInterceptor = new LinkClickObserver(this, element);
1161 }
1162 start() {
1163 this.linkInterceptor.start();
1164 }
1165 stop() {
1166 this.linkInterceptor.stop();
1167 }
1168 willFollowLinkToLocation(link, location, originalEvent) {
1169 return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
1170 link.hasAttribute("data-turbo-method"));
1171 }
1172 followedLinkToLocation(link, location) {
1173 const action = location.href;
1174 const form = document.createElement("form");
1175 form.setAttribute("data-turbo", "true");
1176 form.setAttribute("action", action);
1177 form.setAttribute("hidden", "");
1178 const method = link.getAttribute("data-turbo-method");
1179 if (method)
1180 form.setAttribute("method", method);
1181 const turboFrame = link.getAttribute("data-turbo-frame");
1182 if (turboFrame)
1183 form.setAttribute("data-turbo-frame", turboFrame);
1184 const turboAction = link.getAttribute("data-turbo-action");
1185 if (turboAction)
1186 form.setAttribute("data-turbo-action", turboAction);
1187 const turboConfirm = link.getAttribute("data-turbo-confirm");
1188 if (turboConfirm)
1189 form.setAttribute("data-turbo-confirm", turboConfirm);
1190 const turboStream = link.hasAttribute("data-turbo-stream");
1191 if (turboStream)
1192 form.setAttribute("data-turbo-stream", "");
1193 this.delegate.submittedFormLinkToLocation(link, location, form);
1194 document.body.appendChild(form);
1195 form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
1196 requestAnimationFrame(() => form.requestSubmit());
1197 }
1198}
1199
1200class Bardo {
1201 constructor(delegate, permanentElementMap) {
1202 this.delegate = delegate;
1203 this.permanentElementMap = permanentElementMap;
1204 }
1205 static preservingPermanentElements(delegate, permanentElementMap, callback) {
1206 const bardo = new this(delegate, permanentElementMap);
1207 bardo.enter();
1208 callback();
1209 bardo.leave();
1210 }
1211 enter() {
1212 for (const id in this.permanentElementMap) {
1213 const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
1214 this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
1215 this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
1216 }
1217 }
1218 leave() {
1219 for (const id in this.permanentElementMap) {
1220 const [currentPermanentElement] = this.permanentElementMap[id];
1221 this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
1222 this.replacePlaceholderWithPermanentElement(currentPermanentElement);
1223 this.delegate.leavingBardo(currentPermanentElement);
1224 }
1225 }
1226 replaceNewPermanentElementWithPlaceholder(permanentElement) {
1227 const placeholder = createPlaceholderForPermanentElement(permanentElement);
1228 permanentElement.replaceWith(placeholder);
1229 }
1230 replaceCurrentPermanentElementWithClone(permanentElement) {
1231 const clone = permanentElement.cloneNode(true);
1232 permanentElement.replaceWith(clone);
1233 }
1234 replacePlaceholderWithPermanentElement(permanentElement) {
1235 const placeholder = this.getPlaceholderById(permanentElement.id);
1236 placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
1237 }
1238 getPlaceholderById(id) {
1239 return this.placeholders.find((element) => element.content == id);
1240 }
1241 get placeholders() {
1242 return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
1243 }
1244}
1245function createPlaceholderForPermanentElement(permanentElement) {
1246 const element = document.createElement("meta");
1247 element.setAttribute("name", "turbo-permanent-placeholder");
1248 element.setAttribute("content", permanentElement.id);
1249 return element;
1250}
1251
1252class Renderer {
1253 constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1254 this.activeElement = null;
1255 this.currentSnapshot = currentSnapshot;
1256 this.newSnapshot = newSnapshot;
1257 this.isPreview = isPreview;
1258 this.willRender = willRender;
1259 this.renderElement = renderElement;
1260 this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1261 }
1262 get shouldRender() {
1263 return true;
1264 }
1265 get reloadReason() {
1266 return;
1267 }
1268 prepareToRender() {
1269 return;
1270 }
1271 finishRendering() {
1272 if (this.resolvingFunctions) {
1273 this.resolvingFunctions.resolve();
1274 delete this.resolvingFunctions;
1275 }
1276 }
1277 preservingPermanentElements(callback) {
1278 Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
1279 }
1280 focusFirstAutofocusableElement() {
1281 const element = this.connectedSnapshot.firstAutofocusableElement;
1282 if (elementIsFocusable(element)) {
1283 element.focus();
1284 }
1285 }
1286 enteringBardo(currentPermanentElement) {
1287 if (this.activeElement)
1288 return;
1289 if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1290 this.activeElement = this.currentSnapshot.activeElement;
1291 }
1292 }
1293 leavingBardo(currentPermanentElement) {
1294 if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1295 this.activeElement.focus();
1296 this.activeElement = null;
1297 }
1298 }
1299 get connectedSnapshot() {
1300 return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
1301 }
1302 get currentElement() {
1303 return this.currentSnapshot.element;
1304 }
1305 get newElement() {
1306 return this.newSnapshot.element;
1307 }
1308 get permanentElementMap() {
1309 return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1310 }
1311}
1312function elementIsFocusable(element) {
1313 return element && typeof element.focus == "function";
1314}
1315
1316class FrameRenderer extends Renderer {
1317 constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1318 super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
1319 this.delegate = delegate;
1320 }
1321 static renderElement(currentElement, newElement) {
1322 var _a;
1323 const destinationRange = document.createRange();
1324 destinationRange.selectNodeContents(currentElement);
1325 destinationRange.deleteContents();
1326 const frameElement = newElement;
1327 const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1328 if (sourceRange) {
1329 sourceRange.selectNodeContents(frameElement);
1330 currentElement.appendChild(sourceRange.extractContents());
1331 }
1332 }
1333 get shouldRender() {
1334 return true;
1335 }
1336 async render() {
1337 await nextAnimationFrame();
1338 this.preservingPermanentElements(() => {
1339 this.loadFrameElement();
1340 });
1341 this.scrollFrameIntoView();
1342 await nextAnimationFrame();
1343 this.focusFirstAutofocusableElement();
1344 await nextAnimationFrame();
1345 this.activateScriptElements();
1346 }
1347 loadFrameElement() {
1348 this.delegate.willRenderFrame(this.currentElement, this.newElement);
1349 this.renderElement(this.currentElement, this.newElement);
1350 }
1351 scrollFrameIntoView() {
1352 if (this.currentElement.autoscroll || this.newElement.autoscroll) {
1353 const element = this.currentElement.firstElementChild;
1354 const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
1355 const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
1356 if (element) {
1357 element.scrollIntoView({ block, behavior });
1358 return true;
1359 }
1360 }
1361 return false;
1362 }
1363 activateScriptElements() {
1364 for (const inertScriptElement of this.newScriptElements) {
1365 const activatedScriptElement = activateScriptElement(inertScriptElement);
1366 inertScriptElement.replaceWith(activatedScriptElement);
1367 }
1368 }
1369 get newScriptElements() {
1370 return this.currentElement.querySelectorAll("script");
1371 }
1372}
1373function readScrollLogicalPosition(value, defaultValue) {
1374 if (value == "end" || value == "start" || value == "center" || value == "nearest") {
1375 return value;
1376 }
1377 else {
1378 return defaultValue;
1379 }
1380}
1381function readScrollBehavior(value, defaultValue) {
1382 if (value == "auto" || value == "smooth") {
1383 return value;
1384 }
1385 else {
1386 return defaultValue;
1387 }
1388}
1389
1390class ProgressBar {
1391 constructor() {
1392 this.hiding = false;
1393 this.value = 0;
1394 this.visible = false;
1395 this.trickle = () => {
1396 this.setValue(this.value + Math.random() / 100);
1397 };
1398 this.stylesheetElement = this.createStylesheetElement();
1399 this.progressElement = this.createProgressElement();
1400 this.installStylesheetElement();
1401 this.setValue(0);
1402 }
1403 static get defaultCSS() {
1404 return unindent `
1405 .turbo-progress-bar {
1406 position: fixed;
1407 display: block;
1408 top: 0;
1409 left: 0;
1410 height: 3px;
1411 background: #0076ff;
1412 z-index: 2147483647;
1413 transition:
1414 width ${ProgressBar.animationDuration}ms ease-out,
1415 opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
1416 transform: translate3d(0, 0, 0);
1417 }
1418 `;
1419 }
1420 show() {
1421 if (!this.visible) {
1422 this.visible = true;
1423 this.installProgressElement();
1424 this.startTrickling();
1425 }
1426 }
1427 hide() {
1428 if (this.visible && !this.hiding) {
1429 this.hiding = true;
1430 this.fadeProgressElement(() => {
1431 this.uninstallProgressElement();
1432 this.stopTrickling();
1433 this.visible = false;
1434 this.hiding = false;
1435 });
1436 }
1437 }
1438 setValue(value) {
1439 this.value = value;
1440 this.refresh();
1441 }
1442 installStylesheetElement() {
1443 document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
1444 }
1445 installProgressElement() {
1446 this.progressElement.style.width = "0";
1447 this.progressElement.style.opacity = "1";
1448 document.documentElement.insertBefore(this.progressElement, document.body);
1449 this.refresh();
1450 }
1451 fadeProgressElement(callback) {
1452 this.progressElement.style.opacity = "0";
1453 setTimeout(callback, ProgressBar.animationDuration * 1.5);
1454 }
1455 uninstallProgressElement() {
1456 if (this.progressElement.parentNode) {
1457 document.documentElement.removeChild(this.progressElement);
1458 }
1459 }
1460 startTrickling() {
1461 if (!this.trickleInterval) {
1462 this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
1463 }
1464 }
1465 stopTrickling() {
1466 window.clearInterval(this.trickleInterval);
1467 delete this.trickleInterval;
1468 }
1469 refresh() {
1470 requestAnimationFrame(() => {
1471 this.progressElement.style.width = `${10 + this.value * 90}%`;
1472 });
1473 }
1474 createStylesheetElement() {
1475 const element = document.createElement("style");
1476 element.type = "text/css";
1477 element.textContent = ProgressBar.defaultCSS;
1478 if (this.cspNonce) {
1479 element.nonce = this.cspNonce;
1480 }
1481 return element;
1482 }
1483 createProgressElement() {
1484 const element = document.createElement("div");
1485 element.className = "turbo-progress-bar";
1486 return element;
1487 }
1488 get cspNonce() {
1489 return getMetaContent("csp-nonce");
1490 }
1491}
1492ProgressBar.animationDuration = 300;
1493
1494class HeadSnapshot extends Snapshot {
1495 constructor() {
1496 super(...arguments);
1497 this.detailsByOuterHTML = this.children
1498 .filter((element) => !elementIsNoscript(element))
1499 .map((element) => elementWithoutNonce(element))
1500 .reduce((result, element) => {
1501 const { outerHTML } = element;
1502 const details = outerHTML in result
1503 ? result[outerHTML]
1504 : {
1505 type: elementType(element),
1506 tracked: elementIsTracked(element),
1507 elements: [],
1508 };
1509 return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
1510 }, {});
1511 }
1512 get trackedElementSignature() {
1513 return Object.keys(this.detailsByOuterHTML)
1514 .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
1515 .join("");
1516 }
1517 getScriptElementsNotInSnapshot(snapshot) {
1518 return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
1519 }
1520 getStylesheetElementsNotInSnapshot(snapshot) {
1521 return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
1522 }
1523 getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
1524 return Object.keys(this.detailsByOuterHTML)
1525 .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
1526 .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
1527 .filter(({ type }) => type == matchedType)
1528 .map(({ elements: [element] }) => element);
1529 }
1530 get provisionalElements() {
1531 return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1532 const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML];
1533 if (type == null && !tracked) {
1534 return [...result, ...elements];
1535 }
1536 else if (elements.length > 1) {
1537 return [...result, ...elements.slice(1)];
1538 }
1539 else {
1540 return result;
1541 }
1542 }, []);
1543 }
1544 getMetaValue(name) {
1545 const element = this.findMetaElementByName(name);
1546 return element ? element.getAttribute("content") : null;
1547 }
1548 findMetaElementByName(name) {
1549 return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1550 const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
1551 return elementIsMetaElementWithName(element, name) ? element : result;
1552 }, undefined);
1553 }
1554}
1555function elementType(element) {
1556 if (elementIsScript(element)) {
1557 return "script";
1558 }
1559 else if (elementIsStylesheet(element)) {
1560 return "stylesheet";
1561 }
1562}
1563function elementIsTracked(element) {
1564 return element.getAttribute("data-turbo-track") == "reload";
1565}
1566function elementIsScript(element) {
1567 const tagName = element.localName;
1568 return tagName == "script";
1569}
1570function elementIsNoscript(element) {
1571 const tagName = element.localName;
1572 return tagName == "noscript";
1573}
1574function elementIsStylesheet(element) {
1575 const tagName = element.localName;
1576 return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
1577}
1578function elementIsMetaElementWithName(element, name) {
1579 const tagName = element.localName;
1580 return tagName == "meta" && element.getAttribute("name") == name;
1581}
1582function elementWithoutNonce(element) {
1583 if (element.hasAttribute("nonce")) {
1584 element.setAttribute("nonce", "");
1585 }
1586 return element;
1587}
1588
1589class PageSnapshot extends Snapshot {
1590 constructor(element, headSnapshot) {
1591 super(element);
1592 this.headSnapshot = headSnapshot;
1593 }
1594 static fromHTMLString(html = "") {
1595 return this.fromDocument(parseHTMLDocument(html));
1596 }
1597 static fromElement(element) {
1598 return this.fromDocument(element.ownerDocument);
1599 }
1600 static fromDocument({ head, body }) {
1601 return new this(body, new HeadSnapshot(head));
1602 }
1603 clone() {
1604 const clonedElement = this.element.cloneNode(true);
1605 const selectElements = this.element.querySelectorAll("select");
1606 const clonedSelectElements = clonedElement.querySelectorAll("select");
1607 for (const [index, source] of selectElements.entries()) {
1608 const clone = clonedSelectElements[index];
1609 for (const option of clone.selectedOptions)
1610 option.selected = false;
1611 for (const option of source.selectedOptions)
1612 clone.options[option.index].selected = true;
1613 }
1614 for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
1615 clonedPasswordInput.value = "";
1616 }
1617 return new PageSnapshot(clonedElement, this.headSnapshot);
1618 }
1619 get headElement() {
1620 return this.headSnapshot.element;
1621 }
1622 get rootLocation() {
1623 var _a;
1624 const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
1625 return expandURL(root);
1626 }
1627 get cacheControlValue() {
1628 return this.getSetting("cache-control");
1629 }
1630 get isPreviewable() {
1631 return this.cacheControlValue != "no-preview";
1632 }
1633 get isCacheable() {
1634 return this.cacheControlValue != "no-cache";
1635 }
1636 get isVisitable() {
1637 return this.getSetting("visit-control") != "reload";
1638 }
1639 getSetting(name) {
1640 return this.headSnapshot.getMetaValue(`turbo-${name}`);
1641 }
1642}
1643
1644var TimingMetric;
1645(function (TimingMetric) {
1646 TimingMetric["visitStart"] = "visitStart";
1647 TimingMetric["requestStart"] = "requestStart";
1648 TimingMetric["requestEnd"] = "requestEnd";
1649 TimingMetric["visitEnd"] = "visitEnd";
1650})(TimingMetric || (TimingMetric = {}));
1651var VisitState;
1652(function (VisitState) {
1653 VisitState["initialized"] = "initialized";
1654 VisitState["started"] = "started";
1655 VisitState["canceled"] = "canceled";
1656 VisitState["failed"] = "failed";
1657 VisitState["completed"] = "completed";
1658})(VisitState || (VisitState = {}));
1659const defaultOptions = {
1660 action: "advance",
1661 historyChanged: false,
1662 visitCachedSnapshot: () => { },
1663 willRender: true,
1664 updateHistory: true,
1665 shouldCacheSnapshot: true,
1666 acceptsStreamResponse: false,
1667};
1668var SystemStatusCode;
1669(function (SystemStatusCode) {
1670 SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
1671 SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
1672 SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
1673})(SystemStatusCode || (SystemStatusCode = {}));
1674class Visit {
1675 constructor(delegate, location, restorationIdentifier, options = {}) {
1676 this.identifier = uuid();
1677 this.timingMetrics = {};
1678 this.followedRedirect = false;
1679 this.historyChanged = false;
1680 this.scrolled = false;
1681 this.shouldCacheSnapshot = true;
1682 this.acceptsStreamResponse = false;
1683 this.snapshotCached = false;
1684 this.state = VisitState.initialized;
1685 this.delegate = delegate;
1686 this.location = location;
1687 this.restorationIdentifier = restorationIdentifier || uuid();
1688 const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
1689 this.action = action;
1690 this.historyChanged = historyChanged;
1691 this.referrer = referrer;
1692 this.snapshot = snapshot;
1693 this.snapshotHTML = snapshotHTML;
1694 this.response = response;
1695 this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
1696 this.visitCachedSnapshot = visitCachedSnapshot;
1697 this.willRender = willRender;
1698 this.updateHistory = updateHistory;
1699 this.scrolled = !willRender;
1700 this.shouldCacheSnapshot = shouldCacheSnapshot;
1701 this.acceptsStreamResponse = acceptsStreamResponse;
1702 }
1703 get adapter() {
1704 return this.delegate.adapter;
1705 }
1706 get view() {
1707 return this.delegate.view;
1708 }
1709 get history() {
1710 return this.delegate.history;
1711 }
1712 get restorationData() {
1713 return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
1714 }
1715 get silent() {
1716 return this.isSamePage;
1717 }
1718 start() {
1719 if (this.state == VisitState.initialized) {
1720 this.recordTimingMetric(TimingMetric.visitStart);
1721 this.state = VisitState.started;
1722 this.adapter.visitStarted(this);
1723 this.delegate.visitStarted(this);
1724 }
1725 }
1726 cancel() {
1727 if (this.state == VisitState.started) {
1728 if (this.request) {
1729 this.request.cancel();
1730 }
1731 this.cancelRender();
1732 this.state = VisitState.canceled;
1733 }
1734 }
1735 complete() {
1736 if (this.state == VisitState.started) {
1737 this.recordTimingMetric(TimingMetric.visitEnd);
1738 this.state = VisitState.completed;
1739 this.followRedirect();
1740 if (!this.followedRedirect) {
1741 this.adapter.visitCompleted(this);
1742 this.delegate.visitCompleted(this);
1743 }
1744 }
1745 }
1746 fail() {
1747 if (this.state == VisitState.started) {
1748 this.state = VisitState.failed;
1749 this.adapter.visitFailed(this);
1750 }
1751 }
1752 changeHistory() {
1753 var _a;
1754 if (!this.historyChanged && this.updateHistory) {
1755 const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
1756 const method = getHistoryMethodForAction(actionForHistory);
1757 this.history.update(method, this.location, this.restorationIdentifier);
1758 this.historyChanged = true;
1759 }
1760 }
1761 issueRequest() {
1762 if (this.hasPreloadedResponse()) {
1763 this.simulateRequest();
1764 }
1765 else if (this.shouldIssueRequest() && !this.request) {
1766 this.request = new FetchRequest(this, FetchMethod.get, this.location);
1767 this.request.perform();
1768 }
1769 }
1770 simulateRequest() {
1771 if (this.response) {
1772 this.startRequest();
1773 this.recordResponse();
1774 this.finishRequest();
1775 }
1776 }
1777 startRequest() {
1778 this.recordTimingMetric(TimingMetric.requestStart);
1779 this.adapter.visitRequestStarted(this);
1780 }
1781 recordResponse(response = this.response) {
1782 this.response = response;
1783 if (response) {
1784 const { statusCode } = response;
1785 if (isSuccessful(statusCode)) {
1786 this.adapter.visitRequestCompleted(this);
1787 }
1788 else {
1789 this.adapter.visitRequestFailedWithStatusCode(this, statusCode);
1790 }
1791 }
1792 }
1793 finishRequest() {
1794 this.recordTimingMetric(TimingMetric.requestEnd);
1795 this.adapter.visitRequestFinished(this);
1796 }
1797 loadResponse() {
1798 if (this.response) {
1799 const { statusCode, responseHTML } = this.response;
1800 this.render(async () => {
1801 if (this.shouldCacheSnapshot)
1802 this.cacheSnapshot();
1803 if (this.view.renderPromise)
1804 await this.view.renderPromise;
1805 if (isSuccessful(statusCode) && responseHTML != null) {
1806 await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1807 this.performScroll();
1808 this.adapter.visitRendered(this);
1809 this.complete();
1810 }
1811 else {
1812 await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
1813 this.adapter.visitRendered(this);
1814 this.fail();
1815 }
1816 });
1817 }
1818 }
1819 getCachedSnapshot() {
1820 const snapshot = this.view.getCachedSnapshotForLocation(this.location) || this.getPreloadedSnapshot();
1821 if (snapshot && (!getAnchor(this.location) || snapshot.hasAnchor(getAnchor(this.location)))) {
1822 if (this.action == "restore" || snapshot.isPreviewable) {
1823 return snapshot;
1824 }
1825 }
1826 }
1827 getPreloadedSnapshot() {
1828 if (this.snapshotHTML) {
1829 return PageSnapshot.fromHTMLString(this.snapshotHTML);
1830 }
1831 }
1832 hasCachedSnapshot() {
1833 return this.getCachedSnapshot() != null;
1834 }
1835 loadCachedSnapshot() {
1836 const snapshot = this.getCachedSnapshot();
1837 if (snapshot) {
1838 const isPreview = this.shouldIssueRequest();
1839 this.render(async () => {
1840 this.cacheSnapshot();
1841 if (this.isSamePage) {
1842 this.adapter.visitRendered(this);
1843 }
1844 else {
1845 if (this.view.renderPromise)
1846 await this.view.renderPromise;
1847 await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1848 this.performScroll();
1849 this.adapter.visitRendered(this);
1850 if (!isPreview) {
1851 this.complete();
1852 }
1853 }
1854 });
1855 }
1856 }
1857 followRedirect() {
1858 var _a;
1859 if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1860 this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1861 action: "replace",
1862 response: this.response,
1863 });
1864 this.followedRedirect = true;
1865 }
1866 }
1867 goToSamePageAnchor() {
1868 if (this.isSamePage) {
1869 this.render(async () => {
1870 this.cacheSnapshot();
1871 this.performScroll();
1872 this.changeHistory();
1873 this.adapter.visitRendered(this);
1874 });
1875 }
1876 }
1877 prepareHeadersForRequest(headers, request) {
1878 if (this.acceptsStreamResponse) {
1879 request.acceptResponseType(StreamMessage.contentType);
1880 }
1881 }
1882 requestStarted() {
1883 this.startRequest();
1884 }
1885 requestPreventedHandlingResponse(_request, _response) { }
1886 async requestSucceededWithResponse(request, response) {
1887 const responseHTML = await response.responseHTML;
1888 const { redirected, statusCode } = response;
1889 if (responseHTML == undefined) {
1890 this.recordResponse({
1891 statusCode: SystemStatusCode.contentTypeMismatch,
1892 redirected,
1893 });
1894 }
1895 else {
1896 this.redirectedToLocation = response.redirected ? response.location : undefined;
1897 this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1898 }
1899 }
1900 async requestFailedWithResponse(request, response) {
1901 const responseHTML = await response.responseHTML;
1902 const { redirected, statusCode } = response;
1903 if (responseHTML == undefined) {
1904 this.recordResponse({
1905 statusCode: SystemStatusCode.contentTypeMismatch,
1906 redirected,
1907 });
1908 }
1909 else {
1910 this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1911 }
1912 }
1913 requestErrored(_request, _error) {
1914 this.recordResponse({
1915 statusCode: SystemStatusCode.networkFailure,
1916 redirected: false,
1917 });
1918 }
1919 requestFinished() {
1920 this.finishRequest();
1921 }
1922 performScroll() {
1923 if (!this.scrolled && !this.view.forceReloaded) {
1924 if (this.action == "restore") {
1925 this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
1926 }
1927 else {
1928 this.scrollToAnchor() || this.view.scrollToTop();
1929 }
1930 if (this.isSamePage) {
1931 this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
1932 }
1933 this.scrolled = true;
1934 }
1935 }
1936 scrollToRestoredPosition() {
1937 const { scrollPosition } = this.restorationData;
1938 if (scrollPosition) {
1939 this.view.scrollToPosition(scrollPosition);
1940 return true;
1941 }
1942 }
1943 scrollToAnchor() {
1944 const anchor = getAnchor(this.location);
1945 if (anchor != null) {
1946 this.view.scrollToAnchor(anchor);
1947 return true;
1948 }
1949 }
1950 recordTimingMetric(metric) {
1951 this.timingMetrics[metric] = new Date().getTime();
1952 }
1953 getTimingMetrics() {
1954 return Object.assign({}, this.timingMetrics);
1955 }
1956 getHistoryMethodForAction(action) {
1957 switch (action) {
1958 case "replace":
1959 return history.replaceState;
1960 case "advance":
1961 case "restore":
1962 return history.pushState;
1963 }
1964 }
1965 hasPreloadedResponse() {
1966 return typeof this.response == "object";
1967 }
1968 shouldIssueRequest() {
1969 if (this.isSamePage) {
1970 return false;
1971 }
1972 else if (this.action == "restore") {
1973 return !this.hasCachedSnapshot();
1974 }
1975 else {
1976 return this.willRender;
1977 }
1978 }
1979 cacheSnapshot() {
1980 if (!this.snapshotCached) {
1981 this.view.cacheSnapshot(this.snapshot).then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
1982 this.snapshotCached = true;
1983 }
1984 }
1985 async render(callback) {
1986 this.cancelRender();
1987 await new Promise((resolve) => {
1988 this.frame = requestAnimationFrame(() => resolve());
1989 });
1990 await callback();
1991 delete this.frame;
1992 }
1993 cancelRender() {
1994 if (this.frame) {
1995 cancelAnimationFrame(this.frame);
1996 delete this.frame;
1997 }
1998 }
1999}
2000function isSuccessful(statusCode) {
2001 return statusCode >= 200 && statusCode < 300;
2002}
2003
2004class BrowserAdapter {
2005 constructor(session) {
2006 this.progressBar = new ProgressBar();
2007 this.showProgressBar = () => {
2008 this.progressBar.show();
2009 };
2010 this.session = session;
2011 }
2012 visitProposedToLocation(location, options) {
2013 this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
2014 }
2015 visitStarted(visit) {
2016 this.location = visit.location;
2017 visit.loadCachedSnapshot();
2018 visit.issueRequest();
2019 visit.goToSamePageAnchor();
2020 }
2021 visitRequestStarted(visit) {
2022 this.progressBar.setValue(0);
2023 if (visit.hasCachedSnapshot() || visit.action != "restore") {
2024 this.showVisitProgressBarAfterDelay();
2025 }
2026 else {
2027 this.showProgressBar();
2028 }
2029 }
2030 visitRequestCompleted(visit) {
2031 visit.loadResponse();
2032 }
2033 visitRequestFailedWithStatusCode(visit, statusCode) {
2034 switch (statusCode) {
2035 case SystemStatusCode.networkFailure:
2036 case SystemStatusCode.timeoutFailure:
2037 case SystemStatusCode.contentTypeMismatch:
2038 return this.reload({
2039 reason: "request_failed",
2040 context: {
2041 statusCode,
2042 },
2043 });
2044 default:
2045 return visit.loadResponse();
2046 }
2047 }
2048 visitRequestFinished(_visit) {
2049 this.progressBar.setValue(1);
2050 this.hideVisitProgressBar();
2051 }
2052 visitCompleted(_visit) { }
2053 pageInvalidated(reason) {
2054 this.reload(reason);
2055 }
2056 visitFailed(_visit) { }
2057 visitRendered(_visit) { }
2058 formSubmissionStarted(_formSubmission) {
2059 this.progressBar.setValue(0);
2060 this.showFormProgressBarAfterDelay();
2061 }
2062 formSubmissionFinished(_formSubmission) {
2063 this.progressBar.setValue(1);
2064 this.hideFormProgressBar();
2065 }
2066 showVisitProgressBarAfterDelay() {
2067 this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
2068 }
2069 hideVisitProgressBar() {
2070 this.progressBar.hide();
2071 if (this.visitProgressBarTimeout != null) {
2072 window.clearTimeout(this.visitProgressBarTimeout);
2073 delete this.visitProgressBarTimeout;
2074 }
2075 }
2076 showFormProgressBarAfterDelay() {
2077 if (this.formProgressBarTimeout == null) {
2078 this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
2079 }
2080 }
2081 hideFormProgressBar() {
2082 this.progressBar.hide();
2083 if (this.formProgressBarTimeout != null) {
2084 window.clearTimeout(this.formProgressBarTimeout);
2085 delete this.formProgressBarTimeout;
2086 }
2087 }
2088 reload(reason) {
2089 var _a;
2090 dispatch("turbo:reload", { detail: reason });
2091 window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
2092 }
2093 get navigator() {
2094 return this.session.navigator;
2095 }
2096}
2097
2098class CacheObserver {
2099 constructor() {
2100 this.started = false;
2101 this.removeStaleElements = ((_event) => {
2102 const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
2103 for (const element of staleElements) {
2104 element.remove();
2105 }
2106 });
2107 }
2108 start() {
2109 if (!this.started) {
2110 this.started = true;
2111 addEventListener("turbo:before-cache", this.removeStaleElements, false);
2112 }
2113 }
2114 stop() {
2115 if (this.started) {
2116 this.started = false;
2117 removeEventListener("turbo:before-cache", this.removeStaleElements, false);
2118 }
2119 }
2120}
2121
2122class FrameRedirector {
2123 constructor(session, element) {
2124 this.session = session;
2125 this.element = element;
2126 this.linkInterceptor = new LinkInterceptor(this, element);
2127 this.formSubmitObserver = new FormSubmitObserver(this, element);
2128 }
2129 start() {
2130 this.linkInterceptor.start();
2131 this.formSubmitObserver.start();
2132 }
2133 stop() {
2134 this.linkInterceptor.stop();
2135 this.formSubmitObserver.stop();
2136 }
2137 shouldInterceptLinkClick(element, _location, _event) {
2138 return this.shouldRedirect(element);
2139 }
2140 linkClickIntercepted(element, url, event) {
2141 const frame = this.findFrameElement(element);
2142 if (frame) {
2143 frame.delegate.linkClickIntercepted(element, url, event);
2144 }
2145 }
2146 willSubmitForm(element, submitter) {
2147 return (element.closest("turbo-frame") == null &&
2148 this.shouldSubmit(element, submitter) &&
2149 this.shouldRedirect(element, submitter));
2150 }
2151 formSubmitted(element, submitter) {
2152 const frame = this.findFrameElement(element, submitter);
2153 if (frame) {
2154 frame.delegate.formSubmitted(element, submitter);
2155 }
2156 }
2157 shouldSubmit(form, submitter) {
2158 var _a;
2159 const action = getAction(form, submitter);
2160 const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
2161 const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
2162 return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
2163 }
2164 shouldRedirect(element, submitter) {
2165 const isNavigatable = element instanceof HTMLFormElement
2166 ? this.session.submissionIsNavigatable(element, submitter)
2167 : this.session.elementIsNavigatable(element);
2168 if (isNavigatable) {
2169 const frame = this.findFrameElement(element, submitter);
2170 return frame ? frame != element.closest("turbo-frame") : false;
2171 }
2172 else {
2173 return false;
2174 }
2175 }
2176 findFrameElement(element, submitter) {
2177 const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
2178 if (id && id != "_top") {
2179 const frame = this.element.querySelector(`#${id}:not([disabled])`);
2180 if (frame instanceof FrameElement) {
2181 return frame;
2182 }
2183 }
2184 }
2185}
2186
2187class History {
2188 constructor(delegate) {
2189 this.restorationIdentifier = uuid();
2190 this.restorationData = {};
2191 this.started = false;
2192 this.pageLoaded = false;
2193 this.onPopState = (event) => {
2194 if (this.shouldHandlePopState()) {
2195 const { turbo } = event.state || {};
2196 if (turbo) {
2197 this.location = new URL(window.location.href);
2198 const { restorationIdentifier } = turbo;
2199 this.restorationIdentifier = restorationIdentifier;
2200 this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
2201 }
2202 }
2203 };
2204 this.onPageLoad = async (_event) => {
2205 await nextMicrotask();
2206 this.pageLoaded = true;
2207 };
2208 this.delegate = delegate;
2209 }
2210 start() {
2211 if (!this.started) {
2212 addEventListener("popstate", this.onPopState, false);
2213 addEventListener("load", this.onPageLoad, false);
2214 this.started = true;
2215 this.replace(new URL(window.location.href));
2216 }
2217 }
2218 stop() {
2219 if (this.started) {
2220 removeEventListener("popstate", this.onPopState, false);
2221 removeEventListener("load", this.onPageLoad, false);
2222 this.started = false;
2223 }
2224 }
2225 push(location, restorationIdentifier) {
2226 this.update(history.pushState, location, restorationIdentifier);
2227 }
2228 replace(location, restorationIdentifier) {
2229 this.update(history.replaceState, location, restorationIdentifier);
2230 }
2231 update(method, location, restorationIdentifier = uuid()) {
2232 const state = { turbo: { restorationIdentifier } };
2233 method.call(history, state, "", location.href);
2234 this.location = location;
2235 this.restorationIdentifier = restorationIdentifier;
2236 }
2237 getRestorationDataForIdentifier(restorationIdentifier) {
2238 return this.restorationData[restorationIdentifier] || {};
2239 }
2240 updateRestorationData(additionalData) {
2241 const { restorationIdentifier } = this;
2242 const restorationData = this.restorationData[restorationIdentifier];
2243 this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
2244 }
2245 assumeControlOfScrollRestoration() {
2246 var _a;
2247 if (!this.previousScrollRestoration) {
2248 this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
2249 history.scrollRestoration = "manual";
2250 }
2251 }
2252 relinquishControlOfScrollRestoration() {
2253 if (this.previousScrollRestoration) {
2254 history.scrollRestoration = this.previousScrollRestoration;
2255 delete this.previousScrollRestoration;
2256 }
2257 }
2258 shouldHandlePopState() {
2259 return this.pageIsLoaded();
2260 }
2261 pageIsLoaded() {
2262 return this.pageLoaded || document.readyState == "complete";
2263 }
2264}
2265
2266class Navigator {
2267 constructor(delegate) {
2268 this.delegate = delegate;
2269 }
2270 proposeVisit(location, options = {}) {
2271 if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
2272 if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
2273 this.delegate.visitProposedToLocation(location, options);
2274 }
2275 else {
2276 window.location.href = location.toString();
2277 }
2278 }
2279 }
2280 startVisit(locatable, restorationIdentifier, options = {}) {
2281 this.stop();
2282 this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
2283 this.currentVisit.start();
2284 }
2285 submitForm(form, submitter) {
2286 this.stop();
2287 this.formSubmission = new FormSubmission(this, form, submitter, true);
2288 this.formSubmission.start();
2289 }
2290 stop() {
2291 if (this.formSubmission) {
2292 this.formSubmission.stop();
2293 delete this.formSubmission;
2294 }
2295 if (this.currentVisit) {
2296 this.currentVisit.cancel();
2297 delete this.currentVisit;
2298 }
2299 }
2300 get adapter() {
2301 return this.delegate.adapter;
2302 }
2303 get view() {
2304 return this.delegate.view;
2305 }
2306 get history() {
2307 return this.delegate.history;
2308 }
2309 formSubmissionStarted(formSubmission) {
2310 if (typeof this.adapter.formSubmissionStarted === "function") {
2311 this.adapter.formSubmissionStarted(formSubmission);
2312 }
2313 }
2314 async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
2315 if (formSubmission == this.formSubmission) {
2316 const responseHTML = await fetchResponse.responseHTML;
2317 if (responseHTML) {
2318 const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
2319 if (!shouldCacheSnapshot) {
2320 this.view.clearSnapshotCache();
2321 }
2322 const { statusCode, redirected } = fetchResponse;
2323 const action = this.getActionForFormSubmission(formSubmission);
2324 const visitOptions = {
2325 action,
2326 shouldCacheSnapshot,
2327 response: { statusCode, responseHTML, redirected },
2328 };
2329 this.proposeVisit(fetchResponse.location, visitOptions);
2330 }
2331 }
2332 }
2333 async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
2334 const responseHTML = await fetchResponse.responseHTML;
2335 if (responseHTML) {
2336 const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2337 if (fetchResponse.serverError) {
2338 await this.view.renderError(snapshot, this.currentVisit);
2339 }
2340 else {
2341 await this.view.renderPage(snapshot, false, true, this.currentVisit);
2342 }
2343 this.view.scrollToTop();
2344 this.view.clearSnapshotCache();
2345 }
2346 }
2347 formSubmissionErrored(formSubmission, error) {
2348 console.error(error);
2349 }
2350 formSubmissionFinished(formSubmission) {
2351 if (typeof this.adapter.formSubmissionFinished === "function") {
2352 this.adapter.formSubmissionFinished(formSubmission);
2353 }
2354 }
2355 visitStarted(visit) {
2356 this.delegate.visitStarted(visit);
2357 }
2358 visitCompleted(visit) {
2359 this.delegate.visitCompleted(visit);
2360 }
2361 locationWithActionIsSamePage(location, action) {
2362 const anchor = getAnchor(location);
2363 const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2364 const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
2365 return (action !== "replace" &&
2366 getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
2367 (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
2368 }
2369 visitScrolledToSamePageLocation(oldURL, newURL) {
2370 this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
2371 }
2372 get location() {
2373 return this.history.location;
2374 }
2375 get restorationIdentifier() {
2376 return this.history.restorationIdentifier;
2377 }
2378 getActionForFormSubmission(formSubmission) {
2379 const { formElement, submitter } = formSubmission;
2380 const action = getAttribute("data-turbo-action", submitter, formElement);
2381 return isAction(action) ? action : "advance";
2382 }
2383}
2384
2385var PageStage;
2386(function (PageStage) {
2387 PageStage[PageStage["initial"] = 0] = "initial";
2388 PageStage[PageStage["loading"] = 1] = "loading";
2389 PageStage[PageStage["interactive"] = 2] = "interactive";
2390 PageStage[PageStage["complete"] = 3] = "complete";
2391})(PageStage || (PageStage = {}));
2392class PageObserver {
2393 constructor(delegate) {
2394 this.stage = PageStage.initial;
2395 this.started = false;
2396 this.interpretReadyState = () => {
2397 const { readyState } = this;
2398 if (readyState == "interactive") {
2399 this.pageIsInteractive();
2400 }
2401 else if (readyState == "complete") {
2402 this.pageIsComplete();
2403 }
2404 };
2405 this.pageWillUnload = () => {
2406 this.delegate.pageWillUnload();
2407 };
2408 this.delegate = delegate;
2409 }
2410 start() {
2411 if (!this.started) {
2412 if (this.stage == PageStage.initial) {
2413 this.stage = PageStage.loading;
2414 }
2415 document.addEventListener("readystatechange", this.interpretReadyState, false);
2416 addEventListener("pagehide", this.pageWillUnload, false);
2417 this.started = true;
2418 }
2419 }
2420 stop() {
2421 if (this.started) {
2422 document.removeEventListener("readystatechange", this.interpretReadyState, false);
2423 removeEventListener("pagehide", this.pageWillUnload, false);
2424 this.started = false;
2425 }
2426 }
2427 pageIsInteractive() {
2428 if (this.stage == PageStage.loading) {
2429 this.stage = PageStage.interactive;
2430 this.delegate.pageBecameInteractive();
2431 }
2432 }
2433 pageIsComplete() {
2434 this.pageIsInteractive();
2435 if (this.stage == PageStage.interactive) {
2436 this.stage = PageStage.complete;
2437 this.delegate.pageLoaded();
2438 }
2439 }
2440 get readyState() {
2441 return document.readyState;
2442 }
2443}
2444
2445class ScrollObserver {
2446 constructor(delegate) {
2447 this.started = false;
2448 this.onScroll = () => {
2449 this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
2450 };
2451 this.delegate = delegate;
2452 }
2453 start() {
2454 if (!this.started) {
2455 addEventListener("scroll", this.onScroll, false);
2456 this.onScroll();
2457 this.started = true;
2458 }
2459 }
2460 stop() {
2461 if (this.started) {
2462 removeEventListener("scroll", this.onScroll, false);
2463 this.started = false;
2464 }
2465 }
2466 updatePosition(position) {
2467 this.delegate.scrollPositionChanged(position);
2468 }
2469}
2470
2471class StreamMessageRenderer {
2472 render({ fragment }) {
2473 Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
2474 }
2475 enteringBardo(currentPermanentElement, newPermanentElement) {
2476 newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
2477 }
2478 leavingBardo() { }
2479}
2480function getPermanentElementMapForFragment(fragment) {
2481 const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
2482 const permanentElementMap = {};
2483 for (const permanentElementInDocument of permanentElementsInDocument) {
2484 const { id } = permanentElementInDocument;
2485 for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
2486 const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
2487 if (elementInStream) {
2488 permanentElementMap[id] = [permanentElementInDocument, elementInStream];
2489 }
2490 }
2491 }
2492 return permanentElementMap;
2493}
2494
2495class StreamObserver {
2496 constructor(delegate) {
2497 this.sources = new Set();
2498 this.started = false;
2499 this.inspectFetchResponse = ((event) => {
2500 const response = fetchResponseFromEvent(event);
2501 if (response && fetchResponseIsStream(response)) {
2502 event.preventDefault();
2503 this.receiveMessageResponse(response);
2504 }
2505 });
2506 this.receiveMessageEvent = (event) => {
2507 if (this.started && typeof event.data == "string") {
2508 this.receiveMessageHTML(event.data);
2509 }
2510 };
2511 this.delegate = delegate;
2512 }
2513 start() {
2514 if (!this.started) {
2515 this.started = true;
2516 addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
2517 }
2518 }
2519 stop() {
2520 if (this.started) {
2521 this.started = false;
2522 removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
2523 }
2524 }
2525 connectStreamSource(source) {
2526 if (!this.streamSourceIsConnected(source)) {
2527 this.sources.add(source);
2528 source.addEventListener("message", this.receiveMessageEvent, false);
2529 }
2530 }
2531 disconnectStreamSource(source) {
2532 if (this.streamSourceIsConnected(source)) {
2533 this.sources.delete(source);
2534 source.removeEventListener("message", this.receiveMessageEvent, false);
2535 }
2536 }
2537 streamSourceIsConnected(source) {
2538 return this.sources.has(source);
2539 }
2540 async receiveMessageResponse(response) {
2541 const html = await response.responseHTML;
2542 if (html) {
2543 this.receiveMessageHTML(html);
2544 }
2545 }
2546 receiveMessageHTML(html) {
2547 this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
2548 }
2549}
2550function fetchResponseFromEvent(event) {
2551 var _a;
2552 const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
2553 if (fetchResponse instanceof FetchResponse) {
2554 return fetchResponse;
2555 }
2556}
2557function fetchResponseIsStream(response) {
2558 var _a;
2559 const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
2560 return contentType.startsWith(StreamMessage.contentType);
2561}
2562
2563class ErrorRenderer extends Renderer {
2564 static renderElement(currentElement, newElement) {
2565 const { documentElement, body } = document;
2566 documentElement.replaceChild(newElement, body);
2567 }
2568 async render() {
2569 this.replaceHeadAndBody();
2570 this.activateScriptElements();
2571 }
2572 replaceHeadAndBody() {
2573 const { documentElement, head } = document;
2574 documentElement.replaceChild(this.newHead, head);
2575 this.renderElement(this.currentElement, this.newElement);
2576 }
2577 activateScriptElements() {
2578 for (const replaceableElement of this.scriptElements) {
2579 const parentNode = replaceableElement.parentNode;
2580 if (parentNode) {
2581 const element = activateScriptElement(replaceableElement);
2582 parentNode.replaceChild(element, replaceableElement);
2583 }
2584 }
2585 }
2586 get newHead() {
2587 return this.newSnapshot.headSnapshot.element;
2588 }
2589 get scriptElements() {
2590 return document.documentElement.querySelectorAll("script");
2591 }
2592}
2593
2594class PageRenderer extends Renderer {
2595 static renderElement(currentElement, newElement) {
2596 if (document.body && newElement instanceof HTMLBodyElement) {
2597 document.body.replaceWith(newElement);
2598 }
2599 else {
2600 document.documentElement.appendChild(newElement);
2601 }
2602 }
2603 get shouldRender() {
2604 return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
2605 }
2606 get reloadReason() {
2607 if (!this.newSnapshot.isVisitable) {
2608 return {
2609 reason: "turbo_visit_control_is_reload",
2610 };
2611 }
2612 if (!this.trackedElementsAreIdentical) {
2613 return {
2614 reason: "tracked_element_mismatch",
2615 };
2616 }
2617 }
2618 async prepareToRender() {
2619 await this.mergeHead();
2620 }
2621 async render() {
2622 if (this.willRender) {
2623 this.replaceBody();
2624 }
2625 }
2626 finishRendering() {
2627 super.finishRendering();
2628 if (!this.isPreview) {
2629 this.focusFirstAutofocusableElement();
2630 }
2631 }
2632 get currentHeadSnapshot() {
2633 return this.currentSnapshot.headSnapshot;
2634 }
2635 get newHeadSnapshot() {
2636 return this.newSnapshot.headSnapshot;
2637 }
2638 get newElement() {
2639 return this.newSnapshot.element;
2640 }
2641 async mergeHead() {
2642 const newStylesheetElements = this.copyNewHeadStylesheetElements();
2643 this.copyNewHeadScriptElements();
2644 this.removeCurrentHeadProvisionalElements();
2645 this.copyNewHeadProvisionalElements();
2646 await newStylesheetElements;
2647 }
2648 replaceBody() {
2649 this.preservingPermanentElements(() => {
2650 this.activateNewBody();
2651 this.assignNewBody();
2652 });
2653 }
2654 get trackedElementsAreIdentical() {
2655 return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
2656 }
2657 async copyNewHeadStylesheetElements() {
2658 const loadingElements = [];
2659 for (const element of this.newHeadStylesheetElements) {
2660 loadingElements.push(waitForLoad(element));
2661 document.head.appendChild(element);
2662 }
2663 await Promise.all(loadingElements);
2664 }
2665 copyNewHeadScriptElements() {
2666 for (const element of this.newHeadScriptElements) {
2667 document.head.appendChild(activateScriptElement(element));
2668 }
2669 }
2670 removeCurrentHeadProvisionalElements() {
2671 for (const element of this.currentHeadProvisionalElements) {
2672 document.head.removeChild(element);
2673 }
2674 }
2675 copyNewHeadProvisionalElements() {
2676 for (const element of this.newHeadProvisionalElements) {
2677 document.head.appendChild(element);
2678 }
2679 }
2680 activateNewBody() {
2681 document.adoptNode(this.newElement);
2682 this.activateNewBodyScriptElements();
2683 }
2684 activateNewBodyScriptElements() {
2685 for (const inertScriptElement of this.newBodyScriptElements) {
2686 const activatedScriptElement = activateScriptElement(inertScriptElement);
2687 inertScriptElement.replaceWith(activatedScriptElement);
2688 }
2689 }
2690 assignNewBody() {
2691 this.renderElement(this.currentElement, this.newElement);
2692 }
2693 get newHeadStylesheetElements() {
2694 return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
2695 }
2696 get newHeadScriptElements() {
2697 return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot);
2698 }
2699 get currentHeadProvisionalElements() {
2700 return this.currentHeadSnapshot.provisionalElements;
2701 }
2702 get newHeadProvisionalElements() {
2703 return this.newHeadSnapshot.provisionalElements;
2704 }
2705 get newBodyScriptElements() {
2706 return this.newElement.querySelectorAll("script");
2707 }
2708}
2709
2710class SnapshotCache {
2711 constructor(size) {
2712 this.keys = [];
2713 this.snapshots = {};
2714 this.size = size;
2715 }
2716 has(location) {
2717 return toCacheKey(location) in this.snapshots;
2718 }
2719 get(location) {
2720 if (this.has(location)) {
2721 const snapshot = this.read(location);
2722 this.touch(location);
2723 return snapshot;
2724 }
2725 }
2726 put(location, snapshot) {
2727 this.write(location, snapshot);
2728 this.touch(location);
2729 return snapshot;
2730 }
2731 clear() {
2732 this.snapshots = {};
2733 }
2734 read(location) {
2735 return this.snapshots[toCacheKey(location)];
2736 }
2737 write(location, snapshot) {
2738 this.snapshots[toCacheKey(location)] = snapshot;
2739 }
2740 touch(location) {
2741 const key = toCacheKey(location);
2742 const index = this.keys.indexOf(key);
2743 if (index > -1)
2744 this.keys.splice(index, 1);
2745 this.keys.unshift(key);
2746 this.trim();
2747 }
2748 trim() {
2749 for (const key of this.keys.splice(this.size)) {
2750 delete this.snapshots[key];
2751 }
2752 }
2753}
2754
2755class PageView extends View {
2756 constructor() {
2757 super(...arguments);
2758 this.snapshotCache = new SnapshotCache(10);
2759 this.lastRenderedLocation = new URL(location.href);
2760 this.forceReloaded = false;
2761 }
2762 renderPage(snapshot, isPreview = false, willRender = true, visit) {
2763 const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
2764 if (!renderer.shouldRender) {
2765 this.forceReloaded = true;
2766 }
2767 else {
2768 visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2769 }
2770 return this.render(renderer);
2771 }
2772 renderError(snapshot, visit) {
2773 visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2774 const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
2775 return this.render(renderer);
2776 }
2777 clearSnapshotCache() {
2778 this.snapshotCache.clear();
2779 }
2780 async cacheSnapshot(snapshot = this.snapshot) {
2781 if (snapshot.isCacheable) {
2782 this.delegate.viewWillCacheSnapshot();
2783 const { lastRenderedLocation: location } = this;
2784 await nextEventLoopTick();
2785 const cachedSnapshot = snapshot.clone();
2786 this.snapshotCache.put(location, cachedSnapshot);
2787 return cachedSnapshot;
2788 }
2789 }
2790 getCachedSnapshotForLocation(location) {
2791 return this.snapshotCache.get(location);
2792 }
2793 get snapshot() {
2794 return PageSnapshot.fromElement(this.element);
2795 }
2796}
2797
2798class Preloader {
2799 constructor(delegate) {
2800 this.selector = "a[data-turbo-preload]";
2801 this.delegate = delegate;
2802 }
2803 get snapshotCache() {
2804 return this.delegate.navigator.view.snapshotCache;
2805 }
2806 start() {
2807 if (document.readyState === "loading") {
2808 return document.addEventListener("DOMContentLoaded", () => {
2809 this.preloadOnLoadLinksForView(document.body);
2810 });
2811 }
2812 else {
2813 this.preloadOnLoadLinksForView(document.body);
2814 }
2815 }
2816 preloadOnLoadLinksForView(element) {
2817 for (const link of element.querySelectorAll(this.selector)) {
2818 this.preloadURL(link);
2819 }
2820 }
2821 async preloadURL(link) {
2822 const location = new URL(link.href);
2823 if (this.snapshotCache.has(location)) {
2824 return;
2825 }
2826 try {
2827 const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
2828 const responseText = await response.text();
2829 const snapshot = PageSnapshot.fromHTMLString(responseText);
2830 this.snapshotCache.put(location, snapshot);
2831 }
2832 catch (_) {
2833 }
2834 }
2835}
2836
2837class Session {
2838 constructor() {
2839 this.navigator = new Navigator(this);
2840 this.history = new History(this);
2841 this.preloader = new Preloader(this);
2842 this.view = new PageView(this, document.documentElement);
2843 this.adapter = new BrowserAdapter(this);
2844 this.pageObserver = new PageObserver(this);
2845 this.cacheObserver = new CacheObserver();
2846 this.linkClickObserver = new LinkClickObserver(this, window);
2847 this.formSubmitObserver = new FormSubmitObserver(this, document);
2848 this.scrollObserver = new ScrollObserver(this);
2849 this.streamObserver = new StreamObserver(this);
2850 this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
2851 this.frameRedirector = new FrameRedirector(this, document.documentElement);
2852 this.streamMessageRenderer = new StreamMessageRenderer();
2853 this.drive = true;
2854 this.enabled = true;
2855 this.progressBarDelay = 500;
2856 this.started = false;
2857 this.formMode = "on";
2858 }
2859 start() {
2860 if (!this.started) {
2861 this.pageObserver.start();
2862 this.cacheObserver.start();
2863 this.formLinkClickObserver.start();
2864 this.linkClickObserver.start();
2865 this.formSubmitObserver.start();
2866 this.scrollObserver.start();
2867 this.streamObserver.start();
2868 this.frameRedirector.start();
2869 this.history.start();
2870 this.preloader.start();
2871 this.started = true;
2872 this.enabled = true;
2873 }
2874 }
2875 disable() {
2876 this.enabled = false;
2877 }
2878 stop() {
2879 if (this.started) {
2880 this.pageObserver.stop();
2881 this.cacheObserver.stop();
2882 this.formLinkClickObserver.stop();
2883 this.linkClickObserver.stop();
2884 this.formSubmitObserver.stop();
2885 this.scrollObserver.stop();
2886 this.streamObserver.stop();
2887 this.frameRedirector.stop();
2888 this.history.stop();
2889 this.started = false;
2890 }
2891 }
2892 registerAdapter(adapter) {
2893 this.adapter = adapter;
2894 }
2895 visit(location, options = {}) {
2896 const frameElement = options.frame ? document.getElementById(options.frame) : null;
2897 if (frameElement instanceof FrameElement) {
2898 frameElement.src = location.toString();
2899 frameElement.loaded;
2900 }
2901 else {
2902 this.navigator.proposeVisit(expandURL(location), options);
2903 }
2904 }
2905 connectStreamSource(source) {
2906 this.streamObserver.connectStreamSource(source);
2907 }
2908 disconnectStreamSource(source) {
2909 this.streamObserver.disconnectStreamSource(source);
2910 }
2911 renderStreamMessage(message) {
2912 this.streamMessageRenderer.render(StreamMessage.wrap(message));
2913 }
2914 clearCache() {
2915 this.view.clearSnapshotCache();
2916 }
2917 setProgressBarDelay(delay) {
2918 this.progressBarDelay = delay;
2919 }
2920 setFormMode(mode) {
2921 this.formMode = mode;
2922 }
2923 get location() {
2924 return this.history.location;
2925 }
2926 get restorationIdentifier() {
2927 return this.history.restorationIdentifier;
2928 }
2929 historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
2930 if (this.enabled) {
2931 this.navigator.startVisit(location, restorationIdentifier, {
2932 action: "restore",
2933 historyChanged: true,
2934 });
2935 }
2936 else {
2937 this.adapter.pageInvalidated({
2938 reason: "turbo_disabled",
2939 });
2940 }
2941 }
2942 scrollPositionChanged(position) {
2943 this.history.updateRestorationData({ scrollPosition: position });
2944 }
2945 willSubmitFormLinkToLocation(link, location) {
2946 return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
2947 }
2948 submittedFormLinkToLocation() { }
2949 willFollowLinkToLocation(link, location, event) {
2950 return (this.elementIsNavigatable(link) &&
2951 locationIsVisitable(location, this.snapshot.rootLocation) &&
2952 this.applicationAllowsFollowingLinkToLocation(link, location, event));
2953 }
2954 followedLinkToLocation(link, location) {
2955 const action = this.getActionForLink(link);
2956 const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
2957 this.visit(location.href, { action, acceptsStreamResponse });
2958 }
2959 allowsVisitingLocationWithAction(location, action) {
2960 return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
2961 }
2962 visitProposedToLocation(location, options) {
2963 extendURLWithDeprecatedProperties(location);
2964 this.adapter.visitProposedToLocation(location, options);
2965 }
2966 visitStarted(visit) {
2967 if (!visit.acceptsStreamResponse) {
2968 markAsBusy(document.documentElement);
2969 }
2970 extendURLWithDeprecatedProperties(visit.location);
2971 if (!visit.silent) {
2972 this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
2973 }
2974 }
2975 visitCompleted(visit) {
2976 clearBusyState(document.documentElement);
2977 this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
2978 }
2979 locationWithActionIsSamePage(location, action) {
2980 return this.navigator.locationWithActionIsSamePage(location, action);
2981 }
2982 visitScrolledToSamePageLocation(oldURL, newURL) {
2983 this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
2984 }
2985 willSubmitForm(form, submitter) {
2986 const action = getAction(form, submitter);
2987 return (this.submissionIsNavigatable(form, submitter) &&
2988 locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
2989 }
2990 formSubmitted(form, submitter) {
2991 this.navigator.submitForm(form, submitter);
2992 }
2993 pageBecameInteractive() {
2994 this.view.lastRenderedLocation = this.location;
2995 this.notifyApplicationAfterPageLoad();
2996 }
2997 pageLoaded() {
2998 this.history.assumeControlOfScrollRestoration();
2999 }
3000 pageWillUnload() {
3001 this.history.relinquishControlOfScrollRestoration();
3002 }
3003 receivedMessageFromStream(message) {
3004 this.renderStreamMessage(message);
3005 }
3006 viewWillCacheSnapshot() {
3007 var _a;
3008 if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
3009 this.notifyApplicationBeforeCachingSnapshot();
3010 }
3011 }
3012 allowsImmediateRender({ element }, options) {
3013 const event = this.notifyApplicationBeforeRender(element, options);
3014 const { defaultPrevented, detail: { render }, } = event;
3015 if (this.view.renderer && render) {
3016 this.view.renderer.renderElement = render;
3017 }
3018 return !defaultPrevented;
3019 }
3020 viewRenderedSnapshot(_snapshot, _isPreview) {
3021 this.view.lastRenderedLocation = this.history.location;
3022 this.notifyApplicationAfterRender();
3023 }
3024 preloadOnLoadLinksForView(element) {
3025 this.preloader.preloadOnLoadLinksForView(element);
3026 }
3027 viewInvalidated(reason) {
3028 this.adapter.pageInvalidated(reason);
3029 }
3030 frameLoaded(frame) {
3031 this.notifyApplicationAfterFrameLoad(frame);
3032 }
3033 frameRendered(fetchResponse, frame) {
3034 this.notifyApplicationAfterFrameRender(fetchResponse, frame);
3035 }
3036 applicationAllowsFollowingLinkToLocation(link, location, ev) {
3037 const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
3038 return !event.defaultPrevented;
3039 }
3040 applicationAllowsVisitingLocation(location) {
3041 const event = this.notifyApplicationBeforeVisitingLocation(location);
3042 return !event.defaultPrevented;
3043 }
3044 notifyApplicationAfterClickingLinkToLocation(link, location, event) {
3045 return dispatch("turbo:click", {
3046 target: link,
3047 detail: { url: location.href, originalEvent: event },
3048 cancelable: true,
3049 });
3050 }
3051 notifyApplicationBeforeVisitingLocation(location) {
3052 return dispatch("turbo:before-visit", {
3053 detail: { url: location.href },
3054 cancelable: true,
3055 });
3056 }
3057 notifyApplicationAfterVisitingLocation(location, action) {
3058 return dispatch("turbo:visit", { detail: { url: location.href, action } });
3059 }
3060 notifyApplicationBeforeCachingSnapshot() {
3061 return dispatch("turbo:before-cache");
3062 }
3063 notifyApplicationBeforeRender(newBody, options) {
3064 return dispatch("turbo:before-render", {
3065 detail: Object.assign({ newBody }, options),
3066 cancelable: true,
3067 });
3068 }
3069 notifyApplicationAfterRender() {
3070 return dispatch("turbo:render");
3071 }
3072 notifyApplicationAfterPageLoad(timing = {}) {
3073 return dispatch("turbo:load", {
3074 detail: { url: this.location.href, timing },
3075 });
3076 }
3077 notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
3078 dispatchEvent(new HashChangeEvent("hashchange", {
3079 oldURL: oldURL.toString(),
3080 newURL: newURL.toString(),
3081 }));
3082 }
3083 notifyApplicationAfterFrameLoad(frame) {
3084 return dispatch("turbo:frame-load", { target: frame });
3085 }
3086 notifyApplicationAfterFrameRender(fetchResponse, frame) {
3087 return dispatch("turbo:frame-render", {
3088 detail: { fetchResponse },
3089 target: frame,
3090 cancelable: true,
3091 });
3092 }
3093 submissionIsNavigatable(form, submitter) {
3094 if (this.formMode == "off") {
3095 return false;
3096 }
3097 else {
3098 const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
3099 if (this.formMode == "optin") {
3100 return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
3101 }
3102 else {
3103 return submitterIsNavigatable && this.elementIsNavigatable(form);
3104 }
3105 }
3106 }
3107 elementIsNavigatable(element) {
3108 const container = element.closest("[data-turbo]");
3109 const withinFrame = element.closest("turbo-frame");
3110 if (this.drive || withinFrame) {
3111 if (container) {
3112 return container.getAttribute("data-turbo") != "false";
3113 }
3114 else {
3115 return true;
3116 }
3117 }
3118 else {
3119 if (container) {
3120 return container.getAttribute("data-turbo") == "true";
3121 }
3122 else {
3123 return false;
3124 }
3125 }
3126 }
3127 getActionForLink(link) {
3128 const action = link.getAttribute("data-turbo-action");
3129 return isAction(action) ? action : "advance";
3130 }
3131 get snapshot() {
3132 return this.view.snapshot;
3133 }
3134}
3135function extendURLWithDeprecatedProperties(url) {
3136 Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
3137}
3138const deprecatedLocationPropertyDescriptors = {
3139 absoluteURL: {
3140 get() {
3141 return this.toString();
3142 },
3143 },
3144};
3145
3146class Cache {
3147 constructor(session) {
3148 this.session = session;
3149 }
3150 clear() {
3151 this.session.clearCache();
3152 }
3153 resetCacheControl() {
3154 this.setCacheControl("");
3155 }
3156 exemptPageFromCache() {
3157 this.setCacheControl("no-cache");
3158 }
3159 exemptPageFromPreview() {
3160 this.setCacheControl("no-preview");
3161 }
3162 setCacheControl(value) {
3163 setMetaContent("turbo-cache-control", value);
3164 }
3165}
3166
3167const StreamActions = {
3168 after() {
3169 this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3170 },
3171 append() {
3172 this.removeDuplicateTargetChildren();
3173 this.targetElements.forEach((e) => e.append(this.templateContent));
3174 },
3175 before() {
3176 this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3177 },
3178 prepend() {
3179 this.removeDuplicateTargetChildren();
3180 this.targetElements.forEach((e) => e.prepend(this.templateContent));
3181 },
3182 remove() {
3183 this.targetElements.forEach((e) => e.remove());
3184 },
3185 replace() {
3186 this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
3187 },
3188 update() {
3189 this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
3190 },
3191};
3192
3193const session = new Session();
3194const cache = new Cache(session);
3195const { navigator: navigator$1 } = session;
3196function start() {
3197 session.start();
3198}
3199function registerAdapter(adapter) {
3200 session.registerAdapter(adapter);
3201}
3202function visit(location, options) {
3203 session.visit(location, options);
3204}
3205function connectStreamSource(source) {
3206 session.connectStreamSource(source);
3207}
3208function disconnectStreamSource(source) {
3209 session.disconnectStreamSource(source);
3210}
3211function renderStreamMessage(message) {
3212 session.renderStreamMessage(message);
3213}
3214function clearCache() {
3215 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.`");
3216 session.clearCache();
3217}
3218function setProgressBarDelay(delay) {
3219 session.setProgressBarDelay(delay);
3220}
3221function setConfirmMethod(confirmMethod) {
3222 FormSubmission.confirmMethod = confirmMethod;
3223}
3224function setFormMode(mode) {
3225 session.setFormMode(mode);
3226}
3227
3228var Turbo = /*#__PURE__*/Object.freeze({
3229 __proto__: null,
3230 navigator: navigator$1,
3231 session: session,
3232 cache: cache,
3233 PageRenderer: PageRenderer,
3234 PageSnapshot: PageSnapshot,
3235 FrameRenderer: FrameRenderer,
3236 start: start,
3237 registerAdapter: registerAdapter,
3238 visit: visit,
3239 connectStreamSource: connectStreamSource,
3240 disconnectStreamSource: disconnectStreamSource,
3241 renderStreamMessage: renderStreamMessage,
3242 clearCache: clearCache,
3243 setProgressBarDelay: setProgressBarDelay,
3244 setConfirmMethod: setConfirmMethod,
3245 setFormMode: setFormMode,
3246 StreamActions: StreamActions
3247});
3248
3249class FrameController {
3250 constructor(element) {
3251 this.fetchResponseLoaded = (_fetchResponse) => { };
3252 this.currentFetchRequest = null;
3253 this.resolveVisitPromise = () => { };
3254 this.connected = false;
3255 this.hasBeenLoaded = false;
3256 this.ignoredAttributes = new Set();
3257 this.action = null;
3258 this.visitCachedSnapshot = ({ element }) => {
3259 const frame = element.querySelector("#" + this.element.id);
3260 if (frame && this.previousFrameElement) {
3261 frame.replaceChildren(...this.previousFrameElement.children);
3262 }
3263 delete this.previousFrameElement;
3264 };
3265 this.element = element;
3266 this.view = new FrameView(this, this.element);
3267 this.appearanceObserver = new AppearanceObserver(this, this.element);
3268 this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
3269 this.linkInterceptor = new LinkInterceptor(this, this.element);
3270 this.restorationIdentifier = uuid();
3271 this.formSubmitObserver = new FormSubmitObserver(this, this.element);
3272 }
3273 connect() {
3274 if (!this.connected) {
3275 this.connected = true;
3276 if (this.loadingStyle == FrameLoadingStyle.lazy) {
3277 this.appearanceObserver.start();
3278 }
3279 else {
3280 this.loadSourceURL();
3281 }
3282 this.formLinkClickObserver.start();
3283 this.linkInterceptor.start();
3284 this.formSubmitObserver.start();
3285 }
3286 }
3287 disconnect() {
3288 if (this.connected) {
3289 this.connected = false;
3290 this.appearanceObserver.stop();
3291 this.formLinkClickObserver.stop();
3292 this.linkInterceptor.stop();
3293 this.formSubmitObserver.stop();
3294 }
3295 }
3296 disabledChanged() {
3297 if (this.loadingStyle == FrameLoadingStyle.eager) {
3298 this.loadSourceURL();
3299 }
3300 }
3301 sourceURLChanged() {
3302 if (this.isIgnoringChangesTo("src"))
3303 return;
3304 if (this.element.isConnected) {
3305 this.complete = false;
3306 }
3307 if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
3308 this.loadSourceURL();
3309 }
3310 }
3311 sourceURLReloaded() {
3312 const { src } = this.element;
3313 this.ignoringChangesToAttribute("complete", () => {
3314 this.element.removeAttribute("complete");
3315 });
3316 this.element.src = null;
3317 this.element.src = src;
3318 return this.element.loaded;
3319 }
3320 completeChanged() {
3321 if (this.isIgnoringChangesTo("complete"))
3322 return;
3323 this.loadSourceURL();
3324 }
3325 loadingStyleChanged() {
3326 if (this.loadingStyle == FrameLoadingStyle.lazy) {
3327 this.appearanceObserver.start();
3328 }
3329 else {
3330 this.appearanceObserver.stop();
3331 this.loadSourceURL();
3332 }
3333 }
3334 async loadSourceURL() {
3335 if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3336 this.element.loaded = this.visit(expandURL(this.sourceURL));
3337 this.appearanceObserver.stop();
3338 await this.element.loaded;
3339 this.hasBeenLoaded = true;
3340 }
3341 }
3342 async loadResponse(fetchResponse) {
3343 if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
3344 this.sourceURL = fetchResponse.response.url;
3345 }
3346 try {
3347 const html = await fetchResponse.responseHTML;
3348 if (html) {
3349 const { body } = parseHTMLDocument(html);
3350 const newFrameElement = await this.extractForeignFrameElement(body);
3351 if (newFrameElement) {
3352 const snapshot = new Snapshot(newFrameElement);
3353 const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
3354 if (this.view.renderPromise)
3355 await this.view.renderPromise;
3356 this.changeHistory();
3357 await this.view.render(renderer);
3358 this.complete = true;
3359 session.frameRendered(fetchResponse, this.element);
3360 session.frameLoaded(this.element);
3361 this.fetchResponseLoaded(fetchResponse);
3362 }
3363 else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
3364 console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
3365 this.visitResponse(fetchResponse.response);
3366 }
3367 }
3368 }
3369 catch (error) {
3370 console.error(error);
3371 this.view.invalidate();
3372 }
3373 finally {
3374 this.fetchResponseLoaded = () => { };
3375 }
3376 }
3377 elementAppearedInViewport(_element) {
3378 this.loadSourceURL();
3379 }
3380 willSubmitFormLinkToLocation(link) {
3381 return this.shouldInterceptNavigation(link);
3382 }
3383 submittedFormLinkToLocation(link, _location, form) {
3384 const frame = this.findFrameElement(link);
3385 if (frame)
3386 form.setAttribute("data-turbo-frame", frame.id);
3387 }
3388 shouldInterceptLinkClick(element, _location, _event) {
3389 return this.shouldInterceptNavigation(element);
3390 }
3391 linkClickIntercepted(element, location) {
3392 this.navigateFrame(element, location);
3393 }
3394 willSubmitForm(element, submitter) {
3395 return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
3396 }
3397 formSubmitted(element, submitter) {
3398 if (this.formSubmission) {
3399 this.formSubmission.stop();
3400 }
3401 this.formSubmission = new FormSubmission(this, element, submitter);
3402 const { fetchRequest } = this.formSubmission;
3403 this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
3404 this.formSubmission.start();
3405 }
3406 prepareHeadersForRequest(headers, request) {
3407 var _a;
3408 headers["Turbo-Frame"] = this.id;
3409 if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
3410 request.acceptResponseType(StreamMessage.contentType);
3411 }
3412 }
3413 requestStarted(_request) {
3414 markAsBusy(this.element);
3415 }
3416 requestPreventedHandlingResponse(_request, _response) {
3417 this.resolveVisitPromise();
3418 }
3419 async requestSucceededWithResponse(request, response) {
3420 await this.loadResponse(response);
3421 this.resolveVisitPromise();
3422 }
3423 async requestFailedWithResponse(request, response) {
3424 console.error(response);
3425 await this.loadResponse(response);
3426 this.resolveVisitPromise();
3427 }
3428 requestErrored(request, error) {
3429 console.error(error);
3430 this.resolveVisitPromise();
3431 }
3432 requestFinished(_request) {
3433 clearBusyState(this.element);
3434 }
3435 formSubmissionStarted({ formElement }) {
3436 markAsBusy(formElement, this.findFrameElement(formElement));
3437 }
3438 formSubmissionSucceededWithResponse(formSubmission, response) {
3439 const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
3440 frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
3441 frame.delegate.loadResponse(response);
3442 }
3443 formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
3444 this.element.delegate.loadResponse(fetchResponse);
3445 }
3446 formSubmissionErrored(formSubmission, error) {
3447 console.error(error);
3448 }
3449 formSubmissionFinished({ formElement }) {
3450 clearBusyState(formElement, this.findFrameElement(formElement));
3451 }
3452 allowsImmediateRender({ element: newFrame }, options) {
3453 const event = dispatch("turbo:before-frame-render", {
3454 target: this.element,
3455 detail: Object.assign({ newFrame }, options),
3456 cancelable: true,
3457 });
3458 const { defaultPrevented, detail: { render }, } = event;
3459 if (this.view.renderer && render) {
3460 this.view.renderer.renderElement = render;
3461 }
3462 return !defaultPrevented;
3463 }
3464 viewRenderedSnapshot(_snapshot, _isPreview) { }
3465 preloadOnLoadLinksForView(element) {
3466 session.preloadOnLoadLinksForView(element);
3467 }
3468 viewInvalidated() { }
3469 willRenderFrame(currentElement, _newElement) {
3470 this.previousFrameElement = currentElement.cloneNode(true);
3471 }
3472 async visit(url) {
3473 var _a;
3474 const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
3475 (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3476 this.currentFetchRequest = request;
3477 return new Promise((resolve) => {
3478 this.resolveVisitPromise = () => {
3479 this.resolveVisitPromise = () => { };
3480 this.currentFetchRequest = null;
3481 resolve();
3482 };
3483 request.perform();
3484 });
3485 }
3486 navigateFrame(element, url, submitter) {
3487 const frame = this.findFrameElement(element, submitter);
3488 this.pageSnapshot = PageSnapshot.fromElement(frame).clone();
3489 frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3490 this.withCurrentNavigationElement(element, () => {
3491 frame.src = url;
3492 });
3493 }
3494 proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3495 this.action = getVisitAction(submitter, element, frame);
3496 if (isAction(this.action)) {
3497 const { visitCachedSnapshot } = frame.delegate;
3498 frame.delegate.fetchResponseLoaded = (fetchResponse) => {
3499 if (frame.src) {
3500 const { statusCode, redirected } = fetchResponse;
3501 const responseHTML = frame.ownerDocument.documentElement.outerHTML;
3502 const response = { statusCode, redirected, responseHTML };
3503 const options = {
3504 response,
3505 visitCachedSnapshot,
3506 willRender: false,
3507 updateHistory: false,
3508 restorationIdentifier: this.restorationIdentifier,
3509 snapshot: this.pageSnapshot,
3510 };
3511 if (this.action)
3512 options.action = this.action;
3513 session.visit(frame.src, options);
3514 }
3515 };
3516 }
3517 }
3518 changeHistory() {
3519 if (this.action) {
3520 const method = getHistoryMethodForAction(this.action);
3521 session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
3522 }
3523 }
3524 willHandleFrameMissingFromResponse(fetchResponse) {
3525 this.element.setAttribute("complete", "");
3526 const response = fetchResponse.response;
3527 const visit = async (url, options = {}) => {
3528 if (url instanceof Response) {
3529 this.visitResponse(url);
3530 }
3531 else {
3532 session.visit(url, options);
3533 }
3534 };
3535 const event = dispatch("turbo:frame-missing", {
3536 target: this.element,
3537 detail: { response, visit },
3538 cancelable: true,
3539 });
3540 return !event.defaultPrevented;
3541 }
3542 async visitResponse(response) {
3543 const wrapped = new FetchResponse(response);
3544 const responseHTML = await wrapped.responseHTML;
3545 const { location, redirected, statusCode } = wrapped;
3546 return session.visit(location, { response: { redirected, statusCode, responseHTML } });
3547 }
3548 findFrameElement(element, submitter) {
3549 var _a;
3550 const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
3551 return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
3552 }
3553 async extractForeignFrameElement(container) {
3554 let element;
3555 const id = CSS.escape(this.id);
3556 try {
3557 element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
3558 if (element) {
3559 return element;
3560 }
3561 element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
3562 if (element) {
3563 await element.loaded;
3564 return await this.extractForeignFrameElement(element);
3565 }
3566 }
3567 catch (error) {
3568 console.error(error);
3569 return new FrameElement();
3570 }
3571 return null;
3572 }
3573 formActionIsVisitable(form, submitter) {
3574 const action = getAction(form, submitter);
3575 return locationIsVisitable(expandURL(action), this.rootLocation);
3576 }
3577 shouldInterceptNavigation(element, submitter) {
3578 const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
3579 if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
3580 return false;
3581 }
3582 if (!this.enabled || id == "_top") {
3583 return false;
3584 }
3585 if (id) {
3586 const frameElement = getFrameElementById(id);
3587 if (frameElement) {
3588 return !frameElement.disabled;
3589 }
3590 }
3591 if (!session.elementIsNavigatable(element)) {
3592 return false;
3593 }
3594 if (submitter && !session.elementIsNavigatable(submitter)) {
3595 return false;
3596 }
3597 return true;
3598 }
3599 get id() {
3600 return this.element.id;
3601 }
3602 get enabled() {
3603 return !this.element.disabled;
3604 }
3605 get sourceURL() {
3606 if (this.element.src) {
3607 return this.element.src;
3608 }
3609 }
3610 set sourceURL(sourceURL) {
3611 this.ignoringChangesToAttribute("src", () => {
3612 this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3613 });
3614 }
3615 get loadingStyle() {
3616 return this.element.loading;
3617 }
3618 get isLoading() {
3619 return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
3620 }
3621 get complete() {
3622 return this.element.hasAttribute("complete");
3623 }
3624 set complete(value) {
3625 this.ignoringChangesToAttribute("complete", () => {
3626 if (value) {
3627 this.element.setAttribute("complete", "");
3628 }
3629 else {
3630 this.element.removeAttribute("complete");
3631 }
3632 });
3633 }
3634 get isActive() {
3635 return this.element.isActive && this.connected;
3636 }
3637 get rootLocation() {
3638 var _a;
3639 const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
3640 const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
3641 return expandURL(root);
3642 }
3643 isIgnoringChangesTo(attributeName) {
3644 return this.ignoredAttributes.has(attributeName);
3645 }
3646 ignoringChangesToAttribute(attributeName, callback) {
3647 this.ignoredAttributes.add(attributeName);
3648 callback();
3649 this.ignoredAttributes.delete(attributeName);
3650 }
3651 withCurrentNavigationElement(element, callback) {
3652 this.currentNavigationElement = element;
3653 callback();
3654 delete this.currentNavigationElement;
3655 }
3656}
3657function getFrameElementById(id) {
3658 if (id != null) {
3659 const element = document.getElementById(id);
3660 if (element instanceof FrameElement) {
3661 return element;
3662 }
3663 }
3664}
3665function activateElement(element, currentURL) {
3666 if (element) {
3667 const src = element.getAttribute("src");
3668 if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
3669 throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);
3670 }
3671 if (element.ownerDocument !== document) {
3672 element = document.importNode(element, true);
3673 }
3674 if (element instanceof FrameElement) {
3675 element.connectedCallback();
3676 element.disconnectedCallback();
3677 return element;
3678 }
3679 }
3680}
3681
3682class StreamElement extends HTMLElement {
3683 static async renderElement(newElement) {
3684 await newElement.performAction();
3685 }
3686 async connectedCallback() {
3687 try {
3688 await this.render();
3689 }
3690 catch (error) {
3691 console.error(error);
3692 }
3693 finally {
3694 this.disconnect();
3695 }
3696 }
3697 async render() {
3698 var _a;
3699 return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3700 const event = this.beforeRenderEvent;
3701 if (this.dispatchEvent(event)) {
3702 await nextAnimationFrame();
3703 await event.detail.render(this);
3704 }
3705 })()));
3706 }
3707 disconnect() {
3708 try {
3709 this.remove();
3710 }
3711 catch (_a) { }
3712 }
3713 removeDuplicateTargetChildren() {
3714 this.duplicateChildren.forEach((c) => c.remove());
3715 }
3716 get duplicateChildren() {
3717 var _a;
3718 const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
3719 const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
3720 return existingChildren.filter((c) => newChildrenIds.includes(c.id));
3721 }
3722 get performAction() {
3723 if (this.action) {
3724 const actionFunction = StreamActions[this.action];
3725 if (actionFunction) {
3726 return actionFunction;
3727 }
3728 this.raise("unknown action");
3729 }
3730 this.raise("action attribute is missing");
3731 }
3732 get targetElements() {
3733 if (this.target) {
3734 return this.targetElementsById;
3735 }
3736 else if (this.targets) {
3737 return this.targetElementsByQuery;
3738 }
3739 else {
3740 this.raise("target or targets attribute is missing");
3741 }
3742 }
3743 get templateContent() {
3744 return this.templateElement.content.cloneNode(true);
3745 }
3746 get templateElement() {
3747 if (this.firstElementChild === null) {
3748 const template = this.ownerDocument.createElement("template");
3749 this.appendChild(template);
3750 return template;
3751 }
3752 else if (this.firstElementChild instanceof HTMLTemplateElement) {
3753 return this.firstElementChild;
3754 }
3755 this.raise("first child element must be a <template> element");
3756 }
3757 get action() {
3758 return this.getAttribute("action");
3759 }
3760 get target() {
3761 return this.getAttribute("target");
3762 }
3763 get targets() {
3764 return this.getAttribute("targets");
3765 }
3766 raise(message) {
3767 throw new Error(`${this.description}: ${message}`);
3768 }
3769 get description() {
3770 var _a, _b;
3771 return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
3772 }
3773 get beforeRenderEvent() {
3774 return new CustomEvent("turbo:before-stream-render", {
3775 bubbles: true,
3776 cancelable: true,
3777 detail: { newStream: this, render: StreamElement.renderElement },
3778 });
3779 }
3780 get targetElementsById() {
3781 var _a;
3782 const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
3783 if (element !== null) {
3784 return [element];
3785 }
3786 else {
3787 return [];
3788 }
3789 }
3790 get targetElementsByQuery() {
3791 var _a;
3792 const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
3793 if (elements.length !== 0) {
3794 return Array.prototype.slice.call(elements);
3795 }
3796 else {
3797 return [];
3798 }
3799 }
3800}
3801
3802class StreamSourceElement extends HTMLElement {
3803 constructor() {
3804 super(...arguments);
3805 this.streamSource = null;
3806 }
3807 connectedCallback() {
3808 this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
3809 connectStreamSource(this.streamSource);
3810 }
3811 disconnectedCallback() {
3812 if (this.streamSource) {
3813 disconnectStreamSource(this.streamSource);
3814 }
3815 }
3816 get src() {
3817 return this.getAttribute("src") || "";
3818 }
3819}
3820
3821FrameElement.delegateConstructor = FrameController;
3822if (customElements.get("turbo-frame") === undefined) {
3823 customElements.define("turbo-frame", FrameElement);
3824}
3825if (customElements.get("turbo-stream") === undefined) {
3826 customElements.define("turbo-stream", StreamElement);
3827}
3828if (customElements.get("turbo-stream-source") === undefined) {
3829 customElements.define("turbo-stream-source", StreamSourceElement);
3830}
3831
3832(() => {
3833 let element = document.currentScript;
3834 if (!element)
3835 return;
3836 if (element.hasAttribute("data-turbo-suppress-warning"))
3837 return;
3838 element = element.parentElement;
3839 while (element) {
3840 if (element == document.body) {
3841 return console.warn(unindent `
3842 You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
3843
3844 Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
3845
3846 For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
3847
3848 ——
3849 Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
3850 `, element.outerHTML);
3851 }
3852 element = element.parentElement;
3853 }
3854})();
3855
3856window.Turbo = Turbo;
3857start();
3858
3859export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };